1 use super::Weekday::{self, Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday};
2 #[allow(unused_imports)]
3 use standback::prelude::*;
4 
is_leap_year(year: i32) -> bool5 fn is_leap_year(year: i32) -> bool {
6     (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
7 }
8 
days_in_year(year: i32) -> u169 pub(crate) fn days_in_year(year: i32) -> u16 {
10     365 + is_leap_year(year) as u16
11 }
12 
13 /// The number of days in a month in both common and leap years.
14 const DAYS_IN_MONTH_COMMON_LEAP: [[u16; 12]; 2] = [
15     [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
16     [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
17 ];
18 
days_in_year_month(year: i32, month: u8) -> u819 pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 {
20     DAYS_IN_MONTH_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1] as u8
21 }
22 
weeks_in_year(year: i32) -> u823 pub(crate) fn weeks_in_year(year: i32) -> u8 {
24     let weekday = Date { year, ordinal: 1 }.weekday();
25 
26     if (weekday == Thursday) || (weekday == Wednesday && is_leap_year(year)) {
27         53
28     } else {
29         52
30     }
31 }
32 
33 pub(crate) struct Date {
34     year: i32,
35     pub(crate) ordinal: u16,
36 }
37 
38 impl Date {
as_yo(&self) -> (i32, u16)39     pub(crate) const fn as_yo(&self) -> (i32, u16) {
40         (self.year, self.ordinal)
41     }
42 
month_day(&self) -> (u8, u8)43     pub(crate) fn month_day(&self) -> (u8, u8) {
44         const CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP: [[u16; 11]; 2] = [
45             [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
46             [31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
47         ];
48 
49         let days = CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP[is_leap_year(self.year) as usize];
50         let ordinal = self.ordinal;
51 
52         if ordinal > days[10] {
53             (12, (ordinal - days[10]) as u8)
54         } else if ordinal > days[9] {
55             (11, (ordinal - days[9]) as u8)
56         } else if ordinal > days[8] {
57             (10, (ordinal - days[8]) as u8)
58         } else if ordinal > days[7] {
59             (9, (ordinal - days[7]) as u8)
60         } else if ordinal > days[6] {
61             (8, (ordinal - days[6]) as u8)
62         } else if ordinal > days[5] {
63             (7, (ordinal - days[5]) as u8)
64         } else if ordinal > days[4] {
65             (6, (ordinal - days[4]) as u8)
66         } else if ordinal > days[3] {
67             (5, (ordinal - days[3]) as u8)
68         } else if ordinal > days[2] {
69             (4, (ordinal - days[2]) as u8)
70         } else if ordinal > days[1] {
71             (3, (ordinal - days[1]) as u8)
72         } else if ordinal > days[0] {
73             (2, (ordinal - days[0]) as u8)
74         } else {
75             (1, ordinal as u8)
76         }
77     }
78 
weekday(&self) -> Weekday79     pub(crate) fn weekday(&self) -> Weekday {
80         let (month, day) = self.month_day();
81 
82         let (month, adjusted_year) = if month < 3 {
83             (month + 12, self.year - 1)
84         } else {
85             (month, self.year)
86         };
87 
88         match (day as i32 + (13 * (month as i32 + 1)) / 5 + adjusted_year + adjusted_year / 4
89             - adjusted_year / 100
90             + adjusted_year / 400)
91             .rem_euclid(7)
92         {
93             0 => Saturday,
94             1 => Sunday,
95             2 => Monday,
96             3 => Tuesday,
97             4 => Wednesday,
98             5 => Thursday,
99             6 => Friday,
100             _ => unreachable!("A value mod 7 is always in the range 0..7"),
101         }
102     }
103 
from_iso_ywd_unchecked(year: i32, week: u8, iso_weekday_number: u8) -> Date104     pub(crate) fn from_iso_ywd_unchecked(year: i32, week: u8, iso_weekday_number: u8) -> Date {
105         let ordinal = week as u16 * 7 + iso_weekday_number as u16
106             - (Self::from_yo_unchecked(year, 4)
107                 .weekday()
108                 .iso_weekday_number() as u16
109                 + 3);
110 
111         if ordinal < 1 {
112             return Self::from_yo_unchecked(year - 1, ordinal + days_in_year(year - 1));
113         }
114 
115         let days_in_cur_year = days_in_year(year);
116         if ordinal > days_in_cur_year {
117             Self::from_yo_unchecked(year + 1, ordinal - days_in_cur_year)
118         } else {
119             Self::from_yo_unchecked(year, ordinal)
120         }
121     }
122 
from_ymd_unchecked(year: i32, month: u8, day: u8) -> Date123     pub(crate) fn from_ymd_unchecked(year: i32, month: u8, day: u8) -> Date {
124         /// Cumulative days through the beginning of a month in both common and
125         /// leap years.
126         const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [
127             [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
128             [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
129         ];
130 
131         let ordinal = DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1];
132 
133         Date {
134             year,
135             ordinal: ordinal + day as u16,
136         }
137     }
138 
from_yo_unchecked(year: i32, ordinal: u16) -> Date139     pub(crate) const fn from_yo_unchecked(year: i32, ordinal: u16) -> Date {
140         Date { year, ordinal }
141     }
142 }
143