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/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts b/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts index 4963e2aaff..7130022215 100644 --- a/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts +++ b/packages/quicktype-core/src/language/Swift/SwiftRenderer.ts @@ -48,6 +48,8 @@ export class SwiftRenderer extends ConvenienceRenderer { private _needNull = false; + private _haveDateTime = false; + public constructor( targetLanguage: TargetLanguage, renderContext: RenderContext, @@ -215,6 +217,7 @@ export class SwiftRenderer extends ConvenienceRenderer { }, (transformedStringType) => { if (transformedStringType.kind === "date-time") { + this._haveDateTime = true; return "Date"; } if (transformedStringType.kind === "uuid") return "UUID"; @@ -743,6 +746,23 @@ export class SwiftRenderer extends ConvenienceRenderer { } private emitNewEncoderDecoder(): void { + if (this._haveDateTime) { + this.emitMultiline(`private func isValidDate(_ value: String) -> Bool { + let formatter = DateFormatter() + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = "u-MM-dd" + formatter.isLenient = false + return formatter.date(from: String(value.prefix(10))) != nil +}`); + this.ensureBlankLine(); + } + const dateValidation = this._haveDateTime + ? ` + guard isValidDate(dateStr) else { + throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Invalid date")) + }` + : ""; this.emitBlock("func newJSONDecoder() -> JSONDecoder", () => { this.emitLine("let decoder = JSONDecoder()"); if (!this._options.linux) { @@ -750,7 +770,7 @@ export class SwiftRenderer extends ConvenienceRenderer { "if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)", () => { this.emitMultiline(`decoder.dateDecodingStrategy = .custom { decoder in - let dateStr = try decoder.singleValueContainer().decode(String.self).uppercased() + let dateStr = try decoder.singleValueContainer().decode(String.self).uppercased()${dateValidation} let dateData = try JSONEncoder().encode(dateStr) let dateDecoder = JSONDecoder() dateDecoder.dateDecodingStrategy = .iso8601 @@ -761,7 +781,7 @@ export class SwiftRenderer extends ConvenienceRenderer { } else { this.emitMultiline(`decoder.dateDecodingStrategy = .custom({ (decoder) -> Date in let container = try decoder.singleValueContainer() - let dateStr = try container.decode(String.self).uppercased() + let dateStr = try container.decode(String.self).uppercased()${dateValidation} let formatter = DateFormatter() formatter.calendar = Calendar(identifier: .iso8601) diff --git a/test/inputs/schema/date-time.11.fail.date-time.json b/test/inputs/schema/date-time.11.fail.date-time.json new file mode 100644 index 0000000000..34582b9df5 --- /dev/null +++ b/test/inputs/schema/date-time.11.fail.date-time.json @@ -0,0 +1,7 @@ +{ + "date": "1985-04-12", + "time": "23:20:50.52Z", + "date-time": "1900-02-29T12:00:00Z", + "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/inputs/schema/date-time.9.fail.date-time.json b/test/inputs/schema/date-time.9.fail.date-time.json new file mode 100644 index 0000000000..9c07dc2bbb --- /dev/null +++ b/test/inputs/schema/date-time.9.fail.date-time.json @@ -0,0 +1,7 @@ +{ + "date": "1985-04-12", + "time": "23:20:50.52Z", + "date-time": "2023-02-29T12:00:00Z", + "union-array": ["1985-04-12", "23:20:50.52Z"], + "complex-union-array": ["2018-08-13T21:31:01+00:10", "foo", 123] +}