Skip to content

Commit 0136931

Browse files
authoredAug 24, 2024··
feat: support time.Location (#326)
* 1. Added support `time.Location` 2. Added minimal config [EditorConfig](https://editorconfig.org/) * Added missing tests for parsing errors * Corrected the method names * I removed the editorconfig file so as not to confuse other developers with its presence. * I removed the editorconfig file so as not to confuse other developers with its presence.
1 parent aa50469 commit 0136931

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
lines changed
 

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Complete list:
7979
- `uint8`
8080
- `uint`
8181
- `time.Duration`
82+
- `time.Location`
8283
- `encoding.TextUnmarshaler`
8384
- `url.URL`
8485

‎env.go

+27-14
Original file line numberDiff line numberDiff line change
@@ -87,21 +87,34 @@ var (
8787

8888
func defaultTypeParsers() map[reflect.Type]ParserFunc {
8989
return map[reflect.Type]ParserFunc{
90-
reflect.TypeOf(url.URL{}): func(v string) (interface{}, error) {
91-
u, err := url.Parse(v)
92-
if err != nil {
93-
return nil, newParseValueError("unable to parse URL", err)
94-
}
95-
return *u, nil
96-
},
97-
reflect.TypeOf(time.Nanosecond): func(v string) (interface{}, error) {
98-
s, err := time.ParseDuration(v)
99-
if err != nil {
100-
return nil, newParseValueError("unable to parse duration", err)
101-
}
102-
return s, err
103-
},
90+
reflect.TypeOf(url.URL{}): parseURL,
91+
reflect.TypeOf(time.Nanosecond): parseDuration,
92+
reflect.TypeOf(time.Location{}): parseLocation,
93+
}
94+
}
95+
96+
func parseURL(v string) (interface{}, error) {
97+
u, err := url.Parse(v)
98+
if err != nil {
99+
return nil, newParseValueError("unable to parse URL", err)
100+
}
101+
return *u, nil
102+
}
103+
104+
func parseDuration(v string) (interface{}, error) {
105+
d, err := time.ParseDuration(v)
106+
if err != nil {
107+
return nil, newParseValueError("unable to parse duration", err)
108+
}
109+
return d, err
110+
}
111+
112+
func parseLocation(v string) (interface{}, error) {
113+
loc, err := time.LoadLocation(v)
114+
if err != nil {
115+
return nil, newParseValueError("unable to parse location", err)
104116
}
117+
return *loc, nil
105118
}
106119

107120
// ParserFunc defines the signature of a function that can be used within

‎env_test.go

+34-2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ type Config struct {
108108
DurationPtr *time.Duration `env:"DURATION"`
109109
DurationPtrs []*time.Duration `env:"DURATIONS"`
110110

111+
Location time.Location `env:"LOCATION"`
112+
Locations []time.Location `env:"LOCATIONS"`
113+
LocationPtr *time.Location `env:"LOCATION"`
114+
LocationPtrs []*time.Location `env:"LOCATIONS"`
115+
111116
Unmarshaler unmarshaler `env:"UNMARSHALER"`
112117
UnmarshalerPtr *unmarshaler `env:"UNMARSHALER"`
113118
Unmarshalers []unmarshaler `env:"UNMARSHALERS"`
@@ -118,7 +123,7 @@ type Config struct {
118123
URLs []url.URL `env:"URLS"`
119124
URLPtrs []*url.URL `env:"URLS"`
120125

121-
StringWithdefault string `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/db"`
126+
StringWithDefault string `env:"DATABASE_URL" envDefault:"postgres://localhost:5432/db"`
122127

123128
CustomSeparator []string `env:"SEPSTRINGS" envSeparator:":"`
124129

@@ -254,6 +259,12 @@ func TestParsesEnv(t *testing.T) {
254259
t.Setenv("DURATION", tos(duration1))
255260
t.Setenv("DURATIONS", toss(duration1, duration2))
256261

262+
location1 := time.UTC
263+
location2, errLoadLocation := time.LoadLocation("Europe/Berlin")
264+
isNoErr(t, errLoadLocation)
265+
t.Setenv("LOCATION", tos(location1))
266+
t.Setenv("LOCATIONS", toss(location1, location2))
267+
257268
unmarshaler1 := unmarshaler{time.Minute}
258269
unmarshaler2 := unmarshaler{time.Millisecond * 1232}
259270
t.Setenv("UNMARSHALER", tos(unmarshaler1.Duration))
@@ -377,6 +388,13 @@ func TestParsesEnv(t *testing.T) {
377388
isEqual(t, &duration1, cfg.DurationPtrs[0])
378389
isEqual(t, &duration2, cfg.DurationPtrs[1])
379390

391+
isEqual(t, *location1, cfg.Location)
392+
isEqual(t, location1, cfg.LocationPtr)
393+
isEqual(t, *location1, cfg.Locations[0])
394+
isEqual(t, *location2, cfg.Locations[1])
395+
isEqual(t, location1, cfg.LocationPtrs[0])
396+
isEqual(t, location2, cfg.LocationPtrs[1])
397+
380398
isEqual(t, unmarshaler1, cfg.Unmarshaler)
381399
isEqual(t, &unmarshaler1, cfg.UnmarshalerPtr)
382400
isEqual(t, unmarshaler1, cfg.Unmarshalers[0])
@@ -391,7 +409,7 @@ func TestParsesEnv(t *testing.T) {
391409
isEqual(t, url1, cfg.URLPtrs[0].String())
392410
isEqual(t, url2, cfg.URLPtrs[1].String())
393411

394-
isEqual(t, "postgres://localhost:5432/db", cfg.StringWithdefault)
412+
isEqual(t, "postgres://localhost:5432/db", cfg.StringWithDefault)
395413
isEqual(t, nonDefinedStr, cfg.NonDefined.String)
396414
isEqual(t, nonDefinedStr, cfg.NestedNonDefined.NonDefined.String)
397415

@@ -801,6 +819,20 @@ func TestInvalidDurations(t *testing.T) {
801819
isTrue(t, errors.Is(err, ParseError{}))
802820
}
803821

822+
func TestInvalidLocation(t *testing.T) {
823+
t.Setenv("LOCATION", "should-be-a-valid-location")
824+
err := Parse(&Config{})
825+
isErrorWithMessage(t, err, `env: parse error on field "Location" of type "time.Location": unable to parse location: unknown time zone should-be-a-valid-location; parse error on field "LocationPtr" of type "*time.Location": unable to parse location: unknown time zone should-be-a-valid-location`)
826+
isTrue(t, errors.Is(err, ParseError{}))
827+
}
828+
829+
func TestInvalidLocations(t *testing.T) {
830+
t.Setenv("LOCATIONS", "should-be-a-valid-location,UTC,Europe/Berlin")
831+
err := Parse(&Config{})
832+
isErrorWithMessage(t, err, `env: parse error on field "Locations" of type "[]time.Location": unable to parse location: unknown time zone should-be-a-valid-location; parse error on field "LocationPtrs" of type "[]*time.Location": unable to parse location: unknown time zone should-be-a-valid-location`)
833+
isTrue(t, errors.Is(err, ParseError{}))
834+
}
835+
804836
func TestParseStructWithoutEnvTag(t *testing.T) {
805837
cfg := Config{}
806838
isNoErr(t, Parse(&cfg))

0 commit comments

Comments
 (0)
Please sign in to comment.