From 4bf9eddd87e71f3502299b17c28741d75a2a006e Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 7 Sep 2026 11:01:29 -0700 Subject: [PATCH 1/8] fix(typescript-effect-schema): validate date and time formats --- .../TypeScriptEffectSchemaRenderer.ts | 6 +++--- test/languages.ts | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/quicktype-core/src/language/TypeScriptEffectSchema/TypeScriptEffectSchemaRenderer.ts b/packages/quicktype-core/src/language/TypeScriptEffectSchema/TypeScriptEffectSchemaRenderer.ts index b61a3764b2..ac2d00bf12 100644 --- a/packages/quicktype-core/src/language/TypeScriptEffectSchema/TypeScriptEffectSchemaRenderer.ts +++ b/packages/quicktype-core/src/language/TypeScriptEffectSchema/TypeScriptEffectSchemaRenderer.ts @@ -195,11 +195,11 @@ export class TypeScriptEffectSchemaRenderer extends ConvenienceRenderer { ? 'S.transform(S.Literal("true", "false"), S.Boolean, { strict: true, decode: (value) => value === "true", encode: (value) => value ? "true" : "false" })' : 'S.Literal("true", "false")'; if (_transformedStringType.kind === "date") - return "S.String.pipe(S.pattern(/^\\d{4}-\\d{2}-\\d{2}$/))"; + return 'S.String.pipe(S.pattern(/^\\d{4}-(?:0[1-9]|1[0-2])-(?:[0-2]\\d|3[01])$/), S.filter(value => (new Date(value + "T00:00:00Z").toJSON() || "").slice(0, 10) === value))'; if (_transformedStringType.kind === "time") - return "S.String.pipe(S.pattern(/^\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/))"; + return "S.String.pipe(S.pattern(/^(?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$/i))"; if (_transformedStringType.kind === "date-time") - return "S.String.pipe(S.pattern(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/))"; + return 'S.String.pipe(S.pattern(/^\\d{4}-(?:0[1-9]|1[0-2])-(?:[0-2]\\d|3[01])T(?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$/i), S.filter(value => (new Date(value.slice(0, 10) + "T00:00:00Z").toJSON() || "").slice(0, 10) === value.slice(0, 10)))'; if (_transformedStringType.kind === "integer-string") return coerceStrings ? "S.NumberFromString.pipe(S.int())" diff --git a/test/languages.ts b/test/languages.ts index ebfdb206ec..8a7be4ef7b 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -1787,6 +1787,8 @@ export const TypeScriptEffectSchemaLanguage: Language = { "minmaxlength", "bool-string", "date-time", + "date", + "time", "integer-string", "pattern", "minmax", From 94441d5944e659ac06a36cc493665d6750247e78 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 7 Sep 2026 21:46:25 -0400 Subject: [PATCH 2/8] fix(python): validate ISO date-times (#3518) --- .../quicktype-core/src/language/Python/JSONPythonRenderer.ts | 4 ++-- test/languages.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/quicktype-core/src/language/Python/JSONPythonRenderer.ts b/packages/quicktype-core/src/language/Python/JSONPythonRenderer.ts index 738748a087..5ecbb55b7e 100644 --- a/packages/quicktype-core/src/language/Python/JSONPythonRenderer.ts +++ b/packages/quicktype-core/src/language/Python/JSONPythonRenderer.ts @@ -521,7 +521,7 @@ export class JSONPythonRenderer extends PythonRenderer { this.emitLine( "assert isinstance(x, str) and ", this.withModuleImport("re"), - '.match(r"^\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})?$", x)', + '.match(r"^\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:[Zz]|[+-]\\d{2}:\\d{2})?$", x)', ); this.emitLine("return dateutil.parser.parse(x).timetz()"); }, @@ -542,7 +542,7 @@ export class JSONPythonRenderer extends PythonRenderer { ], () => { this._haveDateutil = true; - this.emitLine("return dateutil.parser.parse(x)"); + this.emitLine("return dateutil.parser.isoparse(x)"); }, ); } diff --git a/test/languages.ts b/test/languages.ts index 8a7be4ef7b..539bcce001 100644 --- a/test/languages.ts +++ b/test/languages.ts @@ -297,6 +297,8 @@ export const PythonLanguage: Language = { "union", "no-defaults", "date-time", + "date", + "time", "integer-string", "bool-string", "uuid", From 9972111a769f70210e3f9f62bb71483c9af59ea2 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 7 Sep 2026 07:27:56 -0700 Subject: [PATCH 3/8] fix(javascript): reject malformed date strings --- .../language/JavaScript/JavaScriptRenderer.ts | 5 +++ .../JavaScriptPropTypesRenderer.ts | 2 +- .../schema/date-time.8.fail.date-time.json | 7 ++++ test/unit/javascript-date-api.test.ts | 42 +++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 test/inputs/schema/date-time.8.fail.date-time.json create mode 100644 test/unit/javascript-date-api.test.ts diff --git a/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts b/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts index 63d5b5cd66..6186461959 100644 --- a/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts +++ b/packages/quicktype-core/src/language/JavaScript/JavaScriptRenderer.ts @@ -445,10 +445,15 @@ ${hasArrayConstraints ? ' if ((typ.min !== undefined && val.length < typ. if (val === null) { return null; } + if (!(val instanceof Date) && (typeof val !== "string" || !/^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:[0-2][0-9]|3[01])(?:T(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:[.][0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9]))?$/i.test(val))) + return invalidValue(l("Date"), val, key, parent); const d = new Date(val); if (isNaN(d.valueOf())) { return invalidValue(l("Date"), val, key, parent); } + const date = typeof val === "string" ? val.slice(0, 10) : null; + if (date !== null && new Date(date + "T00:00:00Z").toISOString().slice(0, 10) !== date) + return invalidValue(l("Date"), val, key, parent); return d; } diff --git a/packages/quicktype-core/src/language/JavaScriptPropTypes/JavaScriptPropTypesRenderer.ts b/packages/quicktype-core/src/language/JavaScriptPropTypes/JavaScriptPropTypesRenderer.ts index 17b329a2d5..d03969986c 100644 --- a/packages/quicktype-core/src/language/JavaScriptPropTypes/JavaScriptPropTypesRenderer.ts +++ b/packages/quicktype-core/src/language/JavaScriptPropTypes/JavaScriptPropTypesRenderer.ts @@ -209,7 +209,7 @@ export class JavaScriptPropTypesRenderer extends ConvenienceRenderer { return '(props, name) => props[name] == null || /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i.test(props[name]) ? null : new Error("Expected UUID")'; } if (transformedStringType.kind === "date-time") { - return '(props, name) => props[name] == null || typeof props[name] === "string" && !Number.isNaN(Date.parse(props[name])) ? null : new Error("Expected date-time")'; + return '(props, name) => props[name] == null || typeof props[name] === "string" && /^(\\d{4}-(?:0[1-9]|1[0-2])-(?:[0-2]\\d|3[01]))(?:T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d))?$/i.test(props[name]) && !Number.isNaN(Date.parse(`${props[name].slice(0, 10)}T00:00:00Z`)) && new Date(Date.parse(`${props[name].slice(0, 10)}T00:00:00Z`)).toISOString().slice(0, 10) === props[name].slice(0, 10) ? null : new Error("Expected date-time")'; } return "PropTypes.string"; }, diff --git a/test/inputs/schema/date-time.8.fail.date-time.json b/test/inputs/schema/date-time.8.fail.date-time.json new file mode 100644 index 0000000000..23a1d94a10 --- /dev/null +++ b/test/inputs/schema/date-time.8.fail.date-time.json @@ -0,0 +1,7 @@ +{ + "date": "1985-04-12", + "time": "23:20:50.52Z", + "date-time": "1", + "union-array": ["1985-04-12", "23:20:50.52Z"], + "complex-union-array": ["2018-08-13T21:31:01+00:10", "foo", 123] +} diff --git a/test/unit/javascript-date-api.test.ts b/test/unit/javascript-date-api.test.ts new file mode 100644 index 0000000000..f96c04e55d --- /dev/null +++ b/test/unit/javascript-date-api.test.ts @@ -0,0 +1,42 @@ +import { InputData, JSONSchemaInput, quicktype } from "quicktype-core"; +import { expect, test } from "vitest"; + +interface GeneratedConverters { + topLevelToJson: (value: { when: Date }) => string; +} + +async function converters(): Promise { + const schemaInput = new JSONSchemaInput(undefined); + await schemaInput.addSource({ + name: "TopLevel", + schema: JSON.stringify({ + type: "object", + properties: { + when: { type: "string", format: "date-time" }, + }, + required: ["when"], + }), + }); + const inputData = new InputData(); + inputData.addInput(schemaInput); + const result = await quicktype({ inputData, lang: "javascript" }); + const generatedModule: { exports: Partial } = { + exports: {}, + }; + new Function("exports", "module", result.lines.join("\n"))( + generatedModule.exports, + generatedModule, + ); + return generatedModule.exports as GeneratedConverters; +} + +test("JavaScript converter validates Date instances", async () => { + const { topLevelToJson } = await converters(); + + expect( + JSON.parse(topLevelToJson({ when: new Date("2024-02-29T00:00:00Z") })), + ).toEqual({ when: "2024-02-29T00:00:00.000Z" }); + expect(() => topLevelToJson({ when: new Date(Number.NaN) })).toThrow( + "Expected Date", + ); +}); From d6ee70d8b68fa392b2e7eb85d20eb7717aa2c03e Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 7 Sep 2026 07:37:38 -0700 Subject: [PATCH 4/8] fix(dart): accept lowercase RFC 3339 dates --- .../src/language/Dart/DartRenderer.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/quicktype-core/src/language/Dart/DartRenderer.ts b/packages/quicktype-core/src/language/Dart/DartRenderer.ts index ea08e91091..01ada86b05 100644 --- a/packages/quicktype-core/src/language/Dart/DartRenderer.ts +++ b/packages/quicktype-core/src/language/Dart/DartRenderer.ts @@ -53,6 +53,7 @@ export class DartRenderer extends ConvenienceRenderer { >(); private _needEnumValues = false; + private _needDateTimeParser = false; private classCounter = 0; @@ -539,6 +540,7 @@ export class DartRenderer extends ConvenienceRenderer { ]; case "date-time": case "date": + this._needDateTimeParser = true; if ( (transformedStringType.isNullable || isNullable) && !this._options.requiredProperties @@ -546,13 +548,13 @@ export class DartRenderer extends ConvenienceRenderer { return [ dynamic, " == null ? null : ", - "DateTime.parse(", + "_parseDateTime(", dynamic, ")", ]; } - return ["DateTime.parse(", dynamic, ")"]; + return ["_parseDateTime(", dynamic, ")"]; default: return dynamic; } @@ -1065,5 +1067,14 @@ export class DartRenderer extends ConvenienceRenderer { if (this._needEnumValues) { this.emitEnumValues(); } + if (this._needDateTimeParser) + this.emitMultiline(` +DateTime _parseDateTime(String value) { + final date = value.substring(0, 10); + if (!DateTime.parse(date + 'T00:00:00Z').toIso8601String().startsWith(date)) { + throw FormatException('Invalid date-time', value); + } + return DateTime.parse(value.toUpperCase()); +}`); } } From 56cc5169e0c5792a50f48760e8869adfbab872b6 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 7 Sep 2026 07:29:49 -0700 Subject: [PATCH 5/8] fix(swift): accept lowercase RFC 3339 dates --- .../src/language/Swift/SwiftRenderer.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts b/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts index 6d22b46ed2..4963e2aaff 100644 --- a/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts +++ b/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts @@ -749,15 +749,19 @@ export class SwiftRenderer extends ConvenienceRenderer { this.emitBlock( "if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)", () => { - this.emitLine( - "decoder.dateDecodingStrategy = .iso8601", - ); + this.emitMultiline(`decoder.dateDecodingStrategy = .custom { decoder in + let dateStr = try decoder.singleValueContainer().decode(String.self).uppercased() + let dateData = try JSONEncoder().encode(dateStr) + let dateDecoder = JSONDecoder() + dateDecoder.dateDecodingStrategy = .iso8601 + return try dateDecoder.decode(Date.self, from: dateData) +}`); }, ); } else { this.emitMultiline(`decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in let container = try decoder.singleValueContainer() - let dateStr = try container.decode(String.self) + let dateStr = try container.decode(String.self).uppercased() let formatter = DateFormatter() formatter.calendar = Calendar(identifier: .iso8601) From 4da7dab49c6ad697b9fe701c7865f918fd2388c7 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 7 Sep 2026 10:56:56 -0700 Subject: [PATCH 6/8] fix: validate native ISO date-times --- .../src/language/CPlusPlus/CPlusPlusRenderer.ts | 2 +- .../src/language/Objective-C/ObjectiveCRenderer.ts | 5 ++++- packages/quicktype-core/src/language/Php/PhpRenderer.ts | 8 ++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/quicktype-core/src/language/CPlusPlus/CPlusPlusRenderer.ts b/packages/quicktype-core/src/language/CPlusPlus/CPlusPlusRenderer.ts index ab8fce746e..5f51b96a09 100644 --- a/packages/quicktype-core/src/language/CPlusPlus/CPlusPlusRenderer.ts +++ b/packages/quicktype-core/src/language/CPlusPlus/CPlusPlusRenderer.ts @@ -1542,7 +1542,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer { key, ") != j.end() && !std::regex_match(j.at(", key, - ').get(), std::regex("^[0-9]{4}-[0-9]{2}-[0-9]{2}T.*$"))) throw std::runtime_error("Expected date-time");', + ').get(), std::regex("^(?:(?:[0-9]{4}-(?:0[13578]|1[02])-(?:0[1-9]|[12][0-9]|3[01]))|(?:[0-9]{4}-(?:0[469]|11)-(?:0[1-9]|[12][0-9]|30))|(?:[0-9]{4}-02-(?:0[1-9]|1[0-9]|2[0-8]))|(?:(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:[02468][048]|[13579][26])00)-02-29))[Tt](?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:[.][0-9]+)?(?:[Zz]|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])?$"))) throw std::runtime_error("Expected date-time");', ); } diff --git a/packages/quicktype-core/src/language/Objective-C/ObjectiveCRenderer.ts b/packages/quicktype-core/src/language/Objective-C/ObjectiveCRenderer.ts index eb970fda54..8d19dd50f5 100644 --- a/packages/quicktype-core/src/language/Objective-C/ObjectiveCRenderer.ts +++ b/packages/quicktype-core/src/language/Objective-C/ObjectiveCRenderer.ts @@ -936,7 +936,10 @@ export class ObjectiveCRenderer extends ConvenienceRenderer { } if (property.type.kind === "date-time") { this.emitLine( - `if (dict[@"${objectiveCStringEscape(jsonName)}"] && [dict[@"${objectiveCStringEscape(jsonName)}"] rangeOfString:@"^[0-9]{4}-[0-9]{2}-[0-9]{2}T" options:NSRegularExpressionSearch].location == NSNotFound) return nil;`, + `if (dict[@"${objectiveCStringEscape(jsonName)}"] && [dict[@"${objectiveCStringEscape(jsonName)}"] rangeOfString:@"^[0-9]{4}-[0-9]{2}-[0-9]{2}[Tt](?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\\\\.[0-9]+)?(?:[Zz]|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])?$" options:NSRegularExpressionSearch].location == NSNotFound) return nil;`, + ); + this.emitLine( + `if (dict[@"${objectiveCStringEscape(jsonName)}"] && ![[[[NSISO8601DateFormatter new] stringFromDate:[[NSISO8601DateFormatter new] dateFromString:[[dict[@"${objectiveCStringEscape(jsonName)}"] substringToIndex:10] stringByAppendingString:@"T00:00:00Z"]]] substringToIndex:10] isEqualToString:[dict[@"${objectiveCStringEscape(jsonName)}"] substringToIndex:10]]) return nil;`, ); } if ( diff --git a/packages/quicktype-core/src/language/Php/PhpRenderer.ts b/packages/quicktype-core/src/language/Php/PhpRenderer.ts index f468f82635..4d3dde8097 100644 --- a/packages/quicktype-core/src/language/Php/PhpRenderer.ts +++ b/packages/quicktype-core/src/language/Php/PhpRenderer.ts @@ -798,6 +798,14 @@ export class PhpRenderer extends ConvenienceRenderer { (transformedStringType) => { if (transformedStringType.kind === "date-time") { this.emitLine("$tmp = ", "new DateTime(", args, ");"); + this.emitLine("$errors = DateTime::getLastErrors();"); + this.emitBlock( + "if ($errors && ($errors['warning_count'] || $errors['error_count']))", + () => + this.emitLine( + "throw new Exception('Invalid date-time');", + ), + ); this.transformDateTime(className, "", ["$tmp"]); this.emitLine("return $tmp;"); return; From f362b87ccb088868b2455159d702fb4bcdd51f23 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 7 Sep 2026 17:48:29 -0700 Subject: [PATCH 7/8] Add native date-time validation coverage --- test/inputs/schema/date-time.12.fail.date-time.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/inputs/schema/date-time.12.fail.date-time.json diff --git a/test/inputs/schema/date-time.12.fail.date-time.json b/test/inputs/schema/date-time.12.fail.date-time.json new file mode 100644 index 0000000000..fe33668ae1 --- /dev/null +++ b/test/inputs/schema/date-time.12.fail.date-time.json @@ -0,0 +1,7 @@ +{ + "date": "1985-04-12", + "time": "23:20:50.52Z", + "date-time": "2023-00-15T12:00:00Z", + "union-array": ["1985-04-12", "23:20:50.52Z"], + "complex-union-array": ["2018-08-13T21:31:01+00:10", "foo", 123] +} From 02215b0c05425cf334293eb94cc2f27d9ca3b1bd Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 7 Sep 2026 18:04:31 -0700 Subject: [PATCH 8/8] fix(java): reject invalid calendar dates --- .../src/language/Java/DateTimeProvider.ts | 12 ++++-- .../src/language/Java/JavaRenderer.ts | 3 ++ test/unit/java-date-time-emission.test.ts | 37 +++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 test/unit/java-date-time-emission.test.ts diff --git a/packages/quicktype-core/src/language/Java/DateTimeProvider.ts b/packages/quicktype-core/src/language/Java/DateTimeProvider.ts index 2535fcdfac..6298f885a3 100644 --- a/packages/quicktype-core/src/language/Java/DateTimeProvider.ts +++ b/packages/quicktype-core/src/language/Java/DateTimeProvider.ts @@ -6,6 +6,7 @@ export abstract class JavaDateTimeProvider { public constructor( protected readonly _renderer: JavaRenderer, protected readonly _className: string, + protected readonly _useStrictDateTime = false, ) {} public abstract keywords: string[]; @@ -105,15 +106,20 @@ export class Java8DateTimeProvider extends JavaDateTimeProvider { ".appendOptional(DateTimeFormatter.ISO_INSTANT)", ); this._renderer.emitLine( - '.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SX"))', + '.appendOptional(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SX"))', ); this._renderer.emitLine( - '.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"))', + '.appendOptional(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ssX"))', ); this._renderer.emitLine( - '.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))', + '.appendOptional(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"))', ); this._renderer.emitLine(".toFormatter()"); + if (this._useStrictDateTime) { + this._renderer.emitLine( + ".withResolverStyle(java.time.format.ResolverStyle.STRICT)", + ); + } this._renderer.emitLine(".withZone(ZoneOffset.UTC);"); }), ); diff --git a/packages/quicktype-core/src/language/Java/JavaRenderer.ts b/packages/quicktype-core/src/language/Java/JavaRenderer.ts index 3817dcc9f0..a18ad85563 100644 --- a/packages/quicktype-core/src/language/Java/JavaRenderer.ts +++ b/packages/quicktype-core/src/language/Java/JavaRenderer.ts @@ -78,6 +78,9 @@ export class JavaRenderer extends ConvenienceRenderer { this._dateTimeProvider = new Java8DateTimeProvider( this, this._converterClassname, + [...renderContext.typeGraph.allTypesUnordered()].some( + (t) => t.kind === "date-time", + ), ); break; } diff --git a/test/unit/java-date-time-emission.test.ts b/test/unit/java-date-time-emission.test.ts new file mode 100644 index 0000000000..f0583bf419 --- /dev/null +++ b/test/unit/java-date-time-emission.test.ts @@ -0,0 +1,37 @@ +import { InputData, JSONSchemaInput, quicktype } from "quicktype-core"; +import { describe, expect, test } from "vitest"; + +async function renderJava( + propertySchema: object, + name = "TopLevel", +): Promise { + const schemaInput = new JSONSchemaInput(undefined); + await schemaInput.addSource({ + name, + schema: JSON.stringify({ + type: "object", + properties: { value: propertySchema }, + }), + }); + const inputData = new InputData(); + inputData.addInput(schemaInput); + + return (await quicktype({ inputData, lang: "java" })).lines.join("\n"); +} + +describe("Java strict calendar validation", () => { + test("are emitted only for date-time types", async () => { + const plain = await renderJava({ type: "string" }); + const dateTime = await renderJava({ + type: "string", + format: "date-time", + }); + + expect(plain).not.toContain("ResolverStyle"); + expect(await renderJava({ type: "string" }, "ResolverStyle")).toContain( + "class ResolverStyle", + ); + expect(dateTime).toContain("ResolverStyle.STRICT"); + expect(dateTime).toContain("DATE_TIME_FORMATTER"); + }); +});