@@ -18,6 +18,8 @@ import FoundationEssentials
18
18
import Foundation
19
19
#endif
20
20
21
+
22
+
21
23
@propertyWrapper
22
24
public struct ISO8601Coding : Decodable , Sendable {
23
25
public let wrappedValue : Date
@@ -30,8 +32,16 @@ public struct ISO8601Coding: Decodable, Sendable {
30
32
let container = try decoder. singleValueContainer ( )
31
33
let dateString = try container. decode ( String . self)
32
34
35
+ struct InvalidDateError : Error { }
36
+
33
37
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
+ }
35
45
} catch {
36
46
throw DecodingError . dataCorruptedError (
37
47
in: container,
@@ -40,6 +50,14 @@ public struct ISO8601Coding: Decodable, Sendable {
40
50
)
41
51
}
42
52
}
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
+ }
43
61
}
44
62
45
63
@propertyWrapper
@@ -53,8 +71,17 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable, Sendable {
53
71
public init ( from decoder: Decoder ) throws {
54
72
let container = try decoder. singleValueContainer ( )
55
73
let dateString = try container. decode ( String . self)
74
+
75
+ struct InvalidDateError : Error { }
76
+
56
77
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
+ }
58
85
} catch {
59
86
throw DecodingError . dataCorruptedError (
60
87
in: container,
@@ -64,6 +91,15 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable, Sendable {
64
91
}
65
92
}
66
93
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 , * )
67
103
private static var iso8601WithFractionalSeconds : Date . ISO8601FormatStyle {
68
104
Date . ISO8601FormatStyle ( includingFractionalSeconds: true )
69
105
}
@@ -82,7 +118,11 @@ public struct RFC5322DateTimeCoding: Decodable, Sendable {
82
118
let string = try container. decode ( String . self)
83
119
84
120
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
+ }
86
126
} catch {
87
127
throw DecodingError . dataCorruptedError (
88
128
in: container,
@@ -95,7 +135,7 @@ public struct RFC5322DateTimeCoding: Decodable, Sendable {
95
135
96
136
struct RFC5322DateParsingError : Error { }
97
137
98
- struct RFC5322DateStrategy : ParseStrategy {
138
+ struct RFC5322DateStrategy {
99
139
func parse( _ input: String ) throws -> Date {
100
140
guard let components = self . components ( from: input) else {
101
141
throw RFC5322DateParsingError ( )
@@ -219,11 +259,11 @@ struct RFC5322DateStrategy: ParseStrategy {
219
259
guard it. expect ( UInt8 ( ascii: " : " ) ) else { return nil }
220
260
guard let second = parseSecond ( & it) else { return nil }
221
261
222
- guard let timezoneOffset = parseTimezone ( & it) else { return nil }
262
+ guard let timezoneOffsetMinutes = parseTimezone ( & it) else { return nil }
223
263
224
264
return DateComponents (
225
265
calendar: Calendar ( identifier: . gregorian) ,
226
- timeZone: TimeZone ( secondsFromGMT: timezoneOffset * 60 ) ,
266
+ timeZone: TimeZone ( secondsFromGMT: timezoneOffsetMinutes * 60 ) ,
227
267
year: year,
228
268
month: month,
229
269
day: day,
@@ -235,6 +275,9 @@ struct RFC5322DateStrategy: ParseStrategy {
235
275
}
236
276
}
237
277
278
+ @available ( macOS 12 . 0 , * )
279
+ extension RFC5322DateStrategy : ParseStrategy { }
280
+
238
281
extension IteratorProtocol where Self. Element == UInt8 {
239
282
mutating func expect( _ expected: UInt8 ) -> Bool {
240
283
guard self . next ( ) == expected else { return false }
@@ -305,5 +348,4 @@ let timezoneOffsetMap: [Array<UInt8> : Int] = [
305
348
Array ( " CDT " . utf8) : - 5 * 60 ,
306
349
Array ( " MDT " . utf8) : - 6 * 60 ,
307
350
Array ( " PDT " . utf8) : - 7 * 60 ,
308
-
309
351
]
0 commit comments