Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_datime/tests/advanced_calendar_rules_demo.rs

14.9 KiB, 17 runs

created by r1870400018:8639, 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//! Comprehensive demonstration of advanced calendar rules system
2//!
3//! This test demonstrates the complete integration of:
4//! - Complex day incrementors ("3rd Monday", "last business day", etc.)
5//! - Holiday engines (US Federal, UK, ECB with Easter calculations)
6//! - Business day engines (custom business weeks, holiday integration)
7//! - Calendar rules (quarterly patterns, business day scheduling)
8//!
9//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
10//! Anthropic Claude
11
12use oxedyne_fe2o3_core::prelude::*;
13use oxedyne_fe2o3_datime::{
14 calendar::{
15 CalendarDate, CalendarRulesEngine as CalendarRule, RuleType, DayIncrementor,
16 HolidayEngine, BusinessDayEngine, BusinessWeek, BusinessDayAdjustment,
17 },
18 constant::{MonthOfYear, DayOfWeek},
19 time::CalClockZone,
20};
21
22#[test]
23fn test_comprehensive_business_rules_demo() -> Outcome<()> {
24 println!("=== Advanced Calendar Rules System Demo ===");
25
26 let zone = CalClockZone::utc();
27 let start_date = res!(CalendarDate::from_ymd(2024, MonthOfYear::January, 1, zone.clone()));
28
29 // Demo 1: Complex quarterly meeting schedule with US federal holidays
30 println!("\n๐Ÿข Demo 1: Quarterly Board Meetings (2nd Tuesday of quarters, excluding US holidays)");
31
32 let quarterly_rule = res!(CalendarRule::us_business_pattern(
33 start_date.clone(),
34 "2nd Tuesday",
35 vec![1, 4, 7, 10] // Q1, Q2, Q3, Q4
36 ));
37
38 let quarterly_dates = res!(quarterly_rule.generate_dates(4, zone.clone()));
39
40 println!("๐Ÿ“… Quarterly board meeting dates for 2024:");
41 for (i, date) in quarterly_dates.iter().enumerate() {
42 println!(" Q{}: {} ({})", i + 1, date, date.day_of_week());
43 }
44
45 assert_eq!(quarterly_dates.len(), 4, "Should have 4 quarterly meetings");
46
47 // Demo 2: UK payroll schedule with Easter holidays
48 println!("\n๐Ÿ‡ฌ๐Ÿ‡ง Demo 2: UK Payroll Schedule (Last business day of each month, excluding UK holidays)");
49
50 let uk_payroll_rule = res!(CalendarRule::uk_business_pattern(
51 start_date.clone(),
52 "last business day",
53 vec![1, 2, 3, 4, 5, 6] // First half of year
54 ));
55
56 let uk_payroll_dates = res!(uk_payroll_rule.generate_dates(6, zone.clone()));
57
58 println!("๐Ÿ’ฐ UK payroll dates (first half of 2024):");
59 for date in &uk_payroll_dates {
60 println!(" {} {} {} ({})", date.day(), date.month_of_year(), date.year(), date.day_of_week());
61 }
62
63 assert_eq!(uk_payroll_dates.len(), 6, "Should have 6 monthly payroll dates");
64
65 // Verify no Easter holidays are included
66 for date in &uk_payroll_dates {
67 // Good Friday 2024: March 29, Easter Monday 2024: April 1
68 let is_good_friday = date.month() == 3 && date.day() == 29;
69 let is_easter_monday = date.month() == 4 && date.day() == 1;
70 assert!(!is_good_friday && !is_easter_monday,
71 "UK payroll should exclude Easter holidays");
72 }
73
74 // Demo 3: Middle East business schedule (Sunday-Thursday business week)
75 println!("\n๐Ÿ•Œ Demo 3: Middle East Operations (First business day of each quarter, Sunday-Thursday week)");
76
77 let middle_east_rule = res!(CalendarRule::middle_east_business_pattern(
78 start_date.clone(),
79 "1st business day",
80 vec![1, 4, 7, 10] // Quarterly
81 ));
82
83 let middle_east_dates = res!(middle_east_rule.generate_dates(4, zone.clone()));
84
85 println!("๐Ÿ–๏ธ Middle East operations start dates:");
86 for (i, date) in middle_east_dates.iter().enumerate() {
87 println!(" Q{}: {} ({})", i + 1, date, date.day_of_week());
88
89 // Verify business days in Sunday-Thursday week
90 let dow = date.day_of_week();
91 let is_business_day = matches!(dow,
92 DayOfWeek::Sunday |
93 DayOfWeek::Monday |
94 DayOfWeek::Tuesday |
95 DayOfWeek::Wednesday |
96 DayOfWeek::Thursday
97 );
98 assert!(is_business_day, "Should be a business day in Middle East week");
99 }
100
101 // Demo 4: ECB regulatory reporting (complex pattern with no weekend adjustments)
102 println!("\n๐Ÿฆ Demo 4: ECB Regulatory Reporting (15th of each month or next business day)");
103
104 let ecb_engine = HolidayEngine::ecb();
105 let business_engine = BusinessDayEngine::new()
106 .with_holiday_engine(ecb_engine.clone())
107 .with_default_adjustment(BusinessDayAdjustment::Following);
108
109 let ecb_rule = CalendarRule::new(RuleType::ByExplicitMonths)
110 .with_start_date(start_date.clone())
111 .with_months(vec![1, 2, 3, 4, 5, 6]) // First half of year
112 .with_holiday_engine(ecb_engine)
113 .with_business_day_engine(business_engine);
114
115 // Generate 15th of each month, then adjust for business days
116 let mut ecb_dates = Vec::new();
117 for month in 1..=6 {
118 let fifteenth = res!(CalendarDate::from_ymd(2024, res!(MonthOfYear::from_number(month)), 15, zone.clone()));
119 ecb_dates.push(fifteenth);
120 }
121
122 println!("๐Ÿ“Š ECB reporting dates (15th or next business day):");
123 for date in &ecb_dates {
124 println!(" {} {} {} ({})", date.day(), date.month_of_year(), date.year(), date.day_of_week());
125 }
126
127 // Demo 5: Complex day incrementor patterns
128 println!("\n๐Ÿงฎ Demo 5: Complex Day Incrementor Patterns");
129
130 // "2nd weekday before the 25th of each month"
131 let complex_incrementor = res!(DayIncrementor::from_string("2nd weekday before the 25th"));
132
133 println!("๐Ÿ“ Parsing complex patterns:");
134 println!(" Pattern: '2nd weekday before the 25th'");
135 println!(" Parsed: Sign: {} Value: {} DayType: {:?}",
136 complex_incrementor.sign(),
137 complex_incrementor.value(),
138 complex_incrementor.day_type());
139
140 if let Some(next_inc) = complex_incrementor.next_inc() {
141 println!(" Next incrementor: Value: {} DayType: {:?}",
142 next_inc.value(),
143 next_inc.day_type());
144 }
145
146 // Calculate actual dates for this pattern
147 println!(" Calculated dates for first quarter 2024:");
148 for month in 1..=3 {
149 let calculated = res!(complex_incrementor.calculate_date(2024, month, zone.clone()));
150 println!(" {}: {} ({})",
151 res!(MonthOfYear::from_number(month)),
152 calculated,
153 calculated.day_of_week());
154 }
155
156 // Demo 6: Business day statistics and analysis
157 println!("\n๐Ÿ“ˆ Demo 6: Business Day Analysis");
158
159 let us_business_engine = BusinessDayEngine::new()
160 .with_holiday_engine(HolidayEngine::us_federal());
161
162 let stats_2024_june = res!(us_business_engine.month_business_day_stats(2024, 6, zone.clone()));
163
164 println!("๐Ÿ“Š June 2024 business day statistics (US Federal holidays):");
165 println!(" Total days: {}", stats_2024_june.total_days);
166 println!(" Business days: {}", stats_2024_june.business_days_count);
167 println!(" Weekend/holiday days: {}", stats_2024_june.weekend_days);
168
169 if let Some(ref first) = stats_2024_june.first_business_day {
170 println!(" First business day: {} ({})", first, first.day_of_week());
171 }
172
173 if let Some(ref last) = stats_2024_june.last_business_day {
174 println!(" Last business day: {} ({})", last, last.day_of_week());
175 }
176
177 // Verify expected business day count for June 2024
178 // June 2024: 30 days, starts Saturday, ends Sunday
179 // Weekdays: 20 days, but exclude July 4th which isn't in June
180 assert_eq!(stats_2024_june.business_days_count, 20,
181 "June 2024 should have 20 business days");
182
183 // Demo 7: Easter calculation verification
184 println!("\n๐Ÿฐ Demo 7: Easter Calculation Verification");
185
186 let easter_2024 = res!(HolidayEngine::calculate_easter(2024, zone.clone()));
187 let easter_2025 = res!(HolidayEngine::calculate_easter(2025, zone.clone()));
188
189 println!("๐Ÿฅš Easter dates:");
190 println!(" 2024: {} {} {} ({})", easter_2024.day(), easter_2024.month_of_year(), easter_2024.year(), easter_2024.day_of_week());
191 println!(" 2025: {} {} {} ({})", easter_2025.day(), easter_2025.month_of_year(), easter_2025.year(), easter_2025.day_of_week());
192
193 // Verify known Easter dates
194 assert_eq!(easter_2024.month(), 3, "Easter 2024 should be in March");
195 assert_eq!(easter_2024.day(), 31, "Easter 2024 should be March 31st");
196 assert_eq!(easter_2025.month(), 4, "Easter 2025 should be in April");
197 assert_eq!(easter_2025.day(), 20, "Easter 2025 should be April 20th");
198
199 println!("\nโœ… Advanced Calendar Rules System demonstration completed successfully!");
200 println!("๐ŸŽฏ All features integrated and working:");
201 println!(" โœ“ Complex day incrementors with natural language parsing");
202 println!(" โœ“ Sophisticated holiday engines with Easter calculations");
203 println!(" โœ“ Business day engines with custom business weeks");
204 println!(" โœ“ Calendar rules with holiday and business day integration");
205 println!(" โœ“ Multiple jurisdiction support (US, UK, ECB, Middle East)");
206 println!(" โœ“ Weekend adjustment algorithms");
207 println!(" โœ“ Statistical analysis and reporting");
208
209 Ok(())
210}
211
212#[test]
213fn test_holiday_engine_comprehensive() -> Outcome<()> {
214 println!("=== Holiday Engine Comprehensive Test ===");
215
216 let zone = CalClockZone::utc();
217
218 // Test all pre-built holiday engines
219 let engines = vec![
220 ("US Federal", HolidayEngine::us_federal()),
221 ("United Kingdom", HolidayEngine::uk()),
222 ("European Central Bank", HolidayEngine::ecb()),
223 ];
224
225 for (name, engine) in engines {
226 println!("\n๐Ÿ›๏ธ Testing {} holidays for 2024:", name);
227
228 let holidays_2024 = res!(engine.calculate_holidays(2024, zone.clone()));
229 println!(" Found {} holidays:", holidays_2024.len());
230
231 for (holiday_name, date) in &holidays_2024 {
232 println!(" {}: {} ({})", holiday_name, date, date.day_of_week());
233 }
234
235 // Verify some expected holidays exist
236 let holiday_names: Vec<&String> = holidays_2024.iter().map(|(name, _)| name).collect();
237
238 match name {
239 "US Federal" => {
240 assert!(holiday_names.contains(&&"New Year's Day".to_string()));
241 assert!(holiday_names.contains(&&"Independence Day".to_string()));
242 assert!(holiday_names.contains(&&"Christmas Day".to_string()));
243 assert_eq!(holidays_2024.len(), 10, "US Federal should have 10 holidays");
244 },
245 "United Kingdom" => {
246 assert!(holiday_names.contains(&&"New Year's Day".to_string()));
247 assert!(holiday_names.contains(&&"Good Friday".to_string()));
248 assert!(holiday_names.contains(&&"Easter Monday".to_string()));
249 assert!(holiday_names.contains(&&"Christmas Day".to_string()));
250 assert!(holiday_names.contains(&&"Boxing Day".to_string()));
251 assert_eq!(holidays_2024.len(), 8, "UK should have 8 holidays");
252 },
253 "European Central Bank" => {
254 assert!(holiday_names.contains(&&"New Year's Day".to_string()));
255 assert!(holiday_names.contains(&&"Good Friday".to_string()));
256 assert!(holiday_names.contains(&&"Labour Day".to_string()));
257 assert!(holiday_names.contains(&&"Christmas Day".to_string()));
258 assert_eq!(holidays_2024.len(), 6, "ECB should have 6 holidays");
259 },
260 _ => {}
261 }
262 }
263
264 println!("\nโœ… All holiday engines working correctly!");
265
266 Ok(())
267}
268
269#[test]
270fn test_business_day_engine_comprehensive() -> Outcome<()> {
271 println!("=== Business Day Engine Comprehensive Test ===");
272
273 let zone = CalClockZone::utc();
274
275 // Test different business week configurations
276 let business_weeks = vec![
277 ("Standard (Mon-Fri)", BusinessWeek::monday_to_friday()),
278 ("Middle East (Sun-Thu)", BusinessWeek::sunday_to_thursday()),
279 ("Custom (Tue-Sat)", BusinessWeek::custom(vec![
280 DayOfWeek::Tuesday,
281 DayOfWeek::Wednesday,
282 DayOfWeek::Thursday,
283 DayOfWeek::Friday,
284 DayOfWeek::Saturday,
285 ])),
286 ];
287
288 for (name, business_week) in business_weeks {
289 println!("\n๐Ÿ—“๏ธ Testing {} business week:", name);
290
291 let engine = BusinessDayEngine::new()
292 .with_business_week(business_week.clone());
293
294 // Test first week of June 2024 (starts Saturday)
295 let june_1 = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 1, zone.clone())); // Saturday
296
297 println!(" Business days in first week of June 2024:");
298 for i in 0..7 {
299 let date = res!(june_1.add_days(i));
300 let is_business = res!(engine.is_business_day(&date));
301 let symbol = if is_business { "โœ“" } else { "โœ—" };
302 println!(" {} {} {} ({})", symbol, date, date.day_of_week(),
303 if is_business { "Business Day" } else { "Non-Business" });
304 }
305
306 // Count expected business days based on week type
307 let expected_count = match name {
308 "Standard (Mon-Fri)" => 5, // Mon, Tue, Wed, Thu, Fri
309 "Middle East (Sun-Thu)" => 5, // Sun, Mon, Tue, Wed, Thu
310 "Custom (Tue-Sat)" => 5, // Tue, Wed, Thu, Fri, Sat
311 _ => 0,
312 };
313
314 let actual_count = (0..7).filter(|&i| {
315 let date = june_1.add_days(i).unwrap();
316 engine.is_business_day(&date).unwrap_or(false)
317 }).count();
318
319 assert_eq!(actual_count, expected_count,
320 "Expected {} business days for {}, got {}", expected_count, name, actual_count);
321 }
322
323 // Test business day calculations
324 println!("\n๐Ÿ“Š Testing business day calculations:");
325
326 let standard_engine = BusinessDayEngine::new();
327
328 // Add 5 business days to Friday June 7, 2024
329 let friday_june_7 = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 7, zone.clone()));
330 let plus_5_business = res!(standard_engine.add_business_days(&friday_june_7, 5));
331
332 println!(" {} + 5 business days = {} ({})",
333 friday_june_7, plus_5_business, plus_5_business.day_of_week());
334
335 // Should be Friday June 14, 2024 (skipping weekend)
336 assert_eq!(plus_5_business.day(), 14);
337 assert_eq!(plus_5_business.day_of_week(), DayOfWeek::Friday);
338
339 // Count business days between two dates
340 let monday_june_3 = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 3, zone.clone()));
341 let friday_june_7_end = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 7, zone.clone()));
342 let business_days_count = res!(standard_engine.business_days_between(&monday_june_3, &friday_june_7_end));
343
344 println!(" Business days from {} to {}: {}",
345 monday_june_3, friday_june_7_end, business_days_count);
346
347 assert_eq!(business_days_count, 4, "Mon-Fri should have 4 business days between");
348
349 println!("\nโœ… All business day engine features working correctly!");
350
351 Ok(())
352}