Repository navigation
feat(engine): support date, time and date-time formats - #15
Conversation
Extract the JSON Schema string `format` (date | date-time | time) onto string nodes and carry it through the model so the editor can pick a date/time widget. Unsupported formats and formats on non-string types are dropped.
date/time are floating (no offset); date-time is constrained to UTC (Z) with zero seconds for one canonical storage form the editor and any SDK-only writer both produce. Validates both shape and calendar/time validity, deliberately stricter than RFC 3339.
Add the format keyword (date | time | date-time) to the schema spec — the single source of truth for authoring agents — with stored forms, the canonical UTC date-time rule, and validation messages. Correct the validation reference (format was listed as not-checked) and add the node-level format field to the builder/architecture references.
tyge68
left a comment
There was a problem hiding this comment.
Solid implementation — regex shapes, calendar/leap-year math, UTC canonicalization, and empty-value handling all check out against the spec in the description, and the test coverage (leap day, 4-digit-year boundary, offset/seconds rejection) is thorough. CI green. A couple of minor, non-blocking notes inline.
|
|
||
| // Native RFC 3339 date/time hint. Only applies to strings; drives the widget | ||
| // kind and the value-shape check in validation.js. | ||
| if (kind === 'string' && DATE_FORMATS.has(schema?.format)) { |
There was a problem hiding this comment.
Precedence note: this check sits after the x-semantic-type check above, so a string with both x-semantic-type: long-text and format: date silently drops format (semanticType wins). The PR description documents enum-beats-format but not semanticType-beats-format. Worth a doc line (or a schema.test.js case) so the precedence is explicit rather than incidental.
There was a problem hiding this comment.
Good catch. Flipped it so format wins now, since format is a standard JSON Schema keyword and x-semantic-type is just vendor glue that external validators ignore anyway. Documented the precedence (enum, then format, then x-semantic-type) in schema-spec.md and added a test for the combo.
|
|
||
| // Wall-clock components. Seconds allow 60 for RFC 3339 leap seconds. | ||
| function isValidHms(hour, minute, second) { | ||
| return hour <= 23 && minute <= 59 && second <= 60; |
There was a problem hiding this comment.
second <= 60 (leap second) is only reachable via time — date-time always calls this with second hardcoded to 0 (the regex fixes seconds to 00), so leap-second tolerance is dead for date-time and live-but-undocumented for time. Not wrong, just worth confirming that's intended (a time value of 23:59:60 currently validates).
There was a problem hiding this comment.
Yeah, good point. Went the strict route and dropped leap seconds entirely, so seconds max out at 59 for time. Keeps it consistent with date-time, which only stores zero seconds anyway, and it also fixed a small bug where stuff like 12:00:60 was slipping through. Added tests for 23:59:60 and 12:00:60 both getting rejected. Easy to loosen later if anyone ever actually needs it.
| expect(when.kind).to.equal('string'); | ||
| expect(when.format).to.equal(format); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Given the description explicitly calls out "if enum is also present, enum applies and format has no effect," a direct test for that combination (string with both enum and format: date) would close the loop — right now it's only asserted in prose.
There was a problem hiding this comment.
Added it. It was only asserted in prose before, so good call.
# [0.5.0](v0.4.0...v0.5.0) (2026-09-04) ### Features * **engine:** support date, time and date-time formats ([#15](#15)) ([9e1e048](9e1e048))
|
🎉 This PR is included in version 0.5.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
Adds support for the JSON Schema
formatkeyword —date,time, anddate-time— to the state engine. String nodes carry theirformatthrough the model so consumers can render the matching date/time control, and stored values are validated for shape and calendar/clock validity.What changed
Schema → node: the compiler extracts
format(date | time | date-time) onto string nodes (schema.js) and carries it through the model (model.js). Only these three values are recognized;formaton a non-string, an unsupported value, or alongsideenumis dropped.Validation:
validation.jsvalidates the value's shape and real calendar/clock validity, reported underkeyword: "format"withparams: { format }:formatdateYYYY-MM-DD(real date)2026-02-30,08/14/2026Must be a valid date.timeHH:MM/HH:MM:SS(24h)24:00,9:5Must be a valid time.date-time…:00Z, zero seconds+02:00), non-zero seconds, missingZMust be a valid date and time.dateandtimeare floating (no offset).date-timeis an absolute instant constrained to canonical UTC — deliberately stricter than RFC 3339 (onlyZ, zero seconds) so there is one canonical storage form that every writer produces, comparable byte-for-byte.Empty/absent values are not validated (consistent with the existing empty-value semantics).
Node shape
String nodes may now carry an optional
formatfield (peer toenumValues):Docs
schema-spec.md—formatadded to the supported-constraints section with stored forms, the canonical UTC rule, and validation messages (plus the complete example).validation-reference.md— worked date/time examples added; corrected the note that previously listedformatas not checked.architecture.md,model-builder.md,schema-builder.md— node-shape references updated.Testing
validation.test.js— date/time/date-time cases incl. impossible dates, leap day, out-of-range time, offset/seconds rejection, and the 4-digit-year boundary.schema.test.js—formatcaptured for the three values, dropped for unsupported values and non-string types.Compatibility
format: date | time | date-time: previouslyformatwas ignored (any string validated); now these values are validated. In particular,date-timevalues that aren't canonical UTC (offsets, non-zero seconds) will now be reported invalid.