TeraCheckedTime.swift (2384B)
1 import Foundation 2 3 enum TeraClockError: Error, Sendable, Equatable { 4 case nonfinite 5 case beforeUnixEpoch 6 case overflow 7 case zeroNotAllowed 8 } 9 10 struct TeraClock: Sendable { 11 private let now: @Sendable () -> Date 12 13 static let system = Self(now: { Date() }) 14 15 init(now: @escaping @Sendable () -> Date) { 16 self.now = now 17 } 18 19 static func fixed(unixSeconds: UInt64) -> Self { 20 Self(now: { Date(timeIntervalSince1970: TimeInterval(unixSeconds)) }) 21 } 22 23 func unixSeconds(requirePositive: Bool = false) throws -> UInt64 { 24 try Self.unixSeconds(from: now(), requirePositive: requirePositive) 25 } 26 27 func unixMilliseconds(requirePositive: Bool = false) throws -> UInt64 { 28 try Self.unixMilliseconds(from: now(), requirePositive: requirePositive) 29 } 30 31 static func unixSeconds( 32 from date: Date, 33 requirePositive: Bool = false 34 ) throws -> UInt64 { 35 try unsigned(date.timeIntervalSince1970, multiplier: 1, requirePositive: requirePositive) 36 } 37 38 static func unixMilliseconds( 39 from date: Date, 40 requirePositive: Bool = false 41 ) throws -> UInt64 { 42 try unsigned(date.timeIntervalSince1970, multiplier: 1000, requirePositive: requirePositive) 43 } 44 45 static func signedUnixMilliseconds(from date: Date) throws -> Int64 { 46 let value = try unixMilliseconds(from: date) 47 guard let signed = Int64(exactly: value) else { 48 throw TeraClockError.overflow 49 } 50 return signed 51 } 52 53 private static func unsigned( 54 _ seconds: TimeInterval, 55 multiplier: TimeInterval, 56 requirePositive: Bool 57 ) throws -> UInt64 { 58 guard seconds.isFinite else { 59 throw TeraClockError.nonfinite 60 } 61 guard seconds >= 0 else { 62 throw TeraClockError.beforeUnixEpoch 63 } 64 let scaled = seconds * multiplier 65 guard scaled.isFinite, 66 let value = UInt64(exactly: scaled.rounded(.down)) 67 else { 68 throw TeraClockError.overflow 69 } 70 guard !requirePositive || value > 0 else { 71 throw TeraClockError.zeroNotAllowed 72 } 73 return value 74 } 75 } 76 77 enum TeraStateTransitionError: Error, Sendable, Equatable { 78 case generationOverflow 79 } 80 81 enum TeraCheckedStateTransition { 82 static func nextGeneration(after generation: UInt64) throws -> UInt64 { 83 let (next, overflow) = generation.addingReportingOverflow(1) 84 guard !overflow else { 85 throw TeraStateTransitionError.generationOverflow 86 } 87 return next 88 } 89 }