|
10 | 10 | // |
11 | 11 | //===----------------------------------------------------------------------===// |
12 | 12 |
|
13 | | -import struct Foundation.Date |
14 | | -import protocol Foundation.LocalizedError |
15 | | -import func Foundation.floor |
| 13 | +#if os(Windows) |
| 14 | +import WinSDK |
| 15 | +#elseif os(iOS) || os(macOS) || os(tvOS) || os(watchOS) |
| 16 | +import Darwin |
| 17 | +#else |
| 18 | +import Glibc |
| 19 | +#endif |
16 | 20 |
|
17 | | -public extension Date { |
18 | | - init(legacyDriverSecsAndNanos secsAndNanos: [Int]) throws { |
19 | | - enum Errors: LocalizedError { |
20 | | - case needSecondsAndNanoseconds |
| 21 | +/// Represents a time point value with nanosecond precision. |
| 22 | +/// |
| 23 | +/// - Warning: The accuracy of measured `TimePoint` values is an OS-dependent property. |
| 24 | +/// `TimePoint` does not correct for OS-level differences in e.g. |
| 25 | +/// clock epochs. This makes it unsuitable for serialization in |
| 26 | +/// products that are expected to transit between machines. |
| 27 | +public struct TimePoint: Equatable, Comparable, Hashable { |
| 28 | + public var seconds: UInt64 |
| 29 | + public var nanoseconds: UInt32 |
| 30 | + |
| 31 | + public init(seconds: UInt64, nanoseconds: UInt32) { |
| 32 | + self.seconds = seconds |
| 33 | + self.nanoseconds = nanoseconds |
| 34 | + } |
| 35 | + |
| 36 | + public static func < (lhs: TimePoint, rhs: TimePoint) -> Bool { |
| 37 | + return (lhs.seconds, lhs.nanoseconds) < (rhs.seconds, rhs.nanoseconds) |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +extension TimePoint { |
| 42 | + public static func seconds(_ value: Int) -> TimePoint { |
| 43 | + precondition(value >= 0, |
| 44 | + "Duration value in seconds is \(value), but cannot be negative") |
| 45 | + return TimePoint(seconds: UInt64(value), nanoseconds: 0) |
21 | 46 | } |
22 | | - guard secsAndNanos.count == 2 else { |
23 | | - throw Errors.needSecondsAndNanoseconds |
| 47 | + |
| 48 | + public static func nanoseconds(_ value: Int) -> TimePoint { |
| 49 | + precondition(value >= 0, |
| 50 | + "Duration value in nanoseconds is \(value), but cannot be negative") |
| 51 | + let (seconds, nanos) = value.quotientAndRemainder(dividingBy: TimePoint.nanosecondsPerSecond) |
| 52 | + return TimePoint(seconds: UInt64(seconds), |
| 53 | + nanoseconds: UInt32(nanos)) |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +extension TimePoint: AdditiveArithmetic { |
| 58 | + public static var zero: TimePoint { |
| 59 | + return .seconds(0) |
| 60 | + } |
| 61 | + |
| 62 | + public static func + (lhs: TimePoint, rhs: TimePoint) -> TimePoint { |
| 63 | + // Add raw operands |
| 64 | + var seconds = lhs.seconds + rhs.seconds |
| 65 | + var nanos = lhs.nanoseconds + rhs.nanoseconds |
| 66 | + // Normalize nanoseconds |
| 67 | + if nanos >= TimePoint.nanosecondsPerSecond { |
| 68 | + nanos -= UInt32(TimePoint.nanosecondsPerSecond) |
| 69 | + seconds += 1 |
24 | 70 | } |
25 | | - self = Self(legacyDriverSecs: secsAndNanos[0], nanos: secsAndNanos[1]) |
| 71 | + return TimePoint(seconds: seconds, nanoseconds: nanos) |
26 | 72 | } |
27 | | - init(legacyDriverSecs secs: Int, nanos: Int) { |
28 | | - self = Date(timeIntervalSince1970: Double(secs) + Double(nanos) / 1e9) |
| 73 | + |
| 74 | + public static func - (lhs: TimePoint, rhs: TimePoint) -> TimePoint { |
| 75 | + // Subtract raw operands |
| 76 | + var seconds = lhs.seconds - rhs.seconds |
| 77 | + // Normalize nanoseconds |
| 78 | + let nanos: UInt32 |
| 79 | + if lhs.nanoseconds >= rhs.nanoseconds { |
| 80 | + nanos = lhs.nanoseconds - rhs.nanoseconds |
| 81 | + } else { |
| 82 | + // Subtract nanoseconds with carry - order of operations here |
| 83 | + // is important to avoid overflow. |
| 84 | + nanos = lhs.nanoseconds + UInt32(TimePoint.nanosecondsPerSecond) - rhs.nanoseconds |
| 85 | + seconds -= 1 |
| 86 | + } |
| 87 | + return TimePoint(seconds: seconds, nanoseconds: nanos) |
29 | 88 | } |
30 | | - var legacyDriverSecsAndNanos: [Int] { |
31 | | - let totalSecs = timeIntervalSince1970 |
32 | | - let secs = Int( floor(totalSecs)) |
33 | | - let nanos = Int((totalSecs - floor(totalSecs)) * 1e9) |
34 | | - return [secs, nanos] |
| 89 | +} |
| 90 | + |
| 91 | +extension TimePoint{ |
| 92 | + public static func now() -> TimePoint { |
| 93 | + #if os(Windows) |
| 94 | + var ftTime: FILETIME = FILETIME() |
| 95 | + GetSystemTimePreciseAsFileTime(&ftTime) |
| 96 | + |
| 97 | + let result: UInt64 = (UInt64(ftTime.dwLowDateTime) << 0) |
| 98 | + + (UInt64(ftTime.dwHighDateTime) << 32) |
| 99 | + // Windows ticks in 100 nanosecond intervals. |
| 100 | + return .seconds(Int(result / 10_000_000)) |
| 101 | + #else |
| 102 | + var tv = timeval() |
| 103 | + gettimeofday(&tv, nil) |
| 104 | + return TimePoint(seconds: UInt64(tv.tv_sec), |
| 105 | + nanoseconds: UInt32(tv.tv_usec) * UInt32(Self.nanosecondsPerMicrosecond)) |
| 106 | + #endif |
35 | 107 | } |
| 108 | + |
| 109 | + public static var distantPast: TimePoint { |
| 110 | + return .zero |
| 111 | + } |
| 112 | + |
| 113 | + public static var distantFuture: TimePoint { |
| 114 | + // N.B. This is the seconds value of `Foundation.Date.distantFuture.timeIntervalSince1970`. |
| 115 | + // At time of writing, this is January 1, 4001 at 12:00:00 AM GMT, which is |
| 116 | + // far enough in the future that it's a reasonable time point for us to |
| 117 | + // compare against. |
| 118 | + // |
| 119 | + // However, it is important to note that Foundation's value cannot be both |
| 120 | + // fixed in time AND correct because of leap seconds and other calendrical |
| 121 | + // oddities. |
| 122 | + // |
| 123 | + // Other candidates include std::chrono's std::time_point::max - which is |
| 124 | + // about 9223372036854775807 - enough to comfortably bound the age of the |
| 125 | + // universe. |
| 126 | + return .seconds(64_092_211_200) |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +extension TimePoint { |
| 131 | + fileprivate static let nanosecondsPerSecond: Int = 1_000_000_000 |
| 132 | + fileprivate static let nanosecondsPerMillisecond: Int = 1_000_000 |
| 133 | + fileprivate static let nanosecondsPerMicrosecond: Int = 1_000 |
| 134 | + fileprivate static let millisecondsPerSecond: Int = 1_000 |
| 135 | + fileprivate static let microsecondsPerSecond: Int = 1_000_000 |
36 | 136 | } |
0 commit comments