calendrical_calculations/
julian.rs1use crate::helpers::{i64_to_i32, I32CastError};
11use crate::rata_die::RataDie;
12
13const JULIAN_EPOCH: RataDie = RataDie::new(-1);
16
17#[inline(always)]
19pub const fn is_leap_year(year: i32) -> bool {
20 year % 4 == 0
21}
22
23pub const fn fixed_from_julian(year: i32, month: u8, day: u8) -> RataDie {
25 let mut fixed =
26 JULIAN_EPOCH.to_i64_date() - 1 + 365 * (year as i64 - 1) + (year as i64 - 1).div_euclid(4);
27 debug_assert!(month > 0 && month < 13, "Month should be in range 1..=12.");
28 fixed += match month {
29 1 => 0,
30 2 => 31,
31 3 => 59,
32 4 => 90,
33 5 => 120,
34 6 => 151,
35 7 => 181,
36 8 => 212,
37 9 => 243,
38 10 => 273,
39 11 => 304,
40 12 => 334,
41 _ => -1,
42 };
43 if month > 2 && is_leap_year(year) {
45 fixed += 1;
46 }
47 RataDie::new(fixed + (day as i64))
48}
49
50pub fn julian_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> {
52 let approx = (4 * date.to_i64_date() + 1464).div_euclid(1461);
53 let year = i64_to_i32(approx)?;
54 let prior_days = date
55 - fixed_from_julian(year, 1, 1)
56 - if is_leap_year(year) && date > fixed_from_julian(year, 2, 28) {
57 1
58 } else {
59 0
60 };
61 let adjusted_year = if prior_days >= 365 {
62 year.saturating_add(1)
63 } else {
64 year
65 };
66 let adjusted_prior_days = prior_days.rem_euclid(365);
67 debug_assert!((0..365).contains(&adjusted_prior_days));
68 let month = if adjusted_prior_days < 31 {
69 1
70 } else if adjusted_prior_days < 59 {
71 2
72 } else if adjusted_prior_days < 90 {
73 3
74 } else if adjusted_prior_days < 120 {
75 4
76 } else if adjusted_prior_days < 151 {
77 5
78 } else if adjusted_prior_days < 181 {
79 6
80 } else if adjusted_prior_days < 212 {
81 7
82 } else if adjusted_prior_days < 243 {
83 8
84 } else if adjusted_prior_days < 273 {
85 9
86 } else if adjusted_prior_days < 304 {
87 10
88 } else if adjusted_prior_days < 334 {
89 11
90 } else {
91 12
92 };
93 let day = (date - fixed_from_julian(adjusted_year, month, 1) + 1) as u8; debug_assert!(day <= 31, "Day assertion failed; date: {date:?}, adjusted_year: {adjusted_year}, prior_days: {prior_days}, month: {month}, day: {day}");
95
96 Ok((adjusted_year, month, day))
97}
98
99pub const fn fixed_from_julian_book_version(book_year: i32, month: u8, day: u8) -> RataDie {
106 debug_assert!(book_year != 0);
107 fixed_from_julian(
109 if book_year < 0 {
110 book_year + 1
111 } else {
112 book_year
113 },
114 month,
115 day,
116 )
117}