Skip to content

Commit

Permalink
encoding/jsonschema: implement date-time format
Browse files Browse the repository at this point in the history
This is a bit more restrictive than the spec allows,
as the TODO comment suggests, but it's probably OK for
now, and better than not supporting date-time at all.

For #3479.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: Ib0302c1fcf5b6a2f0ca669d17907a599a5d4f11f
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1202107
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
  • Loading branch information
rogpeppe committed Oct 7, 2024
1 parent 44cdfe7 commit c54ac60
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 303 deletions.
9 changes: 8 additions & 1 deletion encoding/jsonschema/constraints_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var formatFuncs = sync.OnceValue(func() map[string]formatFuncInfo {
"byte": {openAPI, formatTODO},
"data": {openAPI, formatTODO},
"date": {vfrom(VersionDraft7), formatTODO},
"date-time": {allVersions | openAPI, formatTODO},
"date-time": {allVersions | openAPI, formatDateTime},
"double": {openAPI, formatTODO},
"duration": {vfrom(VersionDraft2019_09), formatTODO},
"email": {allVersions | openAPI, formatTODO},
Expand Down Expand Up @@ -93,4 +93,11 @@ func formatURIReference(n cue.Value, s *state) {
s.add(n, stringType, ast.NewSel(s.addImport(n, "net"), "URL"))
}

func formatDateTime(n cue.Value, s *state) {
// TODO this is a bit stricter than the spec, because the spec
// allows lower-case "T" and "Z", and leap seconds, but
// it's not bad for now.
s.add(n, stringType, ast.NewSel(s.addImport(n, "time"), "Time"))
}

func formatTODO(n cue.Value, s *state) {}
16 changes: 8 additions & 8 deletions encoding/jsonschema/external_teststats.txt
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# Generated by teststats. DO NOT EDIT
v2:
schema extract (pass / total): 993 / 1363 = 72.9%
tests (pass / total): 3514 / 4803 = 73.2%
tests on extracted schemas (pass / total): 3514 / 3801 = 92.4%
tests (pass / total): 3513 / 4803 = 73.1%
tests on extracted schemas (pass / total): 3513 / 3801 = 92.4%

v3:
schema extract (pass / total): 981 / 1363 = 72.0%
tests (pass / total): 3475 / 4803 = 72.4%
tests on extracted schemas (pass / total): 3475 / 3757 = 92.5%
tests (pass / total): 3474 / 4803 = 72.3%
tests on extracted schemas (pass / total): 3474 / 3757 = 92.5%

Optional tests

v2:
schema extract (pass / total): 230 / 274 = 83.9%
tests (pass / total): 1465 / 2372 = 61.8%
tests on extracted schemas (pass / total): 1465 / 2258 = 64.9%
tests (pass / total): 1505 / 2372 = 63.4%
tests on extracted schemas (pass / total): 1505 / 2258 = 66.7%

v3:
schema extract (pass / total): 230 / 274 = 83.9%
tests (pass / total): 1455 / 2372 = 61.3%
tests on extracted schemas (pass / total): 1455 / 2258 = 64.4%
tests (pass / total): 1495 / 2372 = 63.0%
tests on extracted schemas (pass / total): 1495 / 2258 = 66.2%
Original file line number Diff line number Diff line change
Expand Up @@ -59,48 +59,40 @@
{
"description": "a valid date-time with a leap second, UTC",
"data": "1998-12-31T23:59:60Z",
"valid": true
"valid": true,
"skip": {
"v2": "6 errors in empty disjunction:\nconflicting values \"1998-12-31T23:59:60Z\" and [...] (mismatched types string and list):\n generated.cue:4:1\n generated.cue:4:36\n instance.json:1:1\nconflicting values \"1998-12-31T23:59:60Z\" and bool (mismatched types string and bool):\n generated.cue:4:1\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"1998-12-31T23:59:60Z\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"1998-12-31T23:59:60Z\" and number (mismatched types string and number):\n generated.cue:4:1\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"1998-12-31T23:59:60Z\" and {...} (mismatched types string and struct):\n generated.cue:4:1\n generated.cue:4:44\n instance.json:1:1\ninvalid value \"1998-12-31T23:59:60Z\" (does not satisfy time.Time): error in call to time.Time: invalid time \"1998-12-31T23:59:60Z\":\n generated.cue:1:1\n instance.json:1:1\n",
"v3": "conflicting values \"1998-12-31T23:59:60Z\" and [...] (mismatched types string and list):\n generated.cue:4:36\n instance.json:1:1\nconflicting values \"1998-12-31T23:59:60Z\" and bool (mismatched types string and bool):\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"1998-12-31T23:59:60Z\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"1998-12-31T23:59:60Z\" and number (mismatched types string and number):\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"1998-12-31T23:59:60Z\" and {...} (mismatched types string and struct):\n generated.cue:4:44\n instance.json:1:1\ninvalid value \"1998-12-31T23:59:60Z\" (does not satisfy time.Time): error in call to time.Time: invalid time \"1998-12-31T23:59:60Z\":\n generated.cue:1:1\n instance.json:1:1\n"
}
},
{
"description": "a valid date-time with a leap second, with minus offset",
"data": "1998-12-31T15:59:60.123-08:00",
"valid": true
"valid": true,
"skip": {
"v2": "6 errors in empty disjunction:\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and [...] (mismatched types string and list):\n generated.cue:4:1\n generated.cue:4:36\n instance.json:1:1\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and bool (mismatched types string and bool):\n generated.cue:4:1\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and number (mismatched types string and number):\n generated.cue:4:1\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and {...} (mismatched types string and struct):\n generated.cue:4:1\n generated.cue:4:44\n instance.json:1:1\ninvalid value \"1998-12-31T15:59:60.123-08:00\" (does not satisfy time.Time): error in call to time.Time: invalid time \"1998-12-31T15:59:60.123-08:00\":\n generated.cue:1:1\n instance.json:1:1\n",
"v3": "conflicting values \"1998-12-31T15:59:60.123-08:00\" and [...] (mismatched types string and list):\n generated.cue:4:36\n instance.json:1:1\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and bool (mismatched types string and bool):\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and number (mismatched types string and number):\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"1998-12-31T15:59:60.123-08:00\" and {...} (mismatched types string and struct):\n generated.cue:4:44\n instance.json:1:1\ninvalid value \"1998-12-31T15:59:60.123-08:00\" (does not satisfy time.Time): error in call to time.Time: invalid time \"1998-12-31T15:59:60.123-08:00\":\n generated.cue:1:1\n instance.json:1:1\n"
}
},
{
"description": "an invalid date-time past leap second, UTC",
"data": "1998-12-31T23:59:61Z",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "an invalid date-time with leap second on a wrong minute, UTC",
"data": "1998-12-31T23:58:60Z",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "an invalid date-time with leap second on a wrong hour, UTC",
"data": "1998-12-31T22:59:60Z",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "an invalid day in date-time string",
"data": "1990-02-31T15:59:59.123-08:00",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "an invalid offset in date-time string",
Expand All @@ -114,70 +106,46 @@
{
"description": "an invalid closing Z after time-zone offset",
"data": "1963-06-19T08:30:06.28123+01:00Z",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "an invalid date-time string",
"data": "06/19/1963 08:30:06 PST",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "case-insensitive T and Z",
"data": "1963-06-19t08:30:06.283185z",
"valid": true
"valid": true,
"skip": {
"v2": "6 errors in empty disjunction:\nconflicting values \"1963-06-19t08:30:06.283185z\" and [...] (mismatched types string and list):\n generated.cue:4:1\n generated.cue:4:36\n instance.json:1:1\nconflicting values \"1963-06-19t08:30:06.283185z\" and bool (mismatched types string and bool):\n generated.cue:4:1\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"1963-06-19t08:30:06.283185z\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"1963-06-19t08:30:06.283185z\" and number (mismatched types string and number):\n generated.cue:4:1\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"1963-06-19t08:30:06.283185z\" and {...} (mismatched types string and struct):\n generated.cue:4:1\n generated.cue:4:44\n instance.json:1:1\ninvalid value \"1963-06-19t08:30:06.283185z\" (does not satisfy time.Time): error in call to time.Time: invalid time \"1963-06-19t08:30:06.283185z\":\n generated.cue:1:1\n instance.json:1:1\n",
"v3": "conflicting values \"1963-06-19t08:30:06.283185z\" and [...] (mismatched types string and list):\n generated.cue:4:36\n instance.json:1:1\nconflicting values \"1963-06-19t08:30:06.283185z\" and bool (mismatched types string and bool):\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"1963-06-19t08:30:06.283185z\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"1963-06-19t08:30:06.283185z\" and number (mismatched types string and number):\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"1963-06-19t08:30:06.283185z\" and {...} (mismatched types string and struct):\n generated.cue:4:44\n instance.json:1:1\ninvalid value \"1963-06-19t08:30:06.283185z\" (does not satisfy time.Time): error in call to time.Time: invalid time \"1963-06-19t08:30:06.283185z\":\n generated.cue:1:1\n instance.json:1:1\n"
}
},
{
"description": "only RFC3339 not all of ISO 8601 are valid",
"data": "2013-350T01:01:01",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "invalid non-padded month dates",
"data": "1963-6-19T08:30:06.283185Z",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "invalid non-padded day dates",
"data": "1963-06-1T08:30:06.283185Z",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "invalid non-ASCII '৪' (a Bengali 4) in date portion",
"data": "1963-06-1৪T00:00:00Z",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
},
{
"description": "invalid non-ASCII '৪' (a Bengali 4) in time portion",
"data": "1963-06-11T0৪:00:00Z",
"valid": false,
"skip": {
"v2": "unexpected success",
"v3": "unexpected success"
}
"valid": false
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,11 @@
{
"description": "invalid date-time string is only an annotation by default",
"data": "1990-02-31T15:59:60.123-08:00",
"valid": true
"valid": true,
"skip": {
"v2": "6 errors in empty disjunction:\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and [...] (mismatched types string and list):\n generated.cue:4:1\n generated.cue:4:36\n instance.json:1:1\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and bool (mismatched types string and bool):\n generated.cue:4:1\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and number (mismatched types string and number):\n generated.cue:4:1\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and {...} (mismatched types string and struct):\n generated.cue:4:1\n generated.cue:4:44\n instance.json:1:1\ninvalid value \"1990-02-31T15:59:60.123-08:00\" (does not satisfy time.Time): error in call to time.Time: invalid time \"1990-02-31T15:59:60.123-08:00\":\n generated.cue:1:1\n instance.json:1:1\n",
"v3": "conflicting values \"1990-02-31T15:59:60.123-08:00\" and [...] (mismatched types string and list):\n generated.cue:4:36\n instance.json:1:1\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and bool (mismatched types string and bool):\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and number (mismatched types string and number):\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"1990-02-31T15:59:60.123-08:00\" and {...} (mismatched types string and struct):\n generated.cue:4:44\n instance.json:1:1\ninvalid value \"1990-02-31T15:59:60.123-08:00\" (does not satisfy time.Time): error in call to time.Time: invalid time \"1990-02-31T15:59:60.123-08:00\":\n generated.cue:1:1\n instance.json:1:1\n"
}
}
]
},
Expand Down Expand Up @@ -657,8 +661,8 @@
"data": "//foo.bar/?baz=qux#quux",
"valid": true,
"skip": {
"v2": "6 errors in empty disjunction:\nconflicting values \"//foo.bar/?baz=qux#quux\" and [...] (mismatched types string and list):\n generated.cue:4:1\n generated.cue:4:37\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and bool (mismatched types string and bool):\n generated.cue:4:1\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and number (mismatched types string and number):\n generated.cue:4:1\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and {...} (mismatched types string and struct):\n generated.cue:4:1\n generated.cue:4:45\n instance.json:1:1\ninvalid value \"//foo.bar/?baz=qux#quux\" (does not satisfy net.AbsURL): error in call to net.AbsURL: URL \"//foo.bar/?baz=qux#quux\" is not absolute:\n generated.cue:1:1\n instance.json:1:1\n",
"v3": "conflicting values \"//foo.bar/?baz=qux#quux\" and [...] (mismatched types string and list):\n generated.cue:4:37\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and bool (mismatched types string and bool):\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and number (mismatched types string and number):\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and {...} (mismatched types string and struct):\n generated.cue:4:45\n instance.json:1:1\ninvalid value \"//foo.bar/?baz=qux#quux\" (does not satisfy net.AbsURL): error in call to net.AbsURL: URL \"//foo.bar/?baz=qux#quux\" is not absolute:\n generated.cue:1:1\n instance.json:1:1\n"
"v2": "6 errors in empty disjunction:\nconflicting values \"//foo.bar/?baz=qux#quux\" and [...] (mismatched types string and list):\n generated.cue:4:1\n generated.cue:4:37\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and bool (mismatched types string and bool):\n generated.cue:4:1\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and number (mismatched types string and number):\n generated.cue:4:1\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and {...} (mismatched types string and struct):\n generated.cue:4:1\n generated.cue:4:45\n instance.json:1:1\ninvalid value \"//foo.bar/?baz=qux#quux\" (does not satisfy net.AbsURL): error in call to net.AbsURL: URL is not absolute:\n generated.cue:1:1\n instance.json:1:1\n",
"v3": "conflicting values \"//foo.bar/?baz=qux#quux\" and [...] (mismatched types string and list):\n generated.cue:4:37\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and bool (mismatched types string and bool):\n generated.cue:4:8\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and null (mismatched types string and null):\n generated.cue:4:1\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and number (mismatched types string and number):\n generated.cue:4:15\n instance.json:1:1\nconflicting values \"//foo.bar/?baz=qux#quux\" and {...} (mismatched types string and struct):\n generated.cue:4:45\n instance.json:1:1\ninvalid value \"//foo.bar/?baz=qux#quux\" (does not satisfy net.AbsURL): error in call to net.AbsURL: URL is not absolute:\n generated.cue:1:1\n instance.json:1:1\n"
}
}
]
Expand Down
Loading

0 comments on commit c54ac60

Please sign in to comment.