diff --git a/docs/stdlib-times.md b/docs/stdlib-times.md index ce4b9974..cfb969f9 100644 --- a/docs/stdlib-times.md +++ b/docs/stdlib-times.md @@ -72,6 +72,7 @@ times := import("times") 2006" would be interpreted if it were the value; it serves as an example of the input format. The same interpretation will then be made to the input string. +- `parseInLocation(format string, s string, loc string)`: parseInLocation is like parse but parse interprets a time as UTC; parseInLocation interprets the time as in the given location. - `unix(sec int, nsec int) => time`: returns the local Time corresponding to the given Unix time, sec seconds and nsec nanoseconds since January 1, 1970 UTC. @@ -88,6 +89,7 @@ times := import("times") - `time_month(t time) => int`: returns the month of the year specified by t. - `time_day(t time) => int`: returns the day of the month specified by t. - `time_weekday(t time) => int`: returns the day of the week specified by t. +- `time_yearday(t time) => int`: returns the day of the year specified by t, in the range [1,365] for non-leap years, and [1,366] in leap years. - `time_hour(t time) => int`: returns the hour within the day specified by t, in the range [0, 23]. - `time_minute(t time) => int`: returns the minute offset within the hour @@ -114,6 +116,7 @@ times := import("times") t. - `time_string(t time) => string`: returns the time formatted using the format string "2006-01-02 15:04:05.999999999 -0700 MST". +- `to_location(t time, loc string)`: returns a copy of t representing the same time instant, but with the copy's location information set to loc for display purposes. - `is_zero(t time) => bool`: reports whether t represents the zero time instant, January 1, year 1, 00:00:00 UTC. - `to_local(t time) => time`: returns t with the location set to local time. diff --git a/stdlib/times.go b/stdlib/times.go index 0b6f7bd4..c1cf0797 100644 --- a/stdlib/times.go +++ b/stdlib/times.go @@ -92,6 +92,10 @@ var timesModule = map[string]tengo.Object{ Name: "parse", Value: timesParse, }, // parse(format, str) => time + "parseInLocation": &tengo.UserFunction{ + Name: "parseInLocation", + Value: timesParseInLocation, + }, // parseInLocation(format, str, location) => time "unix": &tengo.UserFunction{ Name: "unix", Value: timesUnix, @@ -132,6 +136,10 @@ var timesModule = map[string]tengo.Object{ Name: "time_weekday", Value: timesTimeWeekday, }, // time_weekday(time) => int + "time_yearday": &tengo.UserFunction{ + Name: "time_yearday", + Value: timesTimeYearday, + }, // time_yearday(time) => int "time_hour": &tengo.UserFunction{ Name: "time_hour", Value: timesTimeHour, @@ -168,6 +176,10 @@ var timesModule = map[string]tengo.Object{ Name: "time_string", Value: timesTimeString, }, // time_string(time) => string + "to_location": &tengo.UserFunction{ + Name: "to_location", + Value: timesToLocation, + }, // to_location(time, location) => time "is_zero": &tengo.UserFunction{ Name: "is_zero", Value: timesIsZero, @@ -555,6 +567,59 @@ func timesParse(args ...tengo.Object) (ret tengo.Object, err error) { return } +func timesParseInLocation(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(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 + } + + s3, ok := tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + + location, err := time.LoadLocation(s3) + if err != nil { + ret = wrapError(err) + return + } + + parsed, err := time.ParseInLocation(s1, s2, location) + if err != nil { + ret = wrapError(err) + return + } + + ret = &tengo.Time{Value: parsed} + + return +} + func timesUnix(args ...tengo.Object) (ret tengo.Object, err error) { if len(args) != 2 { err = tengo.ErrWrongNumArguments @@ -853,6 +918,27 @@ func timesTimeWeekday(args ...tengo.Object) (ret tengo.Object, err error) { return } +func timesTimeYearday(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + 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 + } + + ret = &tengo.Int{Value: int64(t1.YearDay())} + + return +} + func timesTimeHour(args ...tengo.Object) (ret tengo.Object, err error) { if len(args) != 1 { err = tengo.ErrWrongNumArguments @@ -1022,6 +1108,43 @@ func timesTimeFormat(args ...tengo.Object) (ret tengo.Object, err error) { return } +func timesToLocation(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 timesIsZero(args ...tengo.Object) (ret tengo.Object, err error) { if len(args) != 1 { err = tengo.ErrWrongNumArguments diff --git a/stdlib/times_test.go b/stdlib/times_test.go index 6977ba46..d790c277 100644 --- a/stdlib/times_test.go +++ b/stdlib/times_test.go @@ -42,6 +42,9 @@ func TestTimes(t *testing.T) { module(t, "times"). call("parse", time.RFC3339, "1982-09-28T19:21:44+07:00"). expect(parsed) + module(t, "times"). + call("parseInLocation", time.RFC3339, "1982-09-28T19:21:44+07:00", "America/Los_Angeles"). + expect(parsed) module(t, "times"). call("unix", 1234325, 94493). expect(time.Unix(1234325, 94493)) @@ -80,4 +83,8 @@ 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()) + location, _ := time.LoadLocation("America/Los_Angeles") + module(t, "times").call("to_location", time1, "America/Los_Angeles").expect(time1.In(location)) + module(t, "times").call("time_location", time1.In(location)).expect("America/Los_Angeles") + module(t, "times").call("to_location", time1, "invalid location name").expectError() }