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; 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) 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] +}