Oregami
Repositories/oxedyne/fe2o3

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
4use crate::{
5 calendar::DayIncrementor,
6 core::IntervalList,
7 constant::MonthOfYear,
8 time::{
9 CalClockInterval,
10 CalClockZone,
11 },
12};
13
14use oxedyne_fe2o3_core::prelude::*;
15
16#[derive(Clone, Debug)]
17pub struct MonthPeriod {
18 year: i32,
19 month: MonthOfYear,
20 zone: CalClockZone,
21}
22
23impl 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)]
179pub struct YearPeriod {
180 year: i32,
181 #[allow(dead_code)]
182 zone: CalClockZone,
183}
184
185impl 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}