diff --git a/lib/time.go b/lib/time.go index 1cd8055..df0bacb 100644 --- a/lib/time.go +++ b/lib/time.go @@ -91,6 +91,20 @@ import ( // duration("42s").round(duration("10s")) // return "40s" // now.round(duration("1h")) // return "2022-03-30T11:00:00Z" // +// # Truncate +// +// Truncate returns the timestamp or duration truncated down to the nearest multiple of the +// duration parameter using [time.Time.Truncate] for timestamps and [time.Duration.Truncate] +// for durations: +// +// .truncate() -> +// .truncate() -> +// +// Examples: +// +// duration("42s").truncate(duration("10s")) // return "40s" +// now.truncate(duration("1h")) // return "2022-03-30T11:00:00Z" +// // # Global Variables // // A collection of global variable are provided to give access to the start @@ -176,6 +190,20 @@ func (timeLib) CompileOptions() []cel.EnvOption { cel.BinaryBinding(roundTimestamp), ), ), + cel.Function("truncate", + cel.MemberOverload( + "duration_truncate_duration_duration", + []*cel.Type{cel.DurationType, cel.DurationType}, + cel.DurationType, + cel.BinaryBinding(truncateDuration), + ), + cel.MemberOverload( + "time_truncate_duration_time", + []*cel.Type{cel.TimestampType, cel.DurationType}, + cel.TimestampType, + cel.BinaryBinding(truncateTimestamp), + ), + ), } } @@ -289,3 +317,27 @@ func roundTimestamp(arg, layout ref.Val) ref.Val { } return types.Timestamp{Time: t.Time.Round(res.Duration)} } + +func truncateDuration(arg, layout ref.Val) ref.Val { + d, ok := arg.(types.Duration) + if !ok { + return types.ValOrErr(d, "no such overload for duration truncate: %s", arg.Type()) + } + res, ok := layout.(types.Duration) + if !ok { + return types.ValOrErr(res, "no such overload for duration truncate: %s", layout.Type()) + } + return types.Duration{Duration: d.Duration.Truncate(res.Duration)} +} + +func truncateTimestamp(arg, layout ref.Val) ref.Val { + t, ok := arg.(types.Timestamp) + if !ok { + return types.ValOrErr(t, "no such overload for timestamp truncate: %s", arg.Type()) + } + res, ok := layout.(types.Duration) + if !ok { + return types.ValOrErr(res, "no such overload for timestamp truncate: %s", layout.Type()) + } + return types.Timestamp{Time: t.Time.Truncate(res.Duration)} +} diff --git a/testdata/time_truncate.txt b/testdata/time_truncate.txt new file mode 100644 index 0000000..69b09e2 --- /dev/null +++ b/testdata/time_truncate.txt @@ -0,0 +1,14 @@ +mito -use time src.cel +! stderr . +cmp stdout want.txt + +-- src.cel -- +{ + "47s": duration("47s").truncate(duration("10s")), + "RFC3339": "2006-01-02T15:54:05Z".parse_time(time_layout.RFC3339).truncate(duration("1h")), +} +-- want.txt -- +{ + "47s": "40s", + "RFC3339": "2006-01-02T15:00:00Z" +}