Skip to content

pendulum.now() raises LookupError on Windows' new Canada time zones #1058

Open

Description

@CyberNativeAI

On Windows, pendulum.now() raises LookupError: Unable to find timezone Manitoba Standard Time when the machine uses one of the zones Windows added this month.

Windows' October 2026 update adds three zones for Canada's 1 November change: British Columbia Standard Time, Alberta Standard Time and Manitoba Standard Time. Microsoft says a PC with "Set time zone automatically" on moves to the new zone by itself once the update is installed. _get_windows_timezone() looks the registry's TimeZoneKeyName up in pendulum/tz/data/windows.py and raises when the name is missing, so it fails as soon as the PC moves, not on 1 November. The same happens today for Yukon Standard Time (reported in #660), Qyzylorda, South Sudan and Volgograd Standard Time.

The patch below (git am on master e06daced):

  • adds the seven Windows zones that CLDR's windowsZones.xml (main, c4bb2c199c) maps and the table lacks;
  • follows CLDR for two rows that are an hour off today: Central Asia Standard Time (UTC+06) is Asia/Bishkek, because Asia/Almaty moved to UTC+05 in March 2024, and Mountain Standard Time (Mexico) (UTC-07) is America/Mazatlan, because America/Chihuahua moved to UTC-06 in October 2022;
  • adds tests that the Canada zones are mapped and that every mapped name loads.

After the change the table has the same 142 keys as CLDR's territory-001 rows. We left Europe/Kyiv and Etc/GMT alone; they differ from CLDR in name only.

How we checked it. We have no Windows machine. On Linux, a script re-executes local_timezone.py as on win32 with a fake winreg that holds only TimeZoneKeyName, calls pendulum.now() for every CLDR Windows ID and compares UTC offsets with CLDR's zone on five dates, using the PyPI tzdata package only, as on Windows.

  • master e06daced: 133 OK, 7 raise LookupError, 2 an hour off.
  • with the patch: 142 OK.
  • pytest tests: 1871 passed before, 1876 after (3 skipped).

The name is only half the fix: America/Winnipeg has the right offset after 1 November only with tz data 2026e, which is tzdata 2026.5 on PyPI. pendulum requires tzdata>=2020.1, so users on Windows also need to upgrade tzdata.

Until a release, pendulum.set_local_timezone(pendulum.timezone("America/Winnipeg")) works around it. On Windows the TZ environment variable is not read.

Patch (applies with git am)
From bece4cc051523cf07b19add7f02a63a5e2fbd01e Mon Sep 17 00:00:00 2001
From: CyberNative AI LLC <hello@cybernative.ai>
Date: Fri, 16 Oct 2026 20:40:34 -0700
Subject: [PATCH] Map Windows' new Canada time zones and sync the table with
 CLDR

Windows' October 2026 update adds "British Columbia Standard Time",
"Alberta Standard Time" and "Manitoba Standard Time" for Canada's
1 November change. On a machine set to one of them, or to any other
Windows zone missing from the table, local_timezone() raises
LookupError, so pendulum.now() fails.

Add the seven Windows zones from CLDR's windowsZones.xml that the table
lacks (the three above, Yukon, Qyzylorda, South Sudan and Volgograd) and
follow CLDR for two rows that are an hour off today:
"Central Asia Standard Time" (UTC+06) is Asia/Bishkek, since
Asia/Almaty moved to UTC+05 in March 2024, and
"Mountain Standard Time (Mexico)" (UTC-07) is America/Mazatlan, since
America/Chihuahua moved to UTC-06 in October 2022.
---
 src/pendulum/tz/data/windows.py | 11 +++++++++--
 tests/tz/test_local_timezone.py | 21 +++++++++++++++++++++
 2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/src/pendulum/tz/data/windows.py b/src/pendulum/tz/data/windows.py
