Coverage Report

Created: 2025-12-31 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/calendrical_calculations-0.1.2/src/julian.rs
Line
Count
Source
1
// This file is part of ICU4X.
2
//
3
// The contents of this file implement algorithms from Calendrical Calculations
4
// by Reingold & Dershowitz, Cambridge University Press, 4th edition (2018),
5
// which have been released as Lisp code at <https://github.com/EdReingold/calendar-code2/>
6
// under the Apache-2.0 license. Accordingly, this file is released under
7
// the Apache License, Version 2.0 which can be found at the calendrical_calculations
8
// package root or at http://www.apache.org/licenses/LICENSE-2.0.
9
10
use crate::helpers::{i64_to_i32, I32CastError};
11
use crate::rata_die::RataDie;
12
13
// Julian epoch is equivalent to fixed_from_iso of December 30th of 0 year
14
// 1st Jan of 1st year Julian is equivalent to December 30th of 0th year of ISO year
15
const JULIAN_EPOCH: RataDie = RataDie::new(-1);
16
17
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1684-L1687>
18
#[inline(always)]
19
0
pub const fn is_leap_year(year: i32) -> bool {
20
0
    year % 4 == 0
21
0
}
22
23
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1689-L1709>
24
0
pub const fn fixed_from_julian(year: i32, month: u8, day: u8) -> RataDie {
25
0
    let mut fixed =
26
0
        JULIAN_EPOCH.to_i64_date() - 1 + 365 * (year as i64 - 1) + (year as i64 - 1).div_euclid(4);
27
0
    debug_assert!(month > 0 && month < 13, "Month should be in range 1..=12.");
28
0
    fixed += match month {
29
0
        1 => 0,
30
0
        2 => 31,
31
0
        3 => 59,
32
0
        4 => 90,
33
0
        5 => 120,
34
0
        6 => 151,
35
0
        7 => 181,
36
0
        8 => 212,
37
0
        9 => 243,
38
0
        10 => 273,
39
0
        11 => 304,
40
0
        12 => 334,
41
0
        _ => -1,
42
    };
43
    // Only add one if the month is after February (month > 2), since leap days are added to the end of February
44
0
    if month > 2 && is_leap_year(year) {
45
0
        fixed += 1;
46
0
    }
47
0
    RataDie::new(fixed + (day as i64))
48
0
}
49
50
/// Lisp code reference: <https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1711-L1738>
51
0
pub fn julian_from_fixed(date: RataDie) -> Result<(i32, u8, u8), I32CastError> {
52
0
    let approx = (4 * date.to_i64_date() + 1464).div_euclid(1461);
53
0
    let year = i64_to_i32(approx)?;
54
0
    let prior_days = date
55
0
        - fixed_from_julian(year, 1, 1)
56
0
        - if is_leap_year(year) && date > fixed_from_julian(year, 2, 28) {
57
0
            1
58
        } else {
59
0
            0
60
        };
61
0
    let adjusted_year = if prior_days >= 365 {
62
0
        year.saturating_add(1)
63
    } else {
64
0
        year
65
    };
66
0
    let adjusted_prior_days = prior_days.rem_euclid(365);
67
0
    debug_assert!((0..365).contains(&adjusted_prior_days));
68
0
    let month = if adjusted_prior_days < 31 {
69
0
        1
70
0
    } else if adjusted_prior_days < 59 {
71
0
        2
72
0
    } else if adjusted_prior_days < 90 {
73
0
        3
74
0
    } else if adjusted_prior_days < 120 {
75
0
        4
76
0
    } else if adjusted_prior_days < 151 {
77
0
        5
78
0
    } else if adjusted_prior_days < 181 {
79
0
        6
80
0
    } else if adjusted_prior_days < 212 {
81
0
        7
82
0
    } else if adjusted_prior_days < 243 {
83
0
        8
84
0
    } else if adjusted_prior_days < 273 {
85
0
        9
86
0
    } else if adjusted_prior_days < 304 {
87
0
        10
88
0
    } else if adjusted_prior_days < 334 {
89
0
        11
90
    } else {
91
0
        12
92
    };
93
0
    let day = (date - fixed_from_julian(adjusted_year, month, 1) + 1) as u8; // as days_in_month is < u8::MAX
94
0
    debug_assert!(day <= 31, "Day assertion failed; date: {date:?}, adjusted_year: {adjusted_year}, prior_days: {prior_days}, month: {month}, day: {day}");
95
96
0
    Ok((adjusted_year, month, day))
97
0
}
98
99
/// Get a fixed date from the ymd of a Julian date.
100
///
101
/// Years are counted as in _Calendrical Calculations_ by Reingold & Dershowitz,
102
/// meaning there is no year 0. For instance, near the epoch date, years are counted: -3, -2, -1, 1, 2, 3 instead of -2, -1, 0, 1, 2, 3.
103
///
104
/// Primarily useful for use with code constructing epochs specified in the bookg
105
0
pub const fn fixed_from_julian_book_version(book_year: i32, month: u8, day: u8) -> RataDie {
106
0
    debug_assert!(book_year != 0);
107
    // TODO: Should we check the bounds here?
108
0
    fixed_from_julian(
109
0
        if book_year < 0 {
110
0
            book_year + 1
111
        } else {
112
0
            book_year
113
        },
114
0
        month,
115
0
        day,
116
    )
117
0
}