@@ -511,4 +511,250 @@ public void Parse_ComplexExpression_MultipleOperationsWithRounding()
511
511
// Should not equal base time
512
512
Assert . NotEqual ( _baseTime , result ) ;
513
513
}
514
+
515
+ [ Fact ]
516
+ public void Parse_WithTimeZoneInfo_Now_ReturnsCurrentTimeInSpecifiedTimezone ( )
517
+ {
518
+ var utcTimeZone = TimeZoneInfo . Utc ;
519
+ const string expression = "now" ;
520
+
521
+ _logger . LogDebug ( "Testing Parse with TimeZoneInfo for expression: '{Expression}', TimeZone: {TimeZone}" ,
522
+ expression , utcTimeZone . Id ) ;
523
+
524
+ var result = DateMath . Parse ( expression , utcTimeZone ) ;
525
+
526
+ _logger . LogDebug ( "Parse result: {Result}" , result ) ;
527
+
528
+ // Should be close to current UTC time
529
+ var utcNow = DateTimeOffset . UtcNow ;
530
+ Assert . True ( Math . Abs ( ( result - utcNow ) . TotalSeconds ) < 5 ,
531
+ $ "Result { result } should be within 5 seconds of UTC now { utcNow } ") ;
532
+ Assert . Equal ( TimeSpan . Zero , result . Offset ) ; // Should be UTC
533
+ }
534
+
535
+ [ Theory ]
536
+ [ InlineData ( "UTC" , 0 ) ]
537
+ [ InlineData ( "US/Eastern" , - 5 ) ] // EST offset (not considering DST for this test)
538
+ [ InlineData ( "US/Pacific" , - 8 ) ] // PST offset (not considering DST for this test)
539
+ public void Parse_WithTimeZoneInfo_Now_ReturnsCorrectTimezone ( string timeZoneId , int expectedOffsetHours )
540
+ {
541
+ var timeZone = TimeZoneInfo . FindSystemTimeZoneById ( timeZoneId ) ;
542
+ const string expression = "now" ;
543
+
544
+ _logger . LogDebug ( "Testing Parse with TimeZoneInfo for expression: '{Expression}', TimeZone: {TimeZone}" ,
545
+ expression , timeZone . Id ) ;
546
+
547
+ var result = DateMath . Parse ( expression , timeZone ) ;
548
+
549
+ _logger . LogDebug ( "Parse result: {Result}, Expected offset hours: {ExpectedOffsetHours}" , result , expectedOffsetHours ) ;
550
+
551
+ // Note: This test might need adjustment for DST, but it demonstrates the concept
552
+ Assert . Equal ( timeZone . GetUtcOffset ( DateTime . UtcNow ) , result . Offset ) ;
553
+ }
554
+
555
+ [ Fact ]
556
+ public void Parse_WithTimeZoneInfo_ExplicitDateWithoutTimezone_UsesSpecifiedTimezone ( )
557
+ {
558
+ var easternTimeZone = TimeZoneInfo . FindSystemTimeZoneById ( "US/Eastern" ) ;
559
+ const string expression = "2023-06-15T14:30:00||" ;
560
+
561
+ _logger . LogDebug ( "Testing Parse with TimeZoneInfo for expression: '{Expression}', TimeZone: {TimeZone}" ,
562
+ expression , easternTimeZone . Id ) ;
563
+
564
+ var result = DateMath . Parse ( expression , easternTimeZone ) ;
565
+
566
+ _logger . LogDebug ( "Parse result: {Result}" , result ) ;
567
+
568
+ Assert . Equal ( 2023 , result . Year ) ;
569
+ Assert . Equal ( 6 , result . Month ) ;
570
+ Assert . Equal ( 15 , result . Day ) ;
571
+ Assert . Equal ( 14 , result . Hour ) ;
572
+ Assert . Equal ( 30 , result . Minute ) ;
573
+ Assert . Equal ( 0 , result . Second ) ;
574
+
575
+ // Should use the timezone offset from Eastern Time
576
+ var expectedOffset = easternTimeZone . GetUtcOffset ( new DateTime ( 2023 , 6 , 15 , 14 , 30 , 0 ) ) ;
577
+ Assert . Equal ( expectedOffset , result . Offset ) ;
578
+ }
579
+
580
+ [ Fact ]
581
+ public void Parse_WithTimeZoneInfo_ExplicitDateWithTimezone_PreservesOriginalTimezone ( )
582
+ {
583
+ var pacificTimeZone = TimeZoneInfo . FindSystemTimeZoneById ( "US/Pacific" ) ;
584
+ const string expression = "2023-06-15T14:30:00+05:00||" ; // Explicit +05:00 timezone
585
+
586
+ _logger . LogDebug ( "Testing Parse with TimeZoneInfo for expression: '{Expression}', TimeZone: {TimeZone}" ,
587
+ expression , pacificTimeZone . Id ) ;
588
+
589
+ var result = DateMath . Parse ( expression , pacificTimeZone ) ;
590
+
591
+ _logger . LogDebug ( "Parse result: {Result}" , result ) ;
592
+
593
+ Assert . Equal ( 2023 , result . Year ) ;
594
+ Assert . Equal ( 6 , result . Month ) ;
595
+ Assert . Equal ( 15 , result . Day ) ;
596
+ Assert . Equal ( 14 , result . Hour ) ;
597
+ Assert . Equal ( 30 , result . Minute ) ;
598
+ Assert . Equal ( 0 , result . Second ) ;
599
+
600
+ // Should preserve the original +05:00 timezone, not use Pacific
601
+ Assert . Equal ( TimeSpan . FromHours ( 5 ) , result . Offset ) ;
602
+ }
603
+
604
+ [ Theory ]
605
+ [ InlineData ( "now+1h" , 1 ) ]
606
+ [ InlineData ( "now+6h" , 6 ) ]
607
+ [ InlineData ( "now-2h" , - 2 ) ]
608
+ [ InlineData ( "now+24h" , 24 ) ]
609
+ public void Parse_WithTimeZoneInfo_HourOperations_ReturnsCorrectResult ( string expression , int hours )
610
+ {
611
+ var utcTimeZone = TimeZoneInfo . Utc ;
612
+
613
+ _logger . LogDebug ( "Testing Parse with TimeZoneInfo for expression: '{Expression}', TimeZone: {TimeZone}, Hours: {Hours}" ,
614
+ expression , utcTimeZone . Id , hours ) ;
615
+
616
+ var result = DateMath . Parse ( expression , utcTimeZone ) ;
617
+ var utcNow = DateTimeOffset . UtcNow ;
618
+ var expected = utcNow . AddHours ( hours ) ;
619
+
620
+ _logger . LogDebug ( "Parse result: {Result}, Expected: approximately {Expected}" , result , expected ) ;
621
+
622
+ // Should be close to expected time (within 5 seconds to account for execution time)
623
+ Assert . True ( Math . Abs ( ( result - expected ) . TotalSeconds ) < 5 ,
624
+ $ "Result { result } should be within 5 seconds of expected { expected } ") ;
625
+ Assert . Equal ( TimeSpan . Zero , result . Offset ) ; // Should be UTC
626
+ }
627
+
628
+ [ Theory ]
629
+ [ InlineData ( "now/d" , false ) ]
630
+ [ InlineData ( "now/d" , true ) ]
631
+ [ InlineData ( "now/h" , false ) ]
632
+ [ InlineData ( "now/h" , true ) ]
633
+ [ InlineData ( "now/M" , false ) ]
634
+ [ InlineData ( "now/M" , true ) ]
635
+ public void Parse_WithTimeZoneInfo_RoundingOperations_ReturnsCorrectResult ( string expression , bool isUpperLimit )
636
+ {
637
+ var centralTimeZone = TimeZoneInfo . FindSystemTimeZoneById ( "US/Central" ) ;
638
+
639
+ _logger . LogDebug ( "Testing Parse with TimeZoneInfo for expression: '{Expression}', TimeZone: {TimeZone}, IsUpperLimit: {IsUpperLimit}" ,
640
+ expression , centralTimeZone . Id , isUpperLimit ) ;
641
+
642
+ var result = DateMath . Parse ( expression , centralTimeZone , isUpperLimit ) ;
643
+
644
+ _logger . LogDebug ( "Parse result: {Result}" , result ) ;
645
+
646
+ // Verify the result uses Central Time offset
647
+ var expectedOffset = centralTimeZone . GetUtcOffset ( DateTime . UtcNow ) ;
648
+ Assert . Equal ( expectedOffset , result . Offset ) ;
649
+
650
+ // Verify rounding behavior
651
+ if ( expression . EndsWith ( "/d" ) )
652
+ {
653
+ if ( isUpperLimit )
654
+ {
655
+ Assert . Equal ( 23 , result . Hour ) ;
656
+ Assert . Equal ( 59 , result . Minute ) ;
657
+ Assert . Equal ( 59 , result . Second ) ;
658
+ }
659
+ else
660
+ {
661
+ Assert . Equal ( 0 , result . Hour ) ;
662
+ Assert . Equal ( 0 , result . Minute ) ;
663
+ Assert . Equal ( 0 , result . Second ) ;
664
+ }
665
+ }
666
+ else if ( expression . EndsWith ( "/h" ) )
667
+ {
668
+ if ( isUpperLimit )
669
+ {
670
+ Assert . Equal ( 59 , result . Minute ) ;
671
+ Assert . Equal ( 59 , result . Second ) ;
672
+ }
673
+ else
674
+ {
675
+ Assert . Equal ( 0 , result . Minute ) ;
676
+ Assert . Equal ( 0 , result . Second ) ;
677
+ }
678
+ }
679
+ }
680
+
681
+ [ Fact ]
682
+ public void TryParse_WithTimeZoneInfo_ValidExpression_ReturnsTrue ( )
683
+ {
684
+ var mountainTimeZone = TimeZoneInfo . FindSystemTimeZoneById ( "US/Mountain" ) ;
685
+ const string expression = "now+2d" ;
686
+
687
+ _logger . LogDebug ( "Testing TryParse with TimeZoneInfo for expression: '{Expression}', TimeZone: {TimeZone}" ,
688
+ expression , mountainTimeZone . Id ) ;
689
+
690
+ bool success = DateMath . TryParse ( expression , mountainTimeZone , false , out DateTimeOffset result ) ;
691
+
692
+ _logger . LogDebug ( "TryParse success: {Success}, Result: {Result}" , success , result ) ;
693
+
694
+ Assert . True ( success ) ;
695
+ Assert . NotEqual ( default ( DateTimeOffset ) , result ) ;
696
+
697
+ // Should use Mountain Time offset
698
+ var expectedOffset = mountainTimeZone . GetUtcOffset ( DateTime . UtcNow ) ;
699
+ Assert . Equal ( expectedOffset , result . Offset ) ;
700
+ }
701
+
702
+ [ Fact ]
703
+ public void TryParse_WithTimeZoneInfo_InvalidExpression_ReturnsFalse ( )
704
+ {
705
+ var utcTimeZone = TimeZoneInfo . Utc ;
706
+ const string expression = "invalid_expression" ;
707
+
708
+ _logger . LogDebug ( "Testing TryParse with TimeZoneInfo for invalid expression: '{Expression}', TimeZone: {TimeZone}" ,
709
+ expression , utcTimeZone . Id ) ;
710
+
711
+ bool success = DateMath . TryParse ( expression , utcTimeZone , false , out DateTimeOffset result ) ;
712
+
713
+ _logger . LogDebug ( "TryParse success: {Success}, Result: {Result}" , success , result ) ;
714
+
715
+ Assert . False ( success ) ;
716
+ Assert . Equal ( default ( DateTimeOffset ) , result ) ;
717
+ }
718
+
719
+ [ Fact ]
720
+ public void Parse_WithTimeZoneInfo_ComplexExpression_WorksCorrectly ( )
721
+ {
722
+ var utcTimeZone = TimeZoneInfo . Utc ;
723
+ const string expression = "now+1M-2d+3h/h" ;
724
+
725
+ _logger . LogDebug ( "Testing Parse with TimeZoneInfo for complex expression: '{Expression}', TimeZone: {TimeZone}" ,
726
+ expression , utcTimeZone . Id ) ;
727
+
728
+ var result = DateMath . Parse ( expression , utcTimeZone , false ) ;
729
+
730
+ _logger . LogDebug ( "Parse result: {Result}" , result ) ;
731
+
732
+ // Should be UTC
733
+ Assert . Equal ( TimeSpan . Zero , result . Offset ) ;
734
+
735
+ // Should be rounded to start of hour
736
+ Assert . Equal ( 0 , result . Minute ) ;
737
+ Assert . Equal ( 0 , result . Second ) ;
738
+ Assert . Equal ( 0 , result . Millisecond ) ;
739
+ }
740
+
741
+ [ Fact ]
742
+ public void Parse_WithTimeZoneInfo_NullTimeZone_ThrowsArgumentNullException ( )
743
+ {
744
+ const string expression = "now" ;
745
+
746
+ _logger . LogDebug ( "Testing Parse with null TimeZoneInfo for expression: '{Expression}'" , expression ) ;
747
+
748
+ Assert . Throws < ArgumentNullException > ( ( ) => DateMath . Parse ( expression , ( TimeZoneInfo ) null ! ) ) ;
749
+ }
750
+
751
+ [ Fact ]
752
+ public void TryParse_WithTimeZoneInfo_NullTimeZone_ThrowsArgumentNullException ( )
753
+ {
754
+ const string expression = "now" ;
755
+
756
+ _logger . LogDebug ( "Testing TryParse with null TimeZoneInfo for expression: '{Expression}'" , expression ) ;
757
+
758
+ Assert . Throws < ArgumentNullException > ( ( ) => DateMath . TryParse ( expression , ( TimeZoneInfo ) null ! , false , out _ ) ) ;
759
+ }
514
760
}
0 commit comments