Coverage Report

Created: 2025-08-13 21:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/a220/proj/radnelac/src/calendar/holocene.rs
Line
Count
Source
1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5
use crate::calendar::gregorian::Gregorian;
6
use crate::calendar::gregorian::GregorianMonth;
7
use crate::calendar::prelude::CommonDate;
8
use crate::calendar::prelude::CommonWeekOfYear;
9
use crate::calendar::prelude::GuaranteedMonth;
10
use crate::calendar::prelude::HasLeapYears;
11
use crate::calendar::prelude::Quarter;
12
use crate::calendar::prelude::ToFromCommonDate;
13
use crate::calendar::AllowYearZero;
14
use crate::calendar::CalendarMoment;
15
use crate::calendar::OrdinalDate;
16
use crate::calendar::ToFromOrdinalDate;
17
use crate::common::error::CalendarError;
18
use crate::day_count::CalculatedBounds;
19
use crate::day_count::Epoch;
20
use crate::day_count::Fixed;
21
use crate::day_count::FromFixed;
22
use crate::day_count::ToFixed;
23
use std::num::NonZero;
24
25
const HOLOCENE_YEAR_OFFSET: i32 = -10000;
26
27
/// Represents a month in the Holocene calendar
28
pub type HoloceneMonth = GregorianMonth;
29
30
/// Represents a date in the Holocene calendar
31
///
32
/// The Holocene calendar was proposed by Cesare Emiliani.
33
/// It is identical to the proleptic Gregorian calendar, but
34
/// with an extra 10000 years added to each date. Thus 2016
35
/// in the Gregorian calendar is 12016 in the Holocene calendar.
36
///
37
/// ## Further reading
38
/// + [Wikipedia](https://en.wikipedia.org/wiki/Holocene_calendar)
39
/// + [Kurzgesagt](https://www.youtube.com/watch?v=czgOWmtGVGs)
40
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
41
pub struct Holocene(CommonDate);
42
43
impl AllowYearZero for Holocene {}
44
45
impl ToFromOrdinalDate for Holocene {
46
1.28k
    fn valid_ordinal(ord: OrdinalDate) -> Result<(), CalendarError> {
47
1.28k
        Gregorian::valid_ordinal(ord)
48
1.28k
    }
49
50
512
    fn ordinal_from_fixed(fixed_date: Fixed) -> OrdinalDate {
51
512
        let g_ord = Gregorian::ordinal_from_fixed(fixed_date);
52
512
        OrdinalDate {
53
512
            year: (g_ord.year - HOLOCENE_YEAR_OFFSET),
54
512
            day_of_year: g_ord.day_of_year,
55
512
        }
56
512
    }
57
58
1.28k
    fn to_ordinal(self) -> OrdinalDate {
59
1.28k
        let g = Gregorian::try_from_common_date(self.to_common_date())
60
1.28k
            .expect("Same month/day validity");
61
1.28k
        g.to_ordinal()
62
1.28k
    }
63
64
256
    fn from_ordinal_unchecked(ord: OrdinalDate) -> Self {
65
256
        let e = Gregorian::from_ordinal_unchecked(ord);
66
256
        Holocene::try_from_common_date(e.to_common_date()).expect("Same month/day validity")
67
256
    }
68
}
69
70
impl HasLeapYears for Holocene {
71
768
    fn is_leap(h_year: i32) -> bool {
72
768
        Gregorian::is_leap(h_year) //10000 is divisible by 400, so it's ok
73
768
    }
74
}
75
76
impl CalculatedBounds for Holocene {}
77
78
impl Epoch for Holocene {
79
256
    fn epoch() -> Fixed {
80
256
        Gregorian::try_year_start(HOLOCENE_YEAR_OFFSET + 1)
81
256
            .expect("Year known to be valid")
82
256
            .to_fixed()
83
256
    }
84
}
85
86
impl FromFixed for Holocene {
87
10.2k
    fn from_fixed(date: Fixed) -> Holocene {
88
10.2k
        let result = Gregorian::from_fixed(date).to_common_date();
89
10.2k
        Holocene(CommonDate::new(
90
10.2k
            result.year - (HOLOCENE_YEAR_OFFSET as i32),
91
10.2k
            result.month,
92
10.2k
            result.day,
93
10.2k
        ))
94
10.2k
    }
95
}
96
97
impl ToFixed for Holocene {
98
3.84k
    fn to_fixed(self) -> Fixed {
99
3.84k
        let g = Gregorian::try_from_common_date(CommonDate::new(
100
3.84k
            self.0.year + (HOLOCENE_YEAR_OFFSET as i32),
101
3.84k
            self.0.month,
102
3.84k
            self.0.day,
103
        ))
104
3.84k
        .expect("Same month/day rules");
105
3.84k
        g.to_fixed()
106
3.84k
    }
107
}
108
109
impl ToFromCommonDate<HoloceneMonth> for Holocene {
110
15.7k
    fn to_common_date(self) -> CommonDate {
111
15.7k
        self.0
112
15.7k
    }
113
114
7.11k
    fn from_common_date_unchecked(date: CommonDate) -> Self {
115
7.11k
        debug_assert!(Self::valid_ymd(date).is_ok());
116
7.11k
        Self(date)
117
7.11k
    }
118
119
15.7k
    fn valid_ymd(date: CommonDate) -> Result<(), CalendarError> {
120
15.7k
        Gregorian::valid_ymd(date)
121
15.7k
    }
122
123
257
    fn year_end_date(year: i32) -> CommonDate {
124
257
        Gregorian::year_end_date(year)
125
257
    }
126
}
127
128
impl Quarter for Holocene {
129
2.56k
    fn quarter(self) -> NonZero<u8> {
130
2.56k
        NonZero::new(((self.to_common_date().month - 1) / 3) + 1).expect("(m-1)/3 > -1")
131
2.56k
    }
132
}
133
134
impl GuaranteedMonth<HoloceneMonth> for Holocene {}
135
impl CommonWeekOfYear<HoloceneMonth> for Holocene {}
136
137
/// Represents a date *and time* in the Holocen Calendar
138
pub type HoloceneMoment = CalendarMoment<Holocene>;
139
140
#[cfg(test)]
141
mod tests {
142
    use super::*;
143
144
    #[test]
145
1
    fn date_of_proposal() {
146
1
        let dh = CommonDate {
147
1
            year: 11993,
148
1
            month: 12,
149
1
            day: 30,
150
1
        };
151
1
        let dg = CommonDate {
152
1
            year: 1993,
153
1
            month: 12,
154
1
            day: 30,
155
1
        };
156
1
        let fh = Holocene::try_from_common_date(dh).unwrap().to_fixed();
157
1
        let fg = Gregorian::try_from_common_date(dg).unwrap().to_fixed();
158
1
        assert_eq!(fh, fg);
159
1
    }
160
}