pola-rs/polars · error
unexpected time zone offset: {offset!r}
Error message
unexpected time zone offset: {offset!r} What it means
_parse_fixed_tz_offset (lru_cached) parses fixed UTC offset strings by appending them to an ISO datetime and calling datetime.fromisoformat. If the offset is not in the strict ±HH:MM form that fromisoformat accepts (e.g. "UTC+02:00", "+0200", "+2", or trailing garbage), the ValueError from fromisoformat is re-raised as this error with the offending offset.
Source
Thrown at py-polars/src/polars/_utils/convert.py:192
tz = _parse_fixed_tz_offset(time_zone)
return dt.astimezone(tz)
# cache here as we have a single tz per column
# and this function will be called on every conversion
@lru_cache(16)
def _parse_fixed_tz_offset(offset: str) -> tzinfo:
try:
# use fromisoformat to parse the offset
dt_offset = datetime.fromisoformat("2000-01-01T00:00:00" + offset)
# alternatively, we parse the offset ourselves extracting hours and
# minutes, then we can construct:
# tzinfo=timezone(timedelta(hours=..., minutes=...))
except ValueError:
msg = f"unexpected time zone offset: {offset!r}"
raise ValueError(msg) from None
return dt_offset.tzinfo # type: ignore[return-value]
def to_py_timedelta(value: int | float, time_unit: TimeUnit) -> timedelta:
"""Convert an integer or float to a Python timedelta object."""
if time_unit == "us":
return timedelta(microseconds=value)
elif time_unit == "ns":
return timedelta(microseconds=value // 1_000)
elif time_unit == "ms":
return timedelta(milliseconds=value)
else:
_raise_invalid_time_unit(time_unit)
def to_py_decimal(prec: int, value: str) -> Decimal:
"""Convert decimal components to a Python Decimal object."""View on GitHub (pinned to df599052da)
Solutions
- Normalize the offset to ±HH:MM before parsing (strip 'UTC'/'GMT', insert the colon).
- Upgrade to Python ≥ 3.11, where fromisoformat accepts "+0200" and more variants.
- Convert the offset yourself: timezone(timedelta(hours=h, minutes=m)) and pass tzinfo directly instead of the string.
Example fix
// before
pl.Series(["2024-01-01T00:00:00+0200"]).str.to_datetime()
// after
import re
s = pl.Series(["2024-01-01T00:00:00+0200"])
fixed = re.sub(r"([+-]\d{2})(\d{2})$", r"\1:\2", s[0])
pl.Series([fixed]).str.to_datetime() Defensive patterns
Strategy: validation
Validate before calling
import re
VALID_FIXED_OFFSET = re.compile(r"^[+-]\d{2}:\d{2}(:\d{2}(\.\d+)?)?$")
def normalize_offset(off: str) -> str:
off = re.sub(r"^(?:UTC|GMT)", "", off.strip())
m = re.fullmatch(r"([+-]\d{2})(\d{2})", off)
if m:
off = f"{m.group(1)}:{m.group(2)}"
if not VALID_FIXED_OFFSET.fullmatch(off):
raise ValueError(f"unexpected time zone offset: {off!r}")
return off Type guard
def is_iso_fixed_offset(off: str) -> bool:
import re
return bool(re.fullmatch(r"[+-]\d{2}:\d{2}(:\d{2}(\.\d+)?)?", off)) Try / catch
try:
s = pl.Series([dt_string]).str.to_datetime()
except ValueError as e:
if "unexpected time zone offset" in str(e):
s = pl.Series([normalize_offset_string(dt_string)]).str.to_datetime()
else:
raise Prevention
- Normalize tz fields to ±HH:MM before parsing on any Python version.
- Run datetime parsing on Python ≥ 3.11 for lenient fromisoformat.
- Prefer zoneinfo/typed tzinfo over free-form offset strings in your data model.
When it happens
Trigger: Datetime strings with non-canonical fixed offsets ("2024-01-01T00:00:00UTC+02:00", "+0200" on Python < 3.11); offsets like "+0530" without a colon on older interpreters; stray characters appended to the offset field.
Common situations: Parsing logs or exports whose tz field uses "GMT+2"-style or colon-less formats; running on Python 3.10 or older where fromisoformat only accepts ±HH:MM[:SS[.ffffff]]; pandas→polars conversion of tz-aware data with unusual fixed offsets.
Related errors
- time zone of dtype ({dtype_tz!r}) differs from time zone of
- datetime time zone {other.tzinfo!r} does not match Series ti
- comparing datetimes with different units or timezones is not
- 'float' object cannot be interpreted as a {python_dtype.__na
- `time_unit` must be one of {'ms', 'us', 'ns'}, got {time_uni
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/cc0b883f7220fa0c.
Report an issue: GitHub.