datetime handling for gleam
at main 15 kB view raw
1-module(birl_ffi). 2 3-export([ 4 now/0, 5 local_offset/0, 6 monotonic_now/0, 7 to_parts/2, 8 from_parts/2, 9 weekday/2, 10 local_timezone/0 11]). 12 13-define(DaysInMonths, [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]). 14-define(Regions, [ 15 "Africa", 16 "America", 17 "Antarctica", 18 "Arctic", 19 "Asia", 20 "Atlantic", 21 "Australia", 22 "Brazil", 23 "Canada", 24 "Chile", 25 "Etc", 26 "Europe", 27 "Indian", 28 "Mexico", 29 "Pacific", 30 "US" 31]). 32 33now() -> os:system_time(microsecond). 34 35local_offset() -> 36 Timestamp = erlang:timestamp(), 37 {{_, LMo, LD}, {LH, LM, _}} = calendar:now_to_local_time(Timestamp), 38 {{_, UMo, UD}, {UH, UM, _}} = calendar:now_to_universal_time(Timestamp), 39 if 40 LD == UD -> 41 (LH - UH) * 60 + LM - UM; 42 (LD > UD andalso LMo == UMo) or (LD == 1 andalso LMo > UMo) -> 43 (23 - UH) * 60 + (60 - UM) + LH * 60 + LM; 44 true -> 45 -((23 - LH) * 60 + (60 - LM) + UH * 60 + UM) 46 end. 47 48local_timezone() -> 49 case os:type() of 50 {unix, _} -> 51 case os:getenv("TZ") of 52 ":" ++ Path -> 53 case timezone_from_path(Path) of 54 <<>> -> local_timezone_from_etc(); 55 TZ -> {some, TZ} 56 end; 57 false -> 58 local_timezone_from_etc(); 59 TZOrPath -> 60 case timezone_from_path(TZOrPath) of 61 <<>> -> local_timezone_from_etc(); 62 TZ -> {some, TZ} 63 end 64 end; 65 {win32, _} -> 66 {ok, RegHandle} = win32reg:open([read]), 67 win32reg:change_key( 68 RegHandle, 69 "\\local_machine\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation" 70 ), 71 {ok, Values} = win32reg:values(RegHandle), 72 case lists:keyfind("TimeZoneKeyName", 1, Values) of 73 {"TimeZoneKeyName", WinZone} -> win_to_iana(string:trim(WinZone)); 74 false -> none 75 end 76 end. 77 78monotonic_now() -> 79 StartTime = erlang:system_info(start_time), 80 CurrentTime = erlang:monotonic_time(), 81 Difference = (CurrentTime - StartTime), 82 erlang:convert_time_unit(Difference, native, microsecond). 83 84to_parts(Timestamp, Offset) -> 85 {Date, {Hour, Minute, Second}} = calendar:system_time_to_universal_time( 86 Timestamp + Offset, microsecond 87 ), 88 MilliSecond = (Timestamp rem 1_000_000) div 1_000, 89 {Date, 90 {Hour, Minute, Second, 91 if 92 MilliSecond == 0 -> MilliSecond; 93 Timestamp >= 0 -> MilliSecond; 94 true -> 1000 + MilliSecond 95 end}}. 96 97from_parts(Parts, Offset) -> 98 {{Year, Month, Day}, {Hour, Minute, Second, MilliSecond}} = Parts, 99 DaysInYears = calculate_days_from_year(Year - 1, 0), 100 DaysInMonths = calculate_days_from_month(Year, Month - 1, 0), 101 Days = 102 if 103 DaysInYears >= 0 -> DaysInYears + DaysInMonths + Day - 1; 104 true -> DaysInYears + DaysInMonths + Day 105 end, 106 Seconds = (Days * 3600 * 24) + (Hour * 3600) + (Minute * 60) + Second, 107 Seconds * 1_000_000 + MilliSecond * 1_000 - Offset. 108 109weekday(Timestamp, Offset) -> 110 {Date, _} = to_parts(Timestamp, Offset), 111 calendar:day_of_the_week(Date) - 1. 112 113local_timezone_from_etc() -> 114 case file:read_file("/etc/timezone") of 115 {ok, NewLinedTimezone} -> 116 {some, string:trim(NewLinedTimezone)}; 117 {error, _} -> 118 case file:read_link("/etc/localtime") of 119 {ok, Path} -> 120 {some, timezone_from_path(Path)}; 121 {error, _} -> 122 none 123 end 124 end. 125 126timezone_from_path(Path) -> 127 Split = string:split(Path, "/", all), 128 ZoneParts = lists:dropwhile( 129 fun(Segment) -> not lists:member(Segment, ?Regions) end, Split 130 ), 131 binary:list_to_bin(string:join(ZoneParts, "/")). 132 133calculate_days_from_year(1969, Days) -> 134 Days; 135calculate_days_from_year(Year, Days) when Year > 1969 -> 136 case calendar:is_leap_year(Year) of 137 true -> 138 calculate_days_from_year(Year - 1, Days + 366); 139 false -> 140 calculate_days_from_year(Year - 1, Days + 365) 141 end; 142calculate_days_from_year(Year, Days) when Year < 1969 -> 143 case calendar:is_leap_year(Year) of 144 true -> 145 calculate_days_from_year(Year + 1, Days - 366); 146 false -> 147 calculate_days_from_year(Year + 1, Days - 365) 148 end. 149 150calculate_days_from_month(_, 0, Days) -> 151 Days; 152calculate_days_from_month(Year, Month, Days) -> 153 case calendar:is_leap_year(Year) and (Month == 2) of 154 true -> 155 calculate_days_from_month(Year, 1, Days + 29); 156 false -> 157 calculate_days_from_month(Year, Month - 1, Days + lists:nth(Month, ?DaysInMonths)) 158 end. 159 160win_to_iana("Afghanistan Standard Time") -> {some, <<"Asia/Kabul">>}; 161win_to_iana("Alaskan Standard Time") -> {some, <<"America/Anchorage">>}; 162win_to_iana("Aleutian Standard Time") -> {some, <<"America/Adak">>}; 163win_to_iana("Altai Standard Time") -> {some, <<"Asia/Barnaul">>}; 164win_to_iana("Arab Standard Time") -> {some, <<"Asia/Riyadh">>}; 165win_to_iana("Arabian Standard Time") -> {some, <<"Asia/Dubai">>}; 166win_to_iana("Arabic Standard Time") -> {some, <<"Asia/Baghdad">>}; 167win_to_iana("Argentina Standard Time") -> {some, <<"America/Argentina/La_Rioja">>}; 168win_to_iana("Astrakhan Standard Time") -> {some, <<"Europe/Astrakhan">>}; 169win_to_iana("Atlantic Standard Time") -> {some, <<"Atlantic/Bermuda">>}; 170win_to_iana("AUS Central Standard Time") -> {some, <<"Australia/Darwin">>}; 171win_to_iana("Aus Central W. Standard Time") -> {some, <<"Australia/Eucla">>}; 172win_to_iana("AUS Eastern Standard Time") -> {some, <<"Australia/Sydney">>}; 173win_to_iana("Azerbaijan Standard Time") -> {some, <<"Asia/Baku">>}; 174win_to_iana("Azores Standard Time") -> {some, <<"Atlantic/Azores">>}; 175win_to_iana("Bahia Standard Time") -> {some, <<"America/Bahia">>}; 176win_to_iana("Bangladesh Standard Time") -> {some, <<"Asia/Dhaka">>}; 177win_to_iana("Belarus Standard Time") -> {some, <<"Europe/Minsk">>}; 178win_to_iana("Bougainville Standard Time") -> {some, <<"Pacific/Bougainville">>}; 179win_to_iana("Canada Central Standard Time") -> {some, <<"America/Regina">>}; 180win_to_iana("Cape Verde Standard Time") -> {some, <<"Atlantic/Cape_Verde">>}; 181win_to_iana("Caucasus Standard Time") -> {some, <<"Asia/Yerevan">>}; 182win_to_iana("Cen. Australia Standard Time") -> {some, <<"Australia/Adelaide">>}; 183win_to_iana("Central America Standard Time") -> {some, <<"America/Guatemala">>}; 184win_to_iana("Central Asia Standard Time") -> {some, <<"Asia/Almaty">>}; 185win_to_iana("Central Brazilian Standard Time") -> {some, <<"America/Campo_Grande">>}; 186win_to_iana("Central Europe Standard Time") -> {some, <<"Europe/Budapest">>}; 187win_to_iana("Central European Standard Time") -> {some, <<"Europe/Warsaw">>}; 188win_to_iana("Central Pacific Standard Time") -> {some, <<"Pacific/Guadalcanal">>}; 189win_to_iana("Central Standard Time") -> {some, <<"CST6CDT">>}; 190win_to_iana("Central Standard Time (Mexico)") -> {some, <<"America/Mexico_City">>}; 191win_to_iana("Chatham Islands Standard Time") -> {some, <<"Pacific/Chatham">>}; 192win_to_iana("China Standard Time") -> {some, <<"Asia/Shanghai">>}; 193win_to_iana("Cuba Standard Time") -> {some, <<"America/Havana">>}; 194win_to_iana("Dateline Standard Time") -> {some, <<"Etc/GMT+12">>}; 195win_to_iana("E. Africa Standard Time") -> {some, <<"Africa/Nairobi">>}; 196win_to_iana("E. Australia Standard Time") -> {some, <<"Australia/Brisbane">>}; 197win_to_iana("E. Europe Standard Time") -> {some, <<"Europe/Chisinau">>}; 198win_to_iana("E. South America Standard Time") -> {some, <<"America/Sao_Paulo">>}; 199win_to_iana("Easter Island Standard Time") -> {some, <<"Pacific/Easter">>}; 200win_to_iana("Eastern Standard Time") -> {some, <<"EST5EDT">>}; 201win_to_iana("Eastern Standard Time (Mexico)") -> {some, <<"America/Cancun">>}; 202win_to_iana("Egypt Standard Time") -> {some, <<"Africa/Cairo">>}; 203win_to_iana("Ekaterinburg Standard Time") -> {some, <<"Asia/Yekaterinburg">>}; 204win_to_iana("Fiji Standard Time") -> {some, <<"Pacific/Fiji">>}; 205win_to_iana("FLE Standard Time") -> {some, <<"Europe/Riga">>}; 206win_to_iana("Georgian Standard Time") -> {some, <<"Asia/Tbilisi">>}; 207win_to_iana("GMT Standard Time") -> {some, <<"Europe/London">>}; 208win_to_iana("Greenland Standard Time") -> {some, <<"Etc/GMT+2">>}; 209win_to_iana("Greenwich Standard Time") -> {some, <<"Africa/Monrovia">>}; 210win_to_iana("GTB Standard Time") -> {some, <<"Europe/Bucharest">>}; 211win_to_iana("Haiti Standard Time") -> {some, <<"America/Port-au-Prince">>}; 212win_to_iana("Hawaiian Standard Time") -> {some, <<"Etc/GMT+10">>}; 213win_to_iana("Iran Standard Time") -> {some, <<"Asia/Tehran">>}; 214win_to_iana("Israel Standard Time") -> {some, <<"Asia/Jerusalem">>}; 215win_to_iana("Jordan Standard Time") -> {some, <<"Asia/Amman">>}; 216win_to_iana("Kaliningrad Standard Time") -> {some, <<"Europe/Kaliningrad">>}; 217win_to_iana("Korea Standard Time") -> {some, <<"Asia/Seoul">>}; 218win_to_iana("Libya Standard Time") -> {some, <<"Africa/Tripoli">>}; 219win_to_iana("Line Islands Standard Time") -> {some, <<"Pacific/Kiritimati">>}; 220win_to_iana("Lord Howe Standard Time") -> {some, <<"Australia/Lord_Howe">>}; 221win_to_iana("Magadan Standard Time") -> {some, <<"Asia/Magadan">>}; 222win_to_iana("Magallanes Standard Time") -> {some, <<"America/Punta_Arenas">>}; 223win_to_iana("Marquesas Standard Time") -> {some, <<"Pacific/Marquesas">>}; 224win_to_iana("Mauritius Standard Time") -> {some, <<"Indian/Mauritius">>}; 225win_to_iana("Middle East Standard Time") -> {some, <<"Asia/Beirut">>}; 226win_to_iana("Montevideo Standard Time") -> {some, <<"America/Montevideo">>}; 227win_to_iana("Morocco Standard Time") -> {some, <<"Africa/Casablanca">>}; 228win_to_iana("Mountain Standard Time") -> {some, <<"MST7MDT">>}; 229win_to_iana("Mountain Standard Time (Mexico)") -> {some, <<"America/Mazatlan">>}; 230win_to_iana("N. Central Asia Standard Time") -> {some, <<"Asia/Novosibirsk">>}; 231win_to_iana("Namibia Standard Time") -> {some, <<"Africa/Windhoek">>}; 232win_to_iana("New Zealand Standard Time") -> {some, <<"Pacific/Auckland">>}; 233win_to_iana("Newfoundland Standard Time") -> {some, <<"America/St_Johns">>}; 234win_to_iana("Norfolk Standard Time") -> {some, <<"Pacific/Norfolk">>}; 235win_to_iana("North Asia East Standard Time") -> {some, <<"Asia/Irkutsk">>}; 236win_to_iana("North Asia Standard Time") -> {some, <<"Asia/Krasnoyarsk">>}; 237win_to_iana("North Korea Standard Time") -> {some, <<"Asia/Pyongyang">>}; 238win_to_iana("Omsk Standard Time") -> {some, <<"Asia/Omsk">>}; 239win_to_iana("Pacific SA Standard Time") -> {some, <<"America/Santiago">>}; 240win_to_iana("Pacific Standard Time") -> {some, <<"PST8PDT">>}; 241win_to_iana("Pacific Standard Time (Mexico)") -> {some, <<"America/Tijuana">>}; 242win_to_iana("Pakistan Standard Time") -> {some, <<"Asia/Karachi">>}; 243win_to_iana("Paraguay Standard Time") -> {some, <<"America/Asuncion">>}; 244win_to_iana("Qyzylorda Standard Time") -> {some, <<"Asia/Qyzylorda">>}; 245win_to_iana("Romance Standard Time") -> {some, <<"Europe/Paris">>}; 246win_to_iana("Russia Time Zone 10") -> {some, <<"Asia/Srednekolymsk">>}; 247win_to_iana("Russia Time Zone 11") -> {some, <<"Asia/Kamchatka">>}; 248win_to_iana("Russia Time Zone 3") -> {some, <<"Europe/Samara">>}; 249win_to_iana("Russian Standard Time") -> {some, <<"Europe/Moscow">>}; 250win_to_iana("SA Eastern Standard Time") -> {some, <<"America/Santarem">>}; 251win_to_iana("SA Pacific Standard Time") -> {some, <<"Etc/GMT+5">>}; 252win_to_iana("SA Western Standard Time") -> {some, <<"America/Santo_Domingo">>}; 253win_to_iana("Saint Pierre Standard Time") -> {some, <<"America/Miquelon">>}; 254win_to_iana("Sakhalin Standard Time") -> {some, <<"Asia/Sakhalin">>}; 255win_to_iana("Samoa Standard Time") -> {some, <<"Pacific/Apia">>}; 256win_to_iana("Sao Tome Standard Time") -> {some, <<"Africa/Sao_Tome">>}; 257win_to_iana("Saratov Standard Time") -> {some, <<"Europe/Saratov">>}; 258win_to_iana("SE Asia Standard Time") -> {some, <<"Asia/Bangkok">>}; 259win_to_iana("Singapore Standard Time") -> {some, <<"Asia/Singapore">>}; 260win_to_iana("South Africa Standard Time") -> {some, <<"Africa/Johannesburg">>}; 261win_to_iana("South Sudan Standard Time") -> {some, <<"Africa/Juba">>}; 262win_to_iana("Sri Lanka Standard Time") -> {some, <<"Asia/Colombo">>}; 263win_to_iana("Sudan Standard Time") -> {some, <<"Africa/Khartoum">>}; 264win_to_iana("Syria Standard Time") -> {some, <<"Asia/Damascus">>}; 265win_to_iana("Taipei Standard Time") -> {some, <<"Asia/Taipei">>}; 266win_to_iana("Tasmania Standard Time") -> {some, <<"Australia/Hobart">>}; 267win_to_iana("Tocantins Standard Time") -> {some, <<"America/Araguaina">>}; 268win_to_iana("Tokyo Standard Time") -> {some, <<"Asia/Tokyo">>}; 269win_to_iana("Tomsk Standard Time") -> {some, <<"Asia/Tomsk">>}; 270win_to_iana("Tonga Standard Time") -> {some, <<"Pacific/Tongatapu">>}; 271win_to_iana("Transbaikal Standard Time") -> {some, <<"Asia/Chita">>}; 272win_to_iana("Turkey Standard Time") -> {some, <<"Europe/Istanbul">>}; 273win_to_iana("Turks And Caicos Standard Time") -> {some, <<"America/Grand_Turk">>}; 274win_to_iana("Ulaanbaatar Standard Time") -> {some, <<"Asia/Ulaanbaatar">>}; 275win_to_iana("US Eastern Standard Time") -> {some, <<"America/Indiana/Marengo">>}; 276win_to_iana("US Mountain Standard Time") -> {some, <<"Etc/GMT+7">>}; 277win_to_iana("UTC") -> {some, <<"Etc/GMT">>}; 278win_to_iana("UTC-02") -> {some, <<"Etc/GMT+2">>}; 279win_to_iana("UTC-08") -> {some, <<"Pacific/Pitcairn">>}; 280win_to_iana("UTC-09") -> {some, <<"Pacific/Gambier">>}; 281win_to_iana("UTC-11") -> {some, <<"Etc/GMT+11">>}; 282win_to_iana("UTC+12") -> {some, <<"Etc/GMT-12">>}; 283win_to_iana("UTC+13") -> {some, <<"Pacific/Fakaofo">>}; 284win_to_iana("Venezuela Standard Time") -> {some, <<"America/Caracas">>}; 285win_to_iana("Vladivostok Standard Time") -> {some, <<"Asia/Vladivostok">>}; 286win_to_iana("Volgograd Standard Time") -> {some, <<"Europe/Volgograd">>}; 287win_to_iana("W. Australia Standard Time") -> {some, <<"Australia/Perth">>}; 288win_to_iana("W. Central Africa Standard Time") -> {some, <<"Etc/GMT-1">>}; 289win_to_iana("W. Europe Standard Time") -> {some, <<"Europe/Berlin">>}; 290win_to_iana("W. Mongolia Standard Time") -> {some, <<"Asia/Hovd">>}; 291win_to_iana("West Asia Standard Time") -> {some, <<"Asia/Tashkent">>}; 292win_to_iana("West Bank Standard Time") -> {some, <<"Asia/Gaza">>}; 293win_to_iana("West Pacific Standard Time") -> {some, <<"Pacific/Port_Moresby">>}; 294win_to_iana("Yakutsk Standard Time") -> {some, <<"Asia/Yakutsk">>}; 295win_to_iana("Yukon Standard Time") -> {some, <<"America/Whitehorse">>}; 296win_to_iana(_) -> none.