/home/a220/proj/radnelac/src/calendar/cotsworth.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::prelude::CommonDate; |
7 | | use crate::calendar::prelude::GuaranteedMonth; |
8 | | use crate::calendar::prelude::HasLeapYears; |
9 | | use crate::calendar::prelude::Perennial; |
10 | | use crate::calendar::prelude::Quarter; |
11 | | use crate::calendar::prelude::ToFromCommonDate; |
12 | | use crate::calendar::prelude::ToFromOrdinalDate; |
13 | | use crate::calendar::AllowYearZero; |
14 | | use crate::calendar::CalendarMoment; |
15 | | use crate::calendar::HasIntercalaryDays; |
16 | | use crate::calendar::OrdinalDate; |
17 | | use crate::common::error::CalendarError; |
18 | | use crate::common::math::TermNum; |
19 | | use crate::day_count::BoundedDayCount; |
20 | | use crate::day_count::CalculatedBounds; |
21 | | use crate::day_count::Epoch; |
22 | | use crate::day_count::Fixed; |
23 | | use crate::day_count::FromFixed; |
24 | | use crate::day_count::ToFixed; |
25 | | use crate::day_cycle::Weekday; |
26 | | #[allow(unused_imports)] //FromPrimitive is needed for derive |
27 | | use num_traits::FromPrimitive; |
28 | | use std::num::NonZero; |
29 | | |
30 | | /// Represents a month in the Cotsworth Calendar |
31 | | #[derive(Debug, PartialEq, PartialOrd, Clone, Copy, FromPrimitive, ToPrimitive)] |
32 | | pub enum CotsworthMonth { |
33 | | January = 1, |
34 | | February, |
35 | | March, |
36 | | April, |
37 | | May, |
38 | | June, |
39 | | Sol, |
40 | | July, |
41 | | August, |
42 | | September, |
43 | | October, |
44 | | November, |
45 | | December, |
46 | | } |
47 | | |
48 | | /// Represents a complementary day of the Cotsworth Calendar |
49 | | #[derive(Debug, PartialEq, PartialOrd, Clone, Copy, FromPrimitive, ToPrimitive)] |
50 | | pub enum CotsworthComplementaryDay { |
51 | | /// The day that ends every year of the Cotsworth Calendar. |
52 | | /// This is also represented as December 29. It is not part of any week. |
53 | | YearDay = 1, |
54 | | /// The extra day added in leap years of the Cotsworth Calendar. |
55 | | /// This is also represented as June 29. It is not part of any week. |
56 | | LeapDay, |
57 | | } |
58 | | |
59 | | /// Represents a date in the Cotsworth calendar |
60 | | /// (ie. International Fixed Calendar, Eastman plan, Yearal) |
61 | | /// |
62 | | /// This calendar was originally designed by Moses B Cotsworth. |
63 | | /// George Eastman instituted its use within the Eastman Kodak Company from 1928 to 1989. |
64 | | /// |
65 | | /// ## Further reading |
66 | | /// + [Wikipedia](https://en.wikipedia.org/wiki/Cotsworth_calendar) |
67 | | /// + ["Nation's Business" May 1926](https://www.freexenon.com/wp-content/uploads/2018/07/The-Importance-of-Calendar-Reform-to-the-Business-World-George-Eastman.pdf) |
68 | | /// + "The Importance of Calendar Reform to the Business World" |
69 | | /// + by George Eastman, President, Eastman Kodak Company |
70 | | |
71 | | #[derive(Debug, PartialEq, PartialOrd, Clone, Copy)] |
72 | | pub struct Cotsworth(CommonDate); |
73 | | |
74 | | impl AllowYearZero for Cotsworth {} |
75 | | |
76 | | impl ToFromOrdinalDate for Cotsworth { |
77 | 1.28k | fn valid_ordinal(ord: OrdinalDate) -> Result<(), CalendarError> { |
78 | 1.28k | Gregorian::valid_ordinal(ord) |
79 | 1.28k | } |
80 | | |
81 | 15.8k | fn ordinal_from_fixed(fixed_date: Fixed) -> OrdinalDate { |
82 | 15.8k | Gregorian::ordinal_from_fixed(fixed_date) |
83 | 15.8k | } |
84 | | |
85 | 11.3k | fn to_ordinal(self) -> OrdinalDate { |
86 | 11.3k | let approx_m = ((self.0.month as i64) - 1) * 28; |
87 | 11.3k | let offset_m = if self.0.month > 6 && Cotsworth::is_leap6.18k (self.0.year6.18k ) { |
88 | 1.54k | approx_m + 1 |
89 | | } else { |
90 | 9.77k | approx_m |
91 | | }; |
92 | 11.3k | let doy = (offset_m as u16) + (self.0.day as u16); |
93 | 11.3k | OrdinalDate { |
94 | 11.3k | year: self.0.year, |
95 | 11.3k | day_of_year: doy, |
96 | 11.3k | } |
97 | 11.3k | } |
98 | | |
99 | 15.6k | fn from_ordinal_unchecked(ord: OrdinalDate) -> Self { |
100 | | const LEAP_DAY_ORD: u16 = (6 * 28) + 1; |
101 | 15.6k | let result = match (ord.day_of_year, Cotsworth::is_leap(ord.year)) { |
102 | 13 | (366, true) => CommonDate::new(ord.year, 13, 29), |
103 | 24 | (365, false) => CommonDate::new(ord.year, 13, 29), |
104 | 7 | (LEAP_DAY_ORD, true) => CommonDate::new(ord.year, 6, 29), |
105 | 15.5k | (doy, is_leap) => { |
106 | 15.5k | let correction = if doy < LEAP_DAY_ORD || !is_leap8.27k { 013.6k } else { 11.97k }; |
107 | 15.5k | let month = ((((doy - correction) - 1) as i64).div_euclid(28) + 1) as u8; |
108 | 15.5k | let day = ((doy - correction) as i64).adjusted_remainder(28) as u8; |
109 | 15.5k | CommonDate::new(ord.year, month, day) |
110 | | } |
111 | | }; |
112 | 15.6k | Cotsworth(result) |
113 | 15.6k | } |
114 | | } |
115 | | |
116 | | impl HasIntercalaryDays<CotsworthComplementaryDay> for Cotsworth { |
117 | 11.7k | fn complementary(self) -> Option<CotsworthComplementaryDay> { |
118 | 11.7k | if self.0.day == 29 && self.0.month == (CotsworthMonth::December as u8)1.05k { |
119 | 530 | Some(CotsworthComplementaryDay::YearDay) |
120 | 11.2k | } else if self.0.day == 29 && self.0.month == (CotsworthMonth::June as u8)521 { |
121 | 521 | Some(CotsworthComplementaryDay::LeapDay) |
122 | | } else { |
123 | 10.7k | None |
124 | | } |
125 | 11.7k | } |
126 | | |
127 | 0 | fn complementary_count(p_year: i32) -> u8 { |
128 | 0 | if Cotsworth::is_leap(p_year) { |
129 | 0 | 2 |
130 | | } else { |
131 | 0 | 1 |
132 | | } |
133 | 0 | } |
134 | | } |
135 | | |
136 | | impl Perennial<CotsworthMonth, Weekday> for Cotsworth { |
137 | 9.97k | fn weekday(self) -> Option<Weekday> { |
138 | 9.97k | if self.complementary().is_some() { |
139 | 26 | None |
140 | | } else { |
141 | 9.94k | Weekday::from_i64(((self.0.day as i64) - 1).modulus(7)) |
142 | | } |
143 | 9.97k | } |
144 | | |
145 | 2.55k | fn days_per_week() -> u8 { |
146 | 2.55k | 7 |
147 | 2.55k | } |
148 | | |
149 | 2.55k | fn weeks_per_month() -> u8 { |
150 | 2.55k | 4 |
151 | 2.55k | } |
152 | | } |
153 | | |
154 | | impl HasLeapYears for Cotsworth { |
155 | 24.2k | fn is_leap(c_year: i32) -> bool { |
156 | 24.2k | Gregorian::is_leap(c_year) |
157 | 24.2k | } |
158 | | } |
159 | | |
160 | | impl CalculatedBounds for Cotsworth {} |
161 | | |
162 | | impl Epoch for Cotsworth { |
163 | 1.53k | fn epoch() -> Fixed { |
164 | 1.53k | Gregorian::epoch() |
165 | 1.53k | } |
166 | | } |
167 | | |
168 | | impl FromFixed for Cotsworth { |
169 | 15.3k | fn from_fixed(fixed_date: Fixed) -> Cotsworth { |
170 | 15.3k | let ord = Self::ordinal_from_fixed(fixed_date); |
171 | 15.3k | Self::from_ordinal_unchecked(ord) |
172 | 15.3k | } |
173 | | } |
174 | | |
175 | | impl ToFixed for Cotsworth { |
176 | 9.02k | fn to_fixed(self) -> Fixed { |
177 | 9.02k | let offset_y = Gregorian::try_year_start(self.0.year) |
178 | 9.02k | .expect("Year known to be valid") |
179 | 9.02k | .to_fixed() |
180 | 9.02k | .get_day_i() |
181 | 9.02k | - 1; |
182 | 9.02k | let ord = self.to_ordinal(); |
183 | 9.02k | Fixed::cast_new(offset_y + (ord.day_of_year as i64)) |
184 | 9.02k | } |
185 | | } |
186 | | |
187 | | impl ToFromCommonDate<CotsworthMonth> for Cotsworth { |
188 | 40.6k | fn to_common_date(self) -> CommonDate { |
189 | 40.6k | self.0 |
190 | 40.6k | } |
191 | | |
192 | 13.2k | fn from_common_date_unchecked(date: CommonDate) -> Self { |
193 | 13.2k | debug_assert!(Self::valid_ymd(date).is_ok()); |
194 | 13.2k | Self(date) |
195 | 13.2k | } |
196 | | |
197 | 28.0k | fn valid_ymd(date: CommonDate) -> Result<(), CalendarError> { |
198 | 28.0k | if date.month < 1 || date.month > 1327.7k { |
199 | 768 | Err(CalendarError::InvalidMonth) |
200 | 27.2k | } else if date.day < 1 || date.day > 2926.9k { |
201 | 512 | Err(CalendarError::InvalidDay) |
202 | 26.7k | } else if date.day == 29 { |
203 | 4.47k | if date.month == 13 || (Cotsworth::is_leap1.39k (date.year1.39k ) && date.month == 61.39k ) { |
204 | 4.47k | Ok(()) |
205 | | } else { |
206 | 0 | Err(CalendarError::InvalidDay) |
207 | | } |
208 | | } else { |
209 | 22.2k | Ok(()) |
210 | | } |
211 | 28.0k | } |
212 | | |
213 | 258 | fn year_end_date(year: i32) -> CommonDate { |
214 | 258 | CommonDate::new(year, CotsworthMonth::December as u8, 29) |
215 | 258 | } |
216 | | } |
217 | | |
218 | | impl Quarter for Cotsworth { |
219 | 2.81k | fn quarter(self) -> NonZero<u8> { |
220 | 2.81k | let m = self.to_common_date().month; |
221 | 2.81k | if m == 13 { |
222 | 297 | NonZero::new(4 as u8).expect("4 != 0") |
223 | | } else { |
224 | 2.51k | NonZero::new(((m - 1) / 3) + 1).expect("(m-1)/3 > -1") |
225 | | } |
226 | 2.81k | } |
227 | | } |
228 | | |
229 | | impl GuaranteedMonth<CotsworthMonth> for Cotsworth {} |
230 | | |
231 | | /// Represents a date *and time* in the Cotsworth Calendar |
232 | | pub type CotsworthMoment = CalendarMoment<Cotsworth>; |