-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Ethiopian and Coptic calendars (#8)
* Add Ethiopian and Coptic calendars * Fix month names * Fix Coptic and Ethiopian leap year computation * Add Ethiopian and Coptic limits * Alphabetize * Move note * Move note again
- Loading branch information
Showing
8 changed files
with
471 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// | ||
// Copyright © 2021-2023 Stephen F. Booth <[email protected]> | ||
// 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 { | ||
/// Converts a year, month, and day in the Coptic calendar to a Julian day number. | ||
/// | ||
/// - note: No validation checks are performed on the date values. | ||
/// | ||
/// - parameter Y: A year number. | ||
/// - parameter M: A month number between `1` (Tut) and `13` (Nissieh). | ||
/// - parameter D: A day number between `1` and the maximum number of days in month `M` for year `Y`. | ||
/// | ||
/// - returns: The Julian day number corresponding to the specified year, month, and day. | ||
public static func dateToJulianDayNumber(year Y: Int, month M: Int, day D: Int) -> JulianDayNumber { | ||
var Y = Y | ||
var ΔcalendarCycles = 0 | ||
|
||
// Richards' algorithm is only valid for positive JDNs. | ||
// JDN 0 is -4996-05-05 in the Coptic calendar. | ||
// Adjust the year of earlier dates forward in time by a multiple of | ||
// the calendar's cycle before calculating the JDN, and then translate | ||
// the result backward in time by the period of adjustment. | ||
if Y < -4996 || (Y == -4996 && (M < 5 || (M == 5 && D < 5))) { | ||
ΔcalendarCycles = (-4997 - Y) / copticCalendarCycleYears + 1 | ||
Y += ΔcalendarCycles * copticCalendarCycleYears | ||
} | ||
|
||
let h = M - m | ||
let g = Y + y - (n - h) / n | ||
let f = (h - 1 + n) % n | ||
let e = (p * g + q) / r + D - 1 - j | ||
var J = e + (s * f + t) / u | ||
|
||
if ΔcalendarCycles > 0 { | ||
J -= ΔcalendarCycles * copticCalendarCycleDays | ||
} | ||
|
||
return J | ||
} | ||
|
||
/// Converts a Julian day number to a year, month, and day in the Coptic calendar. | ||
/// | ||
/// - parameter J: A Julian day number. | ||
/// | ||
/// - returns: The year, month, and day corresponding to the specified Julian day number. | ||
public static func julianDayNumberToDate(_ J: JulianDayNumber) -> (year: Int, month: Int, day: Int) { | ||
var J = J | ||
var ΔcalendarCycles = 0 | ||
|
||
// Richards' algorithm is only valid for positive JDNs. | ||
// Adjust negative JDNs forward in time by a multiple of | ||
// the calendar's cycle before calculating the JDN, and then translate | ||
// the result backward in time by the period of adjustment. | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// | ||
// Copyright © 2021-2023 Stephen F. Booth <[email protected]> | ||
// Part of https://github.com/sbooth/JulianDayNumber | ||
// MIT license | ||
// | ||
|
||
import Foundation | ||
|
||
/// The Coptic calendar. | ||
/// | ||
/// The Coptic calendar is a solar calendar of 365 days in every year. | ||
/// | ||
/// The Coptic calendar epoch in the Coptic calendar is 1 Tut 1. | ||
/// The Coptic calendar epoch in the Julian calendar is August 29, 284 AD. | ||
/// | ||
/// - seealso: [Coptic calendar](https://en.wikipedia.org/wiki/Coptic_calendar) | ||
public struct CopticCalendar { | ||
/// The year, month, and day of the epoch of the Coptic calendar. | ||
/// | ||
/// The Coptic calendar epoch in the Coptic calendar is 1 Tut 1. | ||
/// The Coptic calendar epoch in the Julian calendar is August 29, 284 AD. | ||
public static let epochDate = (year: 1, month: 1, day: 1) | ||
|
||
/// The Julian day number of the epoch of the Coptic calendar. | ||
/// | ||
/// This JDN corresponds to noon on 1 Tut 1 in the Coptic calendar. | ||
public static let epochJulianDayNumber: JulianDayNumber = 1825030 | ||
|
||
/// The Julian date of the epoch of the Coptic calendar. | ||
/// | ||
/// This JD corresponds to midnight on 1 Tut 1 in the Coptic calendar. | ||
public static let epochJulianDate: JulianDate = 1825029.5 | ||
|
||
/// Returns `true` if the specified year, month, and day form a valid date in the Coptic calendar. | ||
/// | ||
/// - parameter Y: A year number. | ||
/// - parameter M: A month number between `1` (Tut) and `13` (Nissieh). | ||
/// - parameter D: A day number between `1` and the maximum number of days in month `M` for year `Y`. | ||
/// | ||
/// - returns: `true` if the specified year, month, and day form a valid date in the Coptic calendar. | ||
public static func isDateValid(year Y: Int, month M: Int, day D: Int) -> Bool { | ||
M > 0 && M <= 13 && D > 0 && D <= daysInMonth(year: Y, month: M) | ||
} | ||
|
||
/// Returns `true` if the specified year is a leap year in the Coptic calendar. | ||
/// | ||
/// A Coptic year is a leap year if its numerical designation plus one is divisible by 4. | ||
/// | ||
/// - parameter Y: A year number. | ||
/// | ||
/// - returns: `true` if the specified year is a leap year in the Coptic calendar. | ||
public static func isLeapYear(_ Y: Int) -> Bool { | ||
(Y + 1) % 4 == 0 | ||
} | ||
|
||
/// The number of days in each month indexed from `0` (Tut) to `12` (Nissieh). | ||
static let monthLengths = [ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 5 ] | ||
|
||
/// Returns the number of days in the specified month and year in the Coptic calendar. | ||
/// | ||
/// - parameter Y: A year number. | ||
/// - parameter M: A month number between `1` (Tut) and `13` (Nissieh). | ||
/// | ||
/// - returns: The number of days in the specified month and year. | ||
public static func daysInMonth(year Y: Int, month M: Int) -> Int { | ||
guard M > 0, M <= 13 else { | ||
return 0 | ||
} | ||
|
||
if M == 13 { | ||
return isLeapYear(Y) ? 6 : 5 | ||
} else { | ||
return monthLengths[M - 1] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// | ||
// Copyright © 2021-2023 Stephen F. Booth <[email protected]> | ||
// 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 { | ||
/// Converts a year, month, and day in the Ethiopian calendar to a Julian day number. | ||
/// | ||
/// - note: No validation checks are performed on the date values. | ||
/// | ||
/// - parameter Y: A year number. | ||
/// - parameter M: A month number between `1` (Mäskäräm) and `13` (Ṗagumen). | ||
/// - parameter D: A day number between `1` and the maximum number of days in month `M` for year `Y`. | ||
/// | ||
/// - returns: The Julian day number corresponding to the specified year, month, and day. | ||
public static func dateToJulianDayNumber(year Y: Int, month M: Int, day D: Int) -> JulianDayNumber { | ||
var Y = Y | ||
var ΔcalendarCycles = 0 | ||
|
||
// Richards' algorithm is only valid for positive JDNs. | ||
// JDN 0 is -4720-05-05 in the Ethiopian calendar. | ||
// Adjust the year of earlier dates forward in time by a multiple of | ||
// the calendar's cycle before calculating the JDN, and then translate | ||
// the result backward in time by the period of adjustment. | ||
if Y < -4720 || (Y == -4720 && (M < 5 || (M == 5 && D < 5))) { | ||
ΔcalendarCycles = (-4721 - Y) / ethiopianCalendarCycleYears + 1 | ||
Y += ΔcalendarCycles * ethiopianCalendarCycleYears | ||
} | ||
|
||
let h = M - m | ||
let g = Y + y - (n - h) / n | ||
let f = (h - 1 + n) % n | ||
let e = (p * g + q) / r + D - 1 - j | ||
var J = e + (s * f + t) / u | ||
|
||
if ΔcalendarCycles > 0 { | ||
J -= ΔcalendarCycles * ethiopianCalendarCycleDays | ||
} | ||
|
||
return J | ||
} | ||
|
||
/// Converts a Julian day number to a year, month, and day in the Ethiopian calendar. | ||
/// | ||
/// - parameter J: A Julian day number. | ||
/// | ||
/// - returns: The year, month, and day corresponding to the specified Julian day number. | ||
public static func julianDayNumberToDate(_ J: JulianDayNumber) -> (year: Int, month: Int, day: Int) { | ||
var J = J | ||
var ΔcalendarCycles = 0 | ||
|
||
// Richards' algorithm is only valid for positive JDNs. | ||
// Adjust negative JDNs forward in time by a multiple of | ||
// the calendar's cycle before calculating the JDN, and then translate | ||
// the result backward in time by the period of adjustment. | ||
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 |
Oops, something went wrong.