@@ -34,12 +34,64 @@ public typealias TimeInterval = Double
3434 A `Date` is independent of a particular calendar or time zone. To represent a `Date` to a user, you must interpret it in the context of a `Calendar`.
3535*/
3636@available ( macOS 10 . 10 , iOS 8 . 0 , watchOS 2 . 0 , tvOS 9 . 0 , * )
37- public struct Date : Comparable , Hashable , Equatable , Sendable {
38-
39- internal var _time : TimeInterval
37+ public struct Date : Comparable , Hashable , Equatable , Sendable {
38+ /* Date is internally represented as a sum of two Doubles.
39+
40+ Previously Date was backed by a single Double measuring time since
41+ Jan 1 2001 in seconds. Because Double's precision is non-uniform, this
42+ means that times within a hundred days of the epoch are represented
43+ with approximately nanosecond precision, but as you get farther away
44+ from that date the precision decreases. For times close to the time
45+ at which this comment was written, accuracy has been reduced to about
46+ 100ns.
47+
48+ The obvious thing would be to adopt an integer-based representation
49+ similar to C's timespec (32b nanoseconds, 64b seconds) or Swift's
50+ Duration (128b attoseconds). These representations suffer from a few
51+ difficulties:
52+
53+ - Existing API on Date takes and produces `TimeInterval` (aka Double).
54+ Making Date use an integer representation internally would mean that
55+ existing users of the public API would suddently start getting
56+ different results for computations that were previously exact; even
57+ though we could add new API, and the overall system would be more
58+ precise, this would be a surprisingly subtle change for users to
59+ navigate.
60+
61+ - We have been told that some software interprets the raw bytes of Date
62+ as a Double for the purposes of fast serialization. These packages
63+ formally violate Foundation's API boundaries, but that doesn't help
64+ users of those packages who would abruptly be broken by switching to
65+ an integer representation.
66+
67+ Using DoubleDouble instead navigates these problems fairly elegantly.
68+
69+ - Because DoubleDouble is still a floating-point type, it still suffers
70+ from non-uniform precision. However, because DoubleDouble is so
71+ fantastically precise, it can represent dates out to ±2.5 quadrillion
72+ years at ~nanosecond or better precision, so in practice this won't
73+ be much of an issue.
74+
75+ - Existing API on Date will produce exactly the same result as it did
76+ previously in cases where those results were exact, minimizing
77+ surprises. In cases where the existing API was not exact, it will
78+ produce much more accurate results, even if users do not adopt new
79+ API, because its internal calculations are now more precise.
80+
81+ - Software that (incorrectly) interprets the raw bytes of Date as a
82+ Double will get at least as accurate of a value as it did previously
83+ (and often a more accurate value). */
84+ internal var _time : DoubleDouble
85+
86+ internal init ( _ time: DoubleDouble ) {
87+ self . _time = time
88+ }
89+ }
4090
91+ @available ( macOS 10 . 10 , iOS 8 . 0 , watchOS 2 . 0 , tvOS 9 . 0 , * )
92+ extension Date {
4193 /// The number of seconds from 1 January 1970 to the reference date, 1 January 2001.
42- public static let timeIntervalBetween1970AndReferenceDate : TimeInterval = 978307200.0
94+ public static let timeIntervalBetween1970AndReferenceDate : TimeInterval = 978307200.0
4395
4496 /// The number of seconds from 1 January 1601 to the reference date, 1 January 2001.
4597 internal static let timeIntervalBetween1601AndReferenceDate : TimeInterval = 12622780800.0
@@ -51,17 +103,23 @@ public struct Date : Comparable, Hashable, Equatable, Sendable {
51103
52104 /// Returns a `Date` initialized to the current date and time.
53105 public init ( ) {
54- _time = Self . getCurrentAbsoluteTime ( )
106+ _time = . init ( uncheckedHead : Self . getCurrentAbsoluteTime ( ) , tail : 0 )
55107 }
56108
57109 /// Returns a `Date` initialized relative to the current date and time by a given number of seconds.
58110 public init ( timeIntervalSinceNow: TimeInterval ) {
59- self . init ( timeIntervalSinceReferenceDate: timeIntervalSinceNow + Self. getCurrentAbsoluteTime ( ) )
111+ self . init ( . sum(
112+ Self . getCurrentAbsoluteTime ( ) ,
113+ timeIntervalSinceNow
114+ ) )
60115 }
61116
62117 /// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 1970 by a given number of seconds.
63118 public init ( timeIntervalSince1970: TimeInterval ) {
64- self . init ( timeIntervalSinceReferenceDate: timeIntervalSince1970 - Date. timeIntervalBetween1970AndReferenceDate)
119+ self . init ( . sum(
120+ timeIntervalSince1970,
121+ - Date. timeIntervalBetween1970AndReferenceDate
122+ ) )
65123 }
66124
67125 /**
@@ -71,12 +129,12 @@ public struct Date : Comparable, Hashable, Equatable, Sendable {
71129 - Parameter date: The reference date.
72130 */
73131 public init ( timeInterval: TimeInterval , since date: Date ) {
74- self . init ( timeIntervalSinceReferenceDate : date. timeIntervalSinceReferenceDate + timeInterval)
132+ self . init ( date. _time + timeInterval)
75133 }
76134
77135 /// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 2001 by a given number of seconds.
78136 public init ( timeIntervalSinceReferenceDate ti: TimeInterval ) {
79- _time = ti
137+ _time = . init ( uncheckedHead : ti , tail : 0 )
80138 }
81139
82140 /**
@@ -85,7 +143,7 @@ public struct Date : Comparable, Hashable, Equatable, Sendable {
85143 This property's value is negative if the date object is earlier than the system's absolute reference date (00:00:00 UTC on 1 January 2001).
86144 */
87145 public var timeIntervalSinceReferenceDate : TimeInterval {
88- return _time
146+ return _time. head
89147 }
90148
91149 /**
@@ -100,7 +158,7 @@ public struct Date : Comparable, Hashable, Equatable, Sendable {
100158 - SeeAlso: `timeIntervalSinceReferenceDate`
101159 */
102160 public func timeIntervalSince( _ date: Date ) -> TimeInterval {
103- return self . timeIntervalSinceReferenceDate - date. timeIntervalSinceReferenceDate
161+ return ( self . _time - date. _time ) . head
104162 }
105163
106164 /**
@@ -173,9 +231,9 @@ public struct Date : Comparable, Hashable, Equatable, Sendable {
173231
174232 /// Compare two `Date` values.
175233 public func compare( _ other: Date ) -> ComparisonResult {
176- if _time < other. timeIntervalSinceReferenceDate {
234+ if _time < other. _time {
177235 return . orderedAscending
178- } else if _time > other. timeIntervalSinceReferenceDate {
236+ } else if _time > other. _time {
179237 return . orderedDescending
180238 } else {
181239 return . orderedSame
@@ -184,27 +242,27 @@ public struct Date : Comparable, Hashable, Equatable, Sendable {
184242
185243 /// Returns true if the two `Date` values represent the same point in time.
186244 public static func == ( lhs: Date , rhs: Date ) -> Bool {
187- return lhs. timeIntervalSinceReferenceDate == rhs. timeIntervalSinceReferenceDate
245+ return lhs. _time == rhs. _time
188246 }
189247
190248 /// Returns true if the left hand `Date` is earlier in time than the right hand `Date`.
191249 public static func < ( lhs: Date , rhs: Date ) -> Bool {
192- return lhs. timeIntervalSinceReferenceDate < rhs. timeIntervalSinceReferenceDate
250+ return lhs. _time < rhs. _time
193251 }
194252
195253 /// Returns true if the left hand `Date` is later in time than the right hand `Date`.
196254 public static func > ( lhs: Date , rhs: Date ) -> Bool {
197- return lhs. timeIntervalSinceReferenceDate > rhs. timeIntervalSinceReferenceDate
255+ return lhs. _time > rhs. _time
198256 }
199257
200258 /// Returns a `Date` with a specified amount of time added to it.
201259 public static func + ( lhs: Date , rhs: TimeInterval ) -> Date {
202- return Date ( timeIntervalSinceReferenceDate : lhs. timeIntervalSinceReferenceDate + rhs)
260+ return Date ( lhs. _time + rhs)
203261 }
204262
205263 /// Returns a `Date` with a specified amount of time subtracted from it.
206264 public static func - ( lhs: Date , rhs: TimeInterval ) -> Date {
207- return Date ( timeIntervalSinceReferenceDate : lhs. timeIntervalSinceReferenceDate - rhs)
265+ return Date ( lhs. _time - rhs)
208266 }
209267
210268 /// Add a `TimeInterval` to a `Date`.
@@ -220,7 +278,6 @@ public struct Date : Comparable, Hashable, Equatable, Sendable {
220278 public static func -= ( lhs: inout Date , rhs: TimeInterval ) {
221279 lhs = lhs - rhs
222280 }
223-
224281}
225282
226283@available ( macOS 10 . 10 , iOS 8 . 0 , watchOS 2 . 0 , tvOS 9 . 0 , * )
0 commit comments