diff --git a/convert_test.go b/convert_test.go index 172e0c6..dae1deb 100644 --- a/convert_test.go +++ b/convert_test.go @@ -75,9 +75,10 @@ func TestFromTime(base *testing.T) { base.Parallel() type testCase struct { - Time string - Date date.Date - Error string + Time string + Date date.Date + Timezone timezoneMetadata + Error string } cases := []testCase{ @@ -90,8 +91,29 @@ func TestFromTime(base *testing.T) { Error: "timestamp contains more than just date information; 2020-05-11T07:10:55.209309302Z", }, { - Time: "2022-01-31T00:00:00.000-05:00", - Error: "timestamp contains more than just date information; 2022-01-31T00:00:00-05:00", + Time: "2022-01-31T00:00:00.000-05:00", + Timezone: timezoneMetadata{Name: valueToPtr(""), Offset: valueToPtr(-18000)}, + Error: "timestamp contains more than just date information; 2022-01-31T00:00:00-05:00", + }, + { + Time: "2022-01-31T05:00:00.000Z", + Timezone: timezoneMetadata{InTimezone: valueToPtr("America/New_York"), Name: valueToPtr("EST"), Offset: valueToPtr(-18000)}, + Error: "timestamp contains more than just date information; 2022-01-31T00:00:00-05:00", + }, + { + Time: "2024-01-11T00:00:00.000-06:00", + Timezone: timezoneMetadata{Name: valueToPtr(""), Offset: valueToPtr(-21600)}, + Error: "timestamp contains more than just date information; 2024-01-11T00:00:00-06:00", + }, + { + Time: "2024-04-11T00:00:00.000-05:00", + Timezone: timezoneMetadata{Name: valueToPtr(""), Offset: valueToPtr(-18000)}, + Error: "timestamp contains more than just date information; 2024-04-11T00:00:00-05:00", + }, + { + Time: "2024-04-11T05:00:00.000Z", + Timezone: timezoneMetadata{InTimezone: valueToPtr("America/Chicago"), Name: valueToPtr("CDT"), Offset: valueToPtr(-18000)}, + Error: "timestamp contains more than just date information; 2024-04-11T00:00:00-05:00", }, { Time: "2020-05-11T00:00:00.000000001Z", @@ -122,6 +144,12 @@ func TestFromTime(base *testing.T) { timestamp, err := time.Parse(time.RFC3339Nano, tc.Time) assert.Nil(err) + timestamp = tc.Timezone.In(assert, timestamp) + + name, offset := timestamp.Zone() + assert.Equal(tc.Timezone.ExpectedName(timestamp), name) + assert.Equal(tc.Timezone.ExpectedOffset(), offset) + d, err := date.FromTime(timestamp) if tc.Error == "" { assert.Nil(err) @@ -174,3 +202,51 @@ func TestInTimezone(base *testing.T) { }) } } + +// timezoneMetadata is a struct that contains timezone metadata for assertions +// and translation across timezones. Intended to be used with `TestFromTime()`. +type timezoneMetadata struct { + InTimezone *string + Name *string + Offset *int +} + +// In translates a timestamp to an "in timezone" if one is set on this +// metadata struct. +func (tm timezoneMetadata) In(assert *testifyrequire.Assertions, t time.Time) time.Time { + if tm.InTimezone == nil { + return t + } + + tz, err := time.LoadLocation(*tm.InTimezone) + assert.Nil(err) + return t.In(tz) +} + +// ExpectedName returns the expected timezone name. +func (tm timezoneMetadata) ExpectedName(t time.Time) string { + if tm.Name == nil { + return "UTC" + } + + name := *tm.Name + tz := t.Location() + if name == "" && tz == time.Local { + name, _ = t.Zone() + } + + return name +} + +// ExpectedOffset returns the expected timezone offset in seconds. +func (tm timezoneMetadata) ExpectedOffset() int { + if tm.Offset != nil { + return *tm.Offset + } + return 0 +} + +// valueToPtr is a generic function that returns a pointer to the given value. +func valueToPtr[T any](v T) *T { + return &v +}