Skip to content

yggdrasil.enums.timezone

timezone

Timezone normalisation and conversion utilities.

The :class:Timezone dataclass wraps an IANA timezone identifier (e.g. "Europe/Paris", "UTC") and provides:

  • Coercion from any common Python timezone shape — strings (IANA names, abbreviations like CET / EST, fixed offsets like +01:00 / UTC-05), ZoneInfo, datetime.tzinfo, datetime instances with a tzinfo attached, or other Timezone objects.
  • Conversion helperslocalize naive datetimes, convert between zones, extract utc_offset at a given instant.
  • Polars integrationfrom_polars_type extracts the tz from a pl.Datetime, polars_normalize maps a Series/Expr of timezone strings to canonical IANA names.
  • Pre-built constants — :attr:Timezone.UTC, :attr:Timezone.CET, :attr:Timezone.NAIVE, etc.

The single coercion entry point is :meth:Timezone.from_. There is no parse / parse_str API anymore — every call site routes through from_ so the same input rules apply everywhere.

Timezone dataclass

Timezone(iana: str)

An immutable wrapper around an IANA timezone identifier.

Instances are created via :meth:from_ (accepts strings, ZoneInfo, datetime.tzinfo, other Timezone objects, or None → UTC) or directly::

tz = Timezone("Europe/Paris")
tz = Timezone.from_("CET")        # → Timezone("Europe/Paris")
tz = Timezone.from_("+01:00")     # → Timezone("Etc/GMT-1")
tz = Timezone.from_(ZoneInfo("Europe/Paris"))

The naive case is represented by :attr:Timezone.NAIVE — a sentinel instance whose iana is the empty string and whose __bool__ is False. Use it instead of None so a tz: Timezone field type can stay non-optional.

key property

key: str

Alias for :attr:iana — matches ZoneInfo.key.

utc_seconds_offset property

utc_seconds_offset: int | None

Fixed UTC offset in seconds, or None for non-fixed zones.

