From dbf322045545dc016828ed3581ae310a1fc1152e Mon Sep 17 00:00:00 2001 From: Rob Figueiredo Date: Tue, 12 Feb 2019 21:35:06 -0500 Subject: [PATCH] spec.go: adjust times when rolling the clock forward to handle non-existent midnight Fixes #157 --- spec.go | 16 +++++++++++++++- spec_test.go | 8 ++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/spec.go b/spec.go index 6488bc70..fa1e241e 100644 --- a/spec.go +++ b/spec.go @@ -56,7 +56,8 @@ const ( // Next returns the next time this schedule is activated, greater than the given // time. If no time can be found to satisfy the schedule, return the zero time. func (s *SpecSchedule) Next(t time.Time) time.Time { - // General approach: + // General approach + // // For Month, Day, Hour, Minute, Second: // Check if the time value matches. If yes, continue to the next field. // If the field doesn't match the schedule, then increment the field until it matches. @@ -109,12 +110,25 @@ WRAP: } // Now get a day in that month. + // + // NOTE: This causes issues for daylight savings regimes where midnight does + // not exist. For example: Sao Paulo has DST that transforms midnight on + // 11/3 into 1am. Handle that by noticing when the Hour ends up != 0. for !dayMatches(s, t) { if !added { added = true t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc) } t = t.AddDate(0, 0, 1) + // Notice if the hour is no longer midnight due to DST. + // Add an hour if it's 23, subtract an hour if it's 1. + if t.Hour() != 0 { + if t.Hour() > 12 { + t = t.Add(time.Duration(24-t.Hour()) * time.Hour) + } else { + t = t.Add(time.Duration(-t.Hour()) * time.Hour) + } + } if t.Day() == 1 { goto WRAP diff --git a/spec_test.go b/spec_test.go index 2ae9fc76..87f98149 100644 --- a/spec_test.go +++ b/spec_test.go @@ -174,6 +174,14 @@ func TestNext(t *testing.T) { // Unsatisfiable {"Mon Jul 9 23:35 2012", "0 0 0 30 Feb ?", ""}, {"Mon Jul 9 23:35 2012", "0 0 0 31 Apr ?", ""}, + + // Monthly job + {"2012-11-04T00:00:00-0400", "0 0 3 3 * ?", "2012-12-03T03:00:00-0500"}, + + // Test the scenario of DST resulting in midnight not being a valid time. + // https://github.com/robfig/cron/issues/157 + {"2018-10-17T05:00:00-0400", "TZ=America/Sao_Paulo 0 0 9 10 * ?", "2018-11-10T06:00:00-0500"}, + {"2018-02-14T05:00:00-0500", "TZ=America/Sao_Paulo 0 0 9 22 * ?", "2018-02-22T07:00:00-0500"}, } for _, c := range runs {