diff --git a/test/cases/testdata/time/test-time-0948.yaml b/test/cases/testdata/time/test-time-0948.yaml index a3593ff0a1..4630d35c74 100644 --- a/test/cases/testdata/time/test-time-0948.yaml +++ b/test/cases/testdata/time/test-time-0948.yaml @@ -1,95 +1,50 @@ cases: -- data: - a: - - 1 - - 2 - - 3 - - 4 - b: - v1: hello - v2: goodbye - c: - - x: - - true - - false - - foo - "y": - - null - - 3.14159 - z: - p: true - q: false - d: - e: - - bar - - baz - f: - - xs: - - 1 - ys: - - 2 - - xs: - - 2 - ys: - - 3 - g: - a: - - 1 - - 0 - - 0 - - 0 - b: - - 0 - - 2 - - 0 - - 0 - c: - - 0 - - 0 - - 0 - - 4 - h: - - - 1 - - 2 - - 3 - - - 2 - - 3 - - 4 - l: - - a: bob - b: -1 - c: - - 1 - - 2 - - 3 - - 4 - - a: alice - b: 1 - c: - - 2 - - 3 - - 4 - - 5 - d: null - m: [] - numbers: - - "1" - - "2" - - "3" - - "4" - strings: - bar: 2 - baz: 3 - foo: 1 - three: 3 +- note: time/parse_nanos modules: - | package generated - - p = ns { - time.parse_ns("2006-01-02T15:04:05Z07:00", "2017-06-02T19:00:00-07:00", ns) + + p[case_id] = ns { + case := input.cases[case_id] + time.parse_ns(case.layout, case.value, ns) } - note: time/parse nanos query: data.generated.p = x + input: { cases: { + 1: { layout: "2006-01-02T15:04:05Z07:00", value: "2017-06-02T19:00:00-07:00" }, # RFC3339 + 2: { layout: "2006-01-02T15:04:05Z07:00", value: "1970-01-01T00:00:00-00:00" }, # Earliest valid time + 3: { layout: "2006-01-02T15:04:05Z07:00", value: "2262-04-11T23:47:16.854775807-00:00" }, # Latest valid time + 4: { layout: "01/02 03:04:05PM '06 -0700", value: "06/02 07:00:00PM '17 -0700" }, # Layout + 5: { layout: "02 Jan 06 15:04 -0700", value: "02 Jun 17 19:00 -0700" }, # RFC822Z + }} want_result: - - x: 1496455200000000000 + - x: { + 1: 1496455200000000000, + 2: 0, + 3: 9223372036854775807, + 4: 1496455200000000000, + 5: 1496455200000000000 + } +- note: time/parse_nanos_too_small + modules: + - | + package generated + + p = ns { + time.parse_ns("2006-01-02T15:04:05Z07:00", "1969-12-31T23:59:59.999999999-00:00", ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: 'time is before 1970-01-01 00:00:00 +0000 UTC, and cannot be converted to epoch nano seconds' +- note: time/parse_nanos_too_large + modules: + - | + package generated + + p = ns { + time.parse_ns("2006-01-02T15:04:05Z07:00", "2262-04-11T23:47:16.854775808-00:00", ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: 'time is after 2262-04-11 23:47:16.854775807 +0000 UTC, and cannot be converted to epoch nano seconds' diff --git a/test/cases/testdata/time/test-time-0949.yaml b/test/cases/testdata/time/test-time-0949.yaml index cf73b1f501..665af54073 100644 --- a/test/cases/testdata/time/test-time-0949.yaml +++ b/test/cases/testdata/time/test-time-0949.yaml @@ -1,95 +1,46 @@ cases: -- data: - a: - - 1 - - 2 - - 3 - - 4 - b: - v1: hello - v2: goodbye - c: - - x: - - true - - false - - foo - "y": - - null - - 3.14159 - z: - p: true - q: false - d: - e: - - bar - - baz - f: - - xs: - - 1 - ys: - - 2 - - xs: - - 2 - ys: - - 3 - g: - a: - - 1 - - 0 - - 0 - - 0 - b: - - 0 - - 2 - - 0 - - 0 - c: - - 0 - - 0 - - 0 - - 4 - h: - - - 1 - - 2 - - 3 - - - 2 - - 3 - - 4 - l: - - a: bob - b: -1 - c: - - 1 - - 2 - - 3 - - 4 - - a: alice - b: 1 - c: - - 2 - - 3 - - 4 - - 5 - d: null - m: [] - numbers: - - "1" - - "2" - - "3" - - "4" - strings: - bar: 2 - baz: 3 - foo: 1 - three: 3 +- note: time/parse_rfc3339_nanos modules: - - | - package generated - - p = ns { - time.parse_rfc3339_ns("2017-06-02T19:00:00-07:00", ns) - } - note: time/parse rfc3339 nanos + - | + package generated + + p[time] = ns { + time = input.cases[_] + time.parse_rfc3339_ns(time, ns) + } query: data.generated.p = x + input: { cases: [ + "2017-06-02T19:00:00-07:00", + "1970-01-01T00:00:00-00:00", + "2262-04-11T23:47:16.854775807-00:00" + ]} want_result: - - x: 1496455200000000000 + - x: { + "1970-01-01T00:00:00-00:00": 0, + "2017-06-02T19:00:00-07:00": 1496455200000000000, + "2262-04-11T23:47:16.854775807-00:00": 9223372036854775807 + } +- note: time/parse_rfc3339_nanos_too_small + modules: + - | + package generated + + p = ns { + time.parse_rfc3339_ns("1969-12-31T23:59:59.999999999-00:00", ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: 'time is before 1970-01-01 00:00:00 +0000 UTC, and cannot be converted to epoch nano seconds' +- note: time/parse_rfc3339_nanos_too_large + modules: + - | + package generated + + p = ns { + time.parse_rfc3339_ns("2262-04-11T23:47:16.854775808-00:00", ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: 'time is after 2262-04-11 23:47:16.854775807 +0000 UTC, and cannot be converted to epoch nano seconds' diff --git a/test/cases/testdata/time/test-time-0968.yaml b/test/cases/testdata/time/test-time-0968.yaml index 08bc623d71..39e5b1240f 100644 --- a/test/cases/testdata/time/test-time-0968.yaml +++ b/test/cases/testdata/time/test-time-0968.yaml @@ -1,87 +1,5 @@ cases: -- data: - a: - - 1 - - 2 - - 3 - - 4 - b: - v1: hello - v2: goodbye - c: - - x: - - true - - false - - foo - "y": - - null - - 3.14159 - z: - p: true - q: false - d: - e: - - bar - - baz - f: - - xs: - - 1 - ys: - - 2 - - xs: - - 2 - ys: - - 3 - g: - a: - - 1 - - 0 - - 0 - - 0 - b: - - 0 - - 2 - - 0 - - 0 - c: - - 0 - - 0 - - 0 - - 4 - h: - - - 1 - - 2 - - 3 - - - 2 - - 3 - - 4 - l: - - a: bob - b: -1 - c: - - 1 - - 2 - - 3 - - 4 - - a: alice - b: 1 - c: - - 2 - - 3 - - 4 - - 5 - d: null - m: [] - numbers: - - "1" - - "2" - - "3" - - "4" - strings: - bar: 2 - baz: 3 - foo: 1 - three: 3 +- note: time/add_date year month day modules: - | package generated @@ -90,7 +8,19 @@ cases: time.add_date(1585852421593912000, 3, 9, 12, __local1__) __local0__ = __local1__ } - note: time/add_date year month day + query: data.generated.p = x want_result: - x: 1705257221593912000 +- note: time/add_date too large result + modules: + - | + package generated + + p = ns { + time.add_date(0, 2262, 1, 1, ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: 'time is after 2262-04-11 23:47:16.854775807 +0000 UTC, and cannot be converted to epoch nano seconds' \ No newline at end of file diff --git a/test/cases/testdata/time/test-time-0969.yaml b/test/cases/testdata/time/test-time-0969.yaml index b554093f65..daa1b60364 100644 --- a/test/cases/testdata/time/test-time-0969.yaml +++ b/test/cases/testdata/time/test-time-0969.yaml @@ -1,87 +1,5 @@ cases: -- data: - a: - - 1 - - 2 - - 3 - - 4 - b: - v1: hello - v2: goodbye - c: - - x: - - true - - false - - foo - "y": - - null - - 3.14159 - z: - p: true - q: false - d: - e: - - bar - - baz - f: - - xs: - - 1 - ys: - - 2 - - xs: - - 2 - ys: - - 3 - g: - a: - - 1 - - 0 - - 0 - - 0 - b: - - 0 - - 2 - - 0 - - 0 - c: - - 0 - - 0 - - 0 - - 4 - h: - - - 1 - - 2 - - 3 - - - 2 - - 3 - - 4 - l: - - a: bob - b: -1 - c: - - 1 - - 2 - - 3 - - 4 - - a: alice - b: 1 - c: - - 2 - - 3 - - 4 - - 5 - d: null - m: [] - numbers: - - "1" - - "2" - - "3" - - "4" - strings: - bar: 2 - baz: 3 - foo: 1 - three: 3 +- note: time/add_date negative values modules: - | package generated @@ -90,7 +8,18 @@ cases: time.add_date(1585852421593912000, -1, -1, -1, __local1__) __local0__ = __local1__ } - note: time/add_date negative values query: data.generated.p = x want_result: - x: 1551465221593912000 +- note: time/add_date too small result + modules: + - | + package generated + + p = ns { + time.add_date(0, -1, -1, -1, ns) + } + query: data.generated.p = x + strict_error: true + want_error_code: eval_builtin_error + want_error: 'time is before 1970-01-01 00:00:00 +0000 UTC, and cannot be converted to epoch nano seconds' diff --git a/topdown/time.go b/topdown/time.go index b1e94eb651..b96f64d4b7 100644 --- a/topdown/time.go +++ b/topdown/time.go @@ -19,6 +19,25 @@ import ( var tzCache map[string]*time.Location var tzCacheMutex *sync.Mutex +var minDateAllowedForNsConversion = time.Date(1970, 01, 01, 0, 0, 0, 0, time.UTC) +var maxDateAllowedForNsConversion = time.Date(2262, 4, 11, 23, 47, 16, 854775807, time.UTC) + +func toSafeUnixNano(t time.Time) (int64, error) { + if t.Before(minDateAllowedForNsConversion) { + // Earlier dates than this exhibits undefined Time.UnixNano() behaviour. + return 0, fmt.Errorf("time is before %v, and cannot be converted to epoch nano seconds", + minDateAllowedForNsConversion) + } + + if t.After(maxDateAllowedForNsConversion) { + // Later dates than this exhibits undefined Time.UnixNano() behaviour. + return 0, fmt.Errorf("time is after %v, and cannot be converted to epoch nano seconds", + maxDateAllowedForNsConversion) + } + + return t.UnixNano(), nil +} + func builtinTimeNowNanos(bctx BuiltinContext, _ []*ast.Term, iter func(*ast.Term) error) error { return iter(bctx.Time) } @@ -40,7 +59,12 @@ func builtinTimeParseNanos(a, b ast.Value) (ast.Value, error) { return nil, err } - return ast.Number(int64ToJSONNumber(result.UnixNano())), nil + ns, err := toSafeUnixNano(result) + if err != nil { + return nil, err + } + + return ast.Number(int64ToJSONNumber(ns)), nil } func builtinTimeParseRFC3339Nanos(a ast.Value) (ast.Value, error) { @@ -55,7 +79,12 @@ func builtinTimeParseRFC3339Nanos(a ast.Value) (ast.Value, error) { return nil, err } - return ast.Number(int64ToJSONNumber(result.UnixNano())), nil + ns, err := toSafeUnixNano(result) + if err != nil { + return nil, err + } + + return ast.Number(int64ToJSONNumber(ns)), nil } func builtinParseDurationNanos(a ast.Value) (ast.Value, error) { @@ -121,7 +150,13 @@ func builtinAddDate(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Te } result := t.AddDate(years, months, days) - return iter(ast.NewTerm(ast.Number(int64ToJSONNumber(result.UnixNano())))) + + ns, err := toSafeUnixNano(result) + if err != nil { + return err + } + + return iter(ast.NewTerm(ast.Number(int64ToJSONNumber(ns)))) } func builtinDiff(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {