summaryrefslogtreecommitdiff
path: root/src/time_crate/date.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/time_crate/date.rs')
-rw-r--r--src/time_crate/date.rs147
1 files changed, 147 insertions, 0 deletions
diff --git a/src/time_crate/date.rs b/src/time_crate/date.rs
new file mode 100644
index 0000000..981630e
--- /dev/null
+++ b/src/time_crate/date.rs
@@ -0,0 +1,147 @@
+use super::Weekday::{self, Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday};
+#[allow(unused_imports)]
+use standback::prelude::*;
+
+fn is_leap_year(year: i32) -> bool {
+ (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
+}
+
+pub(crate) fn days_in_year(year: i32) -> u16 {
+ 365 + is_leap_year(year) as u16
+}
+
+/// The number of days in a month in both common and leap years.
+const DAYS_IN_MONTH_COMMON_LEAP: [[u16; 12]; 2] = [
+ [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+ [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+];
+
+#[allow(clippy::cast_possible_truncation)]
+pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 {
+ DAYS_IN_MONTH_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1] as u8
+}
+
+pub(crate) fn weeks_in_year(year: i32) -> u8 {
+ let weekday = Date { year, ordinal: 1 }.weekday();
+
+ if (weekday == Thursday) || (weekday == Wednesday && is_leap_year(year)) {
+ 53
+ } else {
+ 52
+ }
+}
+
+pub(crate) struct Date {
+ year: i32,
+ pub(crate) ordinal: u16,
+}
+
+impl Date {
+ pub(crate) const fn as_yo(&self) -> (i32, u16) {
+ (self.year, self.ordinal)
+ }
+
+ pub(crate) fn month_day(&self) -> (u8, u8) {
+ const CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP: [[u16; 11]; 2] = [
+ [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
+ [31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
+ ];
+
+ let days = CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP[is_leap_year(self.year) as usize];
+ let ordinal = self.ordinal;
+
+ #[allow(clippy::cast_possible_truncation)]
+ {
+ if ordinal > days[10] {
+ (12, (ordinal - days[10]) as u8)
+ } else if ordinal > days[9] {
+ (11, (ordinal - days[9]) as u8)
+ } else if ordinal > days[8] {
+ (10, (ordinal - days[8]) as u8)
+ } else if ordinal > days[7] {
+ (9, (ordinal - days[7]) as u8)
+ } else if ordinal > days[6] {
+ (8, (ordinal - days[6]) as u8)
+ } else if ordinal > days[5] {
+ (7, (ordinal - days[5]) as u8)
+ } else if ordinal > days[4] {
+ (6, (ordinal - days[4]) as u8)
+ } else if ordinal > days[3] {
+ (5, (ordinal - days[3]) as u8)
+ } else if ordinal > days[2] {
+ (4, (ordinal - days[2]) as u8)
+ } else if ordinal > days[1] {
+ (3, (ordinal - days[1]) as u8)
+ } else if ordinal > days[0] {
+ (2, (ordinal - days[0]) as u8)
+ } else {
+ (1, ordinal as u8)
+ }
+ }
+ }
+
+ #[allow(unstable_name_collisions)]
+ pub(crate) fn weekday(&self) -> Weekday {
+ let (month, day) = self.month_day();
+
+ let (month, adjusted_year) = if month < 3 {
+ (month + 12, self.year - 1)
+ } else {
+ (month, self.year)
+ };
+
+ match (day as i32 + (13 * (month as i32 + 1)) / 5 + adjusted_year + adjusted_year / 4
+ - adjusted_year / 100
+ + adjusted_year / 400)
+ .rem_euclid(7)
+ {
+ 0 => Saturday,
+ 1 => Sunday,
+ 2 => Monday,
+ 3 => Tuesday,
+ 4 => Wednesday,
+ 5 => Thursday,
+ 6 => Friday,
+ _ => unreachable!("A value mod 7 is always in the range 0..7"),
+ }
+ }
+
+ pub(crate) fn from_iso_ywd_unchecked(year: i32, week: u8, iso_weekday_number: u8) -> Date {
+ let ordinal = week as u16 * 7 + iso_weekday_number as u16
+ - (Self::from_yo_unchecked(year, 4)
+ .weekday()
+ .iso_weekday_number() as u16
+ + 3);
+
+ if ordinal < 1 {
+ return Self::from_yo_unchecked(year - 1, ordinal + days_in_year(year - 1));
+ }
+
+ let days_in_cur_year = days_in_year(year);
+ if ordinal > days_in_cur_year {
+ Self::from_yo_unchecked(year + 1, ordinal - days_in_cur_year)
+ } else {
+ Self::from_yo_unchecked(year, ordinal)
+ }
+ }
+
+ pub(crate) fn from_ymd_unchecked(year: i32, month: u8, day: u8) -> Date {
+ /// Cumulative days through the beginning of a month in both common and
+ /// leap years.
+ const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [
+ [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
+ [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335],
+ ];
+
+ let ordinal = DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1];
+
+ Date {
+ year,
+ ordinal: ordinal + day as u16,
+ }
+ }
+
+ pub(crate) const fn from_yo_unchecked(year: i32, ordinal: u16) -> Date {
+ Date { year, ordinal }
+ }
+}