github.com/argoproj/argo-events@v1.9.1/common/cronutil_test.go (about) 1 package common 2 3 import ( 4 "strings" 5 "testing" 6 "time" 7 8 cronlib "github.com/robfig/cron/v3" 9 ) 10 11 func TestPrevCronTime(t *testing.T) { 12 runs := []struct { 13 time, spec string 14 expected string 15 expectedErr bool 16 }{ 17 // Simple cases 18 {"Mon Jul 9 15:00 2012", "0 0/15 * * * *", "Mon Jul 9 14:45 2012", false}, 19 {"Mon Jul 9 14:59 2012", "0 0/15 * * * *", "Mon Jul 9 14:45 2012", false}, 20 {"Mon Jul 9 15:01:59 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012", false}, 21 22 // Wrap around hours 23 {"Mon Jul 9 15:10 2012", "0 20-35/15 * * * *", "Mon Jul 9 14:35 2012", false}, 24 25 // Wrap around days 26 {"Tue Jul 10 00:00 2012", "0 */15 * * * *", "Tue Jul 9 23:45 2012", false}, 27 {"Tue Jul 10 00:00 2012", "0 20-35/15 * * * *", "Tue Jul 9 23:35 2012", false}, 28 29 // Wrap around months 30 {"Mon Jul 9 09:35 2012", "0 0 12 9 Apr-Oct ?", "Sat Jun 9 12:00 2012", false}, 31 32 // Leap year 33 {"Mon Jul 9 23:35 2018", "0 0 0 29 Feb ?", "Mon Feb 29 00:00 2016", false}, 34 35 // Daylight savings time 3am EDT (-4) -> 2am EST (-5) 36 {"2013-03-11T02:30:00-0400", "TZ=America/New_York 0 0 12 9 Mar ?", "2013-03-09T12:00:00-0500", false}, 37 38 // hourly job 39 {"2012-03-11T01:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T00:00:00-0500", false}, 40 41 // 2am nightly job (skipped) 42 {"2012-03-12T00:00:00-0400", "TZ=America/New_York 0 0 2 * * ?", "2012-03-10T02:00:00-0500", false}, 43 44 // 2am nightly job 45 {"2012-11-04T02:00:00-0500", "TZ=America/New_York 0 0 0 * * ?", "2012-11-04T00:00:00-0400", false}, 46 {"2012-11-05T02:00:00-0500", "TZ=America/New_York 0 0 2 * * ?", "2012-11-04T02:00:00-0500", false}, 47 48 // Unsatisfiable 49 {"Mon Jul 9 23:35 2012", "0 0 0 30 Feb ?", "", true}, 50 {"Mon Jul 9 23:35 2012", "0 0 0 31 Apr ?", "", true}, 51 52 // Monthly job 53 {"TZ=America/New_York 2012-12-03T00:00:00-0500", "0 0 3 3 * ?", "2012-11-03T03:00:00-0400", false}, 54 } 55 56 parser := cronlib.NewParser(cronlib.Second | cronlib.Minute | cronlib.Hour | cronlib.Dom | cronlib.Month | cronlib.DowOptional | cronlib.Descriptor) 57 58 for _, c := range runs { 59 actual, err := PrevCronTime(c.spec, parser, getTime(c.time)) 60 if c.expectedErr { 61 if err == nil { 62 t.Errorf("%s, \"%s\": should have received error but didn't", c.time, c.spec) 63 } 64 } else { 65 if err != nil { 66 t.Errorf("%s, \"%s\": error: %v", c.time, c.spec, err) 67 } else { 68 expected := getTime(c.expected) 69 if !actual.Equal(expected) { 70 t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual) 71 } 72 } 73 } 74 } 75 } 76 77 func getTime(value string) time.Time { 78 if value == "" { 79 return time.Time{} 80 } 81 82 var location = time.Local 83 if strings.HasPrefix(value, "TZ=") { 84 parts := strings.Fields(value) 85 loc, err := time.LoadLocation(parts[0][len("TZ="):]) 86 if err != nil { 87 panic("could not parse location:" + err.Error()) 88 } 89 location = loc 90 value = parts[1] 91 } 92 93 var layouts = []string{ 94 "Mon Jan 2 15:04 2006", 95 "Mon Jan 2 15:04:05 2006", 96 } 97 for _, layout := range layouts { 98 if t, err := time.ParseInLocation(layout, value, location); err == nil { 99 return t 100 } 101 } 102 if t, err := time.ParseInLocation("2006-01-02T15:04:05-0700", value, location); err == nil { 103 return t 104 } 105 panic("could not parse time value " + value) 106 }