Returns 0 for UTC and its equivalents (Etc/UTC, GMT, Etc/GMT+0, …), the parsed offset for Etc/GMT±N (note IANA's sign-flip — Etc/GMT-3 means UTC+3, so the property reports +10800 not -10800), and None for any zone whose offset depends on DST or for :attr:Timezone.NAIVE. Use :meth:utc_offset instead when you need a DST-aware offset for a specific instant.

Naming follows the "type-suffix unit" convention so utc_seconds_offset makes the unit explicit at the call site — utc_offset returns a :class:datetime.timedelta, utc_offset_hours returns fractional hours, and this property returns whole seconds (or None).

tzinfo property

tzinfo: tzinfo

Return as a stdlib tzinfo (same as to_zoneinfo()).

from_ classmethod

from_(obj: Any, *, default: Any = ...) -> 'Timezone'

Coerce any Python value into a :class:Timezone.

Accepts:

  • :class:Timezone (returned as-is — including Timezone.NAIVE);
  • :class:zoneinfo.ZoneInfo (extracts key);
  • a string — IANA name, alias (CET / EST / Z / GMT / Etc/UTC / +00:00 / …), or fixed offset ("+01:00", "UTC-05", "-0530");
  • datetime.tzinfo instance (ZoneInfo, datetime.timezone, third-party zones) — the key / zone attribute is preferred; otherwise falls back to the fixed UTC offset;
  • timezone-aware datetime / time (extracts tzinfo);
  • objects exposing a tz / time_zone / timezone / iana attribute (PyArrow TimestampType, Polars Datetime, foreign Timezone classes); the attribute is re-funneled through from_;
  • None — returns :attr:UTC for backward compatibility unless default is supplied.

default swallows unknown / unparseable input. Without it, bad input raises :class:ValueError (string parse failure) or :class:TypeError (unsupported value type).

to_zoneinfo cached

to_zoneinfo() -> ZoneInfo

Return the zoneinfo.ZoneInfo for this timezone.

Raises :class:ValueError for :attr:Timezone.NAIVE since there is no concrete zone to materialize.

utc_offset

utc_offset(at: datetime | None = None) -> _dt.timedelta

Return the UTC offset at instant at (default: now).

The result accounts for DST transitions.

Timezone("Europe/Paris").utc_offset(datetime(2024, 7, 1)) datetime.timedelta(seconds=7200) # +02:00 (CEST)

utc_offset_hours

utc_offset_hours(at: datetime | None = None) -> float

Return the UTC offset in fractional hours.

Timezone("Asia/Kolkata").utc_offset_hours() 5.5

is_naive

is_naive() -> bool

Return True for :attr:Timezone.NAIVE — the tz-less sentinel.

is_utc

is_utc() -> bool

Return True if this timezone is UTC (or an equivalent).

is_fixed_offset

is_fixed_offset() -> bool

Return True if this timezone has a fixed UTC offset (no DST).

is_dst

is_dst(at: datetime | None = None) -> bool

Return True if DST is active at instant at (default: now).

Timezone("Europe/Paris").is_dst(datetime(2024, 7, 1)) True Timezone("Europe/Paris").is_dst(datetime(2024, 1, 1)) False

dst_offset

dst_offset(at: datetime | None = None) -> _dt.timedelta

Return the DST adjustment at instant at.

Returns timedelta(0) when DST is not active or for fixed-offset timezones, and for :attr:Timezone.NAIVE.

abbreviation

abbreviation(at: datetime | None = None) -> str

Return the timezone abbreviation (e.g. "CET", "CEST") at at.

Timezone("Europe/Paris").abbreviation(datetime(2024, 7, 1)) 'CEST' Timezone("Europe/Paris").abbreviation(datetime(2024, 1, 1)) 'CET'

distance_to

distance_to(other: 'Timezone', at: datetime | None = None) -> _dt.timedelta

Return the offset difference between self and other at at.

A positive result means other is ahead of self.

Timezone.UTC.distance_to(Timezone.CET, datetime(2024, 7, 1)) datetime.timedelta(seconds=7200)

now

now() -> _dt.datetime

Return the current wall-clock time in this timezone.

The returned datetime is timezone-aware.

today

today() -> _dt.date

Return today's date in this timezone.

localize

localize(naive: datetime) -> _dt.datetime

Stamp a naive datetime with this timezone (no conversion).

Raises:

Type Description
ValueError

If naive is already timezone-aware, or if this is :attr:Timezone.NAIVE.

convert

convert(aware: datetime) -> _dt.datetime

Convert a timezone-aware datetime to this timezone.

Raises:

Type Description
ValueError

If aware is naive, or if this is :attr:Timezone.NAIVE.

midnight

midnight(date: date | None = None) -> _dt.datetime

Return midnight (00:00) in this timezone for date (default: today).

from_polars_type classmethod

from_polars_type(dtype: 'polars.DataType') -> 'Timezone | None'

Extract the timezone from a Polars Datetime dtype.

Returns None if the dtype is not Datetime or has no timezone.

Timezone.from_polars_type(pl.Datetime("us", "Europe/Paris")) Timezone('Europe/Paris')

polars_normalize classmethod

polars_normalize(
    col: "polars.Series | polars.Expr",
    *,
    lazy: bool = True,
    return_value: Literal["iana"] = "iana"
) -> "polars.Series | polars.Expr"

Normalize timezone strings in a Polars column using alias replacement only.

from_arrow_type classmethod

from_arrow_type(dtype: Any) -> 'Timezone | None'

Extract the timezone from a PyArrow TimestampType.

Returns None if the type has no tz attribute or it is empty.

import pyarrow as pa Timezone.from_arrow_type(pa.timestamp("us", tz="Europe/Paris")) Timezone('Europe/Paris')

arrow_timestamp_type

arrow_timestamp_type(unit: str = 'us') -> Any

Return a pa.timestamp(unit, tz=self.iana) type.

Timezone.CET.arrow_timestamp_type("ns") TimestampType(timestamp[ns, tz=Europe/Paris])

all_iana classmethod

all_iana() -> frozenset[str]

Return all IANA timezone identifiers available on this system.