diff --git a/modules/openapi-generator/src/main/resources/swift4/CodableHelper.mustache b/modules/openapi-generator/src/main/resources/swift4/CodableHelper.mustache index 584de8c3d57a..46bf35491530 100644 --- a/modules/openapi-generator/src/main/resources/swift4/CodableHelper.mustache +++ b/modules/openapi-generator/src/main/resources/swift4/CodableHelper.mustache @@ -11,6 +11,50 @@ public typealias EncodeResult = (data: Data?, error: Error?) open class CodableHelper { + /// OpenAPI's "date-time" compatible DateFormater. + /// Supports both OpenAPI's "date-time" formats (with/-out fractional secs) for deserialize dates from JSON format and `Configuration.dateFormat` for serialize dates into JSON format. + /// - SeeAlso: + /// [stackoverflow.com: How to convert a date string with optional fractional seconds using Codable in Swift4](https://stackoverflow.com/questions/46458487/) + class OpenApiIso8601DateFormatter: DateFormatter { + static let formatters: [DateFormatter] = [ + iso8601Formatter(withFractionalSeconds: true), + iso8601Formatter(withFractionalSeconds: false) + ] + + static func iso8601Formatter(withFractionalSeconds fractional: Bool) -> DateFormatter { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = Configuration.getDateFormat(withFractionalSeconds: fractional) + return formatter + } + + override public func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer?, + for string: String, + errorDescription error: AutoreleasingUnsafeMutablePointer?) -> Bool { + // using flatMap instead of compactMap for backward-comp. + guard let date = (type(of: self).formatters.flatMap { $0.date(from: string) }).first else { + error?.pointee = "Invalid ISO8601 date: \(string)" as NSString + return false + } + + obj?.pointee = date as NSDate + return true + } + + override public func string(for obj: Any?) -> String? { + guard let date = obj as? Date else { + return nil + } + + // create new formatter, which is considering dateFormat config + let newFormatter = self.copy() as! DateFormatter + newFormatter.dateFormat = Configuration.dateFormat + return newFormatter.string(from: date) + } + } + public static var dateformatter: DateFormatter? open class func decode(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable { @@ -22,11 +66,7 @@ open class CodableHelper { decoder.dateDecodingStrategy = .formatted(df) } else { decoder.dataDecodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() decoder.dateDecodingStrategy = .formatted(formatter) } @@ -51,11 +91,7 @@ open class CodableHelper { encoder.dateEncodingStrategy = .formatted(df) } else { encoder.dataEncodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() encoder.dateEncodingStrategy = .formatted(formatter) } diff --git a/modules/openapi-generator/src/main/resources/swift4/Configuration.mustache b/modules/openapi-generator/src/main/resources/swift4/Configuration.mustache index 516590da5d9e..11f3e1439704 100644 --- a/modules/openapi-generator/src/main/resources/swift4/Configuration.mustache +++ b/modules/openapi-generator/src/main/resources/swift4/Configuration.mustache @@ -10,6 +10,14 @@ open class Configuration { // This value is used to configure the date formatter that is used to serialize dates into JSON format. // You must set it prior to encoding any dates, and it will only be read once. - public static var dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + public static var dateFormat = Configuration.getDateFormat(withFractionalSeconds: true) + + /// Get one of the two OpenAPI compatible "date-time" formats. Either with or without fractional seconds, which are optional by the OpenAPI specification. + /// + /// - Parameter withFractionalSeconds: use true to get format with fractional seconds and use false to get format without them. + /// - Returns: OpenAPI compatible "date-time" format + public static func getDateFormat(withFractionalSeconds fractional: Bool) -> String { + return "yyyy-MM-dd'T'HH:mm:ss\(fractional ? ".SSS" : "")ZZZZZ" + } } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/swift4/Extensions.mustache b/modules/openapi-generator/src/main/resources/swift4/Extensions.mustache index 61d55e8afec7..cb0848e60180 100644 --- a/modules/openapi-generator/src/main/resources/swift4/Extensions.mustache +++ b/modules/openapi-generator/src/main/resources/swift4/Extensions.mustache @@ -70,11 +70,7 @@ private let dateFormatter: DateFormatter = { if let formatter = CodableHelper.dateformatter { return formatter } else { - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = CodableHelper.OpenApiIso8601DateFormatter() return formatter } }() diff --git a/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift b/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift index 584de8c3d57a..46bf35491530 100644 --- a/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift +++ b/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift @@ -11,6 +11,50 @@ public typealias EncodeResult = (data: Data?, error: Error?) open class CodableHelper { + /// OpenAPI's "date-time" compatible DateFormater. + /// Supports both OpenAPI's "date-time" formats (with/-out fractional secs) for deserialize dates from JSON format and `Configuration.dateFormat` for serialize dates into JSON format. + /// - SeeAlso: + /// [stackoverflow.com: How to convert a date string with optional fractional seconds using Codable in Swift4](https://stackoverflow.com/questions/46458487/) + class OpenApiIso8601DateFormatter: DateFormatter { + static let formatters: [DateFormatter] = [ + iso8601Formatter(withFractionalSeconds: true), + iso8601Formatter(withFractionalSeconds: false) + ] + + static func iso8601Formatter(withFractionalSeconds fractional: Bool) -> DateFormatter { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = Configuration.getDateFormat(withFractionalSeconds: fractional) + return formatter + } + + override public func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer?, + for string: String, + errorDescription error: AutoreleasingUnsafeMutablePointer?) -> Bool { + // using flatMap instead of compactMap for backward-comp. + guard let date = (type(of: self).formatters.flatMap { $0.date(from: string) }).first else { + error?.pointee = "Invalid ISO8601 date: \(string)" as NSString + return false + } + + obj?.pointee = date as NSDate + return true + } + + override public func string(for obj: Any?) -> String? { + guard let date = obj as? Date else { + return nil + } + + // create new formatter, which is considering dateFormat config + let newFormatter = self.copy() as! DateFormatter + newFormatter.dateFormat = Configuration.dateFormat + return newFormatter.string(from: date) + } + } + public static var dateformatter: DateFormatter? open class func decode(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable { @@ -22,11 +66,7 @@ open class CodableHelper { decoder.dateDecodingStrategy = .formatted(df) } else { decoder.dataDecodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() decoder.dateDecodingStrategy = .formatted(formatter) } @@ -51,11 +91,7 @@ open class CodableHelper { encoder.dateEncodingStrategy = .formatted(df) } else { encoder.dataEncodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() encoder.dateEncodingStrategy = .formatted(formatter) } diff --git a/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/Configuration.swift b/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/Configuration.swift index 516590da5d9e..11f3e1439704 100644 --- a/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/Configuration.swift +++ b/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/Configuration.swift @@ -10,6 +10,14 @@ open class Configuration { // This value is used to configure the date formatter that is used to serialize dates into JSON format. // You must set it prior to encoding any dates, and it will only be read once. - public static var dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + public static var dateFormat = Configuration.getDateFormat(withFractionalSeconds: true) + + /// Get one of the two OpenAPI compatible "date-time" formats. Either with or without fractional seconds, which are optional by the OpenAPI specification. + /// + /// - Parameter withFractionalSeconds: use true to get format with fractional seconds and use false to get format without them. + /// - Returns: OpenAPI compatible "date-time" format + public static func getDateFormat(withFractionalSeconds fractional: Bool) -> String { + return "yyyy-MM-dd'T'HH:mm:ss\(fractional ? ".SSS" : "")ZZZZZ" + } } \ No newline at end of file diff --git a/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/Extensions.swift b/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/Extensions.swift index 8bf1829ba806..9b2daffd21f3 100644 --- a/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/Extensions.swift +++ b/samples/client/petstore/swift4/default/PetstoreClient/Classes/OpenAPIs/Extensions.swift @@ -69,11 +69,7 @@ private let dateFormatter: DateFormatter = { if let formatter = CodableHelper.dateformatter { return formatter } else { - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = CodableHelper.OpenApiIso8601DateFormatter() return formatter } }() diff --git a/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift b/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift index 584de8c3d57a..46bf35491530 100644 --- a/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift +++ b/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift @@ -11,6 +11,50 @@ public typealias EncodeResult = (data: Data?, error: Error?) open class CodableHelper { + /// OpenAPI's "date-time" compatible DateFormater. + /// Supports both OpenAPI's "date-time" formats (with/-out fractional secs) for deserialize dates from JSON format and `Configuration.dateFormat` for serialize dates into JSON format. + /// - SeeAlso: + /// [stackoverflow.com: How to convert a date string with optional fractional seconds using Codable in Swift4](https://stackoverflow.com/questions/46458487/) + class OpenApiIso8601DateFormatter: DateFormatter { + static let formatters: [DateFormatter] = [ + iso8601Formatter(withFractionalSeconds: true), + iso8601Formatter(withFractionalSeconds: false) + ] + + static func iso8601Formatter(withFractionalSeconds fractional: Bool) -> DateFormatter { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = Configuration.getDateFormat(withFractionalSeconds: fractional) + return formatter + } + + override public func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer?, + for string: String, + errorDescription error: AutoreleasingUnsafeMutablePointer?) -> Bool { + // using flatMap instead of compactMap for backward-comp. + guard let date = (type(of: self).formatters.flatMap { $0.date(from: string) }).first else { + error?.pointee = "Invalid ISO8601 date: \(string)" as NSString + return false + } + + obj?.pointee = date as NSDate + return true + } + + override public func string(for obj: Any?) -> String? { + guard let date = obj as? Date else { + return nil + } + + // create new formatter, which is considering dateFormat config + let newFormatter = self.copy() as! DateFormatter + newFormatter.dateFormat = Configuration.dateFormat + return newFormatter.string(from: date) + } + } + public static var dateformatter: DateFormatter? open class func decode(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable { @@ -22,11 +66,7 @@ open class CodableHelper { decoder.dateDecodingStrategy = .formatted(df) } else { decoder.dataDecodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() decoder.dateDecodingStrategy = .formatted(formatter) } @@ -51,11 +91,7 @@ open class CodableHelper { encoder.dateEncodingStrategy = .formatted(df) } else { encoder.dataEncodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() encoder.dateEncodingStrategy = .formatted(formatter) } diff --git a/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/Configuration.swift b/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/Configuration.swift index 516590da5d9e..11f3e1439704 100644 --- a/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/Configuration.swift +++ b/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/Configuration.swift @@ -10,6 +10,14 @@ open class Configuration { // This value is used to configure the date formatter that is used to serialize dates into JSON format. // You must set it prior to encoding any dates, and it will only be read once. - public static var dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + public static var dateFormat = Configuration.getDateFormat(withFractionalSeconds: true) + + /// Get one of the two OpenAPI compatible "date-time" formats. Either with or without fractional seconds, which are optional by the OpenAPI specification. + /// + /// - Parameter withFractionalSeconds: use true to get format with fractional seconds and use false to get format without them. + /// - Returns: OpenAPI compatible "date-time" format + public static func getDateFormat(withFractionalSeconds fractional: Bool) -> String { + return "yyyy-MM-dd'T'HH:mm:ss\(fractional ? ".SSS" : "")ZZZZZ" + } } \ No newline at end of file diff --git a/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/Extensions.swift b/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/Extensions.swift index 8bf1829ba806..9b2daffd21f3 100644 --- a/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/Extensions.swift +++ b/samples/client/petstore/swift4/objcCompatible/PetstoreClient/Classes/OpenAPIs/Extensions.swift @@ -69,11 +69,7 @@ private let dateFormatter: DateFormatter = { if let formatter = CodableHelper.dateformatter { return formatter } else { - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = CodableHelper.OpenApiIso8601DateFormatter() return formatter } }() diff --git a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift index 584de8c3d57a..46bf35491530 100644 --- a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift +++ b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift @@ -11,6 +11,50 @@ public typealias EncodeResult = (data: Data?, error: Error?) open class CodableHelper { + /// OpenAPI's "date-time" compatible DateFormater. + /// Supports both OpenAPI's "date-time" formats (with/-out fractional secs) for deserialize dates from JSON format and `Configuration.dateFormat` for serialize dates into JSON format. + /// - SeeAlso: + /// [stackoverflow.com: How to convert a date string with optional fractional seconds using Codable in Swift4](https://stackoverflow.com/questions/46458487/) + class OpenApiIso8601DateFormatter: DateFormatter { + static let formatters: [DateFormatter] = [ + iso8601Formatter(withFractionalSeconds: true), + iso8601Formatter(withFractionalSeconds: false) + ] + + static func iso8601Formatter(withFractionalSeconds fractional: Bool) -> DateFormatter { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = Configuration.getDateFormat(withFractionalSeconds: fractional) + return formatter + } + + override public func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer?, + for string: String, + errorDescription error: AutoreleasingUnsafeMutablePointer?) -> Bool { + // using flatMap instead of compactMap for backward-comp. + guard let date = (type(of: self).formatters.flatMap { $0.date(from: string) }).first else { + error?.pointee = "Invalid ISO8601 date: \(string)" as NSString + return false + } + + obj?.pointee = date as NSDate + return true + } + + override public func string(for obj: Any?) -> String? { + guard let date = obj as? Date else { + return nil + } + + // create new formatter, which is considering dateFormat config + let newFormatter = self.copy() as! DateFormatter + newFormatter.dateFormat = Configuration.dateFormat + return newFormatter.string(from: date) + } + } + public static var dateformatter: DateFormatter? open class func decode(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable { @@ -22,11 +66,7 @@ open class CodableHelper { decoder.dateDecodingStrategy = .formatted(df) } else { decoder.dataDecodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() decoder.dateDecodingStrategy = .formatted(formatter) } @@ -51,11 +91,7 @@ open class CodableHelper { encoder.dateEncodingStrategy = .formatted(df) } else { encoder.dataEncodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() encoder.dateEncodingStrategy = .formatted(formatter) } diff --git a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/Configuration.swift b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/Configuration.swift index 516590da5d9e..11f3e1439704 100644 --- a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/Configuration.swift +++ b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/Configuration.swift @@ -10,6 +10,14 @@ open class Configuration { // This value is used to configure the date formatter that is used to serialize dates into JSON format. // You must set it prior to encoding any dates, and it will only be read once. - public static var dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + public static var dateFormat = Configuration.getDateFormat(withFractionalSeconds: true) + + /// Get one of the two OpenAPI compatible "date-time" formats. Either with or without fractional seconds, which are optional by the OpenAPI specification. + /// + /// - Parameter withFractionalSeconds: use true to get format with fractional seconds and use false to get format without them. + /// - Returns: OpenAPI compatible "date-time" format + public static func getDateFormat(withFractionalSeconds fractional: Bool) -> String { + return "yyyy-MM-dd'T'HH:mm:ss\(fractional ? ".SSS" : "")ZZZZZ" + } } \ No newline at end of file diff --git a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/Extensions.swift b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/Extensions.swift index 97a90f9af490..0f15ff0ba752 100644 --- a/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/Extensions.swift +++ b/samples/client/petstore/swift4/promisekit/PetstoreClient/Classes/OpenAPIs/Extensions.swift @@ -70,11 +70,7 @@ private let dateFormatter: DateFormatter = { if let formatter = CodableHelper.dateformatter { return formatter } else { - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = CodableHelper.OpenApiIso8601DateFormatter() return formatter } }() diff --git a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift index 584de8c3d57a..46bf35491530 100644 --- a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift +++ b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift @@ -11,6 +11,50 @@ public typealias EncodeResult = (data: Data?, error: Error?) open class CodableHelper { + /// OpenAPI's "date-time" compatible DateFormater. + /// Supports both OpenAPI's "date-time" formats (with/-out fractional secs) for deserialize dates from JSON format and `Configuration.dateFormat` for serialize dates into JSON format. + /// - SeeAlso: + /// [stackoverflow.com: How to convert a date string with optional fractional seconds using Codable in Swift4](https://stackoverflow.com/questions/46458487/) + class OpenApiIso8601DateFormatter: DateFormatter { + static let formatters: [DateFormatter] = [ + iso8601Formatter(withFractionalSeconds: true), + iso8601Formatter(withFractionalSeconds: false) + ] + + static func iso8601Formatter(withFractionalSeconds fractional: Bool) -> DateFormatter { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = Configuration.getDateFormat(withFractionalSeconds: fractional) + return formatter + } + + override public func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer?, + for string: String, + errorDescription error: AutoreleasingUnsafeMutablePointer?) -> Bool { + // using flatMap instead of compactMap for backward-comp. + guard let date = (type(of: self).formatters.flatMap { $0.date(from: string) }).first else { + error?.pointee = "Invalid ISO8601 date: \(string)" as NSString + return false + } + + obj?.pointee = date as NSDate + return true + } + + override public func string(for obj: Any?) -> String? { + guard let date = obj as? Date else { + return nil + } + + // create new formatter, which is considering dateFormat config + let newFormatter = self.copy() as! DateFormatter + newFormatter.dateFormat = Configuration.dateFormat + return newFormatter.string(from: date) + } + } + public static var dateformatter: DateFormatter? open class func decode(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable { @@ -22,11 +66,7 @@ open class CodableHelper { decoder.dateDecodingStrategy = .formatted(df) } else { decoder.dataDecodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() decoder.dateDecodingStrategy = .formatted(formatter) } @@ -51,11 +91,7 @@ open class CodableHelper { encoder.dateEncodingStrategy = .formatted(df) } else { encoder.dataEncodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() encoder.dateEncodingStrategy = .formatted(formatter) } diff --git a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/Configuration.swift b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/Configuration.swift index 516590da5d9e..11f3e1439704 100644 --- a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/Configuration.swift +++ b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/Configuration.swift @@ -10,6 +10,14 @@ open class Configuration { // This value is used to configure the date formatter that is used to serialize dates into JSON format. // You must set it prior to encoding any dates, and it will only be read once. - public static var dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + public static var dateFormat = Configuration.getDateFormat(withFractionalSeconds: true) + + /// Get one of the two OpenAPI compatible "date-time" formats. Either with or without fractional seconds, which are optional by the OpenAPI specification. + /// + /// - Parameter withFractionalSeconds: use true to get format with fractional seconds and use false to get format without them. + /// - Returns: OpenAPI compatible "date-time" format + public static func getDateFormat(withFractionalSeconds fractional: Bool) -> String { + return "yyyy-MM-dd'T'HH:mm:ss\(fractional ? ".SSS" : "")ZZZZZ" + } } \ No newline at end of file diff --git a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/Extensions.swift b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/Extensions.swift index 8bf1829ba806..9b2daffd21f3 100644 --- a/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/Extensions.swift +++ b/samples/client/petstore/swift4/rxswift/PetstoreClient/Classes/OpenAPIs/Extensions.swift @@ -69,11 +69,7 @@ private let dateFormatter: DateFormatter = { if let formatter = CodableHelper.dateformatter { return formatter } else { - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = CodableHelper.OpenApiIso8601DateFormatter() return formatter } }() diff --git a/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift b/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift index 584de8c3d57a..46bf35491530 100644 --- a/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift +++ b/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/CodableHelper.swift @@ -11,6 +11,50 @@ public typealias EncodeResult = (data: Data?, error: Error?) open class CodableHelper { + /// OpenAPI's "date-time" compatible DateFormater. + /// Supports both OpenAPI's "date-time" formats (with/-out fractional secs) for deserialize dates from JSON format and `Configuration.dateFormat` for serialize dates into JSON format. + /// - SeeAlso: + /// [stackoverflow.com: How to convert a date string with optional fractional seconds using Codable in Swift4](https://stackoverflow.com/questions/46458487/) + class OpenApiIso8601DateFormatter: DateFormatter { + static let formatters: [DateFormatter] = [ + iso8601Formatter(withFractionalSeconds: true), + iso8601Formatter(withFractionalSeconds: false) + ] + + static func iso8601Formatter(withFractionalSeconds fractional: Bool) -> DateFormatter { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = Configuration.getDateFormat(withFractionalSeconds: fractional) + return formatter + } + + override public func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer?, + for string: String, + errorDescription error: AutoreleasingUnsafeMutablePointer?) -> Bool { + // using flatMap instead of compactMap for backward-comp. + guard let date = (type(of: self).formatters.flatMap { $0.date(from: string) }).first else { + error?.pointee = "Invalid ISO8601 date: \(string)" as NSString + return false + } + + obj?.pointee = date as NSDate + return true + } + + override public func string(for obj: Any?) -> String? { + guard let date = obj as? Date else { + return nil + } + + // create new formatter, which is considering dateFormat config + let newFormatter = self.copy() as! DateFormatter + newFormatter.dateFormat = Configuration.dateFormat + return newFormatter.string(from: date) + } + } + public static var dateformatter: DateFormatter? open class func decode(_ type: T.Type, from data: Data) -> (decodableObj: T?, error: Error?) where T : Decodable { @@ -22,11 +66,7 @@ open class CodableHelper { decoder.dateDecodingStrategy = .formatted(df) } else { decoder.dataDecodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() decoder.dateDecodingStrategy = .formatted(formatter) } @@ -51,11 +91,7 @@ open class CodableHelper { encoder.dateEncodingStrategy = .formatted(df) } else { encoder.dataEncodingStrategy = .base64 - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = OpenApiIso8601DateFormatter() encoder.dateEncodingStrategy = .formatted(formatter) } diff --git a/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/Configuration.swift b/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/Configuration.swift index 516590da5d9e..11f3e1439704 100644 --- a/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/Configuration.swift +++ b/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/Configuration.swift @@ -10,6 +10,14 @@ open class Configuration { // This value is used to configure the date formatter that is used to serialize dates into JSON format. // You must set it prior to encoding any dates, and it will only be read once. - public static var dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ" + public static var dateFormat = Configuration.getDateFormat(withFractionalSeconds: true) + + /// Get one of the two OpenAPI compatible "date-time" formats. Either with or without fractional seconds, which are optional by the OpenAPI specification. + /// + /// - Parameter withFractionalSeconds: use true to get format with fractional seconds and use false to get format without them. + /// - Returns: OpenAPI compatible "date-time" format + public static func getDateFormat(withFractionalSeconds fractional: Bool) -> String { + return "yyyy-MM-dd'T'HH:mm:ss\(fractional ? ".SSS" : "")ZZZZZ" + } } \ No newline at end of file diff --git a/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/Extensions.swift b/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/Extensions.swift index 8bf1829ba806..9b2daffd21f3 100644 --- a/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/Extensions.swift +++ b/samples/client/petstore/swift4/unwrapRequired/PetstoreClient/Classes/OpenAPIs/Extensions.swift @@ -69,11 +69,7 @@ private let dateFormatter: DateFormatter = { if let formatter = CodableHelper.dateformatter { return formatter } else { - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = Configuration.dateFormat + let formatter = CodableHelper.OpenApiIso8601DateFormatter() return formatter } }()