oxedyne/fe2o3/fe2o3_datime/src/calendar/period.rs
6.8 KiB, 22 runs
created by r1870400018:6305, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | //! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\ |
| 2 | //! Anthropic Claude |
| 3 | |
| 4 | use crate::{ |
| 5 | calendar::DayIncrementor, |
| 6 | core::IntervalList, |
| 7 | constant::MonthOfYear, |
| 8 | time::{ |
| 9 | CalClockInterval, |
| 10 | CalClockZone, |
| 11 | }, |
| 12 | }; |
| 13 | |
| 14 | use oxedyne_fe2o3_core::prelude::*; |
| 15 | |
| 16 | #[derive(Clone, Debug)] |
| 17 | pub struct MonthPeriod { |
| 18 | year: i32, |
| 19 | month: MonthOfYear, |
| 20 | zone: CalClockZone, |
| 21 | } |
| 22 | |
| 23 | impl MonthPeriod { |
| 24 | pub fn new(year: i32, month: u8, zone: CalClockZone) -> Outcome<Self> { |
| 25 | let month = res!(MonthOfYear::from_number(month)); |
| 26 | Ok(Self { year, month, zone }) |
| 27 | } |
| 28 | |
| 29 | pub fn from_month(year: i32, month: MonthOfYear, zone: CalClockZone) -> Self { |
| 30 | Self { year, month, zone } |
| 31 | } |
| 32 | |
| 33 | pub fn year(&self) -> i32 { |
| 34 | self.year |
| 35 | } |
| 36 | |
| 37 | pub fn month(&self) -> MonthOfYear { |
| 38 | self.month |
| 39 | } |
| 40 | |
| 41 | pub fn get_month_of_year(&self) -> MonthOfYear { |
| 42 | self.month |
| 43 | } |
| 44 | |
| 45 | pub fn inc(&self, months: i32) -> Self { |
| 46 | let total_months = self.month.of() as i32 + months; |
| 47 | let new_month = ((total_months - 1) % 12 + 12) % 12 + 1; |
| 48 | let year_adjust = (total_months - new_month) / 12; |
| 49 | |
| 50 | Self { |
| 51 | year: self.year + year_adjust, |
| 52 | month: MonthOfYear::from_number(new_month as u8).unwrap(), |
| 53 | zone: self.zone.clone(), |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | pub fn find( |
| 58 | &self, |
| 59 | day_incrementor: &DayIncrementor, |
| 60 | _holidays: Option<&IntervalList<CalClockInterval>> |
| 61 | ) -> Outcome<crate::calendar::CalendarDate> { |
| 62 | use crate::calendar::incrementor::DayType; |
| 63 | |
| 64 | // Start with the first day of the month |
| 65 | let mut candidate_date = res!(crate::calendar::CalendarDate::from_ymd( |
| 66 | self.year, |
| 67 | self.month, |
| 68 | 1, |
| 69 | self.zone.clone() |
| 70 | )); |
| 71 | |
| 72 | // Apply the day incrementor logic |
| 73 | match day_incrementor.day_type() { |
| 74 | Some(DayType::DayOfMonthFromStart) => { |
| 75 | // Find the Nth day from the start of the month |
| 76 | let day_number = day_incrementor.value(); |
| 77 | if day_number > 0 { |
| 78 | let max_days = candidate_date.days_in_month().unwrap_or(31); |
| 79 | let target_day = (day_number as u8).min(max_days); |
| 80 | candidate_date = res!(crate::calendar::CalendarDate::from_ymd( |
| 81 | self.year, |
| 82 | self.month, |
| 83 | target_day, |
| 84 | self.zone.clone() |
| 85 | )); |
| 86 | } |
| 87 | }, |
| 88 | Some(DayType::DayOfMonthFromEnd) => { |
| 89 | // Find the Nth day from the end of the month |
| 90 | let day_number = day_incrementor.value(); |
| 91 | if day_number > 0 { |
| 92 | let max_days = res!(candidate_date.days_in_month()); |
| 93 | let target_day = (max_days as i32 - day_number + 1).max(1) as u8; |
| 94 | candidate_date = res!(crate::calendar::CalendarDate::from_ymd( |
| 95 | self.year, |
| 96 | self.month, |
| 97 | target_day, |
| 98 | self.zone.clone() |
| 99 | )); |
| 100 | } |
| 101 | }, |
| 102 | Some(DayType::DayOfWeek) => { |
| 103 | // Find a specific day of the week in the month |
| 104 | if let Some(target_dow) = day_incrementor.day_of_week() { |
| 105 | let mut search_date: crate::calendar::CalendarDate; |
| 106 | let max_days = res!(candidate_date.days_in_month()); |
| 107 | |
| 108 | // Find the first occurrence of the target day of week |
| 109 | for day in 1..=max_days { |
| 110 | search_date = res!(crate::calendar::CalendarDate::from_ymd( |
| 111 | self.year, |
| 112 | self.month, |
| 113 | day, |
| 114 | self.zone.clone() |
| 115 | )); |
| 116 | |
| 117 | if search_date.day_of_week() == target_dow { |
| 118 | candidate_date = search_date; |
| 119 | break; |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | // If we need the Nth occurrence (not the first) |
| 124 | let occurrence = day_incrementor.value(); |
| 125 | if occurrence > 1 { |
| 126 | for _ in 1..occurrence { |
| 127 | // Move to next week |
| 128 | candidate_date = res!(candidate_date.add_days(7)); |
| 129 | |
| 130 | // Check if we're still in the same month |
| 131 | if candidate_date.month_of_year() != self.month { |
| 132 | // Went past the end of the month, return the last valid occurrence |
| 133 | candidate_date = res!(candidate_date.add_days(-7)); |
| 134 | break; |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | }, |
| 140 | Some(DayType::Workday) => { |
| 141 | // Find business days (excluding weekends and optionally holidays) |
| 142 | let day_number = day_incrementor.value(); |
| 143 | if day_number > 0 { |
| 144 | let mut business_day_count = 0; |
| 145 | let max_days = res!(candidate_date.days_in_month()); |
| 146 | |
| 147 | for day in 1..=max_days { |
| 148 | let test_date = res!(crate::calendar::CalendarDate::from_ymd( |
| 149 | self.year, |
| 150 | self.month, |
| 151 | day, |
| 152 | self.zone.clone() |
| 153 | )); |
| 154 | |
| 155 | // Check if it's a business day (includes holiday checking) |
| 156 | if day_incrementor.is_business_day(&test_date) { |
| 157 | // The business day engine already handles holidays, |
| 158 | // so we don't need additional holiday checking here |
| 159 | business_day_count += 1; |
| 160 | if business_day_count == day_number { |
| 161 | candidate_date = test_date; |
| 162 | break; |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | }, |
| 168 | _ => { |
| 169 | // Default: return the first day of the month |
| 170 | // This handles cases where day_type is None or other variants |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | Ok(candidate_date) |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | #[derive(Clone, Debug)] |
| 179 | pub struct YearPeriod { |
| 180 | year: i32, |
| 181 | #[allow(dead_code)] |
| 182 | zone: CalClockZone, |
| 183 | } |
| 184 | |
| 185 | impl YearPeriod { |
| 186 | pub fn new(year: i32, zone: CalClockZone) -> Self { |
| 187 | Self { year, zone } |
| 188 | } |
| 189 | |
| 190 | pub fn year(&self) -> i32 { |
| 191 | self.year |
| 192 | } |
| 193 | } |