fix: use event TZID for UNTIL date conversion
Add a `tz` field to `rem` to carry the event's DTSTART timezone, and introduce `timedesc_of_utc_or_timestamp_tz` to convert UNTIL timestamps in that timezone instead of the process locale. This makes UNTIL comparisons locale-independent for events with a known TZID.
This commit is contained in:
@@ -202,7 +202,14 @@ let collect_start_end_duration rem ev : (Remind.rem, error) result =
|
|||||||
end)
|
end)
|
||||||
| `Datetime datetime -> begin
|
| `Datetime datetime -> begin
|
||||||
let start_td = Utils.timedesc_of_timestamp datetime in
|
let start_td = Utils.timedesc_of_timestamp datetime in
|
||||||
let rem = { rem with Remind.date = Timedesc.date start_td; Remind.time = Some (Timedesc.time start_td) } in
|
let rem =
|
||||||
|
{
|
||||||
|
rem with
|
||||||
|
Remind.date = Timedesc.date start_td;
|
||||||
|
Remind.time = Some (Timedesc.time start_td);
|
||||||
|
Remind.tz = Some (Timedesc.tz start_td);
|
||||||
|
}
|
||||||
|
in
|
||||||
|
|
||||||
match ev.dtend_or_duration with
|
match ev.dtend_or_duration with
|
||||||
| None -> Ok rem
|
| None -> Ok rem
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type rem = {
|
|||||||
*)
|
*)
|
||||||
exdate : Icalendar.date_or_datetime list; (** List of excluded dates for recurring events *)
|
exdate : Icalendar.date_or_datetime list; (** List of excluded dates for recurring events *)
|
||||||
overrides : rem list; (** Single-event REMs generated from non-cancelled RECURRENCE-ID overrides *)
|
overrides : rem list; (** Single-event REMs generated from non-cancelled RECURRENCE-ID overrides *)
|
||||||
|
tz : Timedesc.Time_zone.t option; (** Timezone of the event's DTSTART, used for UNTIL conversion *)
|
||||||
}
|
}
|
||||||
(** A complete REM command *)
|
(** A complete REM command *)
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ let empty =
|
|||||||
recurring = [];
|
recurring = [];
|
||||||
exdate = [];
|
exdate = [];
|
||||||
overrides = [];
|
overrides = [];
|
||||||
|
tz = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
(* ── buffer primitives ────────────────────────────────────────── *)
|
(* ── buffer primitives ────────────────────────────────────────── *)
|
||||||
@@ -97,7 +99,8 @@ let add_until b rem (w : simple_weekly) =
|
|||||||
match w.count_or_until with
|
match w.count_or_until with
|
||||||
| None -> ()
|
| None -> ()
|
||||||
| Some (`Until d) ->
|
| Some (`Until d) ->
|
||||||
let ts = timedesc_of_utc_or_timestamp_local d in
|
let tz = Option.value ~default:(Timedesc.Time_zone.local_exn ()) rem.tz in
|
||||||
|
let ts = timedesc_of_utc_or_timestamp_tz tz d in
|
||||||
let date = until_date_adjusted ts rem.time in
|
let date = until_date_adjusted ts rem.time in
|
||||||
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 date))
|
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 date))
|
||||||
| Some (`Count count) ->
|
| Some (`Count count) ->
|
||||||
@@ -116,7 +119,8 @@ let add_until_daily b rem (d : simple_daily) =
|
|||||||
match d.count_or_until with
|
match d.count_or_until with
|
||||||
| None -> ()
|
| None -> ()
|
||||||
| Some (`Until dt) ->
|
| Some (`Until dt) ->
|
||||||
let ts = timedesc_of_utc_or_timestamp_local dt in
|
let tz = Option.value ~default:(Timedesc.Time_zone.local_exn ()) rem.tz in
|
||||||
|
let ts = timedesc_of_utc_or_timestamp_tz tz dt in
|
||||||
let date = until_date_adjusted ts rem.time in
|
let date = until_date_adjusted ts rem.time in
|
||||||
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 date))
|
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 date))
|
||||||
| Some (`Count count) ->
|
| Some (`Count count) ->
|
||||||
@@ -128,7 +132,8 @@ let add_until_monthly b rem (m : simple_monthly) =
|
|||||||
match m.count_or_until with
|
match m.count_or_until with
|
||||||
| None -> ()
|
| None -> ()
|
||||||
| Some (`Until dt) ->
|
| Some (`Until dt) ->
|
||||||
let ts = timedesc_of_utc_or_timestamp_local dt in
|
let tz = Option.value ~default:(Timedesc.Time_zone.local_exn ()) rem.tz in
|
||||||
|
let ts = timedesc_of_utc_or_timestamp_tz tz dt in
|
||||||
let date = until_date_adjusted ts rem.time in
|
let date = until_date_adjusted ts rem.time in
|
||||||
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 date))
|
Buffer.add_string b (spf "UNTIL %s " (Timedesc.Date.to_rfc3339 date))
|
||||||
| Some (`Count count) ->
|
| Some (`Count count) ->
|
||||||
|
|||||||
@@ -112,6 +112,14 @@ let timedesc_of_utc_or_timestamp_local (ts : utc_or_timestamp_local) : Timedesc.
|
|||||||
(* this case is not present in my current dataset… *)
|
(* this case is not present in my current dataset… *)
|
||||||
| `Utc t -> t |> Timedesc.Utils.timestamp_of_ptime |> Timedesc.of_timestamp_exn ~tz_of_date_time:local_tz
|
| `Utc t -> t |> Timedesc.Utils.timestamp_of_ptime |> Timedesc.of_timestamp_exn ~tz_of_date_time:local_tz
|
||||||
|
|
||||||
|
(** Convert a UTC-or-local timestamp to a Timedesc.t in the given timezone. Use this (instead of
|
||||||
|
[timedesc_of_utc_or_timestamp_local]) when the event has a known TZID, so that UNTIL comparisons are independent of
|
||||||
|
the process locale. *)
|
||||||
|
let timedesc_of_utc_or_timestamp_tz (tz : Timedesc.Time_zone.t) (ts : utc_or_timestamp_local) : Timedesc.t =
|
||||||
|
match ts with
|
||||||
|
| `Local t -> t |> Timedesc.Utils.timestamp_of_ptime |> Timedesc.of_timestamp_exn ~tz_of_date_time:tz
|
||||||
|
| `Utc t -> t |> Timedesc.Utils.timestamp_of_ptime |> Timedesc.of_timestamp_exn ~tz_of_date_time:tz
|
||||||
|
|
||||||
let get_exdates ev =
|
let get_exdates ev =
|
||||||
let event_props = ev.props in
|
let event_props = ev.props in
|
||||||
let dates_or_datetimes =
|
let dates_or_datetimes =
|
||||||
|
|||||||
Reference in New Issue
Block a user