ferroui_base/utilities/
gregorian_calendar.rs1use super::{CalendarWeekRule, DateTime, DayOfWeek};
6
7const OUT_OF_RANGE: &str = "The result is out of the supported range for this calendar.";
8
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
11pub struct GregorianCalendar;
12
13impl GregorianCalendar {
14 pub const fn new() -> GregorianCalendar {
16 GregorianCalendar
17 }
18
19 pub const fn min_supported_date_time(&self) -> DateTime {
21 DateTime::MIN_VALUE
22 }
23
24 pub const fn max_supported_date_time(&self) -> DateTime {
26 DateTime::MAX_VALUE
27 }
28
29 pub fn try_add_days(&self, time: DateTime, days: i32) -> Option<DateTime> {
32 time.try_add_days(days as f64)
33 }
34
35 #[track_caller]
37 pub fn add_days(&self, time: DateTime, days: i32) -> DateTime {
38 self.try_add_days(time, days).expect(OUT_OF_RANGE)
39 }
40
41 pub fn try_add_months(&self, time: DateTime, months: i32) -> Option<DateTime> {
45 time.try_add_months(months)
46 }
47
48 #[track_caller]
50 pub fn add_months(&self, time: DateTime, months: i32) -> DateTime {
51 self.try_add_months(time, months).expect(OUT_OF_RANGE)
52 }
53
54 pub fn try_add_years(&self, time: DateTime, years: i32) -> Option<DateTime> {
56 years.checked_mul(12).and_then(|months| time.try_add_months(months))
57 }
58
59 #[track_caller]
61 pub fn add_years(&self, time: DateTime, years: i32) -> DateTime {
62 self.try_add_years(time, years).expect(OUT_OF_RANGE)
63 }
64
65 pub fn get_day_of_week(&self, time: DateTime) -> DayOfWeek {
67 time.day_of_week()
68 }
69
70 #[track_caller]
72 pub fn get_days_in_month(&self, year: i32, month: i32) -> i32 {
73 DateTime::days_in_month(year, month)
74 }
75
76 pub fn get_week_of_year(&self, time: DateTime, rule: CalendarWeekRule, first_day_of_week: DayOfWeek) -> i32 {
83 match rule {
84 CalendarWeekRule::FirstDay => {
85 let day_of_year = time.day_of_year() - 1;
86 let offset = (Self::day_of_week_of_january_first(time) - first_day_of_week as i32 + 14) % 7;
87 (day_of_year + offset) / 7 + 1
88 }
89 CalendarWeekRule::FirstFullWeek => Self::week_of_year_with_minimum_days(time, first_day_of_week as i32, 7),
90 CalendarWeekRule::FirstFourDayWeek => Self::week_of_year_with_minimum_days(time, first_day_of_week as i32, 4),
91 }
92 }
93
94 fn day_of_week_of_january_first(time: DateTime) -> i32 {
97 time.day_of_week() as i32 - (time.day_of_year() - 1) % 7
98 }
99
100 fn week_of_year_with_minimum_days(time: DateTime, first_day_of_week: i32, minimum_days: i32) -> i32 {
101 let mut time = time;
102 loop {
103 let day_of_year = time.day_of_year() - 1;
104 let mut offset = (first_day_of_week - Self::day_of_week_of_january_first(time) + 14) % 7;
106 if offset != 0 && offset >= minimum_days {
107 offset -= 7;
108 }
109 let day = day_of_year - offset;
110 if day >= 0 {
111 return day / 7 + 1;
112 }
113 match time.try_add_days(-(day_of_year as f64 + 1.0)) {
115 Some(previous) => time = previous,
116 None => return Self::week_of_year_before_first_year(first_day_of_week, minimum_days),
117 }
118 }
119 }
120
121 fn week_of_year_before_first_year(first_day_of_week: i32, minimum_days: i32) -> i32 {
124 let january_first_of_previous_year = DayOfWeek::Monday as i32 - 1 - 364 % 7;
126 let partial_week = (first_day_of_week - january_first_of_previous_year + 14) % 7;
127 let mut day = 364 - partial_week;
128 if partial_week >= minimum_days {
129 day += 7;
130 }
131 day / 7 + 1
132 }
133}
134
135pub struct IsoWeek;
137
138impl IsoWeek {
139 pub fn get_week_of_year(date: DateTime) -> i32 {
142 date.to_date().iso_week() as i32
143 }
144}