12
12
//
13
13
//===----------------------------------------------------------------------===//
14
14
15
+ #if os(Linux)
16
+ import Glibc
17
+ #else
18
+ import Darwin. C
19
+ #endif
20
+
15
21
import struct Foundation. Date
16
22
import class Foundation. DateFormatter
17
23
import class Foundation. ISO8601DateFormatter
@@ -28,14 +34,46 @@ public struct ISO8601Coding: Decodable {
28
34
public init ( from decoder: Decoder ) throws {
29
35
let container = try decoder. singleValueContainer ( )
30
36
let dateString = try container. decode ( String . self)
31
- guard let date = Self . dateFormatter . date ( from: dateString) else {
37
+ guard let date = Self . decodeDate ( from: dateString) else {
32
38
throw DecodingError . dataCorruptedError ( in: container, debugDescription:
33
- " Expected date to be in iso8601 date format, but ` \( dateString) ` does not forfill format " )
39
+ " Expected date to be in ISO8601 date format, but ` \( dateString) ` is not in the correct format " )
34
40
}
35
41
self . wrappedValue = date
36
42
}
37
43
44
+ private static func decodeDate( from string: String ) -> Date ? {
45
+ #if os(Linux)
46
+ return Self . dateFormatter. date ( from: string)
47
+ #elseif os(macOS)
48
+ if #available( macOS 10 . 12 , * ) {
49
+ return Self . dateFormatter. date ( from: string)
50
+ } else {
51
+ return self . decodeISO8601Date ( from: string) . flatMap ( Date . init ( timeIntervalSince1970: ) )
52
+ }
53
+ #endif
54
+ }
55
+
56
+ @available ( macOS 10 . 12 , * )
38
57
private static let dateFormatter = ISO8601DateFormatter ( )
58
+
59
+ // strptime not avail on through Glibc
60
+ #if os(macOS)
61
+ // 1970-01-01T00:00:00Z
62
+ internal static func decodeISO8601Date( from string: String ) -> Double ? {
63
+ if string. last != " Z " {
64
+ return nil
65
+ }
66
+ var parsedTime = tm ( )
67
+ _ = string. withCString { cstr in
68
+ strptime ( cstr, " %Y-%m-%dT%H:%M:%S%z " , & parsedTime)
69
+ }
70
+ let time = timegm ( & parsedTime)
71
+ if time == - 1 {
72
+ return nil
73
+ }
74
+ return Double ( time)
75
+ }
76
+ #endif
39
77
}
40
78
41
79
@propertyWrapper
@@ -49,14 +87,29 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable {
49
87
public init ( from decoder: Decoder ) throws {
50
88
let container = try decoder. singleValueContainer ( )
51
89
let dateString = try container. decode ( String . self)
52
- guard let date = Self . dateFormatter . date ( from: dateString) else {
90
+ guard let date = Self . decodeDate ( from: dateString) else {
53
91
throw DecodingError . dataCorruptedError ( in: container, debugDescription:
54
- " Expected date to be in iso8601 date format with fractional seconds, but ` \( dateString) ` does not forfill format " )
92
+ " Expected date to be in ISO8601 date format with fractional seconds, but ` \( dateString) ` is not in the correct format " )
55
93
}
56
94
self . wrappedValue = date
57
95
}
58
96
97
+ private static func decodeDate( from string: String ) -> Date ? {
98
+ #if os(Linux)
99
+ return Self . dateFormatter. date ( from: string)
100
+ #elseif os(macOS)
101
+ if #available( macOS 10 . 13 , * ) {
102
+ return self . dateFormatter. date ( from: string)
103
+ } else {
104
+ return self . decodeISO8601Date ( from: string) . flatMap ( Date . init ( timeIntervalSince1970: ) )
105
+ }
106
+ #endif
107
+ }
108
+
109
+ @available ( macOS 10 . 13 , * )
59
110
private static let dateFormatter : ISO8601DateFormatter = Self . createDateFormatter ( )
111
+
112
+ @available ( macOS 10 . 13 , * )
60
113
private static func createDateFormatter( ) -> ISO8601DateFormatter {
61
114
let formatter = ISO8601DateFormatter ( )
62
115
formatter. formatOptions = [
@@ -68,6 +121,28 @@ public struct ISO8601WithFractionalSecondsCoding: Decodable {
68
121
]
69
122
return formatter
70
123
}
124
+
125
+ // strptime not avail on through Glibc
126
+ #if os(macOS)
127
+ // 1970-01-01T00:00:00.00Z
128
+ internal static func decodeISO8601Date( from string: String ) -> Double ? {
129
+ guard let msIndex = string. lastIndex ( of: " . " ) else {
130
+ return nil
131
+ }
132
+ guard let endIndex = string. lastIndex ( of: " Z " ) else {
133
+ return nil
134
+ }
135
+ if endIndex <= msIndex {
136
+ return nil
137
+ }
138
+ let msString = string [ msIndex ..< endIndex]
139
+ guard let ms = Double ( msString) else {
140
+ return nil
141
+ }
142
+
143
+ return ISO8601Coding . decodeISO8601Date ( from: string) ? . advanced ( by: ms)
144
+ }
145
+ #endif
71
146
}
72
147
73
148
@propertyWrapper
@@ -88,7 +163,7 @@ public struct RFC5322DateTimeCoding: Decodable {
88
163
}
89
164
guard let date = Self . dateFormatter. date ( from: string) else {
90
165
throw DecodingError . dataCorruptedError ( in: container, debugDescription:
91
- " Expected date to be in RFC5322 date-time format with fractional seconds, but ` \( string) ` does not forfill format " )
166
+ " Expected date to be in RFC5322 date-time format with fractional seconds, but ` \( string) ` is not in the correct format " )
92
167
}
93
168
self . wrappedValue = date
94
169
}
0 commit comments