feat: support EXDATE, DATE+DURATION, and weekly day inference
- Handle DATE+DURATION all-day events by computing end_date from duration - Add PUSH/POP-OMIT-CONTEXT and OMIT/SKIP support for EXDATE in daily/weekly rendering - Adjust UNTIL date when local time of UNTIL timestamp precedes event start time - Default weekly RRULE to event's own weekday when BYDAY list is empty - Remove EXDATE check from complex-recurrence guard (handled via OMIT now) - Add timedesc_wd_to_ical weekday conversion utility - Remove verbose EXDATE debug logging - Fix minor newline in file processing log message
This commit is contained in:
@@ -67,12 +67,24 @@ let add_interval_daily b (d : simple_daily) =
|
||||
let n = Option.value ~default:1 d.interval in
|
||||
Buffer.add_string b (spf "*%d " n)
|
||||
|
||||
(** Adjust an UNTIL date: if the event has a start time and the local time of the UNTIL timestamp is strictly before
|
||||
that start time, the last valid occurrence is on the previous day. *)
|
||||
let until_date_adjusted (until_ts : Timedesc.t) (event_time : Timedesc.Time.t option) : Timedesc.Date.t =
|
||||
let until_date = Timedesc.date until_ts in
|
||||
match event_time with
|
||||
| None -> until_date
|
||||
| Some evt_t ->
|
||||
let until_t = Timedesc.time until_ts in
|
||||
let cmp = Timedesc.Span.compare (Timedesc.Time.to_span until_t) (Timedesc.Time.to_span evt_t) in
|
||||
if cmp < 0 then Timedesc.Date.add ~days:(-1) until_date else until_date
|
||||
|
||||
let add_until b rem (w : simple_weekly) =
|
||||
match w.count_or_until with
|
||||
| None -> ()
|
||||
| Some (`Until d) ->
|
||||
let ts = timedesc_of_utc_or_timestamp_local d in
|
||||
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 (Timedesc.date ts)))
|
||||
let date = until_date_adjusted ts rem.time in
|
||||
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 date))
|
||||
| Some (`Count count) ->
|
||||
let wd = Timedesc.Date.weekday rem.date in
|
||||
let wd_int = Timedesc.Utils.tm_int_of_weekday wd in
|
||||
@@ -90,7 +102,8 @@ let add_until_daily b rem (d : simple_daily) =
|
||||
| None -> ()
|
||||
| Some (`Until dt) ->
|
||||
let ts = timedesc_of_utc_or_timestamp_local dt in
|
||||
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 (Timedesc.date ts)))
|
||||
let date = until_date_adjusted ts rem.time in
|
||||
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 date))
|
||||
| Some (`Count count) ->
|
||||
let iv = Option.value ~default:1 d.interval in
|
||||
let until = Timedesc.Date.add ~days:((count - 1) * iv) rem.date in
|
||||
@@ -110,10 +123,32 @@ let add_through b = function
|
||||
|
||||
let add_msg b summary = Buffer.add_string b (spf " MSG %s\n" summary)
|
||||
|
||||
let date_of_date_or_datetime (d : Icalendar.date_or_datetime) : Timedesc.Date.t =
|
||||
match d with
|
||||
| `Date (year, month, day) -> Timedesc.Date.Ymd.make_exn ~year ~month ~day
|
||||
| `Datetime ts -> Timedesc.date (timedesc_of_timestamp ts)
|
||||
|
||||
let add_omit b (d : Icalendar.date_or_datetime) =
|
||||
let date = date_of_date_or_datetime d in
|
||||
let day = Timedesc.Date.day date in
|
||||
let month = string_of_month (month_of_int (Timedesc.Date.month date)) in
|
||||
let year = Timedesc.Date.year date in
|
||||
Buffer.add_string b (spf "OMIT %d %s %d\n" day month year)
|
||||
|
||||
let add_omit_context b exdates =
|
||||
if exdates <> [] then begin
|
||||
Buffer.add_string b "PUSH-OMIT-CONTEXT\n";
|
||||
List.iter (add_omit b) exdates
|
||||
end
|
||||
|
||||
let close_omit_context b exdates = if exdates <> [] then Buffer.add_string b "POP-OMIT-CONTEXT\n"
|
||||
let add_skip b exdates = if exdates <> [] then Buffer.add_string b "SKIP "
|
||||
|
||||
(* ── rendering ────────────────────────────────────────────────── *)
|
||||
|
||||
let render_daily rem (d : simple_daily) =
|
||||
let b = Buffer.create 256 in
|
||||
add_omit_context b rem.exdate;
|
||||
add_rem b;
|
||||
add_info b rem.original_uuid;
|
||||
add_source b rem.source;
|
||||
@@ -121,13 +156,16 @@ let render_daily rem (d : simple_daily) =
|
||||
Buffer.add_char b ' ';
|
||||
add_interval_daily b d;
|
||||
add_until_daily b rem d;
|
||||
add_skip b rem.exdate;
|
||||
add_at b rem.time;
|
||||
add_duration b rem.duration;
|
||||
add_msg b rem.summary;
|
||||
close_omit_context b rem.exdate;
|
||||
Buffer.contents b
|
||||
|
||||
let render_weekly rem (w : simple_weekly) =
|
||||
let b = Buffer.create 256 in
|
||||
add_omit_context b rem.exdate;
|
||||
List.iter
|
||||
(fun wd ->
|
||||
add_rem b;
|
||||
@@ -138,10 +176,12 @@ let render_weekly rem (w : simple_weekly) =
|
||||
Buffer.add_char b ' ';
|
||||
add_interval b w;
|
||||
add_until b rem w;
|
||||
add_skip b rem.exdate;
|
||||
add_at b rem.time;
|
||||
add_duration b rem.duration;
|
||||
add_msg b rem.summary)
|
||||
w.byday;
|
||||
close_omit_context b rem.exdate;
|
||||
Buffer.contents b
|
||||
|
||||
let render_single rem =
|
||||
|
||||
Reference in New Issue
Block a user