diff --git a/README.md b/README.md index f64fec4..c160d7d 100644 --- a/README.md +++ b/README.md @@ -3,25 +3,28 @@ [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsbooth%2FJulianDayNumber%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/sbooth/JulianDayNumber) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fsbooth%2FJulianDayNumber%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/sbooth/JulianDayNumber) -Julian day number (JDN) and Julian date (JD) calculations supporting the following calendars: -- [Armenian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/armeniancalendar) -- [Astronomical](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/astronomicalcalendar) -- [Baháʼí](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/bahaicalendar) -- [Coptic](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/copticcalendar) -- [Egyptian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/egyptiancalendar) -- [Ethiopian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/ethiopiancalendar) -- [French Republican](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/frenchrepublicancalendar) -- [Gregorian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/gregoriancalendar) -- [Hebrew](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/hebrewcalendar) -- [Islamic](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/islamiccalendar) -- [Julian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/juliancalendar) -- [Khwarizmian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/khwarizmiancalendar) -- [Maya](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/mayacalendar) -- [Śaka](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/sakacalendar) - -Most of the JDN conversion algorithms are adapted from Richards, E.G. 2012, "[Calendars](https://aa.usno.navy.mil/downloads/c15_usb_online.pdf)," from the *Explanatory Supplement to the Astronomical Almanac, 3rd edition*, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -The JDN algorithms use integer math to avoid rounding errors and the implementations have been round-trip tested for all valid Julian day numbers in the years -999,999 to +999,999. +Julian day number and Julian date calculations supporting the following calendars: + +| Calendar | Epoch ¹ | +| --- | --- | +| [Armenian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/armeniancalendar) | July 11, 552 CE | +| [Astronomical](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/astronomicalcalendar) | January 1, 1 CE | +| [Baháʼí](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/bahaicalendar) | March 21, 1844 | +| [Coptic](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/copticcalendar) | August 29, 284 CE | year. | +| [Egyptian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/egyptiancalendar) | February 26, 747 BCE | +| [Ethiopian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/ethiopiancalendar) | August 29, 8 CE | +| [French Republican](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/frenchrepublicancalendar) | September 22, 1792 | +| [Gregorian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/gregoriancalendar) | January 1, 1 CE | +| [Hebrew](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/hebrewcalendar) | October 7, 3761 BCE | +| [Islamic](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/islamiccalendar) | July 16, 622 CE | +| [Julian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/juliancalendar) | January 1, 1 CE | +| [Khwarizmian](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/khwarizmiancalendar) | June 21, 632 CE | +| [Maya](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/mayacalendar) | September 6, 3114 BCE | +| [Śaka](https://swiftpackageindex.com/sbooth/juliandaynumber/main/documentation/juliandaynumber/sakacalendar) | March 24, 79 CE | + +¹ Epochs given in Julian calendar + +The Julian day number interconverting algorithms use integer math to avoid rounding errors and the implementations have been round-trip tested for all valid Julian day numbers in the years -999,999 to +999,999. ## Installation @@ -67,49 +70,23 @@ The [latest documentation](https://swiftpackageindex.com/sbooth/JulianDayNumber/ ## Limits -### Julian Day Numbers - -The following table summarizes the limits for Julian day numbers. Julian day numbers outside these values will cause an arithmetic overflow. +The following table summarizes the arithmetic limits for Julian day number calculations. | Calendar | Minimum JDN | Maximum JDN | | --- | --- | --- | -| Armenian | -9223372036854775514 | 9223372036854775490 | -| Baháʼí | -9223372036854719351 | 2305795661307959248 | -| Coptic | -9223372036854775664 | 2305843009213693827 | -| Egyptian | -9223372036854775514 | 9223372036854775760 | -| Ethiopian | -9223372036854775664 | 2305843009213693827 | -| French Republican | -9223372036854719351 | 2305795661307960548 | -| Gregorian | -9223372036854719351 | 2305795661307959247 | -| Hebrew | -9223372036747815981 ¹ | 355839970905570 | -| Islamic | -9223372036854775352 | 307445734561818195 | -| Julian | -9223372036854775664 | 2305843009213692550 | -| Khwarizmian | -9223372036854775514 | 9223372036854775490 | -| Maya Long Count | -9223372036854191525 ² | Int.max | -| Śaka | -9223372036854719351 | 2305795661307959298 | - -¹ The smallest round-trippable JDN for the Hebrew calendar is -9223372036747815627. -² The smallest round-trippable JDN for the Maya Long Count is -9223372036854191517. - -### Julian Dates - -The following table summarizes the limits for Julian dates. Julian dates outside these values will cause an arithmetic overflow. - -| Calendar | Minimum JD | Maximum JD | -| --- | --- | --- | -| Armenian | -0x1.fffffffffffffp+62 | 0x1.fffffffffffffp+62 | -| Baháʼí | -0x1.fffffffffffc8p+62 | 0x1.fffd4eff4e5d7p+60 | -| Coptic | -0x1.fffffffffffffp+62 | 0x1.fffffffffffffp+60 | -| Egyptian | -0x1.fffffffffffffp+62 | 0x1.fffffffffffffp+62 | -| Ethiopian | -0x1.fffffffffffffp+62 | 0x1.fffffffffffffp+60 | -| French Republican | -0x1.fffffffffffc8p+62 | 0x1.fffd4eff4e5dcp+60 | -| Gregorian | -0x1.fffffffffffc8p+62 | 0x1.fffd4eff4e5d7p+60 | -| Hebrew | -0x1.ffffffffe67fbp+62 ¹ | 0x1.43a273100de27p+48 | -| Islamic | -0x1.fffffffffffffp+62 | 0x1.1111111111099p+58 | -| Julian | -0x1.fffffffffffffp+62 | 0x1.ffffffffffffap+60 | -| Khwarizmian | -0x1.fffffffffffffp+62 | 0x1.fffffffffffffp+62 | -| Śaka | -0x1.fffffffffffc8p+62 | 0x1.fffd4eff4e5d8p+60 | - -¹ The smallest round-trippable JD for the Hebrew calendar is -0x1.ffffffffe67fap+62. +| Armenian | `Int.min` + 341 | `Int.max` - 317 | +| Baháʼí | `Int.min` + 56457 | 2305795661307959248 | +| Coptic | `Int.min` + 384 | 2305843009213693827 | +| Egyptian | `Int.min` + 611 | `Int.max` - 47 | +| Ethiopian | `Int.min` + 384 | 2305843009213693827 | +| French Republican | `Int.min` + 56759 | 2305795661307960548 | +| Gregorian | `Int.min` + 56457 | 2305795661307959247 | +| Hebrew | `Int.min` + 106960181 | 355839970905570 | +| Islamic | `Int.min` + 325 | 307445734561818195 | +| Julian | `Int.min` + 144 | 2305843009213692550 | +| Khwarizmian | `Int.min` + 341 | `Int.max` - 317 | +| Maya Long Count | `Int.min` + 584291 | `Int.max` | +| Śaka | `Int.min` + 56457 | 2305795661307959298 | ## License diff --git a/Sources/JulianDayNumber/ArmenianCalendar+JDN.swift b/Sources/JulianDayNumber/ArmenianCalendar+JDN.swift deleted file mode 100644 index 809428f..0000000 --- a/Sources/JulianDayNumber/ArmenianCalendar+JDN.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -/// The number of years in a cycle of the Armenian calendar. -/// -/// A cycle in the Armenian calendar consists of 1 year. -let armenianCalendarCycleYears = 1 - -/// The number of days in a cycle of the Armenian calendar. -/// -/// A cycle in the Armenian calendar consists of 1 year of 365 days. -let armenianCalendarCycleDays = 365 - -extension ArmenianCalendar: JulianDayNumberConverting { - /// A date in the Armenian calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -5268-11-18 in the proleptic Armenian calendar. - if date < (-5268, 11, 18) { - ΔcalendarCycles = (-5269 - Y) / armenianCalendarCycleYears + 1 - Y += ΔcalendarCycles * armenianCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * armenianCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / armenianCalendarCycleDays + 1 - J += ΔcalendarCycles * armenianCalendarCycleDays - } - - let f = J + j - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * armenianCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Armenian calendar conversions -private let y = 5268 -private let j = 317 -private let m = 0 -private let n = 13 -private let r = 1 -private let p = 365 -private let q = 0 -private let v = 0 -private let u = 1 -private let s = 30 -private let t = 0 -private let w = 0 diff --git a/Sources/JulianDayNumber/ArmenianCalendar.swift b/Sources/JulianDayNumber/ArmenianCalendar.swift index f16c5d8..3ee267e 100644 --- a/Sources/JulianDayNumber/ArmenianCalendar.swift +++ b/Sources/JulianDayNumber/ArmenianCalendar.swift @@ -78,3 +78,19 @@ public struct ArmenianCalendar { return monthLengths[M - 1] } } + +extension ArmenianCalendar: JulianDayNumberConverting { + /// A date in the Armenian calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the Armenian calendar. + static let converter = JDNConverter(y: 5268, j: 317, m: 0, n: 13, r: 1, p: 365, q: 0, v: 0, u: 1, s: 30, t: 0, w: 0) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/AstronomicalCalendar+JDN.swift b/Sources/JulianDayNumber/AstronomicalCalendar+JDN.swift deleted file mode 100644 index 0b37bdd..0000000 --- a/Sources/JulianDayNumber/AstronomicalCalendar+JDN.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -extension AstronomicalCalendar: JulianDayNumberConverting { - /// A date in the astronomical calendar consists of a year, month, and day in either the Julian or Gregorian calendar. - public typealias DateType = JulianCalendar.DateType - - /// Converts a date in the astromical calendar to a Julian day number. - /// - /// Dates before October 15, 1582 are treated as dates in the Julian calendar while later dates are treated as dates in the Gregorian calendar. - /// - /// - important: No validation checks are performed on the date values. - /// - /// - parameter date: A date to convert. - /// - /// - returns: The Julian day number corresponding to the specified date. - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - date < gregorianCalendarEffectiveDate ? JulianCalendar.julianDayNumberFromDate(date) : GregorianCalendar.julianDayNumberFromDate(date) - } - - /// Converts a Julian day number to a date in the astromical calendar. - /// - /// Julian day numbers less than 2299161 treated as dates in the Julian calendar while equal or larger Julian day numbers are treated as dates in the Gregorian calendar. - /// - /// - parameter J: A Julian day number. - /// - /// - returns: The date corresponding to the specified Julian day number. - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - J < GregorianCalendar.effectiveJulianDayNumber ? JulianCalendar.dateFromJulianDayNumber(J): GregorianCalendar.dateFromJulianDayNumber(J) - } -} diff --git a/Sources/JulianDayNumber/AstronomicalCalendar.swift b/Sources/JulianDayNumber/AstronomicalCalendar.swift index 443182b..eb5f6e0 100644 --- a/Sources/JulianDayNumber/AstronomicalCalendar.swift +++ b/Sources/JulianDayNumber/AstronomicalCalendar.swift @@ -87,3 +87,32 @@ public struct AstronomicalCalendar { Y < gregorianCalendarEffectiveDate.year ? JulianCalendar.easter(year: Y) : GregorianCalendar.easter(year: Y) } } + +extension AstronomicalCalendar: JulianDayNumberConverting { + /// A date in the astronomical calendar consists of a year, month, and day in either the Julian or Gregorian calendar. + public typealias DateType = JulianCalendar.DateType + + /// Converts a date in the astromical calendar to a Julian day number. + /// + /// Dates before October 15, 1582 are treated as dates in the Julian calendar while later dates are treated as dates in the Gregorian calendar. + /// + /// - important: No validation checks are performed on the date values. + /// + /// - parameter date: A date to convert. + /// + /// - returns: The Julian day number corresponding to the specified date. + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + date < gregorianCalendarEffectiveDate ? JulianCalendar.julianDayNumberFromDate(date) : GregorianCalendar.julianDayNumberFromDate(date) + } + + /// Converts a Julian day number to a date in the astromical calendar. + /// + /// Julian day numbers less than 2299161 treated as dates in the Julian calendar while equal or larger Julian day numbers are treated as dates in the Gregorian calendar. + /// + /// - parameter J: A Julian day number. + /// + /// - returns: The date corresponding to the specified Julian day number. + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + J < GregorianCalendar.effectiveJulianDayNumber ? JulianCalendar.dateFromJulianDayNumber(J): GregorianCalendar.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/BahaiCalendar+JDN.swift b/Sources/JulianDayNumber/BahaiCalendar+JDN.swift deleted file mode 100644 index f03cb69..0000000 --- a/Sources/JulianDayNumber/BahaiCalendar+JDN.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Baháʼí calendar. -/// -/// A cycle in the Baháʼí calendar consists of 303 common years and 97 leap years. -let bahaiCalendarCycleYears = 400 - -/// The number of days in a cycle of the Baháʼí calendar. -/// -/// A cycle in the Baháʼí calendar consists of 303 years of 365 days and 97 leap year of 366 days. -let bahaiCalendarCycleDays = 146097 - -extension BahaiCalendar: JulianDayNumberConverting { - /// A date in the Baháʼí calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -6556-14-02 in the proleptic Baháʼí calendar. - if date < (-6556, 14, 2) { - ΔcalendarCycles = (-6557 - Y) / bahaiCalendarCycleYears + 1 - Y += ΔcalendarCycles * bahaiCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - J = J - (3 * ((g + A) / 100)) / 4 - C - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * bahaiCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / bahaiCalendarCycleDays + 1 - J += ΔcalendarCycles * bahaiCalendarCycleDays - } - - var f = J + j - f = f + (((4 * J + B) / 146097) * 3) / 4 + C - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * bahaiCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Baháʼí calendar conversions -private let y = 6560 -private let j = 1412 -private let m = 19 -private let n = 20 -private let r = 4 -private let p = 1461 -private let q = 0 -private let v = 3 -private let u = 1 -private let s = 19 -private let t = 0 -private let w = 0 -private let A = 184 -private let B = 274273 -private let C = -50 diff --git a/Sources/JulianDayNumber/BahaiCalendar.swift b/Sources/JulianDayNumber/BahaiCalendar.swift index 40886cd..5c58b0b 100644 --- a/Sources/JulianDayNumber/BahaiCalendar.swift +++ b/Sources/JulianDayNumber/BahaiCalendar.swift @@ -134,3 +134,19 @@ public struct BahaiCalendar { } } } + +extension BahaiCalendar: JulianDayNumberConverting { + /// A date in the Baháʼí calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the Baháʼí calendar. + static let converter = JDNGregorianTypeConverter(y: 6560, j: 1412, m: 19, n: 20, r: 4, p: 1461, q: 0, v: 3, u: 1, s: 19, t: 0, w: 0, A: 184, B: 274273, C: -50) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/CopticCalendar+JDN.swift b/Sources/JulianDayNumber/CopticCalendar+JDN.swift deleted file mode 100644 index 3c27e18..0000000 --- a/Sources/JulianDayNumber/CopticCalendar+JDN.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Coptic calendar. -/// -/// A cycle in the Coptic calendar consists of 3 common years and 1 leap year. -let copticCalendarCycleYears = 4 - -/// The number of days in a cycle of the Coptic calendar. -/// -/// A cycle in the Coptic calendar consists of 3 years of 365 days and 1 leap year of 366 days. -let copticCalendarCycleDays = 1461 - -extension CopticCalendar: JulianDayNumberConverting { - /// A date in the Coptic calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -4996-05-05 in the proleptic Coptic calendar. - if date < (-4996, 5, 5) { - ΔcalendarCycles = (-4997 - Y) / copticCalendarCycleYears + 1 - Y += ΔcalendarCycles * copticCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * copticCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / copticCalendarCycleDays + 1 - J += ΔcalendarCycles * copticCalendarCycleDays - } - - let f = J + j - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * copticCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Coptic calendar conversions -private let y = 4996 -private let j = 124 -private let m = 0 -private let n = 13 -private let r = 4 -private let p = 1461 -private let q = 0 -private let v = 3 -private let u = 1 -private let s = 30 -private let t = 0 -private let w = 0 diff --git a/Sources/JulianDayNumber/CopticCalendar.swift b/Sources/JulianDayNumber/CopticCalendar.swift index 81f7fae..49bd6de 100644 --- a/Sources/JulianDayNumber/CopticCalendar.swift +++ b/Sources/JulianDayNumber/CopticCalendar.swift @@ -95,3 +95,19 @@ public struct CopticCalendar { } } } + +extension CopticCalendar: JulianDayNumberConverting { + /// A date in the Coptic calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the Coptic calendar. + static let converter = JDNConverter(y: 4996, j: 124, m: 0, n: 13, r: 4, p: 1461, q: 0, v: 3, u: 1, s: 30, t: 0, w: 0) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/EgyptianCalendar+JDN.swift b/Sources/JulianDayNumber/EgyptianCalendar+JDN.swift deleted file mode 100644 index ec6faa0..0000000 --- a/Sources/JulianDayNumber/EgyptianCalendar+JDN.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Egyptian calendar. -/// -/// A cycle in the Egyptian calendar consists of 1 year. -let egyptianCalendarCycleYears = 1 - -/// The number of days in a cycle of the Egyptian calendar. -/// -/// A cycle in the Egyptian calendar consists of 1 year of 365 days. -let egyptianCalendarCycleDays = 365 - -extension EgyptianCalendar: JulianDayNumberConverting { - /// A date in the Egyptian calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -3968-02-18 in the proleptic Egyptian calendar. - if date < (-3968, 2, 18) { - ΔcalendarCycles = (-3969 - Y) / egyptianCalendarCycleYears + 1 - Y += ΔcalendarCycles * egyptianCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * egyptianCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / egyptianCalendarCycleDays + 1 - J += ΔcalendarCycles * egyptianCalendarCycleDays - } - - let f = J + j - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * egyptianCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Egyptian calendar conversions -private let y = 3968 -private let j = 47 -private let m = 0 -private let n = 13 -private let r = 1 -private let p = 365 -private let q = 0 -private let v = 0 -private let u = 1 -private let s = 30 -private let t = 0 -private let w = 0 diff --git a/Sources/JulianDayNumber/EgyptianCalendar.swift b/Sources/JulianDayNumber/EgyptianCalendar.swift index 16c3ab6..3889499 100644 --- a/Sources/JulianDayNumber/EgyptianCalendar.swift +++ b/Sources/JulianDayNumber/EgyptianCalendar.swift @@ -6,7 +6,7 @@ import Foundation -/// The Egyptian calendar is a solar calendar with 365 days in every year. +/// The Egyptian calendar is a solar calendar with 365 days in the year. /// /// The year consists of twelve months having 30 days each. The twelfth month is followed by five epagomenal days. /// @@ -79,3 +79,19 @@ public struct EgyptianCalendar { return monthLengths[M - 1] } } + +extension EgyptianCalendar: JulianDayNumberConverting { + /// A date in the Egyptian calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the Egyptian calendar. + static let converter = JDNConverter(y: 3968, j: 47, m: 0, n: 13, r: 1, p: 365, q: 0, v: 0, u: 1, s: 30, t: 0, w: 0) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/EthiopianCalendar+JDN.swift b/Sources/JulianDayNumber/EthiopianCalendar+JDN.swift deleted file mode 100644 index e1ab099..0000000 --- a/Sources/JulianDayNumber/EthiopianCalendar+JDN.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Ethiopian calendar. -/// -/// A cycle in the Ethiopian calendar consists of 3 common years and 1 leap year. -let ethiopianCalendarCycleYears = 4 - -/// The number of days in a cycle of the Ethiopian calendar. -/// -/// A cycle in the Ethiopian calendar consists of 3 years of 365 days and 1 leap year of 366 days. -let ethiopianCalendarCycleDays = 1461 - -extension EthiopianCalendar: JulianDayNumberConverting { - /// A date in the Ethiopian calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -4720-05-05 in the proleptic Ethiopian calendar. - if date < (-4720, 5, 5) { - ΔcalendarCycles = (-4721 - Y) / ethiopianCalendarCycleYears + 1 - Y += ΔcalendarCycles * ethiopianCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * ethiopianCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / ethiopianCalendarCycleDays + 1 - J += ΔcalendarCycles * ethiopianCalendarCycleDays - } - - let f = J + j - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * ethiopianCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Ethiopian calendar conversions -private let y = 4720 -private let j = 124 -private let m = 0 -private let n = 13 -private let r = 4 -private let p = 1461 -private let q = 0 -private let v = 3 -private let u = 1 -private let s = 30 -private let t = 0 -private let w = 0 diff --git a/Sources/JulianDayNumber/EthiopianCalendar.swift b/Sources/JulianDayNumber/EthiopianCalendar.swift index 406361a..b803547 100644 --- a/Sources/JulianDayNumber/EthiopianCalendar.swift +++ b/Sources/JulianDayNumber/EthiopianCalendar.swift @@ -95,3 +95,19 @@ public struct EthiopianCalendar { } } } + +extension EthiopianCalendar: JulianDayNumberConverting { + /// A date in the Ethiopian calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the Ethiopian calendar. + static let converter = JDNConverter(y: 4720, j: 124, m: 0, n: 13, r: 4, p: 1461, q: 0, v: 3, u: 1, s: 30, t: 0, w: 0) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/FrenchRepublicanCalendar+JDN.swift b/Sources/JulianDayNumber/FrenchRepublicanCalendar+JDN.swift deleted file mode 100644 index 6f63ac3..0000000 --- a/Sources/JulianDayNumber/FrenchRepublicanCalendar+JDN.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the French Republican calendar. -/// -/// A cycle in the modified French Republican calendar consists of 303 common years and 97 leap years. -let frenchRepublicanCalendarCycleYears = 400 - -/// The number of days in a cycle of the French Republican calendar. -/// -/// A cycle in the modified French Republican calendar consists of 303 years of 365 days and 97 leap year of 366 days. -let frenchRepublicanCalendarCycleDays = 146097 - -extension FrenchRepublicanCalendar: JulianDayNumberConverting { - /// A date in the French Republican calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -6504-03-03 in the proleptic French Republican calendar. - if date < (-6504, 3, 3) { - ΔcalendarCycles = (-6505 - Y) / frenchRepublicanCalendarCycleYears + 1 - Y += ΔcalendarCycles * frenchRepublicanCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - J = J - (3 * ((g + A) / 100)) / 4 - C - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * frenchRepublicanCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / frenchRepublicanCalendarCycleDays + 1 - J += ΔcalendarCycles * frenchRepublicanCalendarCycleDays - } - - var f = J + j - f = f + (((4 * J + B) / 146097) * 3) / 4 + C - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * frenchRepublicanCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for French Republican calendar conversions -private let y = 6504 -private let j = 111 -private let m = 0 -private let n = 13 -private let r = 4 -private let p = 1461 -private let q = 0 -private let v = 3 -private let u = 1 -private let s = 30 -private let t = 0 -private let w = 0 -private let A = 396 -private let B = 578797 -private let C = -51 diff --git a/Sources/JulianDayNumber/FrenchRepublicanCalendar.swift b/Sources/JulianDayNumber/FrenchRepublicanCalendar.swift index c5b32b7..e98af4d 100644 --- a/Sources/JulianDayNumber/FrenchRepublicanCalendar.swift +++ b/Sources/JulianDayNumber/FrenchRepublicanCalendar.swift @@ -132,3 +132,19 @@ public struct FrenchRepublicanCalendar { } } } + +extension FrenchRepublicanCalendar: JulianDayNumberConverting { + /// A date in the French Republican calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the French Republican calendar. + static let converter = JDNGregorianTypeConverter(y: 6504, j: 111, m: 0, n: 13, r: 4, p: 1461, q: 0, v: 3, u: 1, s: 30, t: 0, w: 0, A: 396, B: 578797, C: -51) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/GregorianCalendar+JDN.swift b/Sources/JulianDayNumber/GregorianCalendar+JDN.swift deleted file mode 100644 index 33ea1eb..0000000 --- a/Sources/JulianDayNumber/GregorianCalendar+JDN.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Gregorian calendar. -/// -/// A cycle in the Gregorian calendar consists of 303 common years and 97 leap years. -let gregorianCalendarCycleYears = 400 - -/// The number of days in a cycle of the Gregorian calendar. -/// -/// A cycle in the Gregorian calendar consists of 303 years of 365 days and 97 leap year of 366 days. -let gregorianCalendarCycleDays = 146097 - -extension GregorianCalendar: JulianDayNumberConverting { - /// A date in the Gregorian calendar consists of a year, month, and day. - public typealias DateType = JulianCalendar.DateType - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -4713-11-24 in the proleptic Gregorian calendar. - if date < (-4713, 11, 24) { - ΔcalendarCycles = (-4714 - Y) / gregorianCalendarCycleYears + 1 - Y += ΔcalendarCycles * gregorianCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - J = J - (3 * ((g + A) / 100)) / 4 - C - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * gregorianCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / gregorianCalendarCycleDays + 1 - J += ΔcalendarCycles * gregorianCalendarCycleDays - } - - var f = J + j - f = f + (((4 * J + B) / 146097) * 3) / 4 + C - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * gregorianCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Gregorian calendar conversions -private let y = 4716 -private let j = 1401 -private let m = 2 -private let n = 12 -private let r = 4 -private let p = 1461 -private let q = 0 -private let v = 3 -private let u = 5 -private let s = 153 -private let t = 2 -private let w = 2 -private let A = 184 -private let B = 274277 -private let C = -38 diff --git a/Sources/JulianDayNumber/GregorianCalendar.swift b/Sources/JulianDayNumber/GregorianCalendar.swift index 8d5e983..755fe84 100644 --- a/Sources/JulianDayNumber/GregorianCalendar.swift +++ b/Sources/JulianDayNumber/GregorianCalendar.swift @@ -173,3 +173,19 @@ public struct GregorianCalendar { return (M, D) } } + +extension GregorianCalendar: JulianDayNumberConverting { + /// A date in the Gregorian calendar consists of a year, month, and day. + public typealias DateType = JulianCalendar.DateType + + /// The converter for the Gregorian calendar. + static let converter = JDNGregorianTypeConverter(y: 4716, j: 1401, m: 2, n: 12, r: 4, p: 1461, q: 0, v: 3, u: 5, s: 153, t: 2, w: 2, A: 184, B: 274277, C: -38) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/HebrewCalendar+JDN.swift b/Sources/JulianDayNumber/HebrewCalendar+JDN.swift deleted file mode 100644 index 7f17563..0000000 --- a/Sources/JulianDayNumber/HebrewCalendar+JDN.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Hebrew calendar. -/// -/// A cycle in the Hebrew calendar consists of 689,472 years, 8,527,680 months, 35,975,351 weeks, or 251,827,457 days. -let hebrewCalendarCycleYears = 689472 - -/// The number of days in a cycle of the Hebrew calendar. -/// -/// A cycle in the Hebrew calendar consists of 689,472 years, 8,527,680 months, 35,975,351 weeks, or 251,827,457 days. -let hebrewCalendarCycleDays = 251827457 - -extension HebrewCalendar { - /// Returns the Julian day number of the first day of Tishrei in the specified year. - /// - /// - note: No validation check is performed on the year value. - /// - /// - parameter Y: A year A.M. - /// - /// - returns: The Julian day number of the first day of Tishrei in the specified year. - static func firstDayOfTishrei(year Y: Int) -> JulianDayNumber { -// precondition(Y > 0, "First day of Tishrei calculations only valid for year numbers > 0") -// precondition(Y < 974245219737, "Year values above 974245219736 cause numerical overflow using 64-bit integers") - - // It is possible to adjust the year by a multiple of the cycle to have this function - // calculate correct values for the first day of Tishrei in proleptic years. However, - // this isn't a public function and the callers perform the translation before calling. - - let b = 31524 + 765433 * ((235 * Y - 234) / 19) - var d = b / 25920 - let e = b % 25920 - let f = 1 + (d % 7) - let g = ((7 * Y + 13) % 19) / 12 - let h = ((7 * Y + 6) % 19) / 12 - if e >= 19440 || (e >= 9924 && f == 3 && g == 0) || (e >= 16788 && f == 2 && g == 0 && h == 1) { - d = d + 1 - } - return d + (((d + 5) % 7) % 2) + 347997 - } - - /// Returns the year A.M. containing the specified Julian day number. - /// - /// - parameter J: A Julian day number. - /// - /// - returns: The year containing the specified Julian day number. - static func yearContaining(julianDayNumber J: JulianDayNumber) -> Int { -// precondition(J >= epochJulianDayNumber, "Julian day number must be >= epoch") -// precondition(J < 355839970905665, "Julian day numbers above 355839970905664 cause numerical overflow using 64-bit integers") - - let M = (25920 * (J - 347996)) / 765433 + 1 - var Y = 19 * (M / 235) + (19 * (M % 235) - 2) / 235 + 1 - let K = firstDayOfTishrei(year: Y) - if K > J { - Y = Y - 1 - } - return Y - } -} - -extension HebrewCalendar: JulianDayNumberConverting { - /// A date in the Hebrew calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - if Y < 1 { - ΔcalendarCycles = (1 - Y) / hebrewCalendarCycleYears + 1 - Y += ΔcalendarCycles * hebrewCalendarCycleYears - } - - let a = firstDayOfTishrei(year: Y) - let b = firstDayOfTishrei(year: Y + 1) - let K = b - a - 352 - 27 * (((7 * Y + 13) % 19) / 12) - var J = a + A[K - 1][date.month - 1] + date.day - 1 - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * hebrewCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - if J < epochJulianDayNumber { - ΔcalendarCycles = (epochJulianDayNumber - J) / hebrewCalendarCycleDays + 1 - J += ΔcalendarCycles * hebrewCalendarCycleDays - } - - var Y = yearContaining(julianDayNumber: J) - let a = firstDayOfTishrei(year: Y) - let b = firstDayOfTishrei(year: Y + 1) - let K = b - a - 352 - 27 * (((7 * Y + 13) % 19) / 12) - let c = J - a + 1 -// precondition(c >= 0) - let AK = A[K - 1] - let M = AK.lastIndex(where: {$0 < c})! + 1 - let D = c - AK[M - 1] - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * hebrewCalendarCycleYears - } - - return (Y, M, D) - } -} - -/// The number of days preceding the first of the month in a year with characterization K. -/// -/// The array is indexed first by `K - 1` and the resultant array by `M - 1`. -private let A = [ - // A deficient common year. - [0, 30, 59, 88, 117, 147, 176, 206, 235, 265, 294, 324], - // A regular common year. - [0, 30, 59, 89, 118, 148, 177, 207, 236, 266, 295, 325], - // An abundant common year. - [0, 30, 60, 90, 119, 149, 178, 208, 237, 267, 296, 326], - // A deficient leap year. - [0, 30, 59, 88, 117, 147, 177, 206, 236, 265, 295, 324, 354], - // A regular leap year. - [0, 30, 59, 89, 118, 148, 178, 207, 237, 266, 296, 325, 355], - // An abundant leap year. - [0, 30, 60, 90, 119, 149, 179, 208, 238, 267, 297, 326, 356], -] diff --git a/Sources/JulianDayNumber/HebrewCalendar.swift b/Sources/JulianDayNumber/HebrewCalendar.swift index 9b1287c..45d7042 100644 --- a/Sources/JulianDayNumber/HebrewCalendar.swift +++ b/Sources/JulianDayNumber/HebrewCalendar.swift @@ -193,8 +193,8 @@ public struct HebrewCalendar { var ΔcalendarCycles = 0 if Y < 1 { - ΔcalendarCycles = (1 - Y) / hebrewCalendarCycleYears + 1 - Y += ΔcalendarCycles * hebrewCalendarCycleYears + ΔcalendarCycles = (1 - Y) / intercalatingCycle.years + 1 + Y += ΔcalendarCycles * intercalatingCycle.years } let a = firstDayOfTishrei(year: Y) @@ -208,3 +208,125 @@ public struct HebrewCalendar { return monthLengths[K - 1][M - 1] } } + +// Algorithm adapted from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. + +extension HebrewCalendar { + /// Returns the Julian day number of the first day of Tishrei in the specified year. + /// + /// - attention: No validation check is performed on the year value. + /// + /// - parameter Y: A year A.M. + /// + /// - returns: The Julian day number of the first day of Tishrei in the specified year. + static func firstDayOfTishrei(year Y: Int) -> JulianDayNumber { + precondition(Y > 0, "First day of Tishrei calculations only valid for year numbers > 0") + precondition(Y < 974245219737, "Year values above 974245219736 cause numerical overflow using 64-bit integers") + + // It is possible to adjust the year by a multiple of the cycle to have this function + // calculate correct values for the first day of Tishrei in proleptic years. However, + // this isn't a public function and the callers perform the translation before calling. + + let b = 31524 + 765433 * ((235 * Y - 234) / 19) + var d = b / 25920 + let e = b % 25920 + let f = 1 + (d % 7) + let g = ((7 * Y + 13) % 19) / 12 + let h = ((7 * Y + 6) % 19) / 12 + if e >= 19440 || (e >= 9924 && f == 3 && g == 0) || (e >= 16788 && f == 2 && g == 0 && h == 1) { + d = d + 1 + } + return d + (((d + 5) % 7) % 2) + 347997 + } + + /// Returns the year A.M. containing the specified Julian day number. + /// + /// - parameter J: A Julian day number. + /// + /// - returns: The year containing the specified Julian day number. + static func yearContaining(julianDayNumber J: JulianDayNumber) -> Int { + precondition(J >= epochJulianDayNumber, "Julian day number must be >= epoch") + precondition(J < 355839970905665, "Julian day numbers above 355839970905664 cause numerical overflow using 64-bit integers") + + let M = (25920 * (J - 347996)) / 765433 + 1 + var Y = 19 * (M / 235) + (19 * (M % 235) - 2) / 235 + 1 + let K = firstDayOfTishrei(year: Y) + if K > J { + Y = Y - 1 + } + return Y + } +} + +extension HebrewCalendar: JulianDayNumberConverting { + /// A date in the Hebrew calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// An intercalating cycle in the Hebrew calendar consists of 689,472 years, 8,527,680 months, 35,975,351 weeks, or 251,827,457 days. + static let intercalatingCycle = (years: 689472, days: 251827457) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + var Y = date.year + var ΔcalendarCycles = 0 + + if Y < 1 { + ΔcalendarCycles = (1 - Y) / intercalatingCycle.years + 1 + Y += ΔcalendarCycles * intercalatingCycle.years + } + + let a = firstDayOfTishrei(year: Y) + let b = firstDayOfTishrei(year: Y + 1) + let K = b - a - 352 - 27 * (((7 * Y + 13) % 19) / 12) + var J = a + A[K - 1][date.month - 1] + date.day - 1 + + if ΔcalendarCycles > 0 { + J -= ΔcalendarCycles * intercalatingCycle.days + } + + return J + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + var J = J + var ΔcalendarCycles = 0 + + if J < epochJulianDayNumber { + ΔcalendarCycles = (epochJulianDayNumber - J) / intercalatingCycle.days + 1 + J += ΔcalendarCycles * intercalatingCycle.days + } + + var Y = yearContaining(julianDayNumber: J) + let a = firstDayOfTishrei(year: Y) + let b = firstDayOfTishrei(year: Y + 1) + let K = b - a - 352 - 27 * (((7 * Y + 13) % 19) / 12) + let c = J - a + 1 + precondition(c >= 0) + let AK = A[K - 1] + let M = AK.lastIndex(where: {$0 < c})! + 1 + let D = c - AK[M - 1] + + if ΔcalendarCycles > 0 { + Y -= ΔcalendarCycles * intercalatingCycle.years + } + + return (Y, M, D) + } + + /// The number of days preceding the first of the month in a year with characterization K. + /// + /// The array is indexed first by `K - 1` and the resultant array by `M - 1`. + static let A = [ + // A deficient common year. + [0, 30, 59, 88, 117, 147, 176, 206, 235, 265, 294, 324], + // A regular common year. + [0, 30, 59, 89, 118, 148, 177, 207, 236, 266, 295, 325], + // An abundant common year. + [0, 30, 60, 90, 119, 149, 178, 208, 237, 267, 296, 326], + // A deficient leap year. + [0, 30, 59, 88, 117, 147, 177, 206, 236, 265, 295, 324, 354], + // A regular leap year. + [0, 30, 59, 89, 118, 148, 178, 207, 237, 266, 296, 325, 355], + // An abundant leap year. + [0, 30, 60, 90, 119, 149, 179, 208, 238, 267, 297, 326, 356], + ] +} diff --git a/Sources/JulianDayNumber/IslamicCalendar+JDN.swift b/Sources/JulianDayNumber/IslamicCalendar+JDN.swift deleted file mode 100644 index 6c1226e..0000000 --- a/Sources/JulianDayNumber/IslamicCalendar+JDN.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Islamic calendar. -/// -/// A cycle in the Islamic calendar consists of 19 common years and 11 leap years. -let islamicCalendarCycleYears = 30 - -/// The number of days in a cycle of the Islamic calendar. -/// -/// A cycle in the Islamic calendar consists of 19 years of 354 days and 11 leap years of 355 days. -let islamicCalendarCycleDays = 10631 - -extension IslamicCalendar: JulianDayNumberConverting { - /// A date in the Islamic calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -5498-08-16 in the proleptic Islamic calendar. - if date < (-5498, 8, 16) { - ΔcalendarCycles = (-5498 - Y) / islamicCalendarCycleYears + 1 - Y += ΔcalendarCycles * islamicCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * islamicCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / islamicCalendarCycleDays + 1 - J += ΔcalendarCycles * islamicCalendarCycleDays - } - - let f = J + j - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * islamicCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Islamic calendar conversions -private let y = 5519 -private let j = 7664 -// Islamic A is identical except j = 7665 -private let m = 0 -private let n = 12 -private let r = 30 -private let p = 10631 -private let q = 14 -private let v = 15 -private let u = 100 -private let s = 2951 -private let t = 51 -private let w = 10 diff --git a/Sources/JulianDayNumber/IslamicCalendar.swift b/Sources/JulianDayNumber/IslamicCalendar.swift index 46618b0..44b6af2 100644 --- a/Sources/JulianDayNumber/IslamicCalendar.swift +++ b/Sources/JulianDayNumber/IslamicCalendar.swift @@ -97,3 +97,19 @@ public struct IslamicCalendar { } } } + +extension IslamicCalendar: JulianDayNumberConverting { + /// A date in the Islamic calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the Islamic calendar. + static let converter = JDNConverter(y: 5519, j: 7664, m: 0, n: 12, r: 30, p: 10631, q: 14, v: 15, u: 100, s: 2951, t: 51, w: 10) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/JDN.swift b/Sources/JulianDayNumber/JDN.swift deleted file mode 100644 index 92d1964..0000000 --- a/Sources/JulianDayNumber/JDN.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -/// A Julian day number. -/// -/// The Julian day number (JDN) is the integer assigned to a whole solar day in the Julian day count starting from noon Universal Time, -/// with JDN 0 assigned to the day starting at noon on Monday, January 1, 4713 BCE in the proleptic Julian calendar. -/// -/// - seealso: [Julian day](https://en.wikipedia.org/wiki/Julian_day) -public typealias JulianDayNumber = Int - -/// Julian day number to date conversion. -public protocol JulianDayNumberConverting { - /// The type that a Julian day number is converted to and from. - associatedtype DateType - - /// Converts a date to a Julian day number and returns the result. - /// - /// - important: No validation checks are performed on the date values. - /// - /// - parameter date: A date to convert. - /// - /// - returns: The Julian day number corresponding to the specified date. - /// - /// - seealso: ``dateFromJulianDayNumber(_:)`` - static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber - - /// Converts a Julian day number to a date and returns the result. - /// - /// - parameter J: A Julian day number. - /// - /// - returns: The date corresponding to the specified Julian day number. - /// - /// - seealso: ``julianDayNumberFromDate(_:)`` - static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType -} - -extension JulianDayNumberConverting where DateType == (year: Int, month: Int, day: Int) { - /// Converts a year, month, and day to a Julian day number and returns the result. - /// - /// - parameter Y: A year number. - /// - parameter M: A month number. - /// - parameter D: A day number. - /// - /// - returns: The Julian day number corresponding to the specified date. - public static func julianDayNumberFrom(year Y: Int, month M: Int, day D: Int) -> JulianDayNumber { - julianDayNumberFromDate((Y, M, D)) - } -} diff --git a/Sources/JulianDayNumber/JDNConverter.swift b/Sources/JulianDayNumber/JDNConverter.swift new file mode 100644 index 0000000..c8bf580 --- /dev/null +++ b/Sources/JulianDayNumber/JDNConverter.swift @@ -0,0 +1,99 @@ +// +// Copyright © 2021-2023 Stephen F. Booth +// Part of https://github.com/sbooth/JulianDayNumber +// MIT license +// + +import Foundation + +/// A converter implementing algorithms for interconverting a Julian day number and a year, month, and day for selected arithmetic calendars. +/// +/// The algorithms are adapted from Richards, E.G. 2012, "[Calendars](https://aa.usno.navy.mil/downloads/c15_usb_online.pdf)," +/// from the *Explanatory Supplement to the Astronomical Almanac, 3rd edition*, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), +/// Chapter 15, pp. 585-624. +struct JDNConverter { + /// A date consisting of a year, month, and day. + typealias YearMonthDay = (year: Int, month: Int, day: Int) + + /// The number of years in the computational calendar which precede the epoch. + let y: Int + /// The number of days the epoch of the computational calendar (0/0/0) precedes day zero. + let j: Int + /// The month number which corresponds to month zero in the computational calendar. + let m: Int + /// The number of months in a year, counting any epagonomai as an extra month. + let n: Int + /// The number of years in an intercalation cycle. + let r: Int + /// The number of days in an intercalation cycle. + let p: Int + let q: Int + let v: Int + let u: Int + let s: Int + let t: Int + let w: Int + + /// Converts a date to a Julian day numberand returns the result. + /// + /// - important: No validation checks are performed on the date values. + /// + /// - parameter date: A date to convert. + /// + /// - returns: The Julian day number corresponding to the specified date. + func julianDayNumberFromDate(_ date: YearMonthDay) -> JulianDayNumber { + var Y = date.year + var ΔcalendarCycles = 0 + + if Y <= -y { + ΔcalendarCycles = (-y - Y) / r + 1 + Y += ΔcalendarCycles * r + } + + let h = date.month - m + let g = Y + y - (n - h) / n + let f = (h - 1 + n) % n + let e = (p * g + q) / r + date.day - 1 - j + var J = e + (s * f + t) / u + + if ΔcalendarCycles > 0 { + J -= ΔcalendarCycles * p + } + + return J + } + + /// Converts a Julian day number to a date and returns the result. + /// + /// - parameter J: A Julian day number. + /// + /// - returns: The date corresponding to the specified Julian day number. + func dateFromJulianDayNumber(_ J: JulianDayNumber) -> YearMonthDay { +// precondition(J < Int.max - 1) + + var J = J + var ΔcalendarCycles = 0 + + // Richards' algorithm is only valid for positive JDNs. + if J < 0 { + ΔcalendarCycles = -J / p + 1 + J += ΔcalendarCycles * p + } + + precondition(J <= Int.max - j, "Julian day number too large") + + let f = J + j + let e = r * f + v + let g = (e % p) / r + let h = u * g + w + let D = (h % s) / u + 1 + let M = ((h / s + m) % n) + 1 + var Y = e / p - y + (n + m - M) / n + + if ΔcalendarCycles > 0 { + Y -= ΔcalendarCycles * r + } + + return (Y, M, D) + } +} diff --git a/Sources/JulianDayNumber/JDNGregorianTypeConverter.swift b/Sources/JulianDayNumber/JDNGregorianTypeConverter.swift new file mode 100644 index 0000000..9173c05 --- /dev/null +++ b/Sources/JulianDayNumber/JDNGregorianTypeConverter.swift @@ -0,0 +1,108 @@ +// +// Copyright © 2021-2023 Stephen F. Booth +// Part of https://github.com/sbooth/JulianDayNumber +// MIT license +// + +import Foundation + +// TODO: Figure out a way to use the Gregorian intercalating parameters A, B and C instead + +/// The intercalating cycle of the Gregorian calendar is 303 common years of 365 days and 97 leap years of 366 days. +let gregorianIntercalatingCycle = (years: 400, days: 146097) + +/// A converter implementing algorithms for interconverting a Julian day number and a year, month, and day in calendars using Gregorian-type intercalating. +/// +/// The algorithms are adapted from Richards, E.G. 2012, "[Calendars](https://aa.usno.navy.mil/downloads/c15_usb_online.pdf)," +/// from the *Explanatory Supplement to the Astronomical Almanac, 3rd edition*, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), +/// Chapter 15, pp. 585-624. +struct JDNGregorianTypeConverter { + /// A date consisting of a year, month, and day. + typealias YearMonthDay = (year: Int, month: Int, day: Int) + + /// The number of years in the computational calendar which precede the epoch. + let y: Int + /// The number of days the epoch of the computational calendar (0/0/0) precedes day zero. + let j: Int + /// The month number which corresponds to month zero in the computational calendar. + let m: Int + /// The number of months in a year, counting any epagonomai as an extra month. + let n: Int + /// The number of years in an intercalation cycle. + let r: Int + /// The number of days in an intercalation cycle. + let p: Int + let q: Int + let v: Int + let u: Int + let s: Int + let t: Int + let w: Int + + let A: Int + let B: Int + let C: Int + + /// Converts a date to a Julian day number and returns the result. + /// + /// - important: No validation checks are performed on the date values. + /// + /// - parameter date: A date to convert. + /// + /// - returns: The Julian day number corresponding to the specified date. + func julianDayNumberFromDate(_ date: YearMonthDay) -> JulianDayNumber { + var Y = date.year + var ΔcalendarCycles = 0 + + if Y <= -y { + ΔcalendarCycles = (-y - Y) / gregorianIntercalatingCycle.years + 1 + Y += ΔcalendarCycles * gregorianIntercalatingCycle.years + } + + let h = date.month - m + let g = Y + y - (n - h) / n + let f = (h - 1 + n) % n + let e = (p * g + q) / r + date.day - 1 - j + var J = e + (s * f + t) / u + J = J - (3 * ((g + A) / 100)) / 4 - C + + if ΔcalendarCycles > 0 { + J -= ΔcalendarCycles * gregorianIntercalatingCycle.days + } + + return J + } + + /// Converts a Julian day number to a date and returns the result. + /// + /// - parameter J: A Julian day number. + /// + /// - returns: The date corresponding to the specified Julian day number. + func dateFromJulianDayNumber(_ J: JulianDayNumber) -> YearMonthDay { + var J = J + var ΔcalendarCycles = 0 + + // Richards' algorithm is only valid for positive JDNs. + if J < 0 { + ΔcalendarCycles = -J / gregorianIntercalatingCycle.days + 1 + J += ΔcalendarCycles * gregorianIntercalatingCycle.days + } + + precondition(J <= Int.max - j, "Julian day number too large") + + var f = J + j + f = f + (((4 * J + B) / 146097) * 3) / 4 + C + let e = r * f + v + let g = (e % p) / r + let h = u * g + w + let D = (h % s) / u + 1 + let M = ((h / s + m) % n) + 1 + var Y = e / p - y + (n + m - M) / n + + if ΔcalendarCycles > 0 { + Y -= ΔcalendarCycles * gregorianIntercalatingCycle.years + } + + return (Y, M, D) + } +} diff --git a/Sources/JulianDayNumber/JDNSakaConverter.swift b/Sources/JulianDayNumber/JDNSakaConverter.swift new file mode 100644 index 0000000..77a8132 --- /dev/null +++ b/Sources/JulianDayNumber/JDNSakaConverter.swift @@ -0,0 +1,109 @@ +// +// Copyright © 2021-2023 Stephen F. Booth +// Part of https://github.com/sbooth/JulianDayNumber +// MIT license +// + +import Foundation + +/// A converter implementing algorithms for interconverting a Julian day number and a year, month, and day in the Śaka calendar. +/// +/// The algorithms are adapted from Richards, E.G. 2012, "[Calendars](https://aa.usno.navy.mil/downloads/c15_usb_online.pdf)," +/// from the *Explanatory Supplement to the Astronomical Almanac, 3rd edition*, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), +/// Chapter 15, pp. 585-624. +struct JDNSakaConverter { + /// A date consisting of a year, month, and day. + typealias YearMonthDay = (year: Int, month: Int, day: Int) + + /// The number of years in the computational calendar which precede the epoch. + let y = 4794 + /// The number of days the epoch of the computational calendar (0/0/0) precedes day zero. + let j = 1348 + /// The month number which corresponds to month zero in the computational calendar. + let m = 1 + /// The number of months in a year, counting any epagonomai as an extra month. + let n = 12 + /// The number of years in an intercalation cycle. + let r = 4 + /// The number of days in an intercalation cycle. + let p = 1461 + let q = 0 + let v = 3 + let u = 1 + let s = 31 + let t = 0 + let w = 0 + + let A = 184 + let B = 274073 + let C = -36 + + /// Converts a date in the Śaka calendar to a Julian day number and returns the result. + /// + /// - important: No validation checks are performed on the date values. + /// + /// - parameter date: A date to convert. + /// + /// - returns: The Julian day number corresponding to the specified date. + func julianDayNumberFromDate(_ date: YearMonthDay) -> JulianDayNumber { + var Y = date.year + var ΔcalendarCycles = 0 + + if Y <= -y { + ΔcalendarCycles = (-y - Y) / gregorianIntercalatingCycle.years + 1 + Y += ΔcalendarCycles * gregorianIntercalatingCycle.years + } + + let h = date.month - m + let g = Y + y - (n - h) / n + let f = (h - 1 + n) % n + let e = (p * g + q) / r + date.day - 1 - j + let Z = f / 6 + var J = e + ((31 - Z) * f + 5 * Z) / u + J = J - (3 * ((g + A) / 100)) / 4 - C + + if ΔcalendarCycles > 0 { + J -= ΔcalendarCycles * gregorianIntercalatingCycle.days + } + + return J + } + + /// Converts a Julian day number to a date in the Śaka calendar and returns the result. + /// + /// - parameter J: A Julian day number. + /// + /// - returns: The date corresponding to the specified Julian day number. + func dateFromJulianDayNumber(_ J: JulianDayNumber) -> YearMonthDay { + var J = J + var ΔcalendarCycles = 0 + + // Richards' algorithm is only valid for positive JDNs. + if J < 0 { + ΔcalendarCycles = -J / gregorianIntercalatingCycle.days + 1 + J += ΔcalendarCycles * gregorianIntercalatingCycle.days + } + + precondition(J <= Int.max - j, "Julian day number too large") + + var f = J + j + f = f + (((4 * J + B) / 146097) * 3) / 4 + C + let e = r * f + v + let g = (e % p) / r + let X = g / 365 + let Z = g / 185 - X + let s = 31 - Z + let w = -5 * Z + let h = u * g + w + let D = (6 * X + (h % s)) / u + 1 + + let M = ((h / s + m) % n) + 1 + var Y = e / p - y + (n + m - M) / n + + if ΔcalendarCycles > 0 { + Y -= ΔcalendarCycles * gregorianIntercalatingCycle.years + } + + return (Y, M, D) + } +} diff --git a/Sources/JulianDayNumber/JulianCalendar+JDN.swift b/Sources/JulianDayNumber/JulianCalendar+JDN.swift deleted file mode 100644 index ac66b41..0000000 --- a/Sources/JulianDayNumber/JulianCalendar+JDN.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Julian calendar. -/// -/// A cycle in the Julian calendar consists of 3 common years and 1 leap year. -let julianCalendarCycleYears = 4 - -/// The number of days in a cycle of the Julian calendar. -/// -/// A cycle in the Julian calendar consists of 3 years of 365 days and 1 leap year of 366 days. -let julianCalendarCycleDays = 1461 - -extension JulianCalendar: JulianDayNumberConverting { - /// A date in the Julian calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -4712-01-01 in the proleptic Julian calendar. - if Y < -4712 { - ΔcalendarCycles = (-4713 - Y) / julianCalendarCycleYears + 1 - Y += ΔcalendarCycles * julianCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * julianCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / julianCalendarCycleDays + 1 - J += ΔcalendarCycles * julianCalendarCycleDays - } - - let f = J + j - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * julianCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Julian calendar conversions -private let y = 4716 -private let j = 1401 -private let m = 2 -private let n = 12 -private let r = 4 -private let p = 1461 -private let q = 0 -private let v = 3 -private let u = 5 -private let s = 153 -private let t = 2 -private let w = 2 diff --git a/Sources/JulianDayNumber/JulianCalendar.swift b/Sources/JulianDayNumber/JulianCalendar.swift index 77bd034..aa89e49 100644 --- a/Sources/JulianDayNumber/JulianCalendar.swift +++ b/Sources/JulianDayNumber/JulianCalendar.swift @@ -153,3 +153,19 @@ public struct JulianCalendar { return (M, D) } } + +extension JulianCalendar: JulianDayNumberConverting { + /// A date in the Julian calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the Julian calendar. + static let converter = JDNConverter(y: 4716, j: 1401, m: 2, n: 12, r: 4, p: 1461, q: 0, v: 3, u: 5, s: 153, t: 2, w: 2) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/JD.swift b/Sources/JulianDayNumber/JulianDayNumberConverting.swift similarity index 68% rename from Sources/JulianDayNumber/JD.swift rename to Sources/JulianDayNumber/JulianDayNumberConverting.swift index b76c5c6..1ed1384 100644 --- a/Sources/JulianDayNumber/JD.swift +++ b/Sources/JulianDayNumber/JulianDayNumberConverting.swift @@ -6,6 +6,55 @@ import Foundation +/// A Julian day number. +/// +/// The Julian day number (JDN) is the integer assigned to a whole solar day in the Julian day count starting from noon Universal Time, +/// with JDN 0 assigned to the day starting at noon on Monday, January 1, 4713 BCE in the proleptic Julian calendar. +/// +/// - seealso: [Julian day](https://en.wikipedia.org/wiki/Julian_day) +public typealias JulianDayNumber = Int + +/// Julian day number to date conversion. +public protocol JulianDayNumberConverting { + /// The type that a Julian day number is converted to and from. + associatedtype DateType + + /// Converts a date to a Julian day number and returns the result. + /// + /// - important: No validation checks are performed on the date values. + /// + /// - parameter date: A date to convert. + /// + /// - returns: The Julian day number corresponding to the specified date. + /// + /// - seealso: ``dateFromJulianDayNumber(_:)`` + static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber + + /// Converts a Julian day number to a date and returns the result. + /// + /// - parameter J: A Julian day number. + /// + /// - returns: The date corresponding to the specified Julian day number. + /// + /// - seealso: ``julianDayNumberFromDate(_:)`` + static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType +} + +extension JulianDayNumberConverting where DateType == (year: Int, month: Int, day: Int) { + /// Converts a year, month, and day to a Julian day number and returns the result. + /// + /// - parameter Y: A year number. + /// - parameter M: A month number. + /// - parameter D: A day number. + /// + /// - returns: The Julian day number corresponding to the specified date. + public static func julianDayNumberFrom(year Y: Int, month M: Int, day D: Int) -> JulianDayNumber { + julianDayNumberFromDate((Y, M, D)) + } +} + +// MARK: - Julian Date + /// A Julian date. /// /// The Julian date (JD) is the Julian day number (JDN) plus the fraction of a day since the preceding noon in Universal Time. @@ -63,7 +112,7 @@ extension JulianDayNumberConverting where DateType == (year: Int, month: Int, da } } -// MARK: - Internal Functions +// MARK: Internal Functions /// Converts an hour, minute, and second to a decimal fractional day and returns the result. /// diff --git a/Sources/JulianDayNumber/KhwarizmianCalendar+JDN.swift b/Sources/JulianDayNumber/KhwarizmianCalendar+JDN.swift deleted file mode 100644 index a8c148f..0000000 --- a/Sources/JulianDayNumber/KhwarizmianCalendar+JDN.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -/// The number of years in a cycle of the Khwarizmian calendar. -/// -/// A cycle in the Khwarizmian calendar consists of 1 year. -let khwarizmianCalendarCycleYears = 1 - -/// The number of days in a cycle of the Khwarizmian calendar. -/// -/// A cycle in the Khwarizmian calendar consists of 1 year of 365 days. -let khwarizmianCalendarCycleDays = 365 - -extension KhwarizmianCalendar: JulianDayNumberConverting { - /// A date in the Khwarizmian calendar consists of a year, month, and day. - public typealias DateType = (year: Year, month: Month, day: Day) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -5348-11-18 in the proleptic Khwarizmian calendar. - if date < (-5348, 11, 18) { - ΔcalendarCycles = (-5349 - Y) / khwarizmianCalendarCycleYears + 1 - Y += ΔcalendarCycles * khwarizmianCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - var J = e + (s * f + t) / u - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * khwarizmianCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / khwarizmianCalendarCycleDays + 1 - J += ΔcalendarCycles * khwarizmianCalendarCycleDays - } - - let f = J + j - let e = r * f + v - let g = (e % p) / r - let h = u * g + w - let D = (h % s) / u + 1 - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * khwarizmianCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Khwarizmian calendar conversions -private let y = 5348 -private let j = 317 -private let m = 0 -private let n = 13 -private let r = 1 -private let p = 365 -private let q = 0 -private let v = 0 -private let u = 1 -private let s = 30 -private let t = 0 -private let w = 0 diff --git a/Sources/JulianDayNumber/KhwarizmianCalendar.swift b/Sources/JulianDayNumber/KhwarizmianCalendar.swift index f24fcee..55f12e5 100644 --- a/Sources/JulianDayNumber/KhwarizmianCalendar.swift +++ b/Sources/JulianDayNumber/KhwarizmianCalendar.swift @@ -78,3 +78,19 @@ public struct KhwarizmianCalendar { return monthLengths[M - 1] } } + +extension KhwarizmianCalendar: JulianDayNumberConverting { + /// A date in the Khwarizmian calendar consists of a year, month, and day. + public typealias DateType = (year: Year, month: Month, day: Day) + + /// The converter for the Khwarizmian calendar. + static let converter = JDNConverter(y: 5348, j: 317, m: 0, n: 13, r: 1, p: 365, q: 0, v: 0, u: 1, s: 30, t: 0, w: 0) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Sources/JulianDayNumber/MayaCalendar+JDN.swift b/Sources/JulianDayNumber/MayaCalendar+JDN.swift deleted file mode 100644 index 7503e38..0000000 --- a/Sources/JulianDayNumber/MayaCalendar+JDN.swift +++ /dev/null @@ -1,220 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Maya long count cycle lengths - -/// One uinal is composed of 20 kin. -let kinPerUinal = 20 -/// One tun is composed of 18 uinal. -let uinalPerTun = 18 -/// One katun is composed of 20 tun. -let tunPerKatun = 20 -/// One baktun is composed of 20 katun. -let katunPerBaktun = 20 -/// One pictun is composed of 20 baktun. -let baktunPerPictun = 20 -/// One calabtun is composed of 20 pictun. -let pictunPerCalabtun = 20 -/// One kinchiltun is composed of 20 calabtun. -let calabtunPerKinchiltun = 20 -/// One alautun is composed of 20 kinchiltun. -let kinchiltunPerAlautun = 20 - -extension MayaCalendar: JulianDayNumberConverting { - /// A long count in the Maya calendar. - public typealias DateType = (baktun: Baktun, katun: Katun, tun: Tun, uinal: Uinal, kin: Kin) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - julianDayNumberFromLongCount(baktun: date.baktun, katun: date.katun, tun: date.tun, uinal: date.uinal, kin: date.kin) - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - longCountFromJulianDayNumber(J) - } -} - -extension MayaCalendar { - /// Converts a Julian day number to a long count in the Maya calendar. - /// - /// - parameter J: A Julian day number. - /// - /// - returns: The long count corresponding to the specified Julian day number. - public static func longCountFromJulianDayNumber(_ J: JulianDayNumber) -> (/*alautun: Alautun, kinchiltun: Kinchiltun, calabtun: Calabtun, pictun: Pictun, */baktun: Baktun, katun: Katun, tun: Tun, uinal: Uinal, kin: Kin) { - let L = J - longCountEpochJulianDayNumber - -#if false - var alautun, kinchiltun, calabtun, pictun: Int -#endif - var baktun, katun, tun, uinal, kin: Int - - (uinal, kin) = L.quotientAndRemainder(dividingBy: kinPerUinal) - (tun, uinal) = uinal.quotientAndRemainder(dividingBy: uinalPerTun) - (katun, tun) = tun.quotientAndRemainder(dividingBy: tunPerKatun) - (baktun, katun) = katun.quotientAndRemainder(dividingBy: katunPerBaktun) -#if false - (pictun, baktun) = baktun.quotientAndRemainder(dividingBy: baktunPerPictun) - (calabtun, pictun) = pictun.quotientAndRemainder(dividingBy: pictunPerCalabtun) - (kinchiltun, calabtun) = calabtun.quotientAndRemainder(dividingBy: calabtunPerKinchiltun) - (alautun, kinchiltun) = kinchiltun.quotientAndRemainder(dividingBy: kinchiltunPerAlautun) -#endif - - if L < 0 { - if kin < 0 { - kin += kinPerUinal - uinal -= 1 - } - if uinal < 0 { - uinal += uinalPerTun - tun -= 1 - } - if tun < 0 { - tun += tunPerKatun - katun -= 1 - } - if katun < 0 { - katun += katunPerBaktun - baktun -= 1 - } -#if false - if baktun < 1 - baktunPerPictun { - baktun += baktunPerPictun - pictun -= 1 - } - if pictun < 1 - pictunPerCalabtun { - pictun += pictunPerCalabtun - calabtun -= 1 - } - if calabtun < 1 - calabtunPerKinchiltun { - calabtun += calabtunPerKinchiltun - kinchiltun -= 1 - } - if kinchiltun < 1 - kinchiltunPerAlautun { - kinchiltun += kinchiltunPerAlautun - alautun -= 1 - } -#endif - } - - return (/*alautun, kinchiltun, calabtun, pictun, */baktun, katun, tun, uinal, kin) - } - - /// Converts a long count in the Maya calendar to a Julian day number. - /// - /// - note: No validation checks are performed on the cycle values. - /// - /// - parameter alautun: An alautun number. - /// - parameter kinchiltun: A kinchiltun number. - /// - parameter calabtun: A calabtun number. - /// - parameter pictun: A pictun number. - /// - parameter baktun: A baktun number. - /// - parameter katun: A katun number. - /// - parameter tun: A tun number. - /// - parameter uinal: A uinal number. - /// - parameter kin: A kin number. - /// - /// - returns: The Julian day number corresponding to the specified long count. - public static func julianDayNumberFromLongCount(alautun: Alautun = 0, kinchiltun: Kinchiltun = 0, calabtun: Calabtun = 0, pictun: Pictun = 0, baktun: Baktun, katun: Katun, tun: Tun, uinal: Uinal, kin: Kin) -> JulianDayNumber { - longCountEpochJulianDayNumber + (((((((alautun * kinchiltunPerAlautun + kinchiltun) * calabtunPerKinchiltun + calabtun) * pictunPerCalabtun + pictun) * baktunPerPictun + baktun) * katunPerBaktun + katun) * tunPerKatun + tun) * uinalPerTun + uinal) * kinPerUinal + kin - } -} - -extension MayaCalendar { - /// Converts a Julian day number to a Calendar Round in the Maya calendar. - /// - /// - note: A Calendar Round corresponding to a Julian day number - /// can also be represented by the same Julian day number with multiples - /// of 18,980 days added or subtracted. - /// - /// - parameter J: A Julian day number. - /// - /// - returns: The Calendar Round corresponding to the specified Julian day number. - public static func calendarRoundFromJulianDayNumber(_ J: JulianDayNumber) -> (number: TzolkinNumber, name: TzolkinDayName, day: HaabDay, month: HaabMonth) { - let T = J - tzolkinEpochJulianDayNumber - let H = J - haabEpochJulianDayNumber - - let (number, name) = (T % 13 + 1, T % 20) - let (month, day) = (H % 365).quotientAndRemainder(dividingBy: 20) - - return (number, name + 1, day, month + 1) - } - - /// Returns the most recent Julian day number for a Calendar Round in the Maya calendar occurring before a Julian day number. - /// - /// - important: Not all combinations of Tzolkʼin and Haabʼ dates are valid. - /// - /// - parameter number: A Tzolkʼin number. - /// - parameter name: A Tzolkʼin day name. - /// - parameter day: A Haabʼ day. - /// - parameter month: A Haabʼ month. - /// - parameter J0: A Julian day number to anchor the Calendar Round. - /// - /// - returns: The most recent Julian day number corresponding to the specified Calendar Round occurring before the specified Julian day number or `nil` if none. - public static func julianDayNumberFromCalendarRound(number: TzolkinNumber, name: TzolkinDayName, day: HaabDay, month: HaabMonth, before J0: JulianDayNumber) -> JulianDayNumber? { - guard let J = julianDayNumberFromCalendarRound(number: number, name: name, day: day, month: month) else { - return nil - } - return J0 + (J - J0) % 18980 - 18980 - } - - /// Returns the least recent Julian day number for a Calendar Round in the Maya calendar occurring on or after a Julian day number. - /// - /// - important: Not all combinations of Tzolkʼin and Haabʼ dates are valid. - /// - /// - parameter number: A Tzolkʼin number. - /// - parameter name: A Tzolkʼin day name. - /// - parameter day: A Haabʼ day. - /// - parameter month: A Haabʼ month. - /// - parameter J0: A Julian day number to anchor the Calendar Round. - /// - /// - returns: The least recent Julian day number corresponding to the specified Calendar Round occurring on or after the specified Julian day number or `nil` if none. - public static func julianDayNumberFromCalendarRound(number: TzolkinNumber, name: TzolkinDayName, day: HaabDay, month: HaabMonth, onOrAfter J0: JulianDayNumber = longCountEpochJulianDayNumber) -> JulianDayNumber? { - guard let J = julianDayNumberFromCalendarRound(number: number, name: name, day: day, month: month) else { - return nil - } - return J0 - (J0 - J) % 18980 //+ 18980 - } - - /// Returns a possible Julian day number for a Calendar Round in the Maya calendar. - /// - /// - parameter number: A Tzolkʼin number. - /// - parameter name: A Tzolkʼin day name. - /// - parameter day: A Haabʼ day. - /// - parameter month: A Haabʼ month. - /// - /// - returns: A possible Julian day number corresponding to the specified Calendar Round or `nil` if none. - static func julianDayNumberFromCalendarRound(number: TzolkinNumber, name: TzolkinDayName, day: HaabDay, month: HaabMonth) -> JulianDayNumber? { - // The number of days into the Tzolkʼin cycle - let T = (40 * number + 221 * name - 1) % 260 - - // The number of days into the Haabʼ cycle - let H = 20 * (month - 1) + day - - // Not all combinations of H and T are valid - guard (H - T) % 5 == 4 else { - return nil - } - - return (365 * T - 364 * H + 7600) % 18980 - } -} - -extension MayaCalendar { - /// Returns the Lord of the Night for a given uinal and kin. - /// - /// The Lord of the Night is a nine-day cycle conventionally labeled G1 through G9. - /// - /// The Lord of the Night at the long count epoch was G9. - /// - /// - parameter uinal: A uinal number. - /// - parameter kin: A kin number. - /// - /// - returns: The Lord of the Night corresponding to the specified uinal and kin. - public static func lordOfTheNightFrom(uinal: Uinal, kin: Kin) -> Int { - (20 * uinal + kin + 8) % 9 + 1 - } -} diff --git a/Sources/JulianDayNumber/MayaCalendar.swift b/Sources/JulianDayNumber/MayaCalendar.swift index aeb246e..cc2da47 100644 --- a/Sources/JulianDayNumber/MayaCalendar.swift +++ b/Sources/JulianDayNumber/MayaCalendar.swift @@ -136,3 +136,216 @@ public struct MayaCalendar { /// A Haabʼ month from `1` to `19`. public typealias HaabMonth = Int } + +extension MayaCalendar: JulianDayNumberConverting { + /// A long count in the Maya calendar. + public typealias DateType = (baktun: Baktun, katun: Katun, tun: Tun, uinal: Uinal, kin: Kin) + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + julianDayNumberFromLongCount(baktun: date.baktun, katun: date.katun, tun: date.tun, uinal: date.uinal, kin: date.kin) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + longCountFromJulianDayNumber(J) + } +} + +extension MayaCalendar { + // Maya long count cycle lengths + + /// One uinal is composed of 20 kin. + static let kinPerUinal = 20 + /// One tun is composed of 18 uinal. + static let uinalPerTun = 18 + /// One katun is composed of 20 tun. + static let tunPerKatun = 20 + /// One baktun is composed of 20 katun. + static let katunPerBaktun = 20 + /// One pictun is composed of 20 baktun. + static let baktunPerPictun = 20 + /// One calabtun is composed of 20 pictun. + static let pictunPerCalabtun = 20 + /// One kinchiltun is composed of 20 calabtun. + static let calabtunPerKinchiltun = 20 + /// One alautun is composed of 20 kinchiltun. + static let kinchiltunPerAlautun = 20 + + /// Converts a Julian day number to a long count in the Maya calendar. + /// + /// - parameter J: A Julian day number. + /// + /// - returns: The long count corresponding to the specified Julian day number. + public static func longCountFromJulianDayNumber(_ J: JulianDayNumber) -> (/*alautun: Alautun, kinchiltun: Kinchiltun, calabtun: Calabtun, pictun: Pictun, */baktun: Baktun, katun: Katun, tun: Tun, uinal: Uinal, kin: Kin) { + let L = J - longCountEpochJulianDayNumber + +#if false + var alautun, kinchiltun, calabtun, pictun: Int +#endif + var baktun, katun, tun, uinal, kin: Int + + (uinal, kin) = L.quotientAndRemainder(dividingBy: kinPerUinal) + (tun, uinal) = uinal.quotientAndRemainder(dividingBy: uinalPerTun) + (katun, tun) = tun.quotientAndRemainder(dividingBy: tunPerKatun) + (baktun, katun) = katun.quotientAndRemainder(dividingBy: katunPerBaktun) +#if false + (pictun, baktun) = baktun.quotientAndRemainder(dividingBy: baktunPerPictun) + (calabtun, pictun) = pictun.quotientAndRemainder(dividingBy: pictunPerCalabtun) + (kinchiltun, calabtun) = calabtun.quotientAndRemainder(dividingBy: calabtunPerKinchiltun) + (alautun, kinchiltun) = kinchiltun.quotientAndRemainder(dividingBy: kinchiltunPerAlautun) +#endif + + if L < 0 { + if kin < 0 { + kin += kinPerUinal + uinal -= 1 + } + if uinal < 0 { + uinal += uinalPerTun + tun -= 1 + } + if tun < 0 { + tun += tunPerKatun + katun -= 1 + } + if katun < 0 { + katun += katunPerBaktun + baktun -= 1 + } +#if false + if baktun < 1 - baktunPerPictun { + baktun += baktunPerPictun + pictun -= 1 + } + if pictun < 1 - pictunPerCalabtun { + pictun += pictunPerCalabtun + calabtun -= 1 + } + if calabtun < 1 - calabtunPerKinchiltun { + calabtun += calabtunPerKinchiltun + kinchiltun -= 1 + } + if kinchiltun < 1 - kinchiltunPerAlautun { + kinchiltun += kinchiltunPerAlautun + alautun -= 1 + } +#endif + } + + return (/*alautun, kinchiltun, calabtun, pictun, */baktun, katun, tun, uinal, kin) + } + + /// Converts a long count in the Maya calendar to a Julian day number. + /// + /// - note: No validation checks are performed on the cycle values. + /// + /// - parameter alautun: An alautun number. + /// - parameter kinchiltun: A kinchiltun number. + /// - parameter calabtun: A calabtun number. + /// - parameter pictun: A pictun number. + /// - parameter baktun: A baktun number. + /// - parameter katun: A katun number. + /// - parameter tun: A tun number. + /// - parameter uinal: A uinal number. + /// - parameter kin: A kin number. + /// + /// - returns: The Julian day number corresponding to the specified long count. + public static func julianDayNumberFromLongCount(alautun: Alautun = 0, kinchiltun: Kinchiltun = 0, calabtun: Calabtun = 0, pictun: Pictun = 0, baktun: Baktun, katun: Katun, tun: Tun, uinal: Uinal, kin: Kin) -> JulianDayNumber { + longCountEpochJulianDayNumber + (((((((alautun * kinchiltunPerAlautun + kinchiltun) * calabtunPerKinchiltun + calabtun) * pictunPerCalabtun + pictun) * baktunPerPictun + baktun) * katunPerBaktun + katun) * tunPerKatun + tun) * uinalPerTun + uinal) * kinPerUinal + kin + } +} + +extension MayaCalendar { + /// Converts a Julian day number to a Calendar Round in the Maya calendar. + /// + /// - note: A Calendar Round corresponding to a Julian day number + /// can also be represented by the same Julian day number with multiples + /// of 18,980 days added or subtracted. + /// + /// - parameter J: A Julian day number. + /// + /// - returns: The Calendar Round corresponding to the specified Julian day number. + public static func calendarRoundFromJulianDayNumber(_ J: JulianDayNumber) -> (number: TzolkinNumber, name: TzolkinDayName, day: HaabDay, month: HaabMonth) { + let T = J - tzolkinEpochJulianDayNumber + let H = J - haabEpochJulianDayNumber + + let (number, name) = (T % 13 + 1, T % 20) + let (month, day) = (H % 365).quotientAndRemainder(dividingBy: 20) + + return (number, name + 1, day, month + 1) + } + + /// Returns the most recent Julian day number for a Calendar Round in the Maya calendar occurring before a Julian day number. + /// + /// - important: Not all combinations of Tzolkʼin and Haabʼ dates are valid. + /// + /// - parameter number: A Tzolkʼin number. + /// - parameter name: A Tzolkʼin day name. + /// - parameter day: A Haabʼ day. + /// - parameter month: A Haabʼ month. + /// - parameter J0: A Julian day number to anchor the Calendar Round. + /// + /// - returns: The most recent Julian day number corresponding to the specified Calendar Round occurring before the specified Julian day number or `nil` if none. + public static func julianDayNumberFromCalendarRound(number: TzolkinNumber, name: TzolkinDayName, day: HaabDay, month: HaabMonth, before J0: JulianDayNumber) -> JulianDayNumber? { + guard let J = julianDayNumberFromCalendarRound(number: number, name: name, day: day, month: month) else { + return nil + } + return J0 + (J - J0) % 18980 - 18980 + } + + /// Returns the least recent Julian day number for a Calendar Round in the Maya calendar occurring on or after a Julian day number. + /// + /// - important: Not all combinations of Tzolkʼin and Haabʼ dates are valid. + /// + /// - parameter number: A Tzolkʼin number. + /// - parameter name: A Tzolkʼin day name. + /// - parameter day: A Haabʼ day. + /// - parameter month: A Haabʼ month. + /// - parameter J0: A Julian day number to anchor the Calendar Round. + /// + /// - returns: The least recent Julian day number corresponding to the specified Calendar Round occurring on or after the specified Julian day number or `nil` if none. + public static func julianDayNumberFromCalendarRound(number: TzolkinNumber, name: TzolkinDayName, day: HaabDay, month: HaabMonth, onOrAfter J0: JulianDayNumber = longCountEpochJulianDayNumber) -> JulianDayNumber? { + guard let J = julianDayNumberFromCalendarRound(number: number, name: name, day: day, month: month) else { + return nil + } + return J0 - (J0 - J) % 18980 //+ 18980 + } + + /// Returns a possible Julian day number for a Calendar Round in the Maya calendar. + /// + /// - parameter number: A Tzolkʼin number. + /// - parameter name: A Tzolkʼin day name. + /// - parameter day: A Haabʼ day. + /// - parameter month: A Haabʼ month. + /// + /// - returns: A possible Julian day number corresponding to the specified Calendar Round or `nil` if none. + static func julianDayNumberFromCalendarRound(number: TzolkinNumber, name: TzolkinDayName, day: HaabDay, month: HaabMonth) -> JulianDayNumber? { + // The number of days into the Tzolkʼin cycle + let T = (40 * number + 221 * name - 1) % 260 + + // The number of days into the Haabʼ cycle + let H = 20 * (month - 1) + day + + // Not all combinations of H and T are valid + guard (H - T) % 5 == 4 else { + return nil + } + + return (365 * T - 364 * H + 7600) % 18980 + } +} + +extension MayaCalendar { + /// Returns the Lord of the Night for a given uinal and kin. + /// + /// The Lord of the Night is a nine-day cycle conventionally labeled G1 through G9. + /// + /// The Lord of the Night at the long count epoch was G9. + /// + /// - parameter uinal: A uinal number. + /// - parameter kin: A kin number. + /// + /// - returns: The Lord of the Night corresponding to the specified uinal and kin. + public static func lordOfTheNightFrom(uinal: Uinal, kin: Kin) -> Int { + (20 * uinal + kin + 8) % 9 + 1 + } +} diff --git a/Sources/JulianDayNumber/SakaCalendar+JDN.swift b/Sources/JulianDayNumber/SakaCalendar+JDN.swift deleted file mode 100644 index e3c58ab..0000000 --- a/Sources/JulianDayNumber/SakaCalendar+JDN.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// Copyright © 2021-2023 Stephen F. Booth -// Part of https://github.com/sbooth/JulianDayNumber -// MIT license -// - -import Foundation - -// Algorithm from the Explanatory Supplement to the Astronomical Almanac, 3rd edition, S.E Urban and P.K. Seidelmann eds., (Mill Valley, CA: University Science Books), Chapter 15, pp. 585-624. - -/// The number of years in a cycle of the Śaka calendar. -/// -/// A cycle in the Śaka calendar consists of 303 common years and 97 leap years. -let sakaCalendarCycleYears = 400 - -/// The number of days in a cycle of the Śaka calendar. -/// -/// A cycle in the Gregorian calendar consists of 303 years of 365 days and 97 leap year of 366 days. -let sakaCalendarCycleDays = 146097 - -extension SakaCalendar: JulianDayNumberConverting { - /// A date in the Śaka calendar consists of a year, month, and day. - public typealias DateType = (year: Int, month: Int, day: Int) - - public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { - var Y = date.year - var ΔcalendarCycles = 0 - - // JDN 0 is -4791-09-03 in the proleptic Śaka calendar. - if date < (-4791, 9, 3) { - ΔcalendarCycles = (-4792 - Y) / sakaCalendarCycleYears + 1 - Y += ΔcalendarCycles * sakaCalendarCycleYears - } - - let h = date.month - m - let g = Y + y - (n - h) / n - let f = (h - 1 + n) % n - let e = (p * g + q) / r + date.day - 1 - j - let Z = f / 6 - var J = e + ((31 - Z) * f + 5 * Z) / u - J = J - (3 * ((g + A) / 100)) / 4 - C - - if ΔcalendarCycles > 0 { - J -= ΔcalendarCycles * sakaCalendarCycleDays - } - - return J - } - - public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { - var J = J - var ΔcalendarCycles = 0 - - // Richards' algorithm is only valid for positive JDNs. - if J < 0 { - ΔcalendarCycles = -J / sakaCalendarCycleDays + 1 - J += ΔcalendarCycles * sakaCalendarCycleDays - } - - var f = J + j - f = f + (((4 * J + B) / 146097) * 3) / 4 + C - let e = r * f + v - let g = (e % p) / r - let X = g / 365 - let Z = g / 185 - X - let s = 31 - Z - let w = -5 * Z - let h = u * g + w - let D = (6 * X + (h % s)) / u + 1 - - let M = ((h / s + m) % n) + 1 - var Y = e / p - y + (n + m - M) / n - - if ΔcalendarCycles > 0 { - Y -= ΔcalendarCycles * sakaCalendarCycleYears - } - - return (Y, M, D) - } -} - -// Constants for Śaka calendar conversions -private let y = 4794 -private let j = 1348 -private let m = 1 -private let n = 12 -private let r = 4 -private let p = 1461 -private let q = 0 -private let v = 3 -private let u = 1 -private let s = 31 -private let t = 0 -private let w = 0 -private let A = 184 -private let B = 274073 -private let C = -36 diff --git a/Sources/JulianDayNumber/SakaCalendar.swift b/Sources/JulianDayNumber/SakaCalendar.swift index 7470f3c..44a976f 100644 --- a/Sources/JulianDayNumber/SakaCalendar.swift +++ b/Sources/JulianDayNumber/SakaCalendar.swift @@ -128,3 +128,19 @@ public struct SakaCalendar { } } } + +extension SakaCalendar: JulianDayNumberConverting { + /// A date in the Śaka calendar consists of a year, month, and day. + public typealias DateType = (year: Int, month: Int, day: Int) + + /// The converter for the Śaka calendar. + static let converter = JDNSakaConverter() + + public static func julianDayNumberFromDate(_ date: DateType) -> JulianDayNumber { + converter.julianDayNumberFromDate(date) + } + + public static func dateFromJulianDayNumber(_ J: JulianDayNumber) -> DateType { + converter.dateFromJulianDayNumber(J) + } +} diff --git a/Tests/JulianDayNumberTests/ArmenianCalendarTests.swift b/Tests/JulianDayNumberTests/ArmenianCalendarTests.swift index 33c21db..4c142e3 100644 --- a/Tests/JulianDayNumberTests/ArmenianCalendarTests.swift +++ b/Tests/JulianDayNumberTests/ArmenianCalendarTests.swift @@ -48,31 +48,18 @@ final class ArmenianCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForArmenianCalendar = -9223372036854775514 - (Y, M, D) = ArmenianCalendar.dateFromJulianDayNumber(smallestJDNForArmenianCalendar) +// let smallestJDNForArmenianCalendar = Int.min + 294 + // Values smaller than this cause an arithmetic overflow in julianDayNumberFrom + let smallestJDNForArmenianCalendar = Int.min + 341 + var (Y, M, D) = ArmenianCalendar.dateFromJulianDayNumber(smallestJDNForArmenianCalendar) var jdn = ArmenianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForArmenianCalendar, jdn) // Values larger than this cause an arithmetic overflow in dateFromJulianDayNumber - let largestJDNForArmenianCalendar = 9223372036854775490 + let largestJDNForArmenianCalendar = Int.max - 317 (Y, M, D) = ArmenianCalendar.dateFromJulianDayNumber(largestJDNForArmenianCalendar) jdn = ArmenianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForArmenianCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForArmenianCalendar = -0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = ArmenianCalendar.dateAndTimeFromJulianDate(smallestJDForArmenianCalendar) - var jd = ArmenianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForArmenianCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForArmenianCalendar = 0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = ArmenianCalendar.dateAndTimeFromJulianDate(largestJDForArmenianCalendar) - jd = ArmenianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForArmenianCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/BahaiCalendarTests.swift b/Tests/JulianDayNumberTests/BahaiCalendarTests.swift index 60cebc4..a80c5d1 100644 --- a/Tests/JulianDayNumberTests/BahaiCalendarTests.swift +++ b/Tests/JulianDayNumberTests/BahaiCalendarTests.swift @@ -86,12 +86,9 @@ final class BahaiCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForBahaiCalendar = -9223372036854719351 - (Y, M, D) = BahaiCalendar.dateFromJulianDayNumber(smallestJDNForBahaiCalendar) + let smallestJDNForBahaiCalendar = Int.min + 56457 + var (Y, M, D) = BahaiCalendar.dateFromJulianDayNumber(smallestJDNForBahaiCalendar) var jdn = BahaiCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForBahaiCalendar, jdn) @@ -100,17 +97,5 @@ final class BahaiCalendarTests: XCTestCase { (Y, M, D) = BahaiCalendar.dateFromJulianDayNumber(largestJDNForBahaiCalendar) jdn = BahaiCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForBahaiCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForBahaiCalendar = -0x1.fffffffffffc8p+62 - (Y, M, D, h, m, s) = BahaiCalendar.dateAndTimeFromJulianDate(smallestJDForBahaiCalendar) - var jd = BahaiCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForBahaiCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForBahaiCalendar = 0x1.fffd4eff4e5d7p+60 - (Y, M, D, h, m, s) = BahaiCalendar.dateAndTimeFromJulianDate(largestJDForBahaiCalendar) - jd = BahaiCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForBahaiCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/CopticCalendarTests.swift b/Tests/JulianDayNumberTests/CopticCalendarTests.swift index b136c53..22cd21d 100644 --- a/Tests/JulianDayNumberTests/CopticCalendarTests.swift +++ b/Tests/JulianDayNumberTests/CopticCalendarTests.swift @@ -74,12 +74,11 @@ final class CopticCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForCopticCalendar = -9223372036854775664 - (Y, M, D) = CopticCalendar.dateFromJulianDayNumber(smallestJDNForCopticCalendar) +// let smallestJDNForCopticCalendar = Int.min + 144 + // Values smaller than this cause an arithmetic overflow in julianDayNumberFrom + let smallestJDNForCopticCalendar = Int.min + 384 + var (Y, M, D) = CopticCalendar.dateFromJulianDayNumber(smallestJDNForCopticCalendar) var jdn = CopticCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForCopticCalendar, jdn) @@ -88,17 +87,5 @@ final class CopticCalendarTests: XCTestCase { (Y, M, D) = CopticCalendar.dateFromJulianDayNumber(largestJDNForCopticCalendar) jdn = CopticCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForCopticCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForCopticCalendar = -0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = CopticCalendar.dateAndTimeFromJulianDate(smallestJDForCopticCalendar) - var jd = CopticCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForCopticCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForCopticCalendar = 0x1.fffffffffffffp+60 - (Y, M, D, h, m, s) = CopticCalendar.dateAndTimeFromJulianDate(largestJDForCopticCalendar) - jd = CopticCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForCopticCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/EgyptianCalendarTests.swift b/Tests/JulianDayNumberTests/EgyptianCalendarTests.swift index 5ad1179..2a9ebf2 100644 --- a/Tests/JulianDayNumberTests/EgyptianCalendarTests.swift +++ b/Tests/JulianDayNumberTests/EgyptianCalendarTests.swift @@ -54,31 +54,18 @@ final class EgyptianCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForEgyptianCalendar = -9223372036854775514 - (Y, M, D) = EgyptianCalendar.dateFromJulianDayNumber(smallestJDNForEgyptianCalendar) +// let smallestJDNForEgyptianCalendar = Int.min + 293 + // Values smaller than this cause an arithmetic overflow in julianDayNumberFrom + let smallestJDNForEgyptianCalendar = Int.min + 611 + var (Y, M, D) = EgyptianCalendar.dateFromJulianDayNumber(smallestJDNForEgyptianCalendar) var jdn = EgyptianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForEgyptianCalendar, jdn) // Values larger than this cause an arithmetic overflow in dateFromJulianDayNumber - let largestJDNForEgyptianCalendar = 9223372036854775760 + let largestJDNForEgyptianCalendar = Int.max - 47 (Y, M, D) = EgyptianCalendar.dateFromJulianDayNumber(largestJDNForEgyptianCalendar) jdn = EgyptianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForEgyptianCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForEgyptianCalendar = -0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = EgyptianCalendar.dateAndTimeFromJulianDate(smallestJDForEgyptianCalendar) - var jd = EgyptianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForEgyptianCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForEgyptianCalendar = 0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = EgyptianCalendar.dateAndTimeFromJulianDate(largestJDForEgyptianCalendar) - jd = EgyptianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForEgyptianCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/EthiopianCalendarTests.swift b/Tests/JulianDayNumberTests/EthiopianCalendarTests.swift index 4ec9d85..5bf154a 100644 --- a/Tests/JulianDayNumberTests/EthiopianCalendarTests.swift +++ b/Tests/JulianDayNumberTests/EthiopianCalendarTests.swift @@ -77,12 +77,11 @@ final class EthiopianCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForEthiopianCalendar = -9223372036854775664 - (Y, M, D) = EthiopianCalendar.dateFromJulianDayNumber(smallestJDNForEthiopianCalendar) +// let smallestJDNForEthiopianCalendar = Int.min + 144 + // Values smaller than this cause an arithmetic overflow in julianDayNumberFrom + let smallestJDNForEthiopianCalendar = Int.min + 384 + var (Y, M, D) = EthiopianCalendar.dateFromJulianDayNumber(smallestJDNForEthiopianCalendar) var jdn = EthiopianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForEthiopianCalendar, jdn) @@ -91,17 +90,5 @@ final class EthiopianCalendarTests: XCTestCase { (Y, M, D) = EthiopianCalendar.dateFromJulianDayNumber(largestJDNForEthiopianCalendar) jdn = EthiopianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForEthiopianCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForEthiopianCalendar = -0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = EthiopianCalendar.dateAndTimeFromJulianDate(smallestJDForEthiopianCalendar) - var jd = EthiopianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForEthiopianCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForEthiopianCalendar = 0x1.fffffffffffffp+60 - (Y, M, D, h, m, s) = EthiopianCalendar.dateAndTimeFromJulianDate(largestJDForEthiopianCalendar) - jd = EthiopianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForEthiopianCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/FrenchRepublicanCalendarTests.swift b/Tests/JulianDayNumberTests/FrenchRepublicanCalendarTests.swift index 6765f1a..2dc2d54 100644 --- a/Tests/JulianDayNumberTests/FrenchRepublicanCalendarTests.swift +++ b/Tests/JulianDayNumberTests/FrenchRepublicanCalendarTests.swift @@ -69,12 +69,11 @@ final class FrenchRepublicanCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForFrenchRepublicanCalendar = -9223372036854719351 - (Y, M, D) = FrenchRepublicanCalendar.dateFromJulianDayNumber(smallestJDNForFrenchRepublicanCalendar) +// let smallestJDNForFrenchRepublicanCalendar = Int.min + 56456 + // Values smaller than this cause an arithmetic overflow in julianDayNumberFrom + let smallestJDNForFrenchRepublicanCalendar = Int.min + 56759 + var (Y, M, D) = FrenchRepublicanCalendar.dateFromJulianDayNumber(smallestJDNForFrenchRepublicanCalendar) var jdn = FrenchRepublicanCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForFrenchRepublicanCalendar, jdn) @@ -83,17 +82,5 @@ final class FrenchRepublicanCalendarTests: XCTestCase { (Y, M, D) = FrenchRepublicanCalendar.dateFromJulianDayNumber(largestJDNForFrenchRepublicanCalendar) jdn = FrenchRepublicanCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForFrenchRepublicanCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForFrenchRepublicanCalendar = -0x1.fffffffffffc8p+62 - (Y, M, D, h, m, s) = FrenchRepublicanCalendar.dateAndTimeFromJulianDate(smallestJDForFrenchRepublicanCalendar) - var jd = FrenchRepublicanCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForFrenchRepublicanCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForFrenchRepublicanCalendar = 0x1.fffd4eff4e5dcp+60 - (Y, M, D, h, m, s) = FrenchRepublicanCalendar.dateAndTimeFromJulianDate(largestJDForFrenchRepublicanCalendar) - jd = FrenchRepublicanCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForFrenchRepublicanCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/GregorianCalendarTests.swift b/Tests/JulianDayNumberTests/GregorianCalendarTests.swift index 58a63a1..e7c8fbb 100644 --- a/Tests/JulianDayNumberTests/GregorianCalendarTests.swift +++ b/Tests/JulianDayNumberTests/GregorianCalendarTests.swift @@ -105,12 +105,9 @@ final class GregorianCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForGregorianCalendar = -9223372036854719351 - (Y, M, D) = GregorianCalendar.dateFromJulianDayNumber(smallestJDNForGregorianCalendar) + let smallestJDNForGregorianCalendar = Int.min + 56457 + var (Y, M, D) = GregorianCalendar.dateFromJulianDayNumber(smallestJDNForGregorianCalendar) var jdn = GregorianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForGregorianCalendar, jdn) @@ -119,17 +116,5 @@ final class GregorianCalendarTests: XCTestCase { (Y, M, D) = GregorianCalendar.dateFromJulianDayNumber(largestJDNForGregorianCalendar) jdn = GregorianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForGregorianCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForGregorianCalendar = -0x1.fffffffffffc8p+62 - (Y, M, D, h, m, s) = GregorianCalendar.dateAndTimeFromJulianDate(smallestJDForGregorianCalendar) - var jd = GregorianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForGregorianCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForGregorianCalendar = 0x1.fffd4eff4e5d7p+60 - (Y, M, D, h, m, s) = GregorianCalendar.dateAndTimeFromJulianDate(largestJDForGregorianCalendar) - jd = GregorianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForGregorianCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/HebrewCalendarTests.swift b/Tests/JulianDayNumberTests/HebrewCalendarTests.swift index 7678a05..1ff5162 100644 --- a/Tests/JulianDayNumberTests/HebrewCalendarTests.swift +++ b/Tests/JulianDayNumberTests/HebrewCalendarTests.swift @@ -218,15 +218,11 @@ final class HebrewCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber -// let smallestJDNForHebrewCalendar = -9223372036747815981 - // JDN -9223372036747815627 equals date (-25252436095246078-01-01) which is the smallest round-trippable value +// let smallestJDNForHebrewCalendar = Int.min + 106959827 // Values smaller than this cause an arithmetic overflow in julianDayNumberFrom - let smallestJDNForHebrewCalendar = -9223372036747815627 - (Y, M, D) = HebrewCalendar.dateFromJulianDayNumber(smallestJDNForHebrewCalendar) + let smallestJDNForHebrewCalendar = Int.min + 106960181 + var (Y, M, D) = HebrewCalendar.dateFromJulianDayNumber(smallestJDNForHebrewCalendar) var jdn = HebrewCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForHebrewCalendar, jdn) @@ -236,20 +232,5 @@ final class HebrewCalendarTests: XCTestCase { (Y, M, D) = HebrewCalendar.dateFromJulianDayNumber(largestJDNForHebrewCalendar) jdn = HebrewCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForHebrewCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate -// let smallestJDForHebrewCalendar = -0x1.ffffffffe67fbp+62 - // JD -0x1.ffffffffe67fap+62 equals date (-25252436095246077-13-07 00:00:00) which is the smallest round-trippable value - // Values smaller than this cause an arithmetic overflow in julianDateFrom - let smallestJDForHebrewCalendar = -0x1.ffffffffe67fap+62 - (Y, M, D, h, m, s) = HebrewCalendar.dateAndTimeFromJulianDate(smallestJDForHebrewCalendar) - var jd = HebrewCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForHebrewCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForHebrewCalendar = 0x1.43a273100de27p+48 - (Y, M, D, h, m, s) = HebrewCalendar.dateAndTimeFromJulianDate(largestJDForHebrewCalendar) - jd = HebrewCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForHebrewCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/IslamicCalendarTests.swift b/Tests/JulianDayNumberTests/IslamicCalendarTests.swift index 51b20fe..a284e23 100644 --- a/Tests/JulianDayNumberTests/IslamicCalendarTests.swift +++ b/Tests/JulianDayNumberTests/IslamicCalendarTests.swift @@ -101,12 +101,9 @@ final class IslamicCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForIslamicCalendar = -9223372036854775352 - (Y, M, D) = IslamicCalendar.dateFromJulianDayNumber(smallestJDNForIslamicCalendar) + let smallestJDNForIslamicCalendar = Int.min + 325 + var (Y, M, D) = IslamicCalendar.dateFromJulianDayNumber(smallestJDNForIslamicCalendar) var jdn = IslamicCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForIslamicCalendar, jdn) @@ -115,17 +112,5 @@ final class IslamicCalendarTests: XCTestCase { (Y, M, D) = IslamicCalendar.dateFromJulianDayNumber(largestJDNForIslamicCalendar) jdn = IslamicCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForIslamicCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForIslamicCalendar = -0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = IslamicCalendar.dateAndTimeFromJulianDate(smallestJDForIslamicCalendar) - var jd = IslamicCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForIslamicCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForIslamicCalendar = 0x1.1111111111099p+58 - (Y, M, D, h, m, s) = IslamicCalendar.dateAndTimeFromJulianDate(largestJDForIslamicCalendar) - jd = IslamicCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForIslamicCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/JulianCalendarTests.swift b/Tests/JulianDayNumberTests/JulianCalendarTests.swift index 45059c5..2523ab2 100644 --- a/Tests/JulianDayNumberTests/JulianCalendarTests.swift +++ b/Tests/JulianDayNumberTests/JulianCalendarTests.swift @@ -98,12 +98,9 @@ final class JulianCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForJulianCalendar = -9223372036854775664 - (Y, M, D) = JulianCalendar.dateFromJulianDayNumber(smallestJDNForJulianCalendar) + let smallestJDNForJulianCalendar = Int.min + 144 + var (Y, M, D) = JulianCalendar.dateFromJulianDayNumber(smallestJDNForJulianCalendar) var jdn = JulianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForJulianCalendar, jdn) @@ -112,17 +109,5 @@ final class JulianCalendarTests: XCTestCase { (Y, M, D) = JulianCalendar.dateFromJulianDayNumber(largestJDNForJulianCalendar) jdn = JulianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForJulianCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForJulianCalendar = -0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = JulianCalendar.dateAndTimeFromJulianDate(smallestJDForJulianCalendar) - var jd = JulianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForJulianCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForJulianCalendar = 0x1.ffffffffffffap+60 - (Y, M, D, h, m, s) = JulianCalendar.dateAndTimeFromJulianDate(largestJDForJulianCalendar) - jd = JulianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForJulianCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/KhwarizmianCalendarTests.swift b/Tests/JulianDayNumberTests/KhwarizmianCalendarTests.swift index a6f9da1..19ad7dc 100644 --- a/Tests/JulianDayNumberTests/KhwarizmianCalendarTests.swift +++ b/Tests/JulianDayNumberTests/KhwarizmianCalendarTests.swift @@ -46,31 +46,18 @@ final class KhwarizmianCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForKhwarizmianCalendar = -9223372036854775514 - (Y, M, D) = KhwarizmianCalendar.dateFromJulianDayNumber(smallestJDNForKhwarizmianCalendar) +// let smallestJDNForKhwarizmianCalendar = Int.min + 294 + // Values smaller than this cause an arithmetic overflow in julianDayNumberFrom + let smallestJDNForKhwarizmianCalendar = Int.min + 341 + var (Y, M, D) = KhwarizmianCalendar.dateFromJulianDayNumber(smallestJDNForKhwarizmianCalendar) var jdn = KhwarizmianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForKhwarizmianCalendar, jdn) // Values larger than this cause an arithmetic overflow in dateFromJulianDayNumber - let largestJDNForKhwarizmianCalendar = 9223372036854775490 + let largestJDNForKhwarizmianCalendar = Int.max - 317 (Y, M, D) = KhwarizmianCalendar.dateFromJulianDayNumber(largestJDNForKhwarizmianCalendar) jdn = KhwarizmianCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForKhwarizmianCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForKhwarizmianCalendar = -0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = KhwarizmianCalendar.dateAndTimeFromJulianDate(smallestJDForKhwarizmianCalendar) - var jd = KhwarizmianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForKhwarizmianCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForKhwarizmianCalendar = 0x1.fffffffffffffp+62 - (Y, M, D, h, m, s) = KhwarizmianCalendar.dateAndTimeFromJulianDate(largestJDForKhwarizmianCalendar) - jd = KhwarizmianCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForKhwarizmianCalendar, jd) } } diff --git a/Tests/JulianDayNumberTests/MayaCalendarTests.swift b/Tests/JulianDayNumberTests/MayaCalendarTests.swift index 2b2737d..673860f 100644 --- a/Tests/JulianDayNumberTests/MayaCalendarTests.swift +++ b/Tests/JulianDayNumberTests/MayaCalendarTests.swift @@ -68,9 +68,9 @@ final class MayaCalendarTests: XCTestCase { var b, k, t, u, d: Int // Values smaller than this cause an arithmetic overflow in longCountFromJulianDayNumber -// let smallestJDNForMayaCalendar = -9223372036854191525 +// let smallestJDNForMayaCalendar = Int.min + 584283 // Values smaller than this cause an arithmetic overflow in julianDayNumberFromLongCount - let smallestJDNForMayaCalendar = -9223372036854191517 + let smallestJDNForMayaCalendar = Int.min + 584291 (b, k, t, u, d) = MayaCalendar.longCountFromJulianDayNumber(smallestJDNForMayaCalendar) var jdn = MayaCalendar.julianDayNumberFromLongCount(baktun: b, katun: k, tun: t, uinal: u, kin: d) XCTAssertEqual(smallestJDNForMayaCalendar, jdn) diff --git a/Tests/JulianDayNumberTests/SakaCalendarTests.swift b/Tests/JulianDayNumberTests/SakaCalendarTests.swift index 6b355b8..ce9e7b5 100644 --- a/Tests/JulianDayNumberTests/SakaCalendarTests.swift +++ b/Tests/JulianDayNumberTests/SakaCalendarTests.swift @@ -72,12 +72,9 @@ final class SakaCalendarTests: XCTestCase { } func testArithmeticLimits() { - var Y, M, D, h, m: Int - var s: Double - // Values smaller than this cause an arithmetic overflow in dateFromJulianDayNumber - let smallestJDNForSakaCalendar = -9223372036854719351 - (Y, M, D) = SakaCalendar.dateFromJulianDayNumber(smallestJDNForSakaCalendar) + let smallestJDNForSakaCalendar = Int.min + 56457 + var (Y, M, D) = SakaCalendar.dateFromJulianDayNumber(smallestJDNForSakaCalendar) var jdn = SakaCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(smallestJDNForSakaCalendar, jdn) @@ -86,17 +83,5 @@ final class SakaCalendarTests: XCTestCase { (Y, M, D) = SakaCalendar.dateFromJulianDayNumber(largestJDNForSakaCalendar) jdn = SakaCalendar.julianDayNumberFrom(year: Y, month: M, day: D) XCTAssertEqual(largestJDNForSakaCalendar, jdn) - - // Values smaller than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let smallestJDForSakaCalendar = -0x1.fffffffffffc8p+62 - (Y, M, D, h, m, s) = SakaCalendar.dateAndTimeFromJulianDate(smallestJDForSakaCalendar) - var jd = SakaCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(smallestJDForSakaCalendar, jd) - - // Values larger than this cause an arithmetic overflow in dateAndTimeFromJulianDate - let largestJDForSakaCalendar = 0x1.fffd4eff4e5d8p+60 - (Y, M, D, h, m, s) = SakaCalendar.dateAndTimeFromJulianDate(largestJDForSakaCalendar) - jd = SakaCalendar.julianDateFrom(year: Y, month: M, day: D, hour: h, minute: m, second: s) - XCTAssertEqual(largestJDForSakaCalendar, jd) } }