const JDN_RD_OFFSET: i64 = 1_721_425;
#[inline]
pub(crate) fn gregorian_to_rata_die(year: i32, month: u8, day: u8) -> i64 {
gregorian_to_jdn(year, month, day) - JDN_RD_OFFSET
}
#[inline]
pub(crate) fn rata_die_to_gregorian(rd: i64) -> (i32, u8, u8) {
jdn_to_gregorian(rd + JDN_RD_OFFSET)
}
fn gregorian_to_jdn(year: i32, month: u8, day: u8) -> i64 {
let greg_year = i64::from(year);
let greg_month = i64::from(month);
let greg_day = i64::from(day);
let march_offset = (14 - greg_month) / 12;
let shifted_year = greg_year + 4800 - march_offset;
let shifted_month = greg_month + 12 * march_offset - 3;
greg_day + (153 * shifted_month + 2) / 5 + 365 * shifted_year + shifted_year / 4
- shifted_year / 100
+ shifted_year / 400
- 32045
}
fn jdn_to_gregorian(mut jdn: i64) -> (i32, u8, u8) {
jdn += 32044;
let cycles_400y = jdn / 146097;
let day_in_400y_cycle = jdn % 146097;
let century_leap_correction = ((day_in_400y_cycle / 36524 + 1) * 3) / 4;
let day_after_century = day_in_400y_cycle - century_leap_correction * 36524;
let quadrennia_4y = day_after_century / 1461;
let day_in_quadrennium = day_after_century % 1461;
let year_leap_correction = ((day_in_quadrennium / 365 + 1) * 3) / 4;
let day_within_year = day_in_quadrennium - year_leap_correction * 365;
let year_packed = cycles_400y * 400
+ century_leap_correction * 100
+ quadrennia_4y * 4
+ year_leap_correction;
let month_packed = (5 * day_within_year + 308) / 153 - 2;
let day_of_month = day_within_year - (153 * month_packed + 2) / 5 + 1;
let year = (year_packed - 4800 + (month_packed + 2) / 12) as i32;
let month = ((month_packed + 2).rem_euclid(12) + 1) as u8;
let day = day_of_month as u8;
(year, month, day)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn anchor_rd_matches_gregorian_1976_03_21() {
let rd = gregorian_to_rata_die(1976, 3, 21);
assert_eq!(rd, 721_434);
assert_eq!(rata_die_to_gregorian(rd), (1976, 3, 21));
}
#[test]
fn rd_one_is_jan_1_year_1() {
assert_eq!(gregorian_to_rata_die(1, 1, 1), 1);
assert_eq!(rata_die_to_gregorian(1), (1, 1, 1));
}
}