Skip to content

Commit 82b543f

Browse files
authored
Better support for TZ locations in the times module (#397)
* Handle panics by deferring recover as an error * Check for type in recover handler * Added support for times.in_location * Added support for location (TZ) to times.date * Updated documentation
1 parent ecc3d91 commit 82b543f

File tree

3 files changed

+79
-5
lines changed

3 files changed

+79
-5
lines changed

docs/stdlib-times.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ times := import("times")
6262
duration.
6363
- `month_string(month int) => string`: returns the English name of the month
6464
("January", "February", ...).
65-
- `date(year int, month int, day int, hour int, min int, sec int, nsec int) => time`:
66-
returns the Time corresponding to "yyyy-mm-dd hh:mm:ss + nsec nanoseconds".
67-
Current location is used.
65+
- `date(year int, month int, day int, hour int, min int, sec int, nsec int, loc string) => time`:
66+
returns the Time corresponding to "yyyy-mm-dd hh:mm:ss + nsec nanoseconds" in
67+
the appropriate zone for that Time in the given (optional) location.
68+
The Local time zone will be used if executed without specifying a location.
6869
- `now() => time`: returns the current local time.
6970
- `parse(format string, s string) => time`: parses a formatted string and
7071
returns the time value it represents. The layout defines the format by
@@ -116,5 +117,8 @@ times := import("times")
116117
string "2006-01-02 15:04:05.999999999 -0700 MST".
117118
- `is_zero(t time) => bool`: reports whether t represents the zero time
118119
instant, January 1, year 1, 00:00:00 UTC.
120+
- `in_location(t time, l string) => time`: returns a copy of t representing
121+
the same time instant, but with the copy's location information set to l for
122+
display purposes.
119123
- `to_local(t time) => time`: returns t with the location set to local time.
120124
- `to_utc(t time) => time`: returns t with the location set to UTC.

stdlib/times.go

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ var timesModule = map[string]tengo.Object{
180180
Name: "to_utc",
181181
Value: timesToUTC,
182182
}, // to_utc(time) => time
183+
"in_location": &tengo.UserFunction{
184+
Name: "in_location",
185+
Value: timesInLocation,
186+
}, // in_location(time, location) => time
183187
}
184188

185189
func timesSleep(args ...tengo.Object) (ret tengo.Object, err error) {
@@ -430,7 +434,7 @@ func timesDate(args ...tengo.Object) (
430434
ret tengo.Object,
431435
err error,
432436
) {
433-
if len(args) != 7 {
437+
if len(args) < 7 || len(args) > 8 {
434438
err = tengo.ErrWrongNumArguments
435439
return
436440
}
@@ -499,9 +503,29 @@ func timesDate(args ...tengo.Object) (
499503
return
500504
}
501505

506+
var loc *time.Location
507+
if len(args) == 8 {
508+
i8, ok := tengo.ToString(args[7])
509+
if !ok {
510+
err = tengo.ErrInvalidArgumentType{
511+
Name: "eighth",
512+
Expected: "string(compatible)",
513+
Found: args[7].TypeName(),
514+
}
515+
return
516+
}
517+
loc, err = time.LoadLocation(i8)
518+
if err != nil {
519+
ret = wrapError(err)
520+
return
521+
}
522+
} else {
523+
loc = time.Now().Location()
524+
}
525+
502526
ret = &tengo.Time{
503527
Value: time.Date(i1,
504-
time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location()),
528+
time.Month(i2), i3, i4, i5, i6, i7, loc),
505529
}
506530

507531
return
@@ -1113,6 +1137,46 @@ func timesTimeLocation(args ...tengo.Object) (
11131137
return
11141138
}
11151139

1140+
func timesInLocation(args ...tengo.Object) (
1141+
ret tengo.Object,
1142+
err error,
1143+
) {
1144+
if len(args) != 2 {
1145+
err = tengo.ErrWrongNumArguments
1146+
return
1147+
}
1148+
1149+
t1, ok := tengo.ToTime(args[0])
1150+
if !ok {
1151+
err = tengo.ErrInvalidArgumentType{
1152+
Name: "first",
1153+
Expected: "time(compatible)",
1154+
Found: args[0].TypeName(),
1155+
}
1156+
return
1157+
}
1158+
1159+
s2, ok := tengo.ToString(args[1])
1160+
if !ok {
1161+
err = tengo.ErrInvalidArgumentType{
1162+
Name: "second",
1163+
Expected: "string(compatible)",
1164+
Found: args[1].TypeName(),
1165+
}
1166+
return
1167+
}
1168+
1169+
location, err := time.LoadLocation(s2)
1170+
if err != nil {
1171+
ret = wrapError(err)
1172+
return
1173+
}
1174+
1175+
ret = &tengo.Time{Value: t1.In(location)}
1176+
1177+
return
1178+
}
1179+
11161180
func timesTimeString(args ...tengo.Object) (ret tengo.Object, err error) {
11171181
if len(args) != 1 {
11181182
err = tengo.ErrWrongNumArguments

stdlib/times_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
func TestTimes(t *testing.T) {
1212
time1 := time.Date(1982, 9, 28, 19, 21, 44, 999, time.Now().Location())
1313
time2 := time.Now()
14+
location, _ := time.LoadLocation("Pacific/Auckland")
15+
time3 := time.Date(1982, 9, 28, 19, 21, 44, 999, location)
1416

1517
module(t, "times").call("sleep", 1).expect(tengo.UndefinedValue)
1618

@@ -35,6 +37,9 @@ func TestTimes(t *testing.T) {
3537

3638
module(t, "times").call("date", 1982, 9, 28, 19, 21, 44, 999).
3739
expect(time1)
40+
module(t, "times").call("date", 1982, 9, 28, 19, 21, 44, 999, "Pacific/Auckland").
41+
expect(time3)
42+
3843
nowD := time.Until(module(t, "times").call("now").
3944
o.(*tengo.Time).Value).Nanoseconds()
4045
require.True(t, 0 > nowD && nowD > -100000000) // within 100ms
@@ -80,4 +85,5 @@ func TestTimes(t *testing.T) {
8085
module(t, "times").call("time_location", time1).
8186
expect(time1.Location().String())
8287
module(t, "times").call("time_string", time1).expect(time1.String())
88+
module(t, "times").call("in_location", time1, location.String()).expect(time1.In(location))
8389
}

0 commit comments

Comments
 (0)