1use crate::cal::iso::{IsoDateInner, IsoEra};
6use crate::calendar_arithmetic::{ArithmeticDate, DateFieldsResolver};
7use crate::error::{DateError, DateFromFieldsError, EcmaReferenceYearError, UnknownEraError};
8use crate::options::DateFromFieldsOptions;
9use crate::options::{DateAddOptions, DateDifferenceOptions};
10use crate::preferences::CalendarAlgorithm;
11use crate::types::EraYear;
12use crate::{types, Calendar, RangeError};
13use calendrical_calculations::helpers::I32CastError;
14use calendrical_calculations::rata_die::RataDie;
15
16#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub(crate) struct AbstractGregorian<Y: GregorianYears>(pub Y);
18
19pub(crate) trait GregorianYears: Clone + core::fmt::Debug {
20 const EXTENDED_YEAR_OFFSET: i32 = 0;
22
23 fn extended_from_era_year(&self, era: Option<&[u8]>, year: i32)
24 -> Result<i32, UnknownEraError>;
25
26 fn era_year_from_extended(&self, extended_year: i32, month: u8, day: u8) -> EraYear;
27
28 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
29 None
30 }
31
32 fn debug_name(&self) -> &'static str;
33}
34
35impl ArithmeticDate<AbstractGregorian<IsoEra>> {
36 pub(crate) fn new_gregorian<Y: GregorianYears>(
37 year: i32,
38 month: u8,
39 day: u8,
40 ) -> Result<Self, RangeError> {
41 ArithmeticDate::try_from_ymd(year + Y::EXTENDED_YEAR_OFFSET, month, day)
42 }
43}
44
45pub(crate) const REFERENCE_YEAR: i32 = 1972;
46#[cfg(test)]
47pub(crate) const LAST_DAY_OF_REFERENCE_YEAR: RataDie =
48 calendrical_calculations::gregorian::day_before_year(REFERENCE_YEAR + 1);
49
50impl<Y: GregorianYears> DateFieldsResolver for AbstractGregorian<Y> {
51 type YearInfo = i32;
53
54 fn days_in_provided_month(year: i32, month: u8) -> u8 {
55 if month == 2 {
57 28 + calendrical_calculations::gregorian::is_leap_year(year) as u8
58 } else {
59 30 | month ^ (month >> 3)
60 }
61 }
62
63 fn months_in_provided_year(_: i32) -> u8 {
64 12
65 }
66
67 #[inline]
68 fn year_info_from_era(
69 &self,
70 era: &[u8],
71 era_year: i32,
72 ) -> Result<Self::YearInfo, UnknownEraError> {
73 Ok(self.0.extended_from_era_year(Some(era), era_year)? + Y::EXTENDED_YEAR_OFFSET)
74 }
75
76 #[inline]
77 fn year_info_from_extended(&self, extended_year: i32) -> Self::YearInfo {
78 extended_year + Y::EXTENDED_YEAR_OFFSET
79 }
80
81 #[inline]
82 fn reference_year_from_month_day(
83 &self,
84 _month_code: types::ValidMonthCode,
85 _day: u8,
86 ) -> Result<Self::YearInfo, EcmaReferenceYearError> {
87 Ok(REFERENCE_YEAR)
88 }
89}
90
91impl<Y: GregorianYears> crate::cal::scaffold::UnstableSealed for AbstractGregorian<Y> {}
92
93impl<Y: GregorianYears> Calendar for AbstractGregorian<Y> {
94 type DateInner = ArithmeticDate<AbstractGregorian<IsoEra>>;
95 type Year = types::EraYear;
96 type DifferenceError = core::convert::Infallible;
97
98 fn from_codes(
99 &self,
100 era: Option<&str>,
101 year: i32,
102 month_code: types::MonthCode,
103 day: u8,
104 ) -> Result<Self::DateInner, DateError> {
105 ArithmeticDate::from_codes(era, year, month_code, day, self).map(ArithmeticDate::cast)
106 }
107
108 #[cfg(feature = "unstable")]
109 fn from_fields(
110 &self,
111 fields: types::DateFields,
112 options: DateFromFieldsOptions,
113 ) -> Result<Self::DateInner, DateFromFieldsError> {
114 ArithmeticDate::from_fields(fields, options, self).map(ArithmeticDate::cast)
115 }
116
117 fn from_rata_die(&self, date: RataDie) -> Self::DateInner {
118 let iso = match calendrical_calculations::gregorian::gregorian_from_fixed(date) {
119 Err(I32CastError::BelowMin) => {
120 ArithmeticDate::<AbstractGregorian<IsoEra>>::new_unchecked(i32::MIN, 1, 1)
121 }
122 Err(I32CastError::AboveMax) => ArithmeticDate::new_unchecked(i32::MAX, 12, 31),
123 Ok((year, month, day)) => ArithmeticDate::new_unchecked(year, month, day),
124 };
125
126 if iso.year.checked_sub(Y::EXTENDED_YEAR_OFFSET).is_none() {
127 if Y::EXTENDED_YEAR_OFFSET < 0 {
128 ArithmeticDate::new_unchecked(i32::MIN - Y::EXTENDED_YEAR_OFFSET, 1, 1)
129 } else {
130 ArithmeticDate::new_unchecked(i32::MAX - Y::EXTENDED_YEAR_OFFSET, 12, 31)
131 }
132 } else {
133 iso
134 }
135 }
136
137 fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
138 calendrical_calculations::gregorian::fixed_from_gregorian(date.year, date.month, date.day)
139 }
140
141 fn has_cheap_iso_conversion(&self) -> bool {
142 true
143 }
144
145 fn from_iso(&self, iso: IsoDateInner) -> Self::DateInner {
146 iso.0
147 }
148
149 fn to_iso(&self, date: &Self::DateInner) -> IsoDateInner {
150 IsoDateInner(*date)
151 }
152
153 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
154 AbstractGregorian::<IsoEra>::months_in_provided_year(date.year)
155 }
156
157 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
158 365 + calendrical_calculations::gregorian::is_leap_year(date.year) as u16
159 }
160
161 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
162 AbstractGregorian::<IsoEra>::days_in_provided_month(date.year, date.month)
163 }
164
165 #[cfg(feature = "unstable")]
166 fn add(
167 &self,
168 date: &Self::DateInner,
169 duration: types::DateDuration,
170 options: DateAddOptions,
171 ) -> Result<Self::DateInner, DateError> {
172 date.added(duration, &AbstractGregorian(IsoEra), options)
173 }
174
175 #[cfg(feature = "unstable")]
176 fn until(
177 &self,
178 date1: &Self::DateInner,
179 date2: &Self::DateInner,
180 options: DateDifferenceOptions,
181 ) -> Result<types::DateDuration, Self::DifferenceError> {
182 Ok(date1.until(date2, &AbstractGregorian(IsoEra), options))
183 }
184
185 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
186 self.0
187 .era_year_from_extended(date.year - Y::EXTENDED_YEAR_OFFSET, date.month, date.day)
188 }
189
190 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
191 calendrical_calculations::gregorian::is_leap_year(date.year)
192 }
193
194 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
195 types::MonthInfo::non_lunisolar(date.month)
196 }
197
198 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
199 types::DayOfMonth(date.day)
200 }
201
202 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
203 types::DayOfYear(
204 calendrical_calculations::gregorian::days_before_month(date.year, date.month)
205 + date.day as u16,
206 )
207 }
208
209 fn debug_name(&self) -> &'static str {
210 self.0.debug_name()
211 }
212
213 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
214 self.0.calendar_algorithm()
215 }
216}
217
218macro_rules! impl_with_abstract_gregorian {
219 ($cal_ty:ty, $inner_date_ty:ident, $eras_ty:ty, $self_ident:ident, $eras_expr:expr) => {
220 #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
221 pub struct $inner_date_ty(
222 pub(crate) ArithmeticDate<
223 crate::cal::abstract_gregorian::AbstractGregorian<crate::cal::iso::IsoEra>,
224 >,
225 );
226
227 impl crate::cal::scaffold::UnstableSealed for $cal_ty {}
228 impl crate::Calendar for $cal_ty {
229 type DateInner = $inner_date_ty;
230 type Year = types::EraYear;
231 type DifferenceError = core::convert::Infallible;
232
233 fn from_codes(
234 &self,
235 era: Option<&str>,
236 year: i32,
237 month_code: types::MonthCode,
238 day: u8,
239 ) -> Result<Self::DateInner, crate::error::DateError> {
240 let $self_ident = self;
241 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr)
242 .from_codes(era, year, month_code, day)
243 .map($inner_date_ty)
244 }
245
246 #[cfg(feature = "unstable")]
247 fn from_fields(
248 &self,
249 fields: crate::types::DateFields,
250 options: crate::options::DateFromFieldsOptions,
251 ) -> Result<Self::DateInner, crate::error::DateFromFieldsError> {
252 let $self_ident = self;
253 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr)
254 .from_fields(fields, options)
255 .map($inner_date_ty)
256 }
257
258 fn from_rata_die(&self, rd: crate::types::RataDie) -> Self::DateInner {
259 let $self_ident = self;
260 $inner_date_ty(
261 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).from_rata_die(rd),
262 )
263 }
264
265 fn to_rata_die(&self, date: &Self::DateInner) -> crate::types::RataDie {
266 let $self_ident = self;
267 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).to_rata_die(&date.0)
268 }
269
270 fn has_cheap_iso_conversion(&self) -> bool {
271 let $self_ident = self;
272 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr)
273 .has_cheap_iso_conversion()
274 }
275
276 fn from_iso(&self, iso: crate::cal::iso::IsoDateInner) -> Self::DateInner {
277 let $self_ident = self;
278 $inner_date_ty(
279 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).from_iso(iso),
280 )
281 }
282
283 fn to_iso(&self, date: &Self::DateInner) -> crate::cal::iso::IsoDateInner {
284 let $self_ident = self;
285 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).to_iso(&date.0)
286 }
287
288 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
289 let $self_ident = self;
290 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr)
291 .months_in_year(&date.0)
292 }
293
294 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
295 let $self_ident = self;
296 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).days_in_year(&date.0)
297 }
298
299 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
300 let $self_ident = self;
301 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).days_in_month(&date.0)
302 }
303
304 #[cfg(feature = "unstable")]
305 fn add(
306 &self,
307 date: &Self::DateInner,
308 duration: crate::types::DateDuration,
309 options: crate::options::DateAddOptions,
310 ) -> Result<Self::DateInner, DateError> {
311 let $self_ident = self;
312 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr)
313 .add(&date.0, duration, options)
314 .map($inner_date_ty)
315 }
316
317 #[cfg(feature = "unstable")]
318 fn until(
319 &self,
320 date1: &Self::DateInner,
321 date2: &Self::DateInner,
322 options: crate::options::DateDifferenceOptions,
323 ) -> Result<crate::types::DateDuration, Self::DifferenceError> {
324 let $self_ident = self;
325 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr)
326 .until(&date1.0, &date2.0, options)
327 }
328
329 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
330 let $self_ident = self;
331 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).year_info(&date.0)
332 }
333
334 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
335 let $self_ident = self;
336 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr)
337 .is_in_leap_year(&date.0)
338 }
339
340 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
341 let $self_ident = self;
342 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).month(&date.0)
343 }
344
345 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
346 let $self_ident = self;
347 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).day_of_month(&date.0)
348 }
349
350 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
351 let $self_ident = self;
352 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).day_of_year(&date.0)
353 }
354
355 fn debug_name(&self) -> &'static str {
356 let $self_ident = self;
357 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).debug_name()
358 }
359
360 fn calendar_algorithm(&self) -> Option<crate::preferences::CalendarAlgorithm> {
361 let $self_ident = self;
362 crate::cal::abstract_gregorian::AbstractGregorian($eras_expr).calendar_algorithm()
363 }
364 }
365 };
366}
367pub(crate) use impl_with_abstract_gregorian;