From a043bf82eca5f96ac3528ec7a78ffce1ffd1da59 Mon Sep 17 00:00:00 2001 From: Stephen Booth Date: Mon, 30 Oct 2023 22:04:36 -0500 Subject: [PATCH] Easter calculation (#2) * Add Easter month and day calculation * Add comment on algorithm source * Documentation cleanup --- Sources/JulianDayNumber/Easter.swift | 51 ++++++++++++++++++++ Tests/JulianDayNumberTests/EasterTests.swift | 23 +++++++++ 2 files changed, 74 insertions(+) create mode 100644 Sources/JulianDayNumber/Easter.swift create mode 100644 Tests/JulianDayNumberTests/EasterTests.swift diff --git a/Sources/JulianDayNumber/Easter.swift b/Sources/JulianDayNumber/Easter.swift new file mode 100644 index 0000000..90af35f --- /dev/null +++ b/Sources/JulianDayNumber/Easter.swift @@ -0,0 +1,51 @@ +// +// Copyright © 2021-2023 Stephen F. Booth +// Part of https://github.com/sbooth/JulianDayNumber +// MIT license +// + +import Foundation + +// 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. + +/// Returns the month `M` and day `D` of Easter in year `Y`. +/// +/// Years before 1582 are interpreted in the Julian calendar and years after +/// 1582 are interpreted in the Gregorian calendar. +/// +/// - parameter year: A year number. +/// +/// - returns: The month and day of Easter in the requested year. +public func easter(year Y: Int) -> (month: Int, day: Int) { + return Y < 1582 ? easterInJulianCalendar(year: Y) : easterInGregorianCalendar(year: Y) +} + +/// Returns the month `M` and day `D` of Easter in year `Y` in the Julian calendar. +/// +/// - parameter year: A year number. +/// +/// - returns: The month and day of Easter in the requested year. +public func easterInJulianCalendar(year Y: Int) -> (month: Int, day: Int) { + let a = 22 + ((225 - 11 * (Y % 19)) % 30) + let g = a + ((56 + 6 * Y - Y / 4 - a) % 7) + let M = 3 + g / 32 + let D = 1 + ((g - 1) % 31) + return (M, D) +} + +/// Returns the month `M` and day `D` of Easter in year `Y` in the Gregorian calendar. +/// +/// - parameter year: A year number. +/// +/// - returns: The month and day of Easter in the requested year. +public func easterInGregorianCalendar(year Y: Int) -> (month: Int, day: Int) { + let a = Y / 100 + let b = a - a / 4 + let c = (Y % 19) + let e = ((15 + 19 * c + b - (a - (a - 17) / 25) / 3) % 30) + let f = e - (c + 11 * e) / 319 + let g = 22 + f + ((140004 - Y - Y / 4 + b - f) % 7) + let M = 3 + g / 32 + let D = 1 + ((g - 1) % 31) + return (M, D) +} diff --git a/Tests/JulianDayNumberTests/EasterTests.swift b/Tests/JulianDayNumberTests/EasterTests.swift new file mode 100644 index 0000000..27e20be --- /dev/null +++ b/Tests/JulianDayNumberTests/EasterTests.swift @@ -0,0 +1,23 @@ +// +// Copyright © 2021-2023 Stephen F. Booth +// Part of https://github.com/sbooth/JulianDayNumber +// MIT license +// + +import XCTest +@testable import JulianDayNumber + +final class EasterTests: XCTestCase { + func testEaster() { + // Dates from Meeus (1998) + XCTAssertTrue(easter(year: 1991) == (3, 31)) + XCTAssertTrue(easter(year: 1992) == (4, 19)) + XCTAssertTrue(easter(year: 1993) == (4, 11)) + XCTAssertTrue(easter(year: 1954) == (4, 18)) + XCTAssertTrue(easter(year: 2000) == (4, 23)) + XCTAssertTrue(easter(year: 1818) == (3, 22)) + XCTAssertTrue(easter(year: 179) == (4, 12)) + XCTAssertTrue(easter(year: 711) == (4, 12)) + XCTAssertTrue(easter(year: 1243) == (4, 12)) + } +}