diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 60ab883..934bf9b 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -6,6 +6,9 @@ on: - main tags: - 'v*' + pull_request: + branches: + - main permissions: contents: read @@ -47,6 +50,7 @@ jobs: uses: codecov/codecov-action@v5 with: files: coverage.out - fail_ci_if_error: true + # Fork PRs upload tokenlessly, which can be flaky; don't fail their CI. + fail_ci_if_error: ${{ github.event_name != 'pull_request' }} env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.golangci.yaml b/.golangci.yaml index e6a233c..570b386 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -112,3 +112,13 @@ linters: desc: Use github.com/go-resty/resty/v2 instead - pkg: github.com/aws/smithy-go/ptr$ desc: Use github.com/aws/aws-sdk-go-v2/aws instead + + exclusions: + rules: + # TimeZone intentionally mixes receivers: Scan/LoadString/Unmarshal* need + # pointer receivers to mutate, while the rest stay value receivers to keep + # the zero-value-as-UTC value semantics. + - linters: + - recvcheck + path: time_zone.go + text: 'the methods of "TimeZone"' diff --git a/time_zone.go b/time_zone.go index 616b7ad..2e0933a 100644 --- a/time_zone.go +++ b/time_zone.go @@ -46,6 +46,18 @@ func LoadTimeZone(name string) (TimeZone, error) { return z, nil } +// MustLoadTimeZone is like [LoadTimeZone] but panics if the name is invalid. +// It is intended for use in variable initializations and tests where the +// time zone name is known to be valid. +func MustLoadTimeZone(name string) TimeZone { + z, err := LoadTimeZone(name) + if err != nil { + panic(err) + } + + return z +} + // Location returns the *time.Location for this TimeZone. // The zero value (UTC) returns time.UTC; it never returns nil. func (z TimeZone) Location() *time.Location { diff --git a/time_zone_example_test.go b/time_zone_example_test.go index d3e9ea4..98967f6 100644 --- a/time_zone_example_test.go +++ b/time_zone_example_test.go @@ -35,6 +35,14 @@ func ExampleTimeZone_zeroValue() { // true } +func ExampleMustLoadTimeZone() { + z := tz.MustLoadTimeZone("America/New_York") + fmt.Println(z.String()) + + // Output: + // America/New_York +} + func ExampleTimeZone_Scan() { var z tz.TimeZone if err := z.Scan("America/New_York"); err != nil { @@ -71,7 +79,7 @@ func ExampleTimeZone_Value_zeroValue() { } func ExampleUTCTimeZone() { - z, _ := tz.LoadTimeZone("UTC") + z := tz.MustLoadTimeZone("UTC") fmt.Println(z == tz.UTCTimeZone) // Output: @@ -79,7 +87,7 @@ func ExampleUTCTimeZone() { } func ExampleTimeZone_MarshalText() { - z, _ := tz.LoadTimeZone("Asia/Tokyo") + z := tz.MustLoadTimeZone("Asia/Tokyo") text, _ := z.MarshalText() fmt.Println(string(text)) @@ -103,7 +111,10 @@ func ExampleTimeZone_MarshalJSON() { TimeZone tz.TimeZone `json:"time_zone"` } - e := Event{Name: "Meeting", TimeZone: func() tz.TimeZone { z, _ := tz.LoadTimeZone("Europe/London"); return z }()} + e := Event{ + Name: "Meeting", + TimeZone: tz.MustLoadTimeZone("Europe/London"), + } data, err := json.Marshal(e) if err != nil { diff --git a/time_zone_test.go b/time_zone_test.go index 08af274..8992865 100644 --- a/time_zone_test.go +++ b/time_zone_test.go @@ -76,6 +76,61 @@ func TestLoadTimeZone(t *testing.T) { } } +func TestMustLoadTimeZone(t *testing.T) { + t.Parallel() + + tests := []struct { + testName string + name string + want tz.TimeZone + wantPanic bool + }{ + { + testName: "Empty", + name: "", + want: tz.TimeZone{}, + wantPanic: false, + }, + { + testName: "UTC", + name: "UTC", + want: tz.TimeZone{}, + wantPanic: false, + }, + { + testName: "America/New_York", + name: "America/New_York", + want: mustLoadTimeZone(t, "America/New_York"), + wantPanic: false, + }, + { + testName: "Local", + name: "Local", + wantPanic: true, + }, + { + testName: "InvalidName", + name: "Invalid/Zone", + wantPanic: true, + }, + } + for _, tt := range tests { + t.Run(tt.testName, func(t *testing.T) { + t.Parallel() + + if tt.wantPanic { + assert.Panics(t, func() { + tz.MustLoadTimeZone(tt.name) + }) + + return + } + + assert.Equal(t, tt.want, tz.MustLoadTimeZone(tt.name)) + }) + } +} + func TestTimeZone_Location_ZeroValueReturnUTC(t *testing.T) { t.Parallel() assert.Same(t, tz.TimeZone{}.Location(), time.UTC)