mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-05 04:24:41 +00:00
324 lines
15 KiB
Odin
324 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,
|
|
}
|
|
|
|
@(rodata)
|
|
tz_abbrevs := [?]struct{key: string, value: 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(rawptr(iana_name_wstr), allocator)
|
|
|
|
wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, cstring16(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(cstring16(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_Abbrev
|
|
abbrevs_ok: bool
|
|
for pair in tz_abbrevs {
|
|
if pair.key == wintz_name {
|
|
abbrevs = pair.value
|
|
abbrevs_ok = true
|
|
break
|
|
}
|
|
}
|
|
if !abbrevs_ok {
|
|
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(rawptr(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
|
|
}
|