simple_datetime_rs/
date.rs

1use crate::constants::{
2    FOUR_YEARS_DAYS, LEAP_YEAR_DAYS, LEAP_YEAR_MONTH_DAYS, MONTH_DAYS, MONTHS_IN_YEAR,
3    REGULAR_YEAR_DAYS, REGULAR_YEAR_MONTH_DAYS, SECONDS_IN_DAY, THREE_REGULAR_YEAR_DAYS,
4    UNIX_EPOCH_YEAR,
5};
6use crate::date_error::{DateError, DateErrorKind};
7use crate::utils::date_util::{leap_year, month_days, month_index};
8use crate::utils::{crossplatform_util, date_util};
9use core::{cmp, fmt};
10use std::cmp::Ordering;
11use std::str::FromStr;
12
13#[derive(Copy, Clone, Eq, PartialEq)]
14pub struct Date {
15    pub year: u64,
16    pub month: u64,
17    pub day: u64,
18}
19
20impl Date {
21    pub fn new(year: u64, month: u64, day: u64) -> Self {
22        let date = Date { year, month, day };
23        date
24    }
25
26    #[inline]
27    pub fn leap_year(&self) -> bool {
28        date_util::leap_year(self.year)
29    }
30
31    #[inline]
32    pub fn recent_leap_year(&self) -> u64 {
33        date_util::recent_leap_year(self.year)
34    }
35
36    #[inline]
37    pub fn next_leap_year(&self) -> u64 {
38        date_util::next_leap_year(self.year)
39    }
40
41    pub fn year_day(&self) -> u64 {
42        let is_leap = self.leap_year();
43        let month_days = if is_leap {
44            &LEAP_YEAR_MONTH_DAYS
45        } else {
46            &REGULAR_YEAR_MONTH_DAYS
47        };
48
49        let mut days: u64 = 0;
50        for i in 0..(self.month_index()) {
51            days += month_days[i];
52        }
53        days + self.day
54    }
55
56    pub fn days_to_next_year(&self) -> u64 {
57        let total = if self.leap_year() { 366u64 } else { 365 };
58        total - self.year_day()
59    }
60
61    #[inline]
62    pub fn month_index(&self) -> usize {
63        date_util::month_index(self.month)
64    }
65
66    pub fn valid(&self) -> bool {
67        if self.month < 1 || self.month > 12 || self.day < 1 {
68            return false;
69        }
70        let max_day = MONTH_DAYS[self.month_index()];
71        self.day <= max_day || (self.month == 2 && self.leap_year() && self.day == 29)
72    }
73
74    pub fn from_ms_dos_date(mut ms_dos_date: u16) -> Self {
75        let day = ms_dos_date & 0x1f;
76        ms_dos_date >>= 5;
77        let month = ms_dos_date & 0xf;
78        ms_dos_date >>= 4;
79        Date::new(ms_dos_date as u64 + 1980, month as u64, day as u64)
80    }
81
82    pub fn add_days(&self, days: u64) -> Self {
83        let mut result_year = self.year;
84        let mut result_month = self.month;
85        let mut result_day = self.day + days;
86
87        let mut is_leap = date_util::leap_year(result_year);
88        let mut month_days = if is_leap {
89            &LEAP_YEAR_MONTH_DAYS
90        } else {
91            &REGULAR_YEAR_MONTH_DAYS
92        };
93
94        loop {
95            let days_in_current_month = month_days[(result_month.saturating_sub(1)) as usize];
96
97            if result_day <= days_in_current_month {
98                break;
99            }
100
101            result_day -= days_in_current_month;
102            result_month += 1;
103
104            if result_month > 12 {
105                result_month = 1;
106                result_year += 1;
107                is_leap = date_util::leap_year(result_year);
108                month_days = if is_leap {
109                    &LEAP_YEAR_MONTH_DAYS
110                } else {
111                    &REGULAR_YEAR_MONTH_DAYS
112                };
113            }
114        }
115
116        Date::new(result_year, result_month, result_day)
117    }
118
119    pub fn from_seconds_since_unix_epoch(seconds: u64) -> (Self, u64) {
120        let days = seconds / SECONDS_IN_DAY;
121        (
122            Date::new(UNIX_EPOCH_YEAR, 1, 1).add_days(days),
123            seconds % SECONDS_IN_DAY,
124        )
125    }
126
127    pub fn to_seconds_from_unix_epoch(self, included: bool) -> u64 {
128        let days = self.to_days() - Date::new(UNIX_EPOCH_YEAR, 1, 1).to_days();
129        (days + included as u64) * SECONDS_IN_DAY
130    }
131
132    pub fn today() -> Self {
133        let (date, _) = Self::from_seconds_since_unix_epoch(crossplatform_util::now_seconds());
134        date
135    }
136
137    pub fn quarter(&self) -> usize {
138        (self.month_index() / 3) + 1
139    }
140
141    pub fn add_months(&self, months: i64) -> Self {
142        let months = (self.year as i64 * 12 + self.month_index() as i64 + months) as u64;
143        let month = months % MONTHS_IN_YEAR + 1;
144        let year = months / MONTHS_IN_YEAR;
145        let day = cmp::min(
146            date_util::month_days(month, date_util::leap_year(year)),
147            self.day,
148        );
149        Date { year, month, day }
150    }
151
152    pub fn sub_months(&self, months: i64) -> Self {
153        self.add_months(-months)
154    }
155
156    pub fn is_month_last_day(&self) -> bool {
157        self.day == date_util::month_days(self.month, self.leap_year())
158    }
159
160    pub fn month_last_day(&self) -> Self {
161        Date {
162            year: self.year,
163            month: self.month,
164            day: date_util::month_days(self.month, self.leap_year()),
165        }
166    }
167
168    pub fn sub_days(&self, days: u64) -> Self {
169        let mut result_year = self.year;
170        let mut result_month = self.month;
171        let mut result_day = self.day;
172
173        let mut is_leap = date_util::leap_year(result_year);
174        let mut month_days = if is_leap {
175            &LEAP_YEAR_MONTH_DAYS
176        } else {
177            &REGULAR_YEAR_MONTH_DAYS
178        };
179
180        let mut remaining_days = days;
181        while remaining_days > 0 {
182            if result_day > remaining_days {
183                result_day -= remaining_days;
184                break;
185            }
186
187            remaining_days -= result_day;
188            result_month -= 1;
189
190            if result_month == 0 {
191                result_month = 12;
192                result_year -= 1;
193                is_leap = date_util::leap_year(result_year);
194                month_days = if is_leap {
195                    &LEAP_YEAR_MONTH_DAYS
196                } else {
197                    &REGULAR_YEAR_MONTH_DAYS
198                };
199            }
200
201            result_day = month_days[(result_month.saturating_sub(1)) as usize];
202        }
203
204        Date::new(result_year, result_month, result_day)
205    }
206
207    /*
208        The function does not take "Adoption of the Gregorian calendar https://en.wikipedia.org/wiki/Adoption_of_the_Gregorian_calendar"
209        and similar things into consideration.
210    */
211    pub fn to_days(&self) -> u64 {
212        let year_elapsed = self.year - 1;
213        let leap_years = year_elapsed / 4;
214        let regular_years = year_elapsed - leap_years;
215
216        let base_days = leap_years * LEAP_YEAR_DAYS + regular_years * REGULAR_YEAR_DAYS;
217        let is_leap = self.leap_year();
218        let month_days = if is_leap {
219            &LEAP_YEAR_MONTH_DAYS
220        } else {
221            &REGULAR_YEAR_MONTH_DAYS
222        };
223
224        let mut days = 0;
225        for i in 0..(self.month_index()) {
226            days += month_days[i];
227        }
228
229        base_days + days + self.day
230    }
231
232    pub fn from_days(days: u64) -> Self {
233        let days = days - 1;
234        let quarters = days / FOUR_YEARS_DAYS;
235        let days = days % FOUR_YEARS_DAYS;
236        let (years, mut days) = if days / THREE_REGULAR_YEAR_DAYS == 0 {
237            (days / REGULAR_YEAR_DAYS, days % REGULAR_YEAR_DAYS)
238        } else {
239            (3, days % THREE_REGULAR_YEAR_DAYS)
240        };
241        let year = (quarters << 2) + years + 1;
242
243        let is_leap = date_util::leap_year(year);
244        let month_days = if is_leap {
245            &LEAP_YEAR_MONTH_DAYS
246        } else {
247            &REGULAR_YEAR_MONTH_DAYS
248        };
249
250        let mut month = 1;
251        for &days_in_month in month_days {
252            if days < days_in_month {
253                break;
254            }
255            days -= days_in_month;
256            month += 1;
257        }
258        Date::new(year, month, days + 1)
259    }
260
261    /*
262        The function does not take "Adoption of the Gregorian calendar https://en.wikipedia.org/wiki/Adoption_of_the_Gregorian_calendar"
263        and similar things into consideration.
264    */
265    #[inline]
266    fn weekday(&self) -> u8 {
267        (self.to_days() % 7) as u8
268    }
269
270    pub fn is_monday(&self) -> bool {
271        self.weekday() == 2
272    }
273
274    pub fn is_tuesday(&self) -> bool {
275        self.weekday() == 3
276    }
277
278    pub fn is_wednesday(&self) -> bool {
279        self.weekday() == 4
280    }
281
282    pub fn is_thursday(&self) -> bool {
283        self.weekday() == 5
284    }
285
286    pub fn is_friday(&self) -> bool {
287        self.weekday() == 6
288    }
289
290    pub fn is_saturday(&self) -> bool {
291        self.weekday() == 0
292    }
293
294    pub fn is_sunday(&self) -> bool {
295        self.weekday() == 1
296    }
297
298    pub fn is_weekend(&self) -> bool {
299        self.weekday() < 2
300    }
301
302    pub fn is_week_day(&self) -> bool {
303        self.weekday() > 1
304    }
305
306    pub fn normalize(&self) -> Date {
307        let month_index = month_index(self.month) as u64;
308        let year = month_index / 12 + self.year;
309        let month = month_index as u64 % 12 + 1;
310        let mut result = Self::new(year, month, self.day);
311        let max_days = month_days(month, leap_year(year));
312        if max_days < result.day {
313            let add = result.day - max_days;
314            result.day = max_days;
315            result = result.add_days(add);
316        }
317        result
318    }
319}
320
321impl std::ops::Sub for Date {
322    type Output = u64;
323
324    fn sub(self, rhs: Self) -> Self::Output {
325        self.to_days() - rhs.to_days()
326    }
327}
328
329impl fmt::Display for Date {
330    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331        write!(f, "{}-{:02}-{:02}", self.year, self.month, self.day)
332    }
333}
334
335impl fmt::Debug for Date {
336    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
337        fmt::Display::fmt(self, f)
338    }
339}
340
341impl FromStr for Date {
342    type Err = DateError;
343
344    fn from_str(date_str: &str) -> Result<Self, Self::Err> {
345        let bytes = date_str.as_bytes();
346        if bytes.len() != 10 || bytes[4] != b'-' || bytes[7] != b'-' {
347            return Err(DateErrorKind::WrongDateStringFormat.into());
348        }
349
350        let year = parse_digits(&bytes[0..4])?;
351        let month = parse_digits(&bytes[5..7])?;
352        let day = parse_digits(&bytes[8..10])?;
353
354        Ok(Date::new(year, month, day))
355    }
356}
357
358#[inline]
359fn parse_digits(bytes: &[u8]) -> Result<u64, DateError> {
360    let mut result = 0u64;
361    for &byte in bytes {
362        if byte < b'0' || byte > b'9' {
363            return Err(DateErrorKind::WrongDateStringFormat.into());
364        }
365        result = result * 10 + (byte - b'0') as u64;
366    }
367    Ok(result)
368}
369
370impl Ord for Date {
371    fn cmp(&self, other: &Self) -> Ordering {
372        if self.year != other.year {
373            return self.year.cmp(&other.year);
374        }
375        if self.month != other.month {
376            return self.month.cmp(&other.month);
377        }
378        self.day.cmp(&other.day)
379    }
380}
381
382impl PartialOrd for Date {
383    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
384        Some(self.cmp(other))
385    }
386}
387
388#[cfg(test)]
389mod tests {
390    use super::*;
391
392    #[test]
393    fn test_date_year_day() {
394        let date = Date::new(2020, 1, 1);
395        assert_eq!(date.year_day(), 1);
396        let date = Date::new(2018, 2, 28);
397        assert_eq!(date.year_day(), 59);
398        let date = Date::new(2016, 12, 31);
399        assert_eq!(date.year_day(), 366);
400    }
401
402    #[test]
403    fn test_date_sub() {
404        let date_1 = Date::new(2020, 1, 1);
405        let date_2 = Date::new(2019, 12, 31);
406        assert_eq!(date_1 - date_2, 1);
407        let date_1 = Date::new(2020, 1, 1);
408        let date_2 = Date::new(2016, 1, 1);
409        assert_eq!(date_1 - date_2, 1461);
410        let date_1 = Date::new(2020, 1, 1);
411        let date_2 = Date::new(2016, 3, 1);
412        assert_eq!(date_1 - date_2, 1401);
413        let date_1 = Date::new(2020, 3, 4);
414        let date_2 = Date::new(2016, 3, 1);
415        assert_eq!(date_1 - date_2, 1464);
416        let date_1 = Date::new(2021, 3, 2);
417        let date_2 = Date::new(2019, 12, 31);
418        assert_eq!(date_1 - date_2, 427);
419        let date_1 = Date::new(2021, 3, 2);
420        let date_2 = Date::new(2020, 12, 31);
421        assert_eq!(date_1 - date_2, 61);
422        let date_1 = Date::new(2021, 3, 2);
423        let date_2 = Date::new(2020, 1, 15);
424        assert_eq!(date_1 - date_2, 412);
425        let date_1 = Date::new(2020, 12, 31);
426        let date_2 = Date::new(2020, 1, 1);
427        assert_eq!(date_1 - date_2, 365);
428    }
429
430    #[test]
431    fn test_date_from_str() -> Result<(), DateError> {
432        assert_eq!(Date::from_str("2020-02-29")?, Date::new(2020, 2, 29));
433        Ok(())
434    }
435
436    #[test]
437    fn test_add_days() {
438        assert_eq!(Date::new(2019, 12, 31).add_days(1), Date::new(2020, 1, 1));
439        assert_eq!(
440            Date::new(2019, 12, 31).add_days(3753),
441            Date::new(2030, 4, 10)
442        );
443        assert_eq!(Date::new(2019, 2, 28).add_days(365), Date::new(2020, 2, 28));
444        assert_eq!(Date::new(2019, 2, 28).add_days(366), Date::new(2020, 2, 29));
445        assert_eq!(Date::new(2019, 3, 1).add_days(366), Date::new(2020, 3, 1));
446        assert_eq!(Date::new(2018, 1, 1).add_days(1198), Date::new(2021, 4, 13));
447    }
448
449    #[test]
450    fn test_ms_dos_date() {
451        assert_eq!(Date::from_ms_dos_date(0x354b), Date::new(2006, 10, 11));
452    }
453
454    #[test]
455    fn test_date_cmp() {
456        assert!(Date::new(2019, 12, 31) < Date::new(2020, 1, 1));
457        assert!(Date::new(2020, 2, 1) > Date::new(2020, 1, 31));
458        assert!(Date::new(2020, 3, 31) > Date::new(2020, 3, 30));
459        assert_eq!(Date::new(2020, 1, 1), Date::new(2020, 1, 1));
460    }
461
462    #[test]
463    fn test_add_months() {
464        assert_eq!(
465            Date::new(2019, 12, 31).add_months(2),
466            Date::new(2020, 2, 29)
467        );
468        assert_eq!(
469            Date::new(2019, 12, 31).add_months(26),
470            Date::new(2022, 2, 28)
471        );
472        assert_eq!(
473            Date::new(2019, 12, 31).add_months(1),
474            Date::new(2020, 1, 31)
475        );
476        assert_eq!(
477            Date::new(2020, 2, 29).add_months(-2),
478            Date::new(2019, 12, 29)
479        );
480    }
481
482    #[test]
483    fn test_is_month_last_day() {
484        assert!(Date::new(2019, 12, 31).is_month_last_day());
485        assert!(!Date::new(2019, 12, 30).is_month_last_day());
486        assert!(Date::new(2019, 2, 28).is_month_last_day());
487        assert!(!Date::new(2020, 2, 28).is_month_last_day());
488        assert!(Date::new(2020, 2, 29).is_month_last_day());
489    }
490
491    #[test]
492    fn test_month_last_day() {
493        assert_eq!(
494            Date::new(2019, 2, 2).month_last_day(),
495            Date::new(2019, 2, 28)
496        );
497        assert_eq!(
498            Date::new(2020, 2, 2).month_last_day(),
499            Date::new(2020, 2, 29)
500        );
501    }
502
503    #[test]
504    fn test_to_seconds_from_unix_epoch() {
505        assert_eq!(Date::new(1970, 1, 1).to_seconds_from_unix_epoch(false), 0);
506        assert_eq!(
507            Date::new(1970, 1, 1).to_seconds_from_unix_epoch(true),
508            SECONDS_IN_DAY
509        );
510    }
511
512    #[test]
513    fn test_to_days() {
514        assert_eq!(Date::new(1, 1, 1).to_days(), 1);
515        assert_eq!(Date::new(1, 12, 31).to_days(), 365);
516        assert_eq!(Date::new(4, 2, 29).to_days(), 1155);
517        assert_eq!(Date::new(5, 1, 1).to_days(), 1462);
518    }
519
520    #[test]
521    fn test_from_days() {
522        assert_eq!(Date::from_days(1), Date::new(1, 1, 1));
523        assert_eq!(Date::from_days(365), Date::new(1, 12, 31));
524        assert_eq!(Date::from_days(1155), Date::new(4, 2, 29));
525        assert_eq!(Date::from_days(1462), Date::new(5, 1, 1));
526    }
527
528    #[test]
529    fn test_is_monday() {
530        assert_eq!(Date::new(2021, 8, 2).is_monday(), true);
531        assert_eq!(Date::new(2021, 8, 3).is_monday(), false);
532    }
533
534    #[test]
535    fn test_is_tuesday() {
536        assert_eq!(Date::new(2021, 8, 3).is_tuesday(), true);
537        assert_eq!(Date::new(2021, 8, 4).is_tuesday(), false);
538    }
539
540    #[test]
541    fn test_is_wednesday() {
542        assert_eq!(Date::new(2021, 8, 4).is_wednesday(), true);
543        assert_eq!(Date::new(2021, 8, 5).is_wednesday(), false);
544    }
545
546    #[test]
547    fn test_is_thursday() {
548        assert_eq!(Date::new(2021, 8, 5).is_thursday(), true);
549        assert_eq!(Date::new(2021, 8, 6).is_thursday(), false);
550    }
551
552    #[test]
553    fn test_is_friday() {
554        assert_eq!(Date::new(2021, 8, 6).is_friday(), true);
555        assert_eq!(Date::new(2021, 8, 7).is_friday(), false);
556    }
557
558    #[test]
559    fn test_is_saturday() {
560        assert_eq!(Date::new(2021, 8, 7).is_saturday(), true);
561        assert_eq!(Date::new(2021, 8, 8).is_saturday(), false);
562    }
563
564    #[test]
565    fn test_is_sunday() {
566        assert_eq!(Date::new(2021, 8, 8).is_sunday(), true);
567        assert_eq!(Date::new(2021, 8, 9).is_sunday(), false);
568    }
569
570    #[test]
571    fn test_add_sub_days() {
572        let a = Date::new(2020, 12, 31);
573        assert_eq!(a.add_days(1).sub_days(1), a);
574    }
575
576    #[test]
577    fn test_sub_months() {
578        assert_eq!(
579            Date::new(2020, 2, 29).sub_months(2),
580            Date::new(2019, 12, 29)
581        );
582        assert_eq!(Date::new(2020, 4, 30).sub_months(2), Date::new(2020, 2, 29));
583        assert_eq!(
584            Date::new(2022, 2, 28).sub_months(26),
585            Date::new(2019, 12, 28)
586        );
587        assert_eq!(
588            Date::new(2020, 1, 31).sub_months(1),
589            Date::new(2019, 12, 31)
590        );
591    }
592
593    #[test]
594    fn test_normalize() {
595        assert_eq!(Date::new(2020, 49, 32).normalize(), Date::new(2024, 2, 1));
596        assert_eq!(Date::new(2020, 49, 60).normalize(), Date::new(2024, 2, 29));
597        assert_eq!(Date::new(2020, 49, 61).normalize(), Date::new(2024, 3, 1));
598    }
599
600    #[test]
601    fn test_date_validation() {
602        assert!(Date::new(2020, 2, 29).valid()); // Leap year
603        assert!(Date::new(2021, 2, 28).valid()); // Non-leap year
604        assert!(Date::new(2020, 12, 31).valid());
605        assert!(Date::new(2020, 1, 1).valid());
606
607        assert!(!Date::new(2021, 2, 29).valid()); // Feb 29 in non-leap year
608        assert!(!Date::new(2020, 2, 30).valid()); // Feb 30
609        assert!(!Date::new(2020, 4, 31).valid()); // Apr 31
610        assert!(!Date::new(2020, 6, 31).valid()); // Jun 31
611        assert!(!Date::new(2020, 9, 31).valid()); // Sep 31
612        assert!(!Date::new(2020, 11, 31).valid()); // Nov 31
613        assert!(!Date::new(2020, 0, 1).valid()); // Month 0
614        assert!(!Date::new(2020, 13, 1).valid()); // Month 13
615        assert!(!Date::new(2020, 1, 0).valid()); // Day 0
616        assert!(!Date::new(2020, 1, 32).valid()); // Day 32
617    }
618
619    #[test]
620    fn test_date_from_str_invalid() {
621        assert!("invalid".parse::<Date>().is_err());
622        assert!("2020".parse::<Date>().is_err());
623        assert!("2020-13".parse::<Date>().is_err());
624        assert!("not-a-date".parse::<Date>().is_err());
625        assert!("2020/01/01".parse::<Date>().is_err());
626    }
627
628    #[test]
629    fn test_edge_cases() {
630        let date = Date::new(1, 1, 1);
631        assert!(date.valid());
632        assert_eq!(date.to_days(), 1);
633
634        let date = Date::new(9999, 12, 31);
635        assert!(date.valid());
636
637        assert!(Date::new(2000, 1, 1).leap_year());
638        assert!(!Date::new(1900, 1, 1).leap_year());
639        assert!(Date::new(2004, 1, 1).leap_year());
640        assert!(!Date::new(2001, 1, 1).leap_year());
641    }
642
643    #[test]
644    fn test_quarter_calculation() {
645        assert_eq!(Date::new(2020, 1, 1).quarter(), 1);
646        assert_eq!(Date::new(2020, 3, 31).quarter(), 1);
647        assert_eq!(Date::new(2020, 4, 1).quarter(), 2);
648        assert_eq!(Date::new(2020, 6, 30).quarter(), 2);
649        assert_eq!(Date::new(2020, 7, 1).quarter(), 3);
650        assert_eq!(Date::new(2020, 9, 30).quarter(), 3);
651        assert_eq!(Date::new(2020, 10, 1).quarter(), 4);
652        assert_eq!(Date::new(2020, 12, 31).quarter(), 4);
653    }
654
655    #[test]
656    fn test_weekend_weekday() {
657        assert!(Date::new(2021, 8, 7).is_weekend());
658        assert!(Date::new(2021, 8, 8).is_weekend());
659        assert!(!Date::new(2021, 8, 9).is_weekend());
660        assert!(Date::new(2021, 8, 9).is_week_day());
661        assert!(Date::new(2021, 8, 10).is_week_day());
662        assert!(!Date::new(2021, 8, 7).is_week_day());
663        assert!(!Date::new(2021, 8, 8).is_week_day());
664    }
665
666    #[test]
667    fn test_days_to_next_year() {
668        assert_eq!(Date::new(2020, 1, 1).days_to_next_year(), 365);
669        assert_eq!(Date::new(2021, 1, 1).days_to_next_year(), 364);
670        assert_eq!(Date::new(2020, 12, 31).days_to_next_year(), 0);
671        assert_eq!(Date::new(2020, 6, 15).days_to_next_year(), 199);
672    }
673
674    #[test]
675    fn test_year_day() {
676        assert_eq!(Date::new(2020, 1, 1).year_day(), 1);
677        assert_eq!(Date::new(2020, 1, 31).year_day(), 31);
678        assert_eq!(Date::new(2020, 2, 1).year_day(), 32);
679        assert_eq!(Date::new(2020, 2, 29).year_day(), 60);
680        assert_eq!(Date::new(2021, 2, 28).year_day(), 59);
681        assert_eq!(Date::new(2020, 12, 31).year_day(), 366);
682        assert_eq!(Date::new(2021, 12, 31).year_day(), 365);
683    }
684}