Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

types: Make use of the new time.ZoneBounds function in go-1.19 #39038

Merged
merged 4 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 11 additions & 40 deletions types/core_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,42 +184,6 @@ func (t CoreTime) GoTime(loc *gotime.Location) (gotime.Time, error) {
return tm, nil
}

// FindZoneTransition check for one Time Zone transition within +/- 4h
// Currently the needed functions are not exported, if gotime.Location.lookup would be exported
// then it would be easy to use that directly
func FindZoneTransition(tIn gotime.Time) (gotime.Time, error) {
// Check most common case first, DST transition on full hour.
// round truncates away from zero!
t2 := tIn.Round(gotime.Hour).Add(-1 * gotime.Hour)
t1 := t2.Add(-1 * gotime.Second)
_, offset1 := t1.Zone()
_, offset2 := t2.Zone()
if offset1 != offset2 {
return t2, nil
}

// Check if any offset change?
t1 = tIn.Add(-4 * gotime.Hour)
t2 = tIn.Add(4 * gotime.Hour)
_, offset1 = t1.Zone()
_, offset2 = t2.Zone()
if offset1 == offset2 {
return tIn, errors.Trace(ErrWrongValue.GenWithStackByArgs(TimeStr, tIn))
}

// Check generic case, like for 'Australia/Lord_Howe'
for t2.After(t1.Add(gotime.Second)) {
t := t1.Add(t2.Sub(t1) / 2).Round(gotime.Second)
_, offset := t.Zone()
if offset == offset1 {
t1 = t
} else {
t2 = t
}
}
return t2, nil
}

// AdjustedGoTime converts Time to GoTime and adjust for invalid DST times
// like during the DST change with increased offset,
// normally moving to Daylight Saving Time.
Expand All @@ -230,11 +194,18 @@ func (t CoreTime) AdjustedGoTime(loc *gotime.Location) (gotime.Time, error) {
return tm, nil
}

tAdj, err2 := FindZoneTransition(tm)
if err2 == nil {
return tAdj, nil
// The converted go time did not map back to the same time, probably it was between a
// daylight saving transition, adjust the time to the closest Zone bound.
start, end := tm.ZoneBounds()
// time zone transitions are normally 1 hour, allow up to 4 hours before returning error
if start.Sub(tm).Abs().Hours() > 4.0 && end.Sub(tm).Abs().Hours() > 4.0 {
return tm, errors.Trace(ErrWrongValue.GenWithStackByArgs(TimeStr, tm))
}
// use the closest transition time
if tm.Sub(start).Abs() <= tm.Sub(end).Abs() {
return start, nil
}
return tm, err
return end, nil
}

// IsLeapYear returns if it's leap year.
Expand Down
43 changes: 2 additions & 41 deletions types/core_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,45 +293,6 @@ func TestWeekday(t *testing.T) {
}
}

func TestFindZoneTransition(t *testing.T) {
tests := []struct {
TZ string
dt string
Expect string
Success bool
}{
{"Australia/Lord_Howe", "2020-06-29 03:45:00", "", false},
{"Australia/Lord_Howe", "2020-10-04 02:15:00", "2020-10-04 02:30:00 +11 +1100", true},
{"Australia/Lord_Howe", "2020-10-04 02:29:59", "2020-10-04 02:30:00 +11 +1100", true},
{"Australia/Lord_Howe", "2020-10-04 02:29:59.99", "2020-10-04 02:30:00 +11 +1100", true},
{"Australia/Lord_Howe", "2020-10-04 02:30:00.0001", "2020-10-04 02:30:00 +11 +1100", true},
{"Australia/Lord_Howe", "2020-10-04 02:30:00", "2020-10-04 02:30:00 +11 +1100", true},
{"Australia/Lord_Howe", "2020-10-04 02:30:01", "2020-10-04 02:30:00 +11 +1100", true},
{"Europe/Vilnius", "2020-03-29 03:45:00", "2020-03-29 04:00:00 EEST +0300", true},
{"Europe/Vilnius", "2020-10-25 03:45:00", "2020-10-25 03:00:00 EET +0200", true},
{"Europe/Vilnius", "2020-06-29 03:45:00", "", false},
{"Europe/Amsterdam", "2020-03-29 02:45:00", "2020-03-29 03:00:00 CEST +0200", true},
{"Europe/Amsterdam", "2020-10-25 02:35:00", "2020-10-25 02:00:00 CET +0100", true},
{"Europe/Amsterdam", "2020-03-29 02:59:59", "2020-03-29 03:00:00 CEST +0200", true},
{"Europe/Amsterdam", "2020-03-29 02:59:59.999999999", "2020-03-29 03:00:00 CEST +0200", true},
{"Europe/Amsterdam", "2020-03-29 03:00:00.000000001", "2020-03-29 03:00:00 CEST +0200", true},
}

for _, tt := range tests {
loc, err := time.LoadLocation(tt.TZ)
require.NoError(t, err)
tm, err := time.ParseInLocation("2006-01-02 15:04:05", tt.dt, loc)
require.NoError(t, err)
tp, err := FindZoneTransition(tm)
if !tt.Success {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.Expect, tp.Format("2006-01-02 15:04:05.999999999 MST -0700"))
}
}
}

func TestAdjustedGoTime(t *testing.T) {
tests := []struct {
TZ string
Expand Down Expand Up @@ -361,9 +322,9 @@ func TestAdjustedGoTime(t *testing.T) {
require.NoError(t, err)
tp, err := tt.dt.AdjustedGoTime(loc)
if !tt.Success {
require.Error(t, err)
require.Error(t, err, tp.Format("2006-01-02 15:04:05.999999999 MST -0700"))
} else {
require.NoError(t, err)
require.NoError(t, err, tp.Format("2006-01-02 15:04:05.999999999 MST -0700"))
require.Equal(t, tt.Expect, tp.Format("2006-01-02 15:04:05.999999999 MST -0700"))
}
}
Expand Down