I wasn’t able to find a good method for converting windows time zones to the IANA format I needed. This method isn’t perfect but it gets the job done for my purposes.
First you need to get the windowsZones.xml file from:
https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml
The following PowerShell script converts the XML into the body of the switch statement used in the lookup script. This is based on another script I found here on StackOverflow.
# https://github.com/unicode-org/cldr/blob/main/common/supplemental/windowsZones.xml
# Download the xml file.
[Xml]$xml = Get-Content -Path "windowsZones.xml"
# Parse the fields we want from the XML.
$mappings1 = $xml.supplementalData.windowsZones.mapTimezones.mapZone | select Type,Other
# Extrapolate extra rows for entries that contain more than one IANA name seperated by spaces.
# Example: |<mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Nome America/Sitka America/Yakutat"/>
$mappings2 = $mappings1 | %{
$mapping = $_
$_.Type.Split(" ") | %{
New-Object PSObject -Property @{type = $_; other = $mapping.other}
}
}
# Remove dup's
$mappings3 = $mappings2 | sort type -Unique
# Generate the case statements.
$mappings3 | %{ [String]::Format("`"{1}`" {{`"{0}`"}}", $_.Type, $_.Other)}
I need the switch to return only one entry so I pruned that list down. Here’s the lookup script:
$CurrentTimeZone = Get-TimeZone
$tz = Switch -Wildcard ($CurrentTimeZone.id)
{
"Afghanistan Standard Time" { "Asia/Kabul" }
"Alaskan Standard Time" { "America/Anchorage" }
"Aleutian Standard Time" { "America/Adak" }
"Altai Standard Time" { "Asia/Barnaul" }
"Arab Standard Time" { "Asia/Bahrain" }
"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" }
"Atlantic Standard Time" { "Atlantic/Bermuda" }
"AUS Central Standard Time" { "Australia/Darwin" }
"Aus Central W. Standard Time" { "Australia/Eucla" }
"AUS Eastern Standard Time" { "Australia/Sydney" }
"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/Costa_Rica" }
"Central Asia Standard Time" { "Asia/Bishkek" }
"Central Brazilian Standard Time" { "America/Cuiaba" }
"Central Europe Standard Time" { "Europe/Prague" }
"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/Hong_Kong" }
"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" }
"Fiji Standard Time" { "Pacific/Fiji" }
"FLE Standard Time" { "Europe/Helsinki" }
"Georgian Standard Time" { "Asia/Tbilisi" }
"GMT Standard Time" { "Europe/London" }
"Greenland Standard Time" { "America/Godthab" }
"Greenwich Standard Time" { "Atlantic/Reykjavik" }
"GTB Standard Time" { "Europe/Athens" }
"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/Brussels" }
"Russia Time Zone 10" { "Asia/Srednekolymsk" }
"Russia Time Zone 11" { "Asia/Anadyr" }
"Russia Time Zone 3" { "Europe/Samara" }
"Russian Standard Time" { "Europe/Moscow" }
"SA Eastern Standard Time" { "America/Recife" }
"SA Pacific Standard Time" { "America/Bogota" }
"SA Western Standard Time" { "America/Puerto_Rico" }
"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" }
"SE Asia Standard Time" { "Asia/Bangkok" }
"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" }
"Ulaanbaatar Standard Time" { "Asia/Ulaanbaatar" }
"US Eastern Standard Time" { "America/Indianapolis" }
"US Mountain Standard Time" { "America/Phoenix" }
"UTC" { "Etc/UTC" }
"UTC+12" { "Pacific/Funafuti" }
"UTC+13" { "Pacific/Enderbury" }
"UTC-02" { "Atlantic/South_Georgia" }
"UTC-08" { "Pacific/Pitcairn" }
"UTC-09" { "Pacific/Gambier" }
"UTC-11" { "Pacific/Midway" }
"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/Algiers" }
"W. Europe Standard Time" { "Europe/Berlin" }
"W. Mongolia Standard Time" { "Asia/Hovd" }
"West Asia Standard Time" { "Asia/Almaty" }
"West Bank Standard Time" { "Asia/Hebron" }
"West Pacific Standard Time" { "Pacific/Guam" }
"Yakutsk Standard Time" { "Asia/Yakutsk" }
"Yukon Standard Time" { "America/Whitehorse" }
}
write-host $CurrentTimeZone
write-host $tz
4
You can simplify your code significantly by using XPath to lookup mappings directly against the XML document:
$xPath = '//supplementalData/windowsZones/mapTimezones/mapZone[@other = "Bahia Standard Time"]'
$bahiaEntry = $xml.SelectSingleNode($xPath)
$bahiaEntry.Type # resolves to `America/Bahia`
Wrapping this in a function is trivial, but you may want to additionally host the function in a module, like so:
New-Module -Name tzResolutionModule -ScriptBlock {
function Resolve-WindowsTimeZoneId {
param(
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
[Alias('Id')]
[string]$TZIdentifier = $(Get-TimeZone |% Id))
process {
$xPathExpression = '//supplementalData/windowsZones/mapTimezones/mapZone[@other = "{0}"]' -f $TZIdentifier
$mapZoneEntry = $script:_timeZoneMap.SelectSingleNode($xPathExpression)
if (-not $mapZoneEntry){
throw "Unable to resolve time zone label '${TZIdentifier}'"
}
else {
# we found at least one entry, output the first corresponding IANA identifier
return -split $mapZoneEntry.Type |Select-Object -First 1
}
}
}
$_timeZoneMap = Invoke-RestMethod -Uri https://github.com/unicode-org/cldr/raw/main/common/supplemental/windowsZones.xml
} |Import-Module -Force
Wrapping the Resolve-WindowsTimeZoneId
in a module allows us to take advantage of module-scoped variables for persistent storage – in this case to ensure we only download the zone mapping document once, on module import.
Now you can do:
PS ~> Resolve-WindowsTimeZoneId 'Central Standard Time'
America/Chicago
And get a near-instant response – but thanks to the [Parameter(...)]
and [Alias(...)]
decorators on the function parameter, you can now also do:
Get-TimeZone |Resolve-WindowsTimeZoneId
# or
'Central Standard Time' |Resolve-WindowsTimeZoneId
3