If you are using the iso8601
strategy, you may not want to rely on the default Equatable
implementation for Date
(==
) when comparing dates. You can use a good old fashion calendar comparison instead.
JSONEncoder and JSONDecoder can both be configured with a strategy (dateEncodingStrategy
and dateDecodingStrategy
) for encoding and decoding dates. There are four named types in addition to allowing you to provide custom options.
Strategy | Comment |
---|---|
deferredToDate | Defer to Date for choosing an encoding. This is the default strategy. |
millisecondsSince1970 | Encode the Date as UNIX millisecond timestamp (as a JSON number). |
secondsSince1970 | Encode the Date as a UNIX timestamp (as a JSON number). |
iso8601 | Encode the Date as an ISO-8601-formatted string (in RFC 3339 format). |
The defaut is .deferredToDate
which uses a reference date of January 1st, 2001.
Examples of the JSON generated for each strategy when encoding a date:
Strategy | JSON Example |
---|---|
deferredToDate | {"date":586761866.88579297} |
millisecondsSince1970 | {"date":1565069066885.793} |
secondsSince1970 | {"date":1565069066.885793} |
iso8601 | {"date":"2019-08-06T05:24:26Z"} |
After encoding and decoding the data back into a Date:
Strategy | timeIntervalSinceReferenceDate | timeIntervalSince1970 | hashValue |
---|---|---|---|
Original Date | 586761866.885793 | 1565069066.885793 | -549226158746601189 |
deferredToDate | 586761866.885793 | 1565069066.885793 | -549226158746601189 |
millisecondsSince1970 | 586761866.885793 | 1565069066.885793 | -549226158746601189 |
secondsSince1970 | 586761866.885793 | 1565069066.885793 | -549226158746601189 |
iso8601 | 586761866.0 | 1565069066.0 | 1557521680301490026 |
Comparing the original date to the decoded date using both ==
, and a calandar comparison:
Strategy | == |
orderedSame |
---|---|---|
deferredToDate | true | true |
millisecondsSince1970 | true | true |
secondsSince1970 | true | true |
iso8601 | false | true |
date == decodedDate
Calendar.current.compare(date, to: decodedDate, toGranularity: .nanosecond) == .orderedSame) : orderedSame
Sample code I used
struct StrategeryResult {
let date: Date
let encodeStrategy: JSONEncoder.DateEncodingStrategy
let decodeStrategy: JSONDecoder.DateDecodingStrategy
let data: Data
let json: String
let decodedDate: Date
init(date: Date, encodeStrategy: JSONEncoder.DateEncodingStrategy, decodeStrategy: JSONDecoder.DateDecodingStrategy) throws {
// simple container to encode/decode with
struct Foo: Codable {
let date: Date
}
let foo = Foo(date: date)
self.date = date
self.encodeStrategy = encodeStrategy
self.decodeStrategy = decodeStrategy
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = encodeStrategy
self.data = try encoder.encode(foo)
self.json = String(data: data, encoding: .utf8)!
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = decodeStrategy
let decodedFoo = try decoder.decode(Foo.self, from: data)
self.decodedDate = decodedFoo.date
}
}
extension StrategeryResult: CustomDebugStringConvertible {
var debugDescription: String {
var components: [String] = ["StrategeryResult:"]
components.append("\(encodeStrategy) : encodeStrategy")
components.append("\(decodeStrategy) : decodeStrategy")
components.append("\(date) : date")
components.append("\(decodedDate) : decodedDate")
components.append("\(json) : json")
components.append("\(date.hashValue) : date.hashValue")
components.append("\(decodedDate.hashValue) : decodedDate.hashValue")
components.append("\(date.timeIntervalSinceReferenceDate) : date.timeIntervalSinceReferenceDate")
components.append("\(decodedDate.timeIntervalSinceReferenceDate) : decodedDate.timeIntervalSinceReferenceDate")
components.append("\(date.timeIntervalSince1970) : date.timeIntervalSince1970")
components.append("\(decodedDate.timeIntervalSince1970) : decodedDate.timeIntervalSince1970")
components.append("\(date == decodedDate) : Equatable (==)")
components.append("\(Calendar.current.compare(date, to: decodedDate, toGranularity: .nanosecond) == .orderedSame) : orderedSame")
return components.joined(separator: "\n * ")
}
}
// Usage:
let now = Date()
var results = try! StrategeryResult(date: now, encodeStrategy: .iso8601, decodeStrategy: .iso8601)
print(String(reflecting: results))