mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-23 06:05:00 -07:00
312 lines
15 KiB
Odin
312 lines
15 KiB
Odin
#+build windows
|
|
#+private
|
|
package timezone
|
|
|
|
import "core:strings"
|
|
import "core:sys/windows"
|
|
import "core:time/datetime"
|
|
|
|
TZ_Abbrev :: struct {
|
|
std: string,
|
|
dst: string,
|
|
}
|
|
|
|
tz_abbrevs := map[string]TZ_Abbrev {
|
|
"Egypt Standard Time" = {"EET", "EEST"}, // Africa/Cairo
|
|
"Morocco Standard Time" = {"+00", "+01"}, // Africa/Casablanca
|
|
"South Africa Standard Time" = {"SAST", "SAST"}, // Africa/Johannesburg
|
|
"South Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Juba
|
|
"Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Khartoum
|
|
"W. Central Africa Standard Time" = {"WAT", "WAT"}, // Africa/Lagos
|
|
"E. Africa Standard Time" = {"EAT", "EAT"}, // Africa/Nairobi
|
|
"Sao Tome Standard Time" = {"GMT", "GMT"}, // Africa/Sao_Tome
|
|
"Libya Standard Time" = {"EET", "EET"}, // Africa/Tripoli
|
|
"Namibia Standard Time" = {"CAT", "CAT"}, // Africa/Windhoek
|
|
"Aleutian Standard Time" = {"HST", "HDT"}, // America/Adak
|
|
"Alaskan Standard Time" = {"AKST", "AKDT"}, // America/Anchorage
|
|
"Tocantins Standard Time" = {"-03", "-03"}, // America/Araguaina
|
|
"Paraguay Standard Time" = {"-04", "-03"}, // America/Asuncion
|
|
"Bahia Standard Time" = {"-03", "-03"}, // America/Bahia
|
|
"SA Pacific Standard Time" = {"-05", "-05"}, // America/Bogota
|
|
"Argentina Standard Time" = {"-03", "-03"}, // America/Buenos_Aires
|
|
"Eastern Standard Time (Mexico)" = {"EST", "EST"}, // America/Cancun
|
|
"Venezuela Standard Time" = {"-04", "-04"}, // America/Caracas
|
|
"SA Eastern Standard Time" = {"-03", "-03"}, // America/Cayenne
|
|
"Central Standard Time" = {"CST", "CDT"}, // America/Chicago
|
|
"Central Brazilian Standard Time" = {"-04", "-04"}, // America/Cuiaba
|
|
"Mountain Standard Time" = {"MST", "MDT"}, // America/Denver
|
|
"Greenland Standard Time" = {"-03", "-02"}, // America/Godthab
|
|
"Turks And Caicos Standard Time" = {"EST", "EDT"}, // America/Grand_Turk
|
|
"Central America Standard Time" = {"CST", "CST"}, // America/Guatemala
|
|
"Atlantic Standard Time" = {"AST", "ADT"}, // America/Halifax
|
|
"Cuba Standard Time" = {"CST", "CDT"}, // America/Havana
|
|
"US Eastern Standard Time" = {"EST", "EDT"}, // America/Indianapolis
|
|
"SA Western Standard Time" = {"-04", "-04"}, // America/La_Paz
|
|
"Pacific Standard Time" = {"PST", "PDT"}, // America/Los_Angeles
|
|
"Mountain Standard Time (Mexico)" = {"MST", "MST"}, // America/Mazatlan
|
|
"Central Standard Time (Mexico)" = {"CST", "CST"}, // America/Mexico_City
|
|
"Saint Pierre Standard Time" = {"-03", "-02"}, // America/Miquelon
|
|
"Montevideo Standard Time" = {"-03", "-03"}, // America/Montevideo
|
|
"Eastern Standard Time" = {"EST", "EDT"}, // America/New_York
|
|
"US Mountain Standard Time" = {"MST", "MST"}, // America/Phoenix
|
|
"Haiti Standard Time" = {"EST", "EDT"}, // America/Port-au-Prince
|
|
"Magallanes Standard Time" = {"-03", "-03"}, // America/Punta_Arenas
|
|
"Canada Central Standard Time" = {"CST", "CST"}, // America/Regina
|
|
"Pacific SA Standard Time" = {"-04", "-03"}, // America/Santiago
|
|
"E. South America Standard Time" = {"-03", "-03"}, // America/Sao_Paulo
|
|
"Newfoundland Standard Time" = {"NST", "NDT"}, // America/St_Johns
|
|
"Pacific Standard Time (Mexico)" = {"PST", "PDT"}, // America/Tijuana
|
|
"Yukon Standard Time" = {"MST", "MST"}, // America/Whitehorse
|
|
"Central Asia Standard Time" = {"+06", "+06"}, // Asia/Almaty
|
|
"Jordan Standard Time" = {"+03", "+03"}, // Asia/Amman
|
|
"Arabic Standard Time" = {"+03", "+03"}, // Asia/Baghdad
|
|
"Azerbaijan Standard Time" = {"+04", "+04"}, // Asia/Baku
|
|
"SE Asia Standard Time" = {"+07", "+07"}, // Asia/Bangkok
|
|
"Altai Standard Time" = {"+07", "+07"}, // Asia/Barnaul
|
|
"Middle East Standard Time" = {"EET", "EEST"}, // Asia/Beirut
|
|
"India Standard Time" = {"IST", "IST"}, // Asia/Calcutta
|
|
"Transbaikal Standard Time" = {"+09", "+09"}, // Asia/Chita
|
|
"Sri Lanka Standard Time" = {"+0530", "+0530"}, // Asia/Colombo
|
|
"Syria Standard Time" = {"+03", "+03"}, // Asia/Damascus
|
|
"Bangladesh Standard Time" = {"+06", "+06"}, // Asia/Dhaka
|
|
"Arabian Standard Time" = {"+04", "+04"}, // Asia/Dubai
|
|
"West Bank Standard Time" = {"EET", "EEST"}, // Asia/Hebron
|
|
"W. Mongolia Standard Time" = {"+07", "+07"}, // Asia/Hovd
|
|
"North Asia East Standard Time" = {"+08", "+08"}, // Asia/Irkutsk
|
|
"Israel Standard Time" = {"IST", "IDT"}, // Asia/Jerusalem
|
|
"Afghanistan Standard Time" = {"+0430", "+0430"}, // Asia/Kabul
|
|
"Russia Time Zone 11" = {"+12", "+12"}, // Asia/Kamchatka
|
|
"Pakistan Standard Time" = {"PKT", "PKT"}, // Asia/Karachi
|
|
"Nepal Standard Time" = {"+0545", "+0545"}, // Asia/Katmandu
|
|
"North Asia Standard Time" = {"+07", "+07"}, // Asia/Krasnoyarsk
|
|
"Magadan Standard Time" = {"+11", "+11"}, // Asia/Magadan
|
|
"N. Central Asia Standard Time" = {"+07", "+07"}, // Asia/Novosibirsk
|
|
"Omsk Standard Time" = {"+06", "+06"}, // Asia/Omsk
|
|
"North Korea Standard Time" = {"KST", "KST"}, // Asia/Pyongyang
|
|
"Qyzylorda Standard Time" = {"+05", "+05"}, // Asia/Qyzylorda
|
|
"Myanmar Standard Time" = {"+0630", "+0630"}, // Asia/Rangoon
|
|
"Arab Standard Time" = {"+03", "+03"}, // Asia/Riyadh
|
|
"Sakhalin Standard Time" = {"+11", "+11"}, // Asia/Sakhalin
|
|
"Korea Standard Time" = {"KST", "KST"}, // Asia/Seoul
|
|
"China Standard Time" = {"CST", "CST"}, // Asia/Shanghai
|
|
"Singapore Standard Time" = {"+08", "+08"}, // Asia/Singapore
|
|
"Russia Time Zone 10" = {"+11", "+11"}, // Asia/Srednekolymsk
|
|
"Taipei Standard Time" = {"CST", "CST"}, // Asia/Taipei
|
|
"West Asia Standard Time" = {"+05", "+05"}, // Asia/Tashkent
|
|
"Georgian Standard Time" = {"+04", "+04"}, // Asia/Tbilisi
|
|
"Iran Standard Time" = {"+0330", "+0330"}, // Asia/Tehran
|
|
"Tokyo Standard Time" = {"JST", "JST"}, // Asia/Tokyo
|
|
"Tomsk Standard Time" = {"+07", "+07"}, // Asia/Tomsk
|
|
"Ulaanbaatar Standard Time" = {"+08", "+08"}, // Asia/Ulaanbaatar
|
|
"Vladivostok Standard Time" = {"+10", "+10"}, // Asia/Vladivostok
|
|
"Yakutsk Standard Time" = {"+09", "+09"}, // Asia/Yakutsk
|
|
"Ekaterinburg Standard Time" = {"+05", "+05"}, // Asia/Yekaterinburg
|
|
"Caucasus Standard Time" = {"+04", "+04"}, // Asia/Yerevan
|
|
"Azores Standard Time" = {"-01", "+00"}, // Atlantic/Azores
|
|
"Cape Verde Standard Time" = {"-01", "-01"}, // Atlantic/Cape_Verde
|
|
"Greenwich Standard Time" = {"GMT", "GMT"}, // Atlantic/Reykjavik
|
|
"Cen. Australia Standard Time" = {"ACST", "ACDT"}, // Australia/Adelaide
|
|
"E. Australia Standard Time" = {"AEST", "AEST"}, // Australia/Brisbane
|
|
"AUS Central Standard Time" = {"ACST", "ACST"}, // Australia/Darwin
|
|
"Aus Central W. Standard Time" = {"+0845", "+0845"}, // Australia/Eucla
|
|
"Tasmania Standard Time" = {"AEST", "AEDT"}, // Australia/Hobart
|
|
"Lord Howe Standard Time" = {"+1030", "+11"}, // Australia/Lord_Howe
|
|
"W. Australia Standard Time" = {"AWST", "AWST"}, // Australia/Perth
|
|
"AUS Eastern Standard Time" = {"AEST", "AEDT"}, // Australia/Sydney
|
|
"UTC-11" = {"-11", "-11"}, // Etc/GMT+11
|
|
"Dateline Standard Time" = {"-12", "-12"}, // Etc/GMT+12
|
|
"UTC-02" = {"-02", "-02"}, // Etc/GMT+2
|
|
"UTC-08" = {"-08", "-08"}, // Etc/GMT+8
|
|
"UTC-09" = {"-09", "-09"}, // Etc/GMT+9
|
|
"UTC+12" = {"+12", "+12"}, // Etc/GMT-12
|
|
"UTC+13" = {"+13", "+13"}, // Etc/GMT-13
|
|
"UTC" = {"UTC", "UTC"}, // Etc/UTC
|
|
"Astrakhan Standard Time" = {"+04", "+04"}, // Europe/Astrakhan
|
|
"W. Europe Standard Time" = {"CET", "CEST"}, // Europe/Berlin
|
|
"GTB Standard Time" = {"EET", "EEST"}, // Europe/Bucharest
|
|
"Central Europe Standard Time" = {"CET", "CEST"}, // Europe/Budapest
|
|
"E. Europe Standard Time" = {"EET", "EEST"}, // Europe/Chisinau
|
|
"Turkey Standard Time" = {"+03", "+03"}, // Europe/Istanbul
|
|
"Kaliningrad Standard Time" = {"EET", "EET"}, // Europe/Kaliningrad
|
|
"FLE Standard Time" = {"EET", "EEST"}, // Europe/Kiev
|
|
"GMT Standard Time" = {"GMT", "BST"}, // Europe/London
|
|
"Belarus Standard Time" = {"+03", "+03"}, // Europe/Minsk
|
|
"Russian Standard Time" = {"MSK", "MSK"}, // Europe/Moscow
|
|
"Romance Standard Time" = {"CET", "CEST"}, // Europe/Paris
|
|
"Russia Time Zone 3" = {"+04", "+04"}, // Europe/Samara
|
|
"Saratov Standard Time" = {"+04", "+04"}, // Europe/Saratov
|
|
"Volgograd Standard Time" = {"MSK", "MSK"}, // Europe/Volgograd
|
|
"Central European Standard Time" = {"CET", "CEST"}, // Europe/Warsaw
|
|
"Mauritius Standard Time" = {"+04", "+04"}, // Indian/Mauritius
|
|
"Samoa Standard Time" = {"+13", "+13"}, // Pacific/Apia
|
|
"New Zealand Standard Time" = {"NZST", "NZDT"}, // Pacific/Auckland
|
|
"Bougainville Standard Time" = {"+11", "+11"}, // Pacific/Bougainville
|
|
"Chatham Islands Standard Time" = {"+1245", "+1345"}, // Pacific/Chatham
|
|
"Easter Island Standard Time" = {"-06", "-05"}, // Pacific/Easter
|
|
"Fiji Standard Time" = {"+12", "+12"}, // Pacific/Fiji
|
|
"Central Pacific Standard Time" = {"+11", "+11"}, // Pacific/Guadalcanal
|
|
"Hawaiian Standard Time" = {"HST", "HST"}, // Pacific/Honolulu
|
|
"Line Islands Standard Time" = {"+14", "+14"}, // Pacific/Kiritimati
|
|
"Marquesas Standard Time" = {"-0930", "-0930"}, // Pacific/Marquesas
|
|
"Norfolk Standard Time" = {"+11", "+12"}, // Pacific/Norfolk
|
|
"West Pacific Standard Time" = {"+10", "+10"}, // Pacific/Port_Moresby
|
|
"Tonga Standard Time" = {"+13", "+13"}, // Pacific/Tongatapu
|
|
}
|
|
|
|
iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> (name: string, success: bool) {
|
|
wintz_name_buffer: [128]u16
|
|
status: windows.UError
|
|
|
|
iana_name_wstr := windows.utf8_to_wstring(iana_name, allocator)
|
|
defer free(iana_name_wstr, allocator)
|
|
|
|
wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, raw_data(wintz_name_buffer[:]), len(wintz_name_buffer), &status)
|
|
if status != .U_ZERO_ERROR {
|
|
return
|
|
}
|
|
|
|
wintz_name, err := windows.utf16_to_utf8(wintz_name_buffer[:wintz_name_len], allocator)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return wintz_name, true
|
|
}
|
|
|
|
local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: bool) {
|
|
iana_name_buffer: [128]u16
|
|
status: windows.UError
|
|
|
|
zone_str_len := windows.ucal_getDefaultTimeZone(raw_data(iana_name_buffer[:]), len(iana_name_buffer), &status)
|
|
if status != .U_ZERO_ERROR {
|
|
return
|
|
}
|
|
|
|
iana_name, err := windows.utf16_to_utf8(iana_name_buffer[:zone_str_len], allocator)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return iana_name, true
|
|
}
|
|
|
|
REG_TZI_FORMAT :: struct #packed {
|
|
bias: windows.LONG,
|
|
std_bias: windows.LONG,
|
|
dst_bias: windows.LONG,
|
|
std_date: windows.SYSTEMTIME,
|
|
dst_date: windows.SYSTEMTIME,
|
|
}
|
|
|
|
generate_rrule_from_tzi :: proc(tzi: ^REG_TZI_FORMAT, abbrevs: TZ_Abbrev, allocator := context.allocator) -> (rrule: datetime.TZ_RRule, ok: bool) {
|
|
std_name, err := strings.clone(abbrevs.std, allocator)
|
|
if err != nil { return }
|
|
defer if err != nil { delete(std_name, allocator) }
|
|
|
|
if (tzi.std_date.month == 0) {
|
|
return datetime.TZ_RRule{
|
|
has_dst = false,
|
|
|
|
std_name = std_name,
|
|
std_offset = -(i64(tzi.bias) + i64(tzi.std_bias)) * 60,
|
|
dst_date = datetime.TZ_Transition_Date{
|
|
type = .Month_Week_Day,
|
|
month = u8(tzi.std_date.month),
|
|
week = u8(tzi.std_date.day),
|
|
day = tzi.std_date.day_of_week,
|
|
time = (i64(tzi.std_date.hour) * 60 * 60) + (i64(tzi.std_date.minute) * 60) + i64(tzi.std_date.second),
|
|
},
|
|
}, true
|
|
}
|
|
|
|
dst_name: string
|
|
dst_name, err = strings.clone(abbrevs.dst, allocator)
|
|
if err != nil { return }
|
|
defer if err != nil { delete(dst_name, allocator) }
|
|
|
|
return datetime.TZ_RRule{
|
|
has_dst = true,
|
|
|
|
std_name = std_name,
|
|
std_offset = -(i64(tzi.bias) + i64(tzi.std_bias)) * 60,
|
|
dst_date = datetime.TZ_Transition_Date{
|
|
type = .Month_Week_Day,
|
|
month = u8(tzi.std_date.month),
|
|
week = u8(tzi.std_date.day),
|
|
day = tzi.std_date.day_of_week,
|
|
time = (i64(tzi.std_date.hour) * 60 * 60) + (i64(tzi.std_date.minute) * 60) + i64(tzi.std_date.second),
|
|
},
|
|
|
|
dst_name = dst_name,
|
|
dst_offset = -(i64(tzi.bias) + i64(tzi.dst_bias)) * 60,
|
|
std_date = datetime.TZ_Transition_Date{
|
|
type = .Month_Week_Day,
|
|
month = u8(tzi.dst_date.month),
|
|
week = u8(tzi.dst_date.day),
|
|
day = tzi.dst_date.day_of_week,
|
|
time = (i64(tzi.dst_date.hour) * 60 * 60) + (i64(tzi.dst_date.minute) * 60) + i64(tzi.dst_date.second),
|
|
},
|
|
}, true
|
|
}
|
|
|
|
_region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, success: bool) {
|
|
wintz_name: string
|
|
iana_name: string
|
|
|
|
if reg_str == "local" {
|
|
ok := false
|
|
|
|
iana_name = local_tz_name(allocator) or_return
|
|
wintz_name, ok = iana_to_windows_tz(iana_name, allocator)
|
|
if !ok {
|
|
delete(iana_name, allocator)
|
|
return
|
|
}
|
|
} else {
|
|
wintz_name = iana_to_windows_tz(reg_str, allocator) or_return
|
|
iana_name = strings.clone(reg_str, allocator)
|
|
}
|
|
defer delete(wintz_name, allocator)
|
|
defer delete(iana_name, allocator)
|
|
|
|
abbrevs := tz_abbrevs[wintz_name] or_return
|
|
if abbrevs.std == "UTC" && abbrevs.dst == abbrevs.std {
|
|
return nil, true
|
|
}
|
|
|
|
key_base := `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`
|
|
tz_key := strings.join({key_base, wintz_name}, "\\", allocator = allocator)
|
|
defer delete(tz_key, allocator)
|
|
|
|
tz_key_wstr := windows.utf8_to_wstring(tz_key, allocator)
|
|
defer free(tz_key_wstr, allocator)
|
|
|
|
key: windows.HKEY
|
|
res := windows.RegOpenKeyExW(windows.HKEY_LOCAL_MACHINE, tz_key_wstr, 0, windows.KEY_READ, &key)
|
|
if res != 0 { return }
|
|
defer windows.RegCloseKey(key)
|
|
|
|
tzi: REG_TZI_FORMAT
|
|
size := u32(size_of(REG_TZI_FORMAT))
|
|
|
|
res = windows.RegGetValueW(key, nil, windows.L("TZI"), windows.RRF_RT_ANY, nil, &tzi, &size)
|
|
if res != 0 {
|
|
return
|
|
}
|
|
|
|
rrule := generate_rrule_from_tzi(&tzi, abbrevs, allocator) or_return
|
|
|
|
region_name, err := strings.clone(iana_name, allocator)
|
|
if err != nil { return }
|
|
defer if err != nil { delete(region_name, allocator) }
|
|
|
|
region: ^datetime.TZ_Region
|
|
region, err = new_clone(datetime.TZ_Region{
|
|
name = region_name,
|
|
rrule = rrule,
|
|
}, allocator)
|
|
if err != nil { return }
|
|
|
|
return region, true
|
|
}
|