From df230089d1edbb8489eeb7ac57012229e9a4f8e1 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sat, 23 Aug 2025 09:19:51 -0500 Subject: [PATCH] Add constants for upper and lower date limits --- .../JulianDayNumber/GregorianCalendar.swift | 29 ++++++++++++++----- Sources/JulianDayNumber/JulianCalendar.swift | 29 ++++++++++++++----- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/Sources/JulianDayNumber/GregorianCalendar.swift b/Sources/JulianDayNumber/GregorianCalendar.swift index d3b2743..00fbace 100644 --- a/Sources/JulianDayNumber/GregorianCalendar.swift +++ b/Sources/JulianDayNumber/GregorianCalendar.swift @@ -42,9 +42,23 @@ public struct GregorianCalendar: Calendar { /// The recurrence (solar) cycle of the Gregorian calendar is 303 common years of 365 days and 97 leap years of 366 days. static let recurrenceCycle = (years: 400, days: 146097) - /// The Gregorian calendar date corresponding to JDN 0 + /// The Gregorian calendar date corresponding to JDN 0. static let jdnZero: DateType = (-4713, 11, 24) +#if _pointerBitWidth(_64) + // The Gregorian calendar date corresponding to JDN `Int64.max` + static let upperLimit: DateType = (25252734927761842, 6, 20) + // The Gregorian calendar date corresponding to JDN `Int64.min` + static let lowerLimit: DateType = (-25252734927771267, 4, 30) +#elseif _pointerBitWidth(_32) + // The Gregorian calendar date corresponding to JDN `Int32.max` + static let upperLimit: DateType = (5874898, 6, 3) + // The Gregorian calendar date corresponding to JDN `Int32.min` + static let lowerLimit: DateType = (-5884323, 5, 15) +#else +#error("Unsupported pointer bit width") +#endif + // These algorithms are valid for all Gregorian calendar dates // corresponding to JDN ≥ 0. // @@ -53,8 +67,11 @@ public struct GregorianCalendar: Calendar { // https://doi.org/10.1145/364096.364097 public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - // Years greater than maxY cause arithmetic overflow - // when computing J even when the final result is ≤ .max +// guard date <= upperLimit else { return .max } +// guard date >= lowerLimit else { return .min } + + // Years greater than `maxY` cause arithmetic overflow + // when computing `J` even when the final result is ≤ `.max` // N.B. this is an estimated upper bound let maxY = .max / 1461 - 4800 @@ -74,7 +91,6 @@ public struct GregorianCalendar: Calendar { Y += cycles * recurrenceCycle.years + recurrenceCycle.years } -// precondition((Y, date.month, date.day) >= minDate) var J = (1461 * (Y + 4800 + (date.month - 14) / 12)) / 4 + (367 * (date.month - 2 - 12 * ((date.month - 14) / 12))) / 12 - (3 * ((Y + 4900 + (date.month - 14) / 12) / 100)) / 4 @@ -93,8 +109,8 @@ public struct GregorianCalendar: Calendar { } public static func dateFromJulianDayNumber(_ JD: JulianDayNumber) -> DateType { - // JDN values greater than maxJD cause arithmetic overflow - // when computing L + // JDN values greater than `maxJD` cause arithmetic overflow + // when computing `L` let maxJD = .max - 68569 var JD = JD @@ -108,7 +124,6 @@ public struct GregorianCalendar: Calendar { JD = recurrenceCycle.days + qr.remainder } -// precondition(JD >= minJD) var L = JD + 68569 let N = (4 * L) / 146097 L = L - (146097 * N + 3) / 4 diff --git a/Sources/JulianDayNumber/JulianCalendar.swift b/Sources/JulianDayNumber/JulianCalendar.swift index 0dfab81..2734af0 100644 --- a/Sources/JulianDayNumber/JulianCalendar.swift +++ b/Sources/JulianDayNumber/JulianCalendar.swift @@ -37,9 +37,23 @@ public struct JulianCalendar: Calendar { /// The recurrence (solar) cycle of the Julian calendar is 21 common years of 365 days and 7 leap years of 366 days. static let recurrenceCycle = (years: 28, days: 10227) - /// The Julian calendar date corresponding to JDN 0 + /// The Julian calendar date corresponding to JDN 0. static let jdnZero: DateType = (-4712, 1, 1) +#if _pointerBitWidth(_64) + // The Julian calendar date corresponding to JDN `Int64.max` + static let upperLimit: DateType = (25252216391110348, 5, 22) + // The Julian calendar date corresponding to JDN `Int64.min` + static let lowerLimit: DateType = (-25252216391119773, 8, 11) +#elseif _pointerBitWidth(_32) + // The Julian calendar date corresponding to JDN `Int32.max` + static let upperLimit: DateType = (5874777, 10, 17) + // The Julian calendar date corresponding to JDN `Int32.min` + static let lowerLimit: DateType = (-5884202, 3, 16) +#else +#error("Unsupported pointer bit width") +#endif + // These algorithms are valid for all Julian calendar dates // corresponding to JDN ≥ 0. // @@ -48,8 +62,11 @@ public struct JulianCalendar: Calendar { // held at the Jet Propulsion Laboratory in 1970. public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - // Years greater than maxY cause arithmetic overflow - // when computing J even when the final result is ≤ .max +// guard date <= upperLimit else { return .max } +// guard date >= lowerLimit else { return .min } + + // Years greater than `maxY` cause arithmetic overflow + // when computing `J` even when the final result is ≤ `.max` let maxY = .max / 367 var Y = date.year @@ -68,7 +85,6 @@ public struct JulianCalendar: Calendar { Y += cycles * recurrenceCycle.years + recurrenceCycle.years } -// precondition(Y >= minY) var J = 367 * Y - (7 * (Y + 5001 + (date.month - 9) / 7)) / 4 + (275 * date.month) / 9 @@ -87,8 +103,8 @@ public struct JulianCalendar: Calendar { } public static func dateFromJulianDayNumber(_ JD: JulianDayNumber) -> DateType { - // JDN values greater than maxJD cause arithmetic overflow - // when computing J + // JDN values greater than `maxJD` cause arithmetic overflow + // when computing `J` let maxJD = .max - 1402 var JD = JD @@ -102,7 +118,6 @@ public struct JulianCalendar: Calendar { JD = recurrenceCycle.days + qr.remainder } -// precondition(JD >= 0) var J = JD + 1402 let K = (J - 1) / 1461 let L = J - 1461 * K