use super::*; fn chicago_time( year: i32, month: u32, day: u32, hour: u32, minute: u32, second: u32, ) -> DateTime { chrono_tz::America::Chicago .with_ymd_and_hms(year, month, day, hour, minute, second) .unwrap() } fn dt_from_ts(ts: i64) -> DateTime { DateTime::from_timestamp(ts, 0) .unwrap() .with_timezone(&chrono_tz::America::Chicago) } /// Expect that parsing a calendar works #[test] fn calendar_from_str() -> Result<()> { // Blank lines added for clarity let s = r#" BEGIN:VCALENDAR CALSCALE:GREGORIAN X-WR-TIMEZONE:America/Chicago BEGIN:VTIMEZONE TZID:America/Chicago X-LIC-LOCATION:America/Chicago BEGIN:DAYLIGHT TZOFFSETFROM:-0600 TZOFFSETTO:-0500 TZNAME:CDT DTSTART:19700308T020000 RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:-0500 TZOFFSETTO:-0600 TZNAME:CST DTSTART:19701101T020000 RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU END:STANDARD END:VTIMEZONE BEGIN:VEVENT DTSTART;TZID=America/Chicago:20250908T180000 DTEND;TZID=America/Chicago:20250908T200000 UID:Redacted RECURRENCE-ID;TZID=America/Chicago:20250901T180000 CREATED:20241222T171032Z LAST-MODIFIED:20250812T021726Z SEQUENCE:1 STATUS:CONFIRMED SUMMARY:zero roman mummy hatch TRANSP:OPAQUE END:VEVENT END:VCALENDAR "#; let ical = ICal::read_from_str(s)?; let now = dt_from_ts(1755000000); let params = Parameters::new(now)?; let instances = ical.event_instances(¶ms)?; assert_eq!(instances.len(), 1); let event = &instances[0]; let expected_time = DatePerhapsTime { dt: chicago_time(2025, 9, 8, 18, 0, 0), all_day: false, }; assert_eq!(event.dtstart, expected_time); assert_eq!(event.ev.get_summary(), Some("zero roman mummy hatch")); Ok(()) } #[test] fn hand_written() -> Result<()> { let s = r#" BEGIN:VCALENDAR COMMENT:August BEGIN:VEVENT DTSTART;TZID=America/Chicago:20250819T210000 DTEND;TZID=America/Chicago:20250819T223000 SUMMARY:Redacted END:VEVENT BEGIN:VEVENT DTSTART;TZID=America/Chicago:20250823T170000 SUMMARY:Redacted URL:https://www.example.com END:VEVENT END:VCALENDAR "#; let ical = ICal::read_from_str(s)?; let params = Parameters { ignore_before: chicago_time(2025, 1, 1, 0, 0, 0), output_start: chicago_time(2025, 7, 1, 0, 0, 0), output_stop: chicago_time(2025, 10, 1, 0, 0, 0), tz: chrono_tz::America::Chicago, }; let instances = ical.event_instances(¶ms)?; assert_eq!( [instances[0].dtstart, instances[1].dtstart,], [ DatePerhapsTime { dt: chicago_time(2025, 8, 19, 21, 0, 0), all_day: false, }, DatePerhapsTime { dt: chicago_time(2025, 8, 23, 17, 0, 0), all_day: false, }, ] ); assert_eq!(instances.len(), 2); Ok(()) } /// Expect that recurrent exceptions work correctly and don't duplicate events #[test] fn recurrence_exceptions() -> Result<()> { let s = r#" BEGIN:VCALENDAR CALSCALE:GREGORIAN X-WR-TIMEZONE:America/Chicago BEGIN:VTIMEZONE TZID:America/Chicago X-LIC-LOCATION:America/Chicago BEGIN:DAYLIGHT TZOFFSETFROM:-0600 TZOFFSETTO:-0500 TZNAME:CDT DTSTART:19700308T020000 RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:-0500 TZOFFSETTO:-0600 TZNAME:CST DTSTART:19701101T020000 RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU END:STANDARD END:VTIMEZONE BEGIN:VEVENT DTSTART;TZID=America/Chicago:20250708T180000 DTEND;TZID=America/Chicago:20250708T200000 RRULE:FREQ=MONTHLY;BYDAY=2TU UID:jazz repay stout steam CLASS:PUBLIC CREATED:20250703T113806Z LAST-MODIFIED:20250721T232331Z SEQUENCE:1 STATUS:CONFIRMED SUMMARY:coil perm brush zippy TRANSP:OPAQUE END:VEVENT BEGIN:VEVENT DTSTART;TZID=America/Chicago:20250814T180000 DTEND;TZID=America/Chicago:20250814T200000 UID:jazz repay stout steam RECURRENCE-ID;TZID=America/Chicago:20250812T180000 CLASS:PUBLIC CREATED:20250703T113806Z LAST-MODIFIED:20250721T232500Z SEQUENCE:2 STATUS:CONFIRMED SUMMARY:coil perm brush zippy TRANSP:OPAQUE END:VEVENT END:VCALENDAR "#; let ical = ICal::read_from_str(s)?; let params = Parameters { ignore_before: chicago_time(2025, 1, 1, 0, 0, 0), output_start: chicago_time(2025, 7, 1, 0, 0, 0), output_stop: chicago_time(2025, 10, 1, 0, 0, 0), tz: chrono_tz::America::Chicago, }; let instances = ical.event_instances(¶ms)?; assert_eq!( [ instances[0].dtstart, instances[1].dtstart, instances[2].dtstart, ], [ DatePerhapsTime { dt: chicago_time(2025, 7, 8, 18, 0, 0), all_day: false, }, DatePerhapsTime { dt: chicago_time(2025, 9, 9, 18, 0, 0), all_day: false, }, DatePerhapsTime { dt: chicago_time(2025, 8, 14, 18, 0, 0), all_day: false, }, ] ); for instance in &instances { assert_eq!(instance.ev.get_summary(), Some("coil perm brush zippy")); } assert_eq!(instances.len(), 3); Ok(()) }