oxedyne/fe2o3/fe2o3_datime/tests/relative_date_parsing_demo.rs
16.1 KiB, 11 runs
created by r1870400018:8658, 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 relative date parsing functionality. |
| 2 | //! |
| 3 | //! This test demonstrates the advanced natural language relative date parsing |
| 4 | //! capabilities that allow users to express dates in intuitive, human-friendly ways. |
| 5 | //! |
| 6 | //! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\ |
| 7 | //! Anthropic Claude |
| 8 | |
| 9 | use oxedyne_fe2o3_core::prelude::*; |
| 10 | use oxedyne_fe2o3_datime::{ |
| 11 | parser::Parser, |
| 12 | calendar::CalendarDate, |
| 13 | constant::{DayOfWeek, MonthOfYear}, |
| 14 | time::CalClockZone, |
| 15 | }; |
| 16 | |
| 17 | #[test] |
| 18 | fn test_comprehensive_relative_date_parsing() -> Outcome<()> { |
| 19 | println!("=== Comprehensive Relative Date Parsing Demo ==="); |
| 20 | |
| 21 | let zone = CalClockZone::utc(); |
| 22 | // Use a fixed base date for predictable testing: Wednesday, June 12, 2024 |
| 23 | let base_date = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 12, zone.clone())); |
| 24 | |
| 25 | println!("๐ Base date for testing: {} ({})", base_date, base_date.day_of_week()); |
| 26 | |
| 27 | // Test 1: Simple day-of-week relative dates |
| 28 | println!("\n๐๏ธ Test 1: Simple Day-of-Week Relative Dates"); |
| 29 | |
| 30 | let test_cases_dow = [ |
| 31 | ("next Tuesday", "Tuesday, June 18, 2024"), |
| 32 | ("last Friday", "Friday, June 7, 2024"), |
| 33 | ("this Monday", "Monday, June 10, 2024"), |
| 34 | ("next Monday", "Monday, June 17, 2024"), |
| 35 | ("last Monday", "Monday, June 3, 2024"), |
| 36 | ("this Wednesday", "Wednesday, June 12, 2024"), // Today |
| 37 | ("next Wednesday", "Wednesday, June 19, 2024"), |
| 38 | ]; |
| 39 | |
| 40 | for (input, expected_description) in &test_cases_dow { |
| 41 | let result = res!(Parser::parse_relative_date_from(input, &base_date, zone.clone())); |
| 42 | println!(" '{}' โ {} ({})", input, result, result.day_of_week()); |
| 43 | |
| 44 | // Verify the day of week is correct |
| 45 | let expected_day = match input { |
| 46 | s if s.contains("Tuesday") => DayOfWeek::Tuesday, |
| 47 | s if s.contains("Friday") => DayOfWeek::Friday, |
| 48 | s if s.contains("Monday") => DayOfWeek::Monday, |
| 49 | s if s.contains("Wednesday") => DayOfWeek::Wednesday, |
| 50 | _ => panic!("Unexpected day in test case"), |
| 51 | }; |
| 52 | assert_eq!(result.day_of_week(), expected_day, "Day of week mismatch for '{}'", input); |
| 53 | } |
| 54 | |
| 55 | // Test 2: Quantified relative dates (days, weeks, months, years) |
| 56 | println!("\n๐ Test 2: Quantified Relative Dates"); |
| 57 | |
| 58 | let test_cases_quantified = [ |
| 59 | ("in 3 days", 15), // June 15, 2024 |
| 60 | ("3 days ago", 9), // June 9, 2024 |
| 61 | ("in 1 week", 19), // June 19, 2024 |
| 62 | ("2 weeks ago", 29), // May 29, 2024 (previous month) |
| 63 | ("in 2 weeks", 26), // June 26, 2024 |
| 64 | ]; |
| 65 | |
| 66 | for (input, expected_day) in &test_cases_quantified { |
| 67 | let result = res!(Parser::parse_relative_date_from(input, &base_date, zone.clone())); |
| 68 | println!(" '{}' โ {} (day {})", input, result, result.day()); |
| 69 | |
| 70 | // For simple day calculations, verify the day matches |
| 71 | if input.contains("days") && !input.contains("weeks") { |
| 72 | assert_eq!(result.day(), *expected_day as u8, "Day mismatch for '{}'", input); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | // Test 3: Month and year relative dates |
| 77 | println!("\n๐ Test 3: Month and Year Relative Dates"); |
| 78 | |
| 79 | let month_cases = [ |
| 80 | ("next month", 7, 12), // July 12, 2024 |
| 81 | ("last month", 5, 12), // May 12, 2024 |
| 82 | ("in 2 months", 8, 12), // August 12, 2024 |
| 83 | ("3 months ago", 3, 12), // March 12, 2024 |
| 84 | ("next year", 6, 12), // June 12, 2025 |
| 85 | ("last year", 6, 12), // June 12, 2023 |
| 86 | ]; |
| 87 | |
| 88 | for (input, expected_month, expected_day) in &month_cases { |
| 89 | let result = res!(Parser::parse_relative_date_from(input, &base_date, zone.clone())); |
| 90 | println!(" '{}' โ {}", input, result); |
| 91 | |
| 92 | if input.contains("month") { |
| 93 | assert_eq!(result.month(), *expected_month, "Month mismatch for '{}'", input); |
| 94 | assert_eq!(result.day(), *expected_day as u8, "Day mismatch for '{}'", input); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | // Test 4: Period boundaries (beginning/end of periods) |
| 99 | println!("\n๐ Test 4: Period Boundaries"); |
| 100 | |
| 101 | let boundary_cases = [ |
| 102 | ("end of this month", 6, 30), // June 30, 2024 |
| 103 | ("beginning of next month", 7, 1), // July 1, 2024 |
| 104 | ("end of next month", 7, 31), // July 31, 2024 |
| 105 | ("beginning of this year", 1, 1), // January 1, 2024 |
| 106 | ("end of this year", 12, 31), // December 31, 2024 |
| 107 | ]; |
| 108 | |
| 109 | for (input, expected_month, expected_day) in &boundary_cases { |
| 110 | let result = res!(Parser::parse_relative_date_from(input, &base_date, zone.clone())); |
| 111 | println!(" '{}' โ {}", input, result); |
| 112 | |
| 113 | assert_eq!(result.month(), *expected_month, "Month mismatch for '{}'", input); |
| 114 | assert_eq!(result.day(), *expected_day as u8, "Day mismatch for '{}'", input); |
| 115 | } |
| 116 | |
| 117 | // Test 5: Complex expressions |
| 118 | println!("\n๐งฉ Test 5: Complex Expressions"); |
| 119 | |
| 120 | // Test the parser's detailed output |
| 121 | let complex_cases = [ |
| 122 | "the Tuesday after next", |
| 123 | "2 weeks from now", |
| 124 | "end of this month", |
| 125 | "beginning of next quarter", |
| 126 | ]; |
| 127 | |
| 128 | for input in &complex_cases { |
| 129 | if let Ok((expression, calculated_date)) = Parser::parse_relative_date_detailed(input, zone.clone()) { |
| 130 | println!(" '{}' โ", input); |
| 131 | println!(" Expression: {:?}", expression); |
| 132 | println!(" Calculated: {} ({})", calculated_date, calculated_date.day_of_week()); |
| 133 | } else { |
| 134 | // Some expressions might not be fully implemented yet |
| 135 | println!(" '{}' โ [Not yet supported]", input); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | // Test 6: Integration with main parser |
| 140 | println!("\n๐ Test 6: Integration with Main Parser"); |
| 141 | |
| 142 | let parser_integration_cases = [ |
| 143 | "next Tuesday", |
| 144 | "in 2 weeks", |
| 145 | "last Friday", |
| 146 | "3 days ago", |
| 147 | ]; |
| 148 | |
| 149 | for input in &parser_integration_cases { |
| 150 | // Test that relative dates work through the main parse_date function |
| 151 | if let Ok(result) = Parser::parse_date(input, zone.clone()) { |
| 152 | println!(" Parser::parse_date('{}') โ {}", input, result); |
| 153 | } else { |
| 154 | println!(" Parser::parse_date('{}') โ [Failed]", input); |
| 155 | } |
| 156 | |
| 157 | // Test that relative dates work through the main parse_datetime function |
| 158 | if let Ok(result) = Parser::parse_datetime(input, zone.clone()) { |
| 159 | println!(" Parser::parse_datetime('{}') โ {}", input, result); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | // Test 7: Edge cases and error handling |
| 164 | println!("\nโ ๏ธ Test 7: Edge Cases and Error Handling"); |
| 165 | |
| 166 | let edge_cases = [ |
| 167 | ("invalid relative expression", false), |
| 168 | ("next Funday", false), // Invalid day name |
| 169 | ("in 0 days", true), // Edge case but should work |
| 170 | ("", false), // Empty input |
| 171 | ("random text", false), // Non-relative text |
| 172 | ]; |
| 173 | |
| 174 | for (input, should_succeed) in &edge_cases { |
| 175 | match Parser::parse_relative_date_from(input, &base_date, zone.clone()) { |
| 176 | Ok(result) => { |
| 177 | if *should_succeed { |
| 178 | println!(" '{}' โ {} โ", input, result); |
| 179 | } else { |
| 180 | println!(" '{}' โ {} (unexpected success)", input, result); |
| 181 | } |
| 182 | }, |
| 183 | Err(_) => { |
| 184 | if *should_succeed { |
| 185 | println!(" '{}' โ Failed (unexpected)", input); |
| 186 | } else { |
| 187 | println!(" '{}' โ Failed โ", input); |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | // Test 8: Cross-month and cross-year boundaries |
| 194 | println!("\n๐ Test 8: Cross-Month and Cross-Year Boundaries"); |
| 195 | |
| 196 | // Test from end of month |
| 197 | let end_of_month = res!(CalendarDate::from_ymd(2024, MonthOfYear::January, 31, zone.clone())); |
| 198 | let next_month_from_end = res!(Parser::parse_relative_date_from("next month", &end_of_month, zone.clone())); |
| 199 | println!(" From Jan 31: 'next month' โ {}", next_month_from_end); |
| 200 | |
| 201 | // Test from end of year |
| 202 | let end_of_year = res!(CalendarDate::from_ymd(2024, MonthOfYear::December, 31, zone.clone())); |
| 203 | let next_year_from_end = res!(Parser::parse_relative_date_from("next year", &end_of_year, zone.clone())); |
| 204 | println!(" From Dec 31: 'next year' โ {}", next_year_from_end); |
| 205 | |
| 206 | // Test leap year handling |
| 207 | let leap_year_date = res!(CalendarDate::from_ymd(2024, MonthOfYear::February, 28, zone.clone())); |
| 208 | let tomorrow_from_feb28 = res!(Parser::parse_relative_date_from("in 1 day", &leap_year_date, zone.clone())); |
| 209 | println!(" From Feb 28, 2024 (leap year): 'in 1 day' โ {}", tomorrow_from_feb28); |
| 210 | assert_eq!(tomorrow_from_feb28.day(), 29, "Should handle leap year correctly"); |
| 211 | |
| 212 | println!("\nโ Relative Date Parsing Demo completed successfully!"); |
| 213 | println!("๐ฏ Key features demonstrated:"); |
| 214 | println!(" โ Day-of-week relative dates (next Tuesday, last Friday)"); |
| 215 | println!(" โ Quantified relative dates (in 2 weeks, 3 days ago)"); |
| 216 | println!(" โ Month and year calculations"); |
| 217 | println!(" โ Period boundaries (end of month, beginning of year)"); |
| 218 | println!(" โ Integration with main parser functions"); |
| 219 | println!(" โ Edge case handling and error recovery"); |
| 220 | println!(" โ Cross-month and cross-year boundary calculations"); |
| 221 | println!(" โ Leap year awareness"); |
| 222 | |
| 223 | Ok(()) |
| 224 | } |
| 225 | |
| 226 | #[test] |
| 227 | fn test_relative_date_parser_vocabulary() -> Outcome<()> { |
| 228 | println!("=== Relative Date Parser Vocabulary Test ==="); |
| 229 | |
| 230 | let zone = CalClockZone::utc(); |
| 231 | let base_date = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 12, zone.clone())); // Wednesday |
| 232 | |
| 233 | // Test all supported day names |
| 234 | let day_names = [ |
| 235 | ("Monday", DayOfWeek::Monday), |
| 236 | ("Tuesday", DayOfWeek::Tuesday), |
| 237 | ("Wednesday", DayOfWeek::Wednesday), |
| 238 | ("Thursday", DayOfWeek::Thursday), |
| 239 | ("Friday", DayOfWeek::Friday), |
| 240 | ("Saturday", DayOfWeek::Saturday), |
| 241 | ("Sunday", DayOfWeek::Sunday), |
| 242 | ("Mon", DayOfWeek::Monday), |
| 243 | ("Tue", DayOfWeek::Tuesday), |
| 244 | ("Wed", DayOfWeek::Wednesday), |
| 245 | ("Thu", DayOfWeek::Thursday), |
| 246 | ("Fri", DayOfWeek::Friday), |
| 247 | ("Sat", DayOfWeek::Saturday), |
| 248 | ("Sun", DayOfWeek::Sunday), |
| 249 | ]; |
| 250 | |
| 251 | println!("\n๐ Testing day name vocabulary:"); |
| 252 | for (day_name, expected_day) in &day_names { |
| 253 | let input = format!("next {}", day_name); |
| 254 | let result = res!(Parser::parse_relative_date_from(&input, &base_date, zone.clone())); |
| 255 | assert_eq!(result.day_of_week(), *expected_day, "Day name '{}' not recognized correctly", day_name); |
| 256 | println!(" '{}' โ {} โ", input, result.day_of_week()); |
| 257 | } |
| 258 | |
| 259 | // Test reference words |
| 260 | let reference_words = [ |
| 261 | ("next", "Tuesday"), |
| 262 | ("last", "Tuesday"), |
| 263 | ("this", "Tuesday"), |
| 264 | ("upcoming", "Tuesday"), |
| 265 | ("previous", "Tuesday"), |
| 266 | ("coming", "Tuesday"), |
| 267 | ]; |
| 268 | |
| 269 | println!("\n๐ Testing reference word vocabulary:"); |
| 270 | for (reference, day) in &reference_words { |
| 271 | let input = format!("{} {}", reference, day); |
| 272 | if let Ok(result) = Parser::parse_relative_date_from(&input, &base_date, zone.clone()) { |
| 273 | println!(" '{}' โ {} โ", input, result); |
| 274 | } else { |
| 275 | println!(" '{}' โ Failed", input); |
| 276 | } |
| 277 | } |
| 278 | |
| 279 | // Test unit words |
| 280 | let unit_words = [ |
| 281 | ("1 day", 1), |
| 282 | ("2 days", 2), |
| 283 | ("1 week", 7), |
| 284 | ("3 weeks", 21), |
| 285 | ("1 month", 0), // Month calculations are complex |
| 286 | ("2 months", 0), |
| 287 | ("1 year", 0), // Year calculations are complex |
| 288 | ]; |
| 289 | |
| 290 | println!("\n๐ Testing unit word vocabulary:"); |
| 291 | for (input_pattern, expected_day_offset) in &unit_words { |
| 292 | let forward_input = format!("in {}", input_pattern); |
| 293 | let backward_input = format!("{} ago", input_pattern); |
| 294 | |
| 295 | if let Ok(forward_result) = Parser::parse_relative_date_from(&forward_input, &base_date, zone.clone()) { |
| 296 | println!(" '{}' โ {}", forward_input, forward_result); |
| 297 | if *expected_day_offset > 0 { |
| 298 | // Calculate the expected date by adding days to the base date |
| 299 | let expected_forward = res!(base_date.add_days(*expected_day_offset)); |
| 300 | assert_eq!(forward_result, expected_forward, |
| 301 | "Forward calculation incorrect for '{}'", forward_input); |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | if let Ok(backward_result) = Parser::parse_relative_date_from(&backward_input, &base_date, zone.clone()) { |
| 306 | println!(" '{}' โ {}", backward_input, backward_result); |
| 307 | if *expected_day_offset > 0 { |
| 308 | // Calculate the expected date by subtracting days from the base date |
| 309 | let expected_backward = res!(base_date.add_days(-*expected_day_offset)); |
| 310 | assert_eq!(backward_result, expected_backward, |
| 311 | "Backward calculation incorrect for '{}'", backward_input); |
| 312 | } |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | println!("\nโ Vocabulary test completed successfully!"); |
| 317 | |
| 318 | Ok(()) |
| 319 | } |
| 320 | |
| 321 | #[test] |
| 322 | fn test_relative_date_business_scenarios() -> Outcome<()> { |
| 323 | println!("=== Business Scenario Relative Date Tests ==="); |
| 324 | |
| 325 | let zone = CalClockZone::utc(); |
| 326 | |
| 327 | // Scenario 1: Meeting scheduling |
| 328 | println!("\n๐ค Scenario 1: Meeting Scheduling"); |
| 329 | let today = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 12, zone.clone())); // Wednesday |
| 330 | |
| 331 | let meeting_dates = [ |
| 332 | "next Monday", // Team meeting |
| 333 | "next Friday", // Weekly review |
| 334 | "in 2 weeks", // Project deadline |
| 335 | "end of this month", // Monthly report due |
| 336 | ]; |
| 337 | |
| 338 | for date_expr in &meeting_dates { |
| 339 | let meeting_date = res!(Parser::parse_relative_date_from(date_expr, &today, zone.clone())); |
| 340 | println!(" Meeting '{}': {}", date_expr, meeting_date); |
| 341 | } |
| 342 | |
| 343 | // Scenario 2: Project deadlines |
| 344 | println!("\n๐ Scenario 2: Project Deadlines"); |
| 345 | let project_start = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 1, zone.clone())); |
| 346 | |
| 347 | let deadlines = [ |
| 348 | ("Sprint review", "in 2 weeks"), |
| 349 | ("Beta release", "in 6 weeks"), |
| 350 | ("Final release", "in 3 months"), |
| 351 | ("Post-mortem", "end of next month"), |
| 352 | ]; |
| 353 | |
| 354 | for (milestone, date_expr) in &deadlines { |
| 355 | let deadline = res!(Parser::parse_relative_date_from(date_expr, &project_start, zone.clone())); |
| 356 | println!(" {}: '{}' โ {}", milestone, date_expr, deadline); |
| 357 | } |
| 358 | |
| 359 | // Scenario 3: Financial reporting |
| 360 | println!("\n๐ฐ Scenario 3: Financial Reporting"); |
| 361 | let fiscal_start = res!(CalendarDate::from_ymd(2024, MonthOfYear::January, 1, zone.clone())); |
| 362 | |
| 363 | let financial_dates = [ |
| 364 | ("Q1 close", "end of this quarter"), |
| 365 | ("Mid-year review", "in 6 months"), |
| 366 | ("Year-end close", "end of this year"), |
| 367 | ]; |
| 368 | |
| 369 | for (event, date_expr) in &financial_dates { |
| 370 | if let Ok(event_date) = Parser::parse_relative_date_from(date_expr, &fiscal_start, zone.clone()) { |
| 371 | println!(" {}: '{}' โ {}", event, date_expr, event_date); |
| 372 | } else { |
| 373 | println!(" {}: '{}' โ [Not yet supported]", event, date_expr); |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | // Scenario 4: Personal scheduling |
| 378 | println!("\n๐ Scenario 4: Personal Scheduling"); |
| 379 | let weekend = res!(CalendarDate::from_ymd(2024, MonthOfYear::June, 15, zone.clone())); // Saturday |
| 380 | |
| 381 | let personal_events = [ |
| 382 | ("Doctor appointment", "next Tuesday"), |
| 383 | ("Vacation start", "in 3 weeks"), |
| 384 | ("Birthday party", "next Saturday"), |
| 385 | ("Tax deadline", "end of this month"), |
| 386 | ]; |
| 387 | |
| 388 | for (event, date_expr) in &personal_events { |
| 389 | let event_date = res!(Parser::parse_relative_date_from(date_expr, &weekend, zone.clone())); |
| 390 | println!(" {}: '{}' โ {} ({})", event, date_expr, event_date, event_date.day_of_week()); |
| 391 | } |
| 392 | |
| 393 | println!("\nโ Business scenario tests completed successfully!"); |
| 394 | println!("๐ฏ Demonstrated real-world applications:"); |
| 395 | println!(" โ Meeting and event scheduling"); |
| 396 | println!(" โ Project milestone tracking"); |
| 397 | println!(" โ Financial reporting deadlines"); |
| 398 | println!(" โ Personal calendar management"); |
| 399 | |
| 400 | Ok(()) |
| 401 | } |