Skip to content

Instantly share code, notes, and snippets.

@mrled
Last active August 3, 2023 13:34
Show Gist options
  • Save mrled/8d29fde758cfc7dd0b52f3bbf2b8f06e to your computer and use it in GitHub Desktop.
Save mrled/8d29fde758cfc7dd0b52f3bbf2b8f06e to your computer and use it in GitHub Desktop.
[Converting timezones from Windows to IANA in Python]

Converting timezones from Windows to IANA and back in Python

Spoiler alert: I couldn't end up doing this, jesus christ timezones are such a mess

General notes

There are many mappings between timezones.

  1. Obviously I could maintain one myself, but it's sufficiently complex that I don't want to take this on
  2. Unicode maintains a mapping of Windows to IANA zones, but it only has entryes for standard timezone names ("Pacific Standard Time") and not DST timezone names ("Pacific Daylight Time"): https://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml
  3. The tzlocal module has the same problem: Windows mappings only for standard, not DST, timezone names
  4. The pytz module has no support for Windows names at all
  5. As alluded to, Windows has names like "Pacific Standard Time". The IANA tzdb timezone database has names like "US/Pacific".

Converting between Standard (non-DST) timezones

def timezone_py2win(cls, tzname):
    """Convert a Python timezone name to a Windows timezone name

    For example: CST -> Central Standard Time
    """
    winzones_url = 'https://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml'
    winzones_xml = urllib.request.urlopen(winzones_url).read().decode()
    winzones = xml.etree.ElementTree.parse(winzones_xml)
    for zone in winzones.findall('.//mapZone'):
        if zone.attrib['territory'] == '001' and zone.attrib['type'] == tzname:
            return zone.attrib['other']
    raise Exception(
        f"Could not find a Windows timezone for a Python timezone named '{tzname}''")

Allowing user-settable timezones in Docker

I had something like this in my Dockerfile. This will work fine if PSYOPS_TIMEZONE is set to an IANA timezone

Dockerfile (excerpt):

ENV PSYOPS_USER="psyops"
ENV PSYOPS_TIMEZONE="UTC"
RUN true \
    && apk add shadow tzdata \
    && addgroup -S timekeeper \
    && usermod -g timekeeper "$PSYOPS_USER" \
    && chgroup timekeeper /etc/localtime \
    && true
CMD /bin/bash -i
ENTRYPOINT $HOME/.entrypoint.sh

$HOME/.entrypoint.sh:

#!/bin/bash
cat "/usr/share/zoneinfo/${PSYOPS_TIMEZONE}" > /etc/localtime
exec /bin/bash -i

This should work for any IANA value like US/Central or whatever, and, since we don't set the timezone in the Dockerfile but rather in a script that is read when the container is started, we can change the timezone at runtime.

Time zone names on Linux

Linux appears to use IANA names like US/Central

Time zone names on Windows

Windows has its own naming system. These .NET calls, which work in both Powershell and C#:

[System.TimeZoneInfo]::Local.DisplayName
[System.TimeZoneInfo]::Local.Id
[System.TimeZoneInfo]::Local.DaylightName

currently return the following values on my system:

(UTC-06:00) Central Time (US & Canada)
Central Standard Time
Central Daylight Time

Time zone names on macOS

I believe macOS uses a different naming system, but I'm not sure because I don't have a Mac in front of me at the moment

Getting the system time zone name in Python

Python provides

import datetime, time

datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
datetime.datetime.now(datetime.timezone.utc).astimezone().tzname()
time.tzname

Which on my Windows system currently return

datetime.timezone(datetime.timedelta(-1, 68400), 'Central Daylight Time')
Central Daylight Time
('Central Standard Time', 'Central Daylight Time')

respectively. Note that the .tzname() function returns the DST name on a Windows system currently observing DST, while the time.tzname variable holds both names in a list.

Setting the timezone in an ARM template

Off topic for Python, but related to what I was trying to do:

When setting the time for a VM defined in an Azure Resource Manager template, you have to set the osProfile.windowsConfiguration.timeZone property on the VM resource. This property takes only standard names, and if the timezone you pass is currently observing DST, it will still display the correct time (e.g. in the systray or with Powershell's Get-Date).

Python modules

Python provides some support for the concept of timezones, but leaves more robust timezone support to third party modules. There are a couple of Python modules I've seen recommended when dealing with time zones.

  • tzlocal uses the Unicode Consortium's windowsZones.xml to convert between Windows time zone names
  • pytz is more commonly recommended and also maintains a mapping for Windows zone names, but I'm not sure whether it comes from the Unicode Consortium or elsewhere

Getting a comprehensive list of Windows timezone names from Powershell

I opened a bug with Microsoft about the lack of an enumeration, and the answer was basically that there isn't a published enumeration because it would be a maintenance burden (it probably changes frequently, as counterintuitive as that might be), but that you can get a list from .NET (C#/Powershell):

[System.TimeZoneInfo]::GetSystemTimeZones()

This even works with Powershell Core on my Mac! However, the property used in the answer is Id, and on the Mac, that returns IANA timezone names (lol), while on Windows it apparently returns Windows names. The StandardName property always contains the standard (non-DST) Windows timezone name, even on my Mac.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment