@@ -436,5 +436,84 @@ fn test_case_insensitive_dow() {
436436 ) ;
437437}
438438
439+ // Test for invalid range in step with range format (e.g., "1-2-3/5")
440+ #[ test]
441+ fn test_invalid_range_in_step ( ) {
442+ // Multiple dashes in range with step
443+ assert ! ( parse_field( "1-2-3/5" , 0 , 59 ) . is_err( ) ) ;
444+ assert ! ( parse_field( "10-20-30/2" , 0 , 59 ) . is_err( ) ) ;
445+ }
446+
447+ // Test for invalid range where start > end in step format
448+ #[ test]
449+ fn test_reverse_range_with_step ( ) {
450+ // Reverse range with step should error
451+ assert ! ( parse_field( "20-10/2" , 0 , 59 ) . is_err( ) ) ;
452+ assert ! ( parse_field( "50-40/5" , 0 , 59 ) . is_err( ) ) ;
453+ }
454+
455+ // Test cron expression that will never match within 4 years
456+ #[ test]
457+ fn test_cron_never_matches ( ) {
458+ // Invalid combination: February 30 will never occur
459+ let now = Utc . timestamp_opt ( 1_573_151_292 , 0 ) . unwrap ( ) ;
460+ assert ! ( parse( "0 0 30 2 *" , & now) . is_err( ) ) ;
461+ }
462+
463+ // Test with DST transition (spring forward) - skipped hour
464+ #[ test]
465+ fn test_dst_spring_forward_skipped_time ( ) {
466+ // 2024-03-10 02:00 AM PST does not exist (spring forward to 3:00 AM PDT)
467+ // Use Pacific timezone which has DST
468+ let before_dst = Pacific
469+ . with_ymd_and_hms ( 2024 , 3 , 10 , 1 , 30 , 0 )
470+ . unwrap ( ) ;
471+
472+ // Schedule at 2:30 AM which doesn't exist due to DST
473+ // The parser should skip to the next valid time
474+ let result = parse ( "30 2 * * *" , & before_dst) ;
475+ assert ! ( result. is_ok( ) ) ;
476+
477+ // The result should be after the DST transition
478+ let next = result. unwrap ( ) ;
479+ // Should skip the non-existent 2:30 AM and go to the next day
480+ assert ! ( next. day( ) >= 10 ) ;
481+ }
482+
483+ // Test with fall back DST - ambiguous time
484+ #[ test]
485+ fn test_dst_fall_back_ambiguous_time ( ) {
486+ // November 3, 2024: 1:00 AM happens twice (fall back)
487+ // Create a time during the ambiguous period
488+ match Pacific . with_ymd_and_hms ( 2024 , 11 , 3 , 1 , 30 , 0 ) {
489+ chrono:: LocalResult :: Ambiguous ( earlier, _later) => {
490+ // Use the earlier (PDT) time
491+ let result = parse ( "*/15 * * * *" , & earlier) ;
492+ assert ! ( result. is_ok( ) ) ;
493+ }
494+ chrono:: LocalResult :: Single ( dt) => {
495+ // If single, just test it works
496+ let result = parse ( "*/15 * * * *" , & dt) ;
497+ assert ! ( result. is_ok( ) ) ;
498+ }
499+ chrono:: LocalResult :: None => {
500+ panic ! ( "1:30 AM on Nov 3 should exist" ) ;
501+ }
502+ }
503+ }
504+
505+ // Test with extremely restrictive cron that takes long to find next match
506+ #[ test]
507+ fn test_very_restrictive_cron ( ) {
508+ // Feb 29 only on leap years that fall on Friday
509+ let now = Utc . timestamp_opt ( 1_577_836_800 , 0 ) . unwrap ( ) ; // 2020-01-01
510+
511+ // This should work as 2020-02-29 is on Saturday (day 6)
512+ // But if we look for Sunday (day 0), it won't match in 4 years
513+ let result = parse ( "0 0 29 2 0" , & now) ;
514+ // Feb 29 on Sunday doesn't occur in the next 4 years from 2020
515+ assert ! ( result. is_err( ) ) ;
516+ }
517+
439518// 1541322900 -> 1_541_322_900
440519// vim :%s/\(\d\)\(\(\d\d\d\)\+\d\@!\)\@=/\1_/g
0 commit comments