Skip to content

Commit

Permalink
Better support for TZ locations in the times module (#397)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ganehag authored Jan 31, 2023
1 parent ecc3d91 commit 82b543f
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 5 deletions.
10 changes: 7 additions & 3 deletions docs/stdlib-times.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ times := import("times")
duration.
- `month_string(month int) => string`: returns the English name of the month
("January", "February", ...).
- `date(year int, month int, day int, hour int, min int, sec int, nsec int) => time`:
returns the Time corresponding to "yyyy-mm-dd hh:mm:ss + nsec nanoseconds".
Current location is used.
- `date(year int, month int, day int, hour int, min int, sec int, nsec int, loc string) => time`:
returns the Time corresponding to "yyyy-mm-dd hh:mm:ss + nsec nanoseconds" in
the appropriate zone for that Time in the given (optional) location.
The Local time zone will be used if executed without specifying a location.
- `now() => time`: returns the current local time.
- `parse(format string, s string) => time`: parses a formatted string and
returns the time value it represents. The layout defines the format by
Expand Down Expand Up @@ -116,5 +117,8 @@ times := import("times")
string "2006-01-02 15:04:05.999999999 -0700 MST".
- `is_zero(t time) => bool`: reports whether t represents the zero time
instant, January 1, year 1, 00:00:00 UTC.
- `in_location(t time, l string) => time`: returns a copy of t representing
the same time instant, but with the copy's location information set to l for
display purposes.
- `to_local(t time) => time`: returns t with the location set to local time.
- `to_utc(t time) => time`: returns t with the location set to UTC.
68 changes: 66 additions & 2 deletions stdlib/times.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ var timesModule = map[string]tengo.Object{
Name: "to_utc",
Value: timesToUTC,
}, // to_utc(time) => time
"in_location": &tengo.UserFunction{
Name: "in_location",
Value: timesInLocation,
}, // in_location(time, location) => time
}

func timesSleep(args ...tengo.Object) (ret tengo.Object, err error) {
Expand Down Expand Up @@ -430,7 +434,7 @@ func timesDate(args ...tengo.Object) (
ret tengo.Object,
err error,
) {
if len(args) != 7 {
if len(args) < 7 || len(args) > 8 {
err = tengo.ErrWrongNumArguments
return
}
Expand Down Expand Up @@ -499,9 +503,29 @@ func timesDate(args ...tengo.Object) (
return
}

var loc *time.Location
if len(args) == 8 {
i8, ok := tengo.ToString(args[7])
if !ok {
err = tengo.ErrInvalidArgumentType{
Name: "eighth",
Expected: "string(compatible)",
Found: args[7].TypeName(),
}
return
}
loc, err = time.LoadLocation(i8)
if err != nil {
ret = wrapError(err)
return
}
} else {
loc = time.Now().Location()
}

ret = &tengo.Time{
Value: time.Date(i1,
time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location()),
time.Month(i2), i3, i4, i5, i6, i7, loc),
}

return
Expand Down Expand Up @@ -1113,6 +1137,46 @@ func timesTimeLocation(args ...tengo.Object) (
return
}

func timesInLocation(args ...tengo.Object) (
ret tengo.Object,
err error,
) {
if len(args) != 2 {
err = tengo.ErrWrongNumArguments
return
}

t1, ok := tengo.ToTime(args[0])
if !ok {
err = tengo.ErrInvalidArgumentType{
Name: "first",
Expected: "time(compatible)",
Found: args[0].TypeName(),
}
return
}

s2, ok := tengo.ToString(args[1])
if !ok {
err = tengo.ErrInvalidArgumentType{
Name: "second",
Expected: "string(compatible)",
Found: args[1].TypeName(),
}
return
}

location, err := time.LoadLocation(s2)
if err != nil {
ret = wrapError(err)
return
}

ret = &tengo.Time{Value: t1.In(location)}

return
}

func timesTimeString(args ...tengo.Object) (ret tengo.Object, err error) {
if len(args) != 1 {
err = tengo.ErrWrongNumArguments
Expand Down
6 changes: 6 additions & 0 deletions stdlib/times_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
func TestTimes(t *testing.T) {
time1 := time.Date(1982, 9, 28, 19, 21, 44, 999, time.Now().Location())
time2 := time.Now()
location, _ := time.LoadLocation("Pacific/Auckland")
time3 := time.Date(1982, 9, 28, 19, 21, 44, 999, location)

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

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

module(t, "times").call("date", 1982, 9, 28, 19, 21, 44, 999).
expect(time1)
module(t, "times").call("date", 1982, 9, 28, 19, 21, 44, 999, "Pacific/Auckland").
expect(time3)

nowD := time.Until(module(t, "times").call("now").
o.(*tengo.Time).Value).Nanoseconds()
require.True(t, 0 > nowD && nowD > -100000000) // within 100ms
Expand Down Expand Up @@ -80,4 +85,5 @@ func TestTimes(t *testing.T) {
module(t, "times").call("time_location", time1).
expect(time1.Location().String())
module(t, "times").call("time_string", time1).expect(time1.String())
module(t, "times").call("in_location", time1, location.String()).expect(time1.In(location))
}

0 comments on commit 82b543f

Please sign in to comment.