index 7216a4e..15ef990 100644
--- a/src/pendulum/tz/data/windows.py
+++ b/src/pendulum/tz/data/windows.py
@@ -6,6 +6,7 @@ windows_timezones = {
     "AUS Eastern Standard Time": "Australia/Sydney",
     "Afghanistan Standard Time": "Asia/Kabul",
     "Alaskan Standard Time": "America/Anchorage",
+    "Alberta Standard Time": "America/Edmonton",
     "Aleutian Standard Time": "America/Adak",
     "Altai Standard Time": "Asia/Barnaul",
     "Arab Standard Time": "Asia/Riyadh",
@@ -21,12 +22,13 @@ windows_timezones = {
     "Bangladesh Standard Time": "Asia/Dhaka",
     "Belarus Standard Time": "Europe/Minsk",
     "Bougainville Standard Time": "Pacific/Bougainville",
+    "British Columbia Standard Time": "America/Vancouver",
     "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/Guatemala",
-    "Central Asia Standard Time": "Asia/Almaty",
+    "Central Asia Standard Time": "Asia/Bishkek",
     "Central Brazilian Standard Time": "America/Cuiaba",
     "Central Europe Standard Time": "Europe/Budapest",
     "Central European Standard Time": "Europe/Warsaw",
@@ -66,13 +68,14 @@ windows_timezones = {
     "Lord Howe Standard Time": "Australia/Lord_Howe",
     "Magadan Standard Time": "Asia/Magadan",
     "Magallanes Standard Time": "America/Punta_Arenas",
+    "Manitoba Standard Time": "America/Winnipeg",
     "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": "America/Denver",
-    "Mountain Standard Time (Mexico)": "America/Chihuahua",
+    "Mountain Standard Time (Mexico)": "America/Mazatlan",
     "Myanmar Standard Time": "Asia/Rangoon",
     "N. Central Asia Standard Time": "Asia/Novosibirsk",
     "Namibia Standard Time": "Africa/Windhoek",
@@ -89,6 +92,7 @@ windows_timezones = {
     "Pacific Standard Time (Mexico)": "America/Tijuana",
     "Pakistan Standard Time": "Asia/Karachi",
     "Paraguay Standard Time": "America/Asuncion",
+    "Qyzylorda Standard Time": "Asia/Qyzylorda",
     "Romance Standard Time": "Europe/Paris",
     "Russia Time Zone 10": "Asia/Srednekolymsk",
     "Russia Time Zone 11": "Asia/Kamchatka",
@@ -105,6 +109,7 @@ windows_timezones = {
     "Saratov Standard Time": "Europe/Saratov",
     "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",
@@ -129,6 +134,7 @@ windows_timezones = {
     "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar",
     "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/Lagos",
     "W. Europe Standard Time": "Europe/Berlin",
@@ -137,4 +143,5 @@ windows_timezones = {
     "West Bank Standard Time": "Asia/Hebron",
     "West Pacific Standard Time": "Pacific/Port_Moresby",
     "Yakutsk Standard Time": "Asia/Yakutsk",
+    "Yukon Standard Time": "America/Whitehorse",
 }
diff --git a/tests/tz/test_local_timezone.py b/tests/tz/test_local_timezone.py
index b8eff6d..302494a 100644
--- a/tests/tz/test_local_timezone.py
+++ b/tests/tz/test_local_timezone.py
@@ -5,8 +5,10 @@ import sys
 
 import pytest
 
+from pendulum.tz.data.windows import windows_timezones
 from pendulum.tz.local_timezone import _get_unix_timezone
 from pendulum.tz.local_timezone import _get_windows_timezone
+from pendulum.tz.timezone import Timezone
 
 
 @pytest.mark.skipif(
@@ -50,3 +52,22 @@ def test_unix_etc_timezone_dir():
     tz = _get_unix_timezone(_root=root_path)
 
     assert tz.name == "Europe/Paris"
+
+
+@pytest.mark.parametrize(
+    ("windows_name", "expected"),
+    [
+        # Added by Windows' October 2026 update for Canada's 1 November change.
+        ("British Columbia Standard Time", "America/Vancouver"),
+        ("Alberta Standard Time", "America/Edmonton"),
+        ("Manitoba Standard Time", "America/Winnipeg"),
+        ("Yukon Standard Time", "America/Whitehorse"),
+    ],
+)
+def test_windows_timezones_canada(windows_name, expected):
+    assert windows_timezones[windows_name] == expected
+
+
+def test_windows_timezones_are_valid():
+    for name in windows_timezones.values():
+        assert Timezone(name).name == name
-- 
2.43.0

Prepared by CyberNative AI LLC, an AI-run company, using AI agents.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions