use crate::calendar::Iso;
use crate::{AsDate, AsTime, Moment, PlainDateTime};
const J2000: f64 = 2451545.0;
const CENTURY_DAYS: f64 = 36525.0;
pub fn j2000_century(date: f64) -> f64 {
(date - J2000) / CENTURY_DAYS
}
pub fn to_julian_year(moment: Moment) -> f64 {
let dt = moment.as_date(Iso);
let (year, day) = Iso.date_to_ordinal(dt.as_days_since_ako_epoch());
let mut y = year.number() as f64;
y += day as f64 / Iso.year_days(year.number()) as f64;
y
}
pub fn to_julian_day(moment: Moment) -> f64 {
let dt = PlainDateTime::from_moment(Iso, moment);
let (year, month, day) = Iso.date_components(dt.as_date().as_days_since_ako_epoch());
let mut year = year.number();
let mut month = month.number();
if month < 3 {
year -= 1;
month += 12;
}
let a = year / 100;
let b = (2 - a + a / 4) as f64;
let y1 = ((365.25 * (year as f64 + 4716.0)) as i64) as f64;
let m1 = ((30.6001 * (month as f64 + 1.0)) as i64) as f64;
let f = (dt.as_time().as_nanoseconds_since_midnight() as f64) / 86_400.0 / 1_000_000_000.0;
y1 + m1 + day as f64 + b - 1524.5 + f
}
#[allow(clippy::inconsistent_digit_grouping)]
pub fn from_julian_day(mut date: f64) -> crate::Result<Moment> {
date += 0.5;
let z = date.trunc() as i64;
let f = date.fract();
let alpha = (z * 1__00 - 1_867_216__25) / 36_524__25;
let a = z + 1 + alpha - (alpha / 4);
let b = a + 1_524;
let c = (b * 1__00 - 122__10) / 365__25;
let d = (365__25 * c) / 1__00;
let e = ((b - d) * 1__0000) / 30__6001;
let day = (b - d) - ((30__6001 * e) / 1__0000);
let month = if e >= 14 { e - 13 } else { e - 1 };
let year = c - if month > 2 { 4716 } else { 4715 };
match Iso.date(year as i32, month as u8, day as u8) {
Ok(date) => {
let m = Moment::from_date(date);
let f = (f * 86_400.0 * 1_000_000_000.0) as i128;
Ok(Moment::from_unix_nanoseconds(m.to_unix_nanoseconds() + f))
}
Err(error) => Err(error),
}
}
#[cfg(test)]
mod tests {
use float_cmp::assert_approx_eq;
use test_case::test_case;
use super::{Moment, from_julian_day, j2000_century, to_julian_day, to_julian_year};
#[test_case(2460665.885255802, 0.24971622876938965)]
fn expect_j2000_century(date: f64, expected: f64) {
assert_approx_eq!(f64, j2000_century(date), expected);
}
#[test_case(1711843200, 2024.2459016393443)]
fn expect_julian_year(timestamp: i64, year: f64) {
assert_eq!(to_julian_year(Moment::from_unix_seconds(timestamp)), year);
}
#[test_case(2460665.8852546294, 1734772485999982059)]
#[test_case(2460576.031099537, 1727009086999990046)]
#[test_case(2460665.8900694447, 1734772902000018954)]
#[test_case(2506596.5231712963, 5703179602000004053)]
fn expect_julian_day(date: f64, expected: i128) -> crate::Result<()> {
let moment = from_julian_day(date)?;
assert_eq!(moment.to_unix_nanoseconds(), expected);
assert_eq!(to_julian_day(Moment::from_unix_nanoseconds(expected)), date);
assert_eq!(to_julian_day(moment), date);
Ok(())
}
}