11import Foundation
2+ import PreciseISO8601DateFormatter
23
3- // `ISO8601DateFormatter` does not maintain nanosecond precision, which makes it
4+ // `ISO8601DateFormatter` does not maintain microsecond precision, which makes it
45// nearly impossible to equate encodable objects that include `Date` properties.
5- // `DateFormatter` maintains nanosecond precision by storing the exact
6- // bit pattern of `Date.timeIntervalSinceReferenceDate`, which is the type's
7- // underlying primitive. https://developer.apple.com/documentation/foundation/nsdate
8- public struct PreciseDateFormatter {
6+ // `DateFormatter` maintains microsecond precision.
7+ public enum PreciseDateFormatter {
98 public static func string( from date: Date ) -> String {
10- let bitPattern = date. timeIntervalSinceReferenceDate. bitPattern
11- return String ( bitPattern)
9+ formatter. string ( from: date)
1210 }
1311
1412 public static func date( from string: String ) -> Date ? {
15- guard let bitPattern = UInt64 ( string) else { return nil }
16- let double = Double ( bitPattern: bitPattern)
17- return Date ( timeIntervalSinceReferenceDate: double)
13+ formatter. date ( from: string)
1814 }
1915}
2016
21- extension KeyedDecodingContainer {
22- public func decodePreciseDate( forKey key: K ) throws -> Date {
23- let asString = try self . decode ( String . self, forKey: key)
17+ public extension KeyedDecodingContainer {
18+ func decodePreciseDate( forKey key: K ) throws -> Date {
19+ let asString = try decode ( String . self, forKey: key)
2420 guard let date = PreciseDateFormatter . date ( from: asString) else {
2521 let context = DecodingError . Context (
26- codingPath: self . codingPath,
22+ codingPath: codingPath,
2723 debugDescription: " Could not parse ' \( asString) ' into Date. "
2824 )
2925 throw Swift . DecodingError. typeMismatch ( Date . self, context)
3026 }
3127 return date
3228 }
3329
34- public func decodePreciseDateIfPresent( forKey key: K ) throws -> Date ? {
35- guard let asString = try self . decodeIfPresent ( String . self, forKey: key) else { return nil }
30+ func decodePreciseDateIfPresent( forKey key: K ) throws -> Date ? {
31+ guard let asString = try decodeIfPresent ( String . self, forKey: key) else { return nil }
3632 guard let date = PreciseDateFormatter . date ( from: asString) else {
3733 let context = DecodingError . Context (
38- codingPath: self . codingPath,
34+ codingPath: codingPath,
3935 debugDescription: " Could not parse ' \( asString) ' into Date. "
4036 )
4137 throw Swift . DecodingError. typeMismatch ( Date . self, context)
@@ -44,13 +40,15 @@ extension KeyedDecodingContainer {
4440 }
4541}
4642
47- extension KeyedEncodingContainer {
48- public mutating func encode( preciseDate: Date , forKey key: K ) throws {
49- try self . encode ( PreciseDateFormatter . string ( from: preciseDate) , forKey: key)
43+ public extension KeyedEncodingContainer {
44+ mutating func encode( preciseDate: Date , forKey key: K ) throws {
45+ try encode ( PreciseDateFormatter . string ( from: preciseDate) , forKey: key)
5046 }
5147
52- public mutating func encodeIfPresent( preciseDate: Date ? , forKey key: K ) throws {
53- guard let preciseDate = preciseDate else { return }
54- try self . encodeIfPresent ( PreciseDateFormatter . string ( from: preciseDate) , forKey: key)
48+ mutating func encodeIfPresent( preciseDate: Date ? , forKey key: K ) throws {
49+ guard let preciseDate else { return }
50+ try encodeIfPresent ( PreciseDateFormatter . string ( from: preciseDate) , forKey: key)
5551 }
5652}
53+
54+ private let formatter = PreciseISO8601DateFormatter ( )
0 commit comments