@@ -18,6 +18,8 @@ import FoundationEssentials
1818import Foundation
1919#endif
2020
21+
22+
2123@propertyWrapper
2224public struct ISO8601Coding : Decodable , Sendable {
2325 public let wrappedValue : Date
@@ -30,8 +32,16 @@ public struct ISO8601Coding: Decodable, Sendable {
3032 let container = try decoder. singleValueContainer ( )
3133 let dateString = try container. decode ( String . self)
3234
35+ struct InvalidDateError : Error { }
36+
3337 do {
34- self . wrappedValue = try Date ( dateString, strategy: . iso8601)
38+ if #available( macOS 12 . 0 , * ) {
39+ self . wrappedValue = try Date ( dateString, strategy: . iso8601)
40+ } else if let date = Self . dateFormatter. date ( from: dateString) {
41+ self . wrappedValue = date
42+ } else {
43+ throw InvalidDateError ( )
44+ }
3545 } catch {
3646 throw DecodingError . dataCorruptedError (
3747 in: container,
@@ -40,6 +50,14 @@ public struct ISO8601Coding: Decodable, Sendable {
4050 )
4151 }
4252 }
53+
54+ private static var dateFormatter : DateFormatter {
55+ let formatter = DateFormatter ( )
56+ formatter. locale = Locale ( identifier: " en_US_POSIX " )
57+ formatter. timeZone = TimeZone ( secondsFromGMT: 0 )
58+ formatter. dateFormat = " yyyy-MM-dd'T'HH:mm:ssZZZZZ "
59+ return formatter
60+ }
4361}
4462
4563@propertyWrapper
@@ -53,8 +71,17 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable, Sendable {
5371 public init ( from decoder: Decoder ) throws {
5472 let container = try decoder. singleValueContainer ( )
5573 let dateString = try container. decode ( String . self)
74+
75+ struct InvalidDateError : Error { }
76+
5677 do {
57- self . wrappedValue = try Date ( dateString, strategy: Self . iso8601WithFractionalSeconds)
78+ if #available( macOS 12 . 0 , * ) {
79+ self . wrappedValue = try Date ( dateString, strategy: Self . iso8601WithFractionalSeconds)
80+ } else if let date = Self . dateFormatter. date ( from: dateString) {
81+ self . wrappedValue = date
82+ } else {
83+ throw InvalidDateError ( )
84+ }
5885 } catch {
5986 throw DecodingError . dataCorruptedError (
6087 in: container,
@@ -64,6 +91,15 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable, Sendable {
6491 }
6592 }
6693
94+ private static var dateFormatter : DateFormatter {
95+ let formatter = DateFormatter ( )
96+ formatter. locale = Locale ( identifier: " en_US_POSIX " )
97+ formatter. timeZone = TimeZone ( secondsFromGMT: 0 )
98+ formatter. dateFormat = " yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ "
99+ return formatter
100+ }
101+
102+ @available ( macOS 12 . 0 , * )
67103 private static var iso8601WithFractionalSeconds : Date . ISO8601FormatStyle {
68104 Date . ISO8601FormatStyle ( includingFractionalSeconds: true )
69105 }
@@ -82,7 +118,11 @@ public struct RFC5322DateTimeCoding: Decodable, Sendable {
82118 let string = try container. decode ( String . self)
83119
84120 do {
85- self . wrappedValue = try Date ( string, strategy: RFC5322DateStrategy ( ) )
121+ if #available( macOS 12 . 0 , * ) {
122+ self . wrappedValue = try Date ( string, strategy: RFC5322DateStrategy ( ) )
123+ } else {
124+ self . wrappedValue = try RFC5322DateStrategy ( ) . parse ( string)
125+ }
86126 } catch {
87127 throw DecodingError . dataCorruptedError (
88128 in: container,
@@ -95,7 +135,7 @@ public struct RFC5322DateTimeCoding: Decodable, Sendable {
95135
96136struct RFC5322DateParsingError : Error { }
97137
98- struct RFC5322DateStrategy : ParseStrategy {
138+ struct RFC5322DateStrategy {
99139 func parse( _ input: String ) throws -> Date {
100140 guard let components = self . components ( from: input) else {
101141 throw RFC5322DateParsingError ( )
@@ -219,11 +259,11 @@ struct RFC5322DateStrategy: ParseStrategy {
219259 guard it. expect ( UInt8 ( ascii: " : " ) ) else { return nil }
220260 guard let second = parseSecond ( & it) else { return nil }
221261
222- guard let timezoneOffset = parseTimezone ( & it) else { return nil }
262+ guard let timezoneOffsetMinutes = parseTimezone ( & it) else { return nil }
223263
224264 return DateComponents (
225265 calendar: Calendar ( identifier: . gregorian) ,
226- timeZone: TimeZone ( secondsFromGMT: timezoneOffset * 60 ) ,
266+ timeZone: TimeZone ( secondsFromGMT: timezoneOffsetMinutes * 60 ) ,
227267 year: year,
228268 month: month,
229269 day: day,
@@ -235,6 +275,9 @@ struct RFC5322DateStrategy: ParseStrategy {
235275 }
236276}
237277
278+ @available ( macOS 12 . 0 , * )
279+ extension RFC5322DateStrategy : ParseStrategy { }
280+
238281extension IteratorProtocol where Self. Element == UInt8 {
239282 mutating func expect( _ expected: UInt8 ) -> Bool {
240283 guard self . next ( ) == expected else { return false }
@@ -305,5 +348,4 @@ let timezoneOffsetMap: [Array<UInt8> : Int] = [
305348 Array ( " CDT " . utf8) : - 5 * 60 ,
306349 Array ( " MDT " . utf8) : - 6 * 60 ,
307350 Array ( " PDT " . utf8) : - 7 * 60 ,
308-
309351]
0 commit comments