feat(timezone): add Windows timezone name resolution
Add a `Windows_tz` module containing a CLDR-sourced mapping table from Windows timezone names to canonical IANA names. Update `timedesc_of_timestamp` to resolve Windows-style timezone identifiers (e.g. `"W. Europe Standard Time"`) via this table before constructing a `Timedesc.Time_zone.t`, falling back to the local timezone with a warning if resolution fails entirely.
This commit is contained in:
2
bin/dune
2
bin/dune
@@ -1,7 +1,7 @@
|
|||||||
(executable
|
(executable
|
||||||
(public_name remind_sync)
|
(public_name remind_sync)
|
||||||
(name main)
|
(name main)
|
||||||
(modules main commandLine remind eventPredicates utils)
|
(modules main commandLine remind eventPredicates utils windows_tz)
|
||||||
(preprocess
|
(preprocess
|
||||||
(pps ppx_deriving.show))
|
(pps ppx_deriving.show))
|
||||||
(libraries
|
(libraries
|
||||||
|
|||||||
13
bin/utils.ml
13
bin/utils.ml
@@ -105,7 +105,18 @@ let timedesc_of_timestamp (ts : timestamp) : Timedesc.t =
|
|||||||
| `With_tzid (ts, (_b, tz_name)) ->
|
| `With_tzid (ts, (_b, tz_name)) ->
|
||||||
(* The timestamp is stored as if it were UTC but must be interpreted in tz_name.
|
(* The timestamp is stored as if it were UTC but must be interpreted in tz_name.
|
||||||
We reconstruct the wall-clock time in tz_name, then convert to target_tz. *)
|
We reconstruct the wall-clock time in tz_name, then convert to target_tz. *)
|
||||||
let tz = Timedesc.Time_zone.make_exn tz_name in
|
(* Resolve the timezone name: Windows names (e.g. "W. Europe Standard Time") are
|
||||||
|
mapped to IANA via the CLDR table; otherwise the name is used as-is (assumed
|
||||||
|
to already be a valid IANA name). If resolution fails entirely, fall back to
|
||||||
|
target_tz with a warning. *)
|
||||||
|
let tz =
|
||||||
|
let candidate = Option.value ~default:tz_name (Windows_tz.to_iana tz_name) in
|
||||||
|
match Timedesc.Time_zone.make candidate with
|
||||||
|
| Some tz -> tz
|
||||||
|
| None ->
|
||||||
|
Printf.eprintf "Warning: unresolvable timezone %S, falling back to local timezone\n" tz_name;
|
||||||
|
!target_tz
|
||||||
|
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
|
||||||
|
|||||||
149
bin/windows_tz.ml
Normal file
149
bin/windows_tz.ml
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
(** Mapping from Windows timezone names to canonical IANA timezone names. Source: Unicode CLDR
|
||||||
|
common/supplemental/windowsZones.xml, territory="001" entries.
|
||||||
|
https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml *)
|
||||||
|
|
||||||
|
let table : (string * string) list =
|
||||||
|
[
|
||||||
|
("AUS Central Standard Time", "Australia/Darwin");
|
||||||
|
("AUS Eastern Standard Time", "Australia/Sydney");
|
||||||
|
("Afghanistan Standard Time", "Asia/Kabul");
|
||||||
|
("Alaskan Standard Time", "America/Anchorage");
|
||||||
|
("Aleutian Standard Time", "America/Adak");
|
||||||
|
("Altai Standard Time", "Asia/Barnaul");
|
||||||
|
("Arab Standard Time", "Asia/Riyadh");
|
||||||
|
("Arabian Standard Time", "Asia/Dubai");
|
||||||
|
("Arabic Standard Time", "Asia/Baghdad");
|
||||||
|
("Argentina Standard Time", "America/Buenos_Aires");
|
||||||
|
("Astrakhan Standard Time", "Europe/Astrakhan");
|
||||||
|
("Atlantic Standard Time", "America/Halifax");
|
||||||
|
("Aus Central W. Standard Time", "Australia/Eucla");
|
||||||
|
("Azerbaijan Standard Time", "Asia/Baku");
|
||||||
|
("Azores Standard Time", "Atlantic/Azores");
|
||||||
|
("Bahia Standard Time", "America/Bahia");
|
||||||
|
("Bangladesh Standard Time", "Asia/Dhaka");
|
||||||
|
("Belarus Standard Time", "Europe/Minsk");
|
||||||
|
("Bougainville Standard Time", "Pacific/Bougainville");
|
||||||
|
("Canada Central Standard Time", "America/Regina");
|
||||||
|
("Cape Verde Standard Time", "Atlantic/Cape_Verde");
|
||||||
|
("Caucasus Standard Time", "Asia/Yerevan");
|
||||||
|
("Cen. Australia Standard Time", "Australia/Adelaide");
|
||||||
|
("Central America Standard Time", "America/Guatemala");
|
||||||
|
("Central Asia Standard Time", "Asia/Bishkek");
|
||||||
|
("Central Brazilian Standard Time", "America/Cuiaba");
|
||||||
|
("Central Europe Standard Time", "Europe/Budapest");
|
||||||
|
("Central European Standard Time", "Europe/Warsaw");
|
||||||
|
("Central Pacific Standard Time", "Pacific/Guadalcanal");
|
||||||
|
("Central Standard Time (Mexico)", "America/Mexico_City");
|
||||||
|
("Central Standard Time", "America/Chicago");
|
||||||
|
("Chatham Islands Standard Time", "Pacific/Chatham");
|
||||||
|
("China Standard Time", "Asia/Shanghai");
|
||||||
|
("Cuba Standard Time", "America/Havana");
|
||||||
|
("Dateline Standard Time", "Etc/GMT+12");
|
||||||
|
("E. Africa Standard Time", "Africa/Nairobi");
|
||||||
|
("E. Australia Standard Time", "Australia/Brisbane");
|
||||||
|
("E. Europe Standard Time", "Europe/Chisinau");
|
||||||
|
("E. South America Standard Time", "America/Sao_Paulo");
|
||||||
|
("Easter Island Standard Time", "Pacific/Easter");
|
||||||
|
("Eastern Standard Time (Mexico)", "America/Cancun");
|
||||||
|
("Eastern Standard Time", "America/New_York");
|
||||||
|
("Egypt Standard Time", "Africa/Cairo");
|
||||||
|
("Ekaterinburg Standard Time", "Asia/Yekaterinburg");
|
||||||
|
("FLE Standard Time", "Europe/Kiev");
|
||||||
|
("Fiji Standard Time", "Pacific/Fiji");
|
||||||
|
("GMT Standard Time", "Europe/London");
|
||||||
|
("GTB Standard Time", "Europe/Bucharest");
|
||||||
|
("Georgian Standard Time", "Asia/Tbilisi");
|
||||||
|
("Greenland Standard Time", "America/Godthab");
|
||||||
|
("Greenwich Standard Time", "Atlantic/Reykjavik");
|
||||||
|
("Haiti Standard Time", "America/Port-au-Prince");
|
||||||
|
("Hawaiian Standard Time", "Pacific/Honolulu");
|
||||||
|
("India Standard Time", "Asia/Calcutta");
|
||||||
|
("Iran Standard Time", "Asia/Tehran");
|
||||||
|
("Israel Standard Time", "Asia/Jerusalem");
|
||||||
|
("Jordan Standard Time", "Asia/Amman");
|
||||||
|
("Kaliningrad Standard Time", "Europe/Kaliningrad");
|
||||||
|
("Korea Standard Time", "Asia/Seoul");
|
||||||
|
("Libya Standard Time", "Africa/Tripoli");
|
||||||
|
("Line Islands Standard Time", "Pacific/Kiritimati");
|
||||||
|
("Lord Howe Standard Time", "Australia/Lord_Howe");
|
||||||
|
("Magadan Standard Time", "Asia/Magadan");
|
||||||
|
("Magallanes Standard Time", "America/Punta_Arenas");
|
||||||
|
("Marquesas Standard Time", "Pacific/Marquesas");
|
||||||
|
("Mauritius Standard Time", "Indian/Mauritius");
|
||||||
|
("Middle East Standard Time", "Asia/Beirut");
|
||||||
|
("Montevideo Standard Time", "America/Montevideo");
|
||||||
|
("Morocco Standard Time", "Africa/Casablanca");
|
||||||
|
("Mountain Standard Time (Mexico)", "America/Mazatlan");
|
||||||
|
("Mountain Standard Time", "America/Denver");
|
||||||
|
("Myanmar Standard Time", "Asia/Rangoon");
|
||||||
|
("N. Central Asia Standard Time", "Asia/Novosibirsk");
|
||||||
|
("Namibia Standard Time", "Africa/Windhoek");
|
||||||
|
("Nepal Standard Time", "Asia/Katmandu");
|
||||||
|
("New Zealand Standard Time", "Pacific/Auckland");
|
||||||
|
("Newfoundland Standard Time", "America/St_Johns");
|
||||||
|
("Norfolk Standard Time", "Pacific/Norfolk");
|
||||||
|
("North Asia East Standard Time", "Asia/Irkutsk");
|
||||||
|
("North Asia Standard Time", "Asia/Krasnoyarsk");
|
||||||
|
("North Korea Standard Time", "Asia/Pyongyang");
|
||||||
|
("Omsk Standard Time", "Asia/Omsk");
|
||||||
|
("Pacific SA Standard Time", "America/Santiago");
|
||||||
|
("Pacific Standard Time (Mexico)", "America/Tijuana");
|
||||||
|
("Pacific Standard Time", "America/Los_Angeles");
|
||||||
|
("Pakistan Standard Time", "Asia/Karachi");
|
||||||
|
("Paraguay Standard Time", "America/Asuncion");
|
||||||
|
("Qyzylorda Standard Time", "Asia/Qyzylorda");
|
||||||
|
("Romance Standard Time", "Europe/Paris");
|
||||||
|
("Russia Time Zone 10", "Asia/Srednekolymsk");
|
||||||
|
("Russia Time Zone 11", "Asia/Kamchatka");
|
||||||
|
("Russia Time Zone 3", "Europe/Samara");
|
||||||
|
("Russian Standard Time", "Europe/Moscow");
|
||||||
|
("SA Eastern Standard Time", "America/Cayenne");
|
||||||
|
("SA Pacific Standard Time", "America/Bogota");
|
||||||
|
("SA Western Standard Time", "America/La_Paz");
|
||||||
|
("SE Asia Standard Time", "Asia/Bangkok");
|
||||||
|
("Saint Pierre Standard Time", "America/Miquelon");
|
||||||
|
("Sakhalin Standard Time", "Asia/Sakhalin");
|
||||||
|
("Samoa Standard Time", "Pacific/Apia");
|
||||||
|
("Sao Tome Standard Time", "Africa/Sao_Tome");
|
||||||
|
("Saratov Standard Time", "Europe/Saratov");
|
||||||
|
("Singapore Standard Time", "Asia/Singapore");
|
||||||
|
("South Africa Standard Time", "Africa/Johannesburg");
|
||||||
|
("South Sudan Standard Time", "Africa/Juba");
|
||||||
|
("Sri Lanka Standard Time", "Asia/Colombo");
|
||||||
|
("Sudan Standard Time", "Africa/Khartoum");
|
||||||
|
("Syria Standard Time", "Asia/Damascus");
|
||||||
|
("Taipei Standard Time", "Asia/Taipei");
|
||||||
|
("Tasmania Standard Time", "Australia/Hobart");
|
||||||
|
("Tocantins Standard Time", "America/Araguaina");
|
||||||
|
("Tokyo Standard Time", "Asia/Tokyo");
|
||||||
|
("Tomsk Standard Time", "Asia/Tomsk");
|
||||||
|
("Tonga Standard Time", "Pacific/Tongatapu");
|
||||||
|
("Transbaikal Standard Time", "Asia/Chita");
|
||||||
|
("Turkey Standard Time", "Europe/Istanbul");
|
||||||
|
("Turks And Caicos Standard Time", "America/Grand_Turk");
|
||||||
|
("US Eastern Standard Time", "America/Indianapolis");
|
||||||
|
("US Mountain Standard Time", "America/Phoenix");
|
||||||
|
("UTC", "Etc/UTC");
|
||||||
|
("UTC+12", "Etc/GMT-12");
|
||||||
|
("UTC+13", "Etc/GMT-13");
|
||||||
|
("UTC-02", "Etc/GMT+2");
|
||||||
|
("UTC-08", "Etc/GMT+8");
|
||||||
|
("UTC-09", "Etc/GMT+9");
|
||||||
|
("UTC-11", "Etc/GMT+11");
|
||||||
|
("Ulaanbaatar Standard Time", "Asia/Ulaanbaatar");
|
||||||
|
("Venezuela Standard Time", "America/Caracas");
|
||||||
|
("Vladivostok Standard Time", "Asia/Vladivostok");
|
||||||
|
("Volgograd Standard Time", "Europe/Volgograd");
|
||||||
|
("W. Australia Standard Time", "Australia/Perth");
|
||||||
|
("W. Central Africa Standard Time", "Africa/Lagos");
|
||||||
|
("W. Europe Standard Time", "Europe/Berlin");
|
||||||
|
("W. Mongolia Standard Time", "Asia/Hovd");
|
||||||
|
("West Asia Standard Time", "Asia/Tashkent");
|
||||||
|
("West Bank Standard Time", "Asia/Hebron");
|
||||||
|
("West Pacific Standard Time", "Pacific/Port_Moresby");
|
||||||
|
("Yakutsk Standard Time", "Asia/Yakutsk");
|
||||||
|
("Yukon Standard Time", "America/Whitehorse");
|
||||||
|
]
|
||||||
|
|
||||||
|
(** Look up a Windows timezone name and return the canonical IANA name, if known. *)
|
||||||
|
let to_iana (windows_name : string) : string option = List.assoc_opt windows_name table
|
||||||
Reference in New Issue
Block a user