feat: add --timezone CLI option for configurable output timezone
This commit is contained in:
@@ -5,12 +5,16 @@ let files =
|
|||||||
let doc = "Files to process" in
|
let doc = "Files to process" in
|
||||||
Arg.(non_empty & pos_all string [] & info [] ~docv:"FILE" ~doc)
|
Arg.(non_empty & pos_all string [] & info [] ~docv:"FILE" ~doc)
|
||||||
|
|
||||||
|
let timezone =
|
||||||
|
let doc = "Target timezone for output (e.g. Europe/Rome). Defaults to local timezone." in
|
||||||
|
Arg.(value & opt (some string) None & info [ "timezone"; "z" ] ~docv:"TZ" ~doc)
|
||||||
|
|
||||||
let main_command f =
|
let main_command f =
|
||||||
let doc = "Convert iCalendar files to remind format" in
|
let doc = "Convert iCalendar files to remind format" in
|
||||||
let man = [] in
|
let man = [] in
|
||||||
Cmd.make (Cmd.info "ical2rem" ~version:"%%VERSION%%" ~doc ~man)
|
Cmd.make (Cmd.info "ical2rem" ~version:"%%VERSION%%" ~doc ~man)
|
||||||
@@
|
@@
|
||||||
let+ files = files in
|
let+ files = files and+ tz = timezone in
|
||||||
f files
|
f tz files
|
||||||
|
|
||||||
let main f = Cmd.eval @@ main_command f
|
let main f = Cmd.eval @@ main_command f
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ let read_file filename =
|
|||||||
close_in ic;
|
close_in ic;
|
||||||
Bytes.unsafe_to_string s
|
Bytes.unsafe_to_string s
|
||||||
|
|
||||||
let ical2rem ical_files =
|
let ical2rem tz_opt ical_files =
|
||||||
|
Utils.init_target_tz tz_opt;
|
||||||
let good_rems =
|
let good_rems =
|
||||||
ListLabels.fold_left ~init:[] ical_files ~f:(fun good_rems_acc filename ->
|
ListLabels.fold_left ~init:[] ical_files ~f:(fun good_rems_acc filename ->
|
||||||
try
|
try
|
||||||
|
|||||||
27
bin/utils.ml
27
bin/utils.ml
@@ -1,5 +1,14 @@
|
|||||||
open Icalendar
|
open Icalendar
|
||||||
|
|
||||||
|
(** Target timezone for all timestamp conversions. Defaults to local timezone; overridden by --timezone CLI option
|
||||||
|
before any processing begins. *)
|
||||||
|
let target_tz : Timedesc.Time_zone.t ref = ref Timedesc.Time_zone.utc
|
||||||
|
|
||||||
|
let init_target_tz (tz_opt : string option) : unit =
|
||||||
|
match tz_opt with
|
||||||
|
| None -> target_tz := Timedesc.Time_zone.local_exn ()
|
||||||
|
| Some name -> target_tz := Timedesc.Time_zone.make_exn name
|
||||||
|
|
||||||
type months = Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec
|
type months = Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec
|
||||||
|
|
||||||
let timedesc_wd_to_ical (wd : Timedesc.weekday) : Icalendar.weekday =
|
let timedesc_wd_to_ical (wd : Timedesc.weekday) : Icalendar.weekday =
|
||||||
@@ -89,28 +98,28 @@ let string_of_span (sp : Timedesc.Span.t) : string =
|
|||||||
spf "%02d:%02d" hours minutes
|
spf "%02d:%02d" hours minutes
|
||||||
|
|
||||||
let timedesc_of_timestamp (ts : timestamp) : Timedesc.t =
|
let timedesc_of_timestamp (ts : timestamp) : Timedesc.t =
|
||||||
let local_tz = Timedesc.Time_zone.local_exn () in
|
|
||||||
match ts with
|
match ts with
|
||||||
| `Local t -> t |> Timedesc.Utils.timestamp_of_ptime |> Timedesc.of_timestamp_exn ~tz_of_date_time:local_tz
|
| `Local t -> t |> Timedesc.Utils.timestamp_of_ptime |> Timedesc.of_timestamp_exn ~tz_of_date_time:!target_tz
|
||||||
(* 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:!target_tz
|
||||||
| `With_tzid (ts, (_b, tz_name)) ->
|
| `With_tzid (ts, (_b, tz_name)) ->
|
||||||
(* Qui il timestamp è SCRITTO come se fosse UTC (+00:00) ma in realtà va interpretato con
|
(* The timestamp is stored as if it were UTC but must be interpreted in tz_name.
|
||||||
il fuso orario indicato da tz_name. *)
|
We reconstruct the wall-clock time in tz_name, then convert to target_tz. *)
|
||||||
let tz = Timedesc.Time_zone.make_exn tz_name in
|
let tz = Timedesc.Time_zone.make_exn tz_name in
|
||||||
let wrong_ts = Timedesc.Utils.timestamp_of_ptime ts in
|
let wrong_ts = Timedesc.Utils.timestamp_of_ptime ts in
|
||||||
let date = Timedesc.date (Timedesc.of_timestamp_exn ~tz_of_date_time:Timedesc.Time_zone.utc wrong_ts) in
|
let date = Timedesc.date (Timedesc.of_timestamp_exn ~tz_of_date_time:Timedesc.Time_zone.utc wrong_ts) in
|
||||||
let year, month, day = (Timedesc.Date.year date, Timedesc.Date.month date, Timedesc.Date.day date) in
|
let year, month, day = (Timedesc.Date.year date, Timedesc.Date.month date, Timedesc.Date.day date) in
|
||||||
let time = Timedesc.time_view (Timedesc.of_timestamp_exn ~tz_of_date_time:Timedesc.Time_zone.utc wrong_ts) in
|
let time = Timedesc.time_view (Timedesc.of_timestamp_exn ~tz_of_date_time:Timedesc.Time_zone.utc wrong_ts) in
|
||||||
let hour, minute, second = (time.Timedesc.Time.hour, time.Timedesc.Time.minute, time.Timedesc.Time.second) in
|
let hour, minute, second = (time.Timedesc.Time.hour, time.Timedesc.Time.minute, time.Timedesc.Time.second) in
|
||||||
Timedesc.make_exn ~year ~month ~day ~hour ~minute ~second ~tz ()
|
let t_in_named_tz = Timedesc.make_exn ~year ~month ~day ~hour ~minute ~second ~tz () in
|
||||||
|
(* Convert from tz_name to target_tz *)
|
||||||
|
Timedesc.of_timestamp_exn ~tz_of_date_time:!target_tz (Timedesc.to_timestamp_single t_in_named_tz)
|
||||||
|
|
||||||
let timedesc_of_utc_or_timestamp_local (ts : utc_or_timestamp_local) : Timedesc.t =
|
let timedesc_of_utc_or_timestamp_local (ts : utc_or_timestamp_local) : Timedesc.t =
|
||||||
let local_tz = Timedesc.Time_zone.local_exn () in
|
|
||||||
match ts with
|
match ts with
|
||||||
| `Local t -> t |> Timedesc.Utils.timestamp_of_ptime |> Timedesc.of_timestamp_exn ~tz_of_date_time:local_tz
|
| `Local t -> t |> Timedesc.Utils.timestamp_of_ptime |> Timedesc.of_timestamp_exn ~tz_of_date_time:!target_tz
|
||||||
(* 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:!target_tz
|
||||||
|
|
||||||
(** Convert a UTC-or-local timestamp to a Timedesc.t in the given timezone. Use this (instead of
|
(** 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
|
[timedesc_of_utc_or_timestamp_local]) when the event has a known TZID, so that UNTIL comparisons are independent of
|
||||||
|
|||||||
Reference in New Issue
Block a user