---
hide-toc: true
myst:
html_meta:
description: >-
Why ZoneInfo time zone IDs are case-sensitive on some filesystems
but not others, how that breaks portability and even arithmetic, and how
whenever normalizes casing.
---
# Time zone casing is platform-dependent
{class}`zoneinfo.ZoneInfo` does not normalize time zone IDs.
It looks up the given string as a file path, so casing follows the underlying
filesystem. For example, this typically succeeds on macOS but fails on Linux:
```python
ZoneInfo("europe/amsterdam")
```
This makes validation non-portable. A value accepted during development can
be stored in a database or pickle, then fail with
{class}`~zoneinfo.ZoneInfoNotFoundError` in production. When lookup succeeds,
{attr}`ZoneInfo.key ` also preserves the caller's
casing instead of returning the database spelling.
Even more surprisingly, two spellings can change arithmetic on a
case-insensitive filesystem:
```python
canonical = ZoneInfo("Europe/Amsterdam")
lowercase = ZoneInfo("europe/amsterdam")
canonical is lowercase
# False—even though they loaded the same time zone file
before = datetime(2024, 3, 31, 1, 30, tzinfo=canonical)
after1 = datetime(2024, 3, 31, 3, 30, tzinfo=canonical)
after2 = datetime(2024, 3, 31, 3, 30, tzinfo=lowercase)
after1 - before
# datetime.timedelta(seconds=7200)
after2 - before
# datetime.timedelta(seconds=3600)
```
This happens because `datetime` switches subtraction rules based on whether
the two `tzinfo` objects are identical. See {ref}`datetime-ignores-dst`.
The behavior is discussed in
[CPython issue #115022](https://github.com/python/cpython/issues/115022).
The IANA reference implementation is case-sensitive, although its
[naming rules](https://data.iana.org/time-zones/theory.html#naming) forbid
IDs that differ only in case.
## How `whenever` solves this
Whenever matches ASCII casing portably and exposes the database spelling:
```python
>>> ZonedDateTime(2024, 3, 31, 3, 30, tz="europe/amsterdam")
ZonedDateTime("2024-03-31 03:30:00+02:00[Europe/Amsterdam]")
```
All casing variants also share one cached time zone definition, so casing
cannot change comparison or arithmetic behavior.