{"record":{"id":"cc0b883f7220fa0c","repo":"pola-rs/polars","slug":"unexpected-time-zone-offset-offset-r","errorCode":null,"errorMessage":"unexpected time zone offset: {offset!r}","messagePattern":"unexpected time zone offset: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/_utils/convert.py","lineNumber":192,"sourceCode":"        tz = _parse_fixed_tz_offset(time_zone)\n\n    return dt.astimezone(tz)\n\n\n# cache here as we have a single tz per column\n# and this function will be called on every conversion\n@lru_cache(16)\ndef _parse_fixed_tz_offset(offset: str) -> tzinfo:\n    try:\n        # use fromisoformat to parse the offset\n        dt_offset = datetime.fromisoformat(\"2000-01-01T00:00:00\" + offset)\n\n        # alternatively, we parse the offset ourselves extracting hours and\n        # minutes, then we can construct:\n        # tzinfo=timezone(timedelta(hours=..., minutes=...))\n    except ValueError:\n        msg = f\"unexpected time zone offset: {offset!r}\"\n        raise ValueError(msg) from None\n\n    return dt_offset.tzinfo  # type: ignore[return-value]\n\n\ndef to_py_timedelta(value: int | float, time_unit: TimeUnit) -> timedelta:\n    \"\"\"Convert an integer or float to a Python timedelta object.\"\"\"\n    if time_unit == \"us\":\n        return timedelta(microseconds=value)\n    elif time_unit == \"ns\":\n        return timedelta(microseconds=value // 1_000)\n    elif time_unit == \"ms\":\n        return timedelta(milliseconds=value)\n    else:\n        _raise_invalid_time_unit(time_unit)\n\n\ndef to_py_decimal(prec: int, value: str) -> Decimal:\n    \"\"\"Convert decimal components to a Python Decimal object.\"\"\"","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/pola-rs/polars/blob/fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e/py-polars/src/polars/_utils/convert.py#L174-L210","documentation":"`_parse_fixed_tz_offset` parses fixed UTC offset strings like '+05:30' when localizing datetimes. If the offset string cannot be parsed into hours/minutes (malformed format), ValueError 'unexpected time zone offset' is raised with the original offset value.","triggerScenarios":"Converting a Python datetime whose tzinfo renders as a fixed-offset string that fails parsing, e.g. pl.from_datetime / to_local_datetime paths via _localize_datetime with offsets like '+5' (no minutes), '+0530' variants the parser doesn't accept, or garbage offsets.","commonSituations":"Hand-built tzinfo strings from config; offsets produced by other libraries in non-ISO formats; timezone strings stored in data files with inconsistent formatting.","solutions":["Use a full ISO 8601 offset format: '+05:30' or '-08:00'.","Use zoneinfo.ZoneInfo('Asia/Kolkata') instead of fixed-offset strings.","Normalize the offset string before passing: pad to +HH:MM with two-digit hours and minutes."],"exampleFix":"// before\nts = pl.Series(['2024-01-01T00:00+5']).str.to_datetime()\n// after\nts = pl.Series(['2024-01-01T00:00+05:00']).str.to_datetime()","handlingStrategy":"validation","validationCode":"import re\nif not re.fullmatch(r'[+-]\\d{2}:?\\d{2}', offset):\n    raise ValueError(f'offset {offset!r} must look like +HH:MM')","typeGuard":null,"tryCatchPattern":"try:\n    result = convert_fn(dt_with_offset)\nexcept ValueError as e:\n    if 'unexpected time zone offset' in str(e):\n        dt = dt.replace(tzinfo=datetime.timezone(datetime.timedelta(hours=5, minutes=30)))\n        result = convert_fn(dt)\n    else:\n        raise","preventionTips":["Always format fixed offsets as +HH:MM with zero-padded components.","Prefer zoneinfo.ZoneInfo over hand-built fixed-offset tz strings.","Validate timezone strings from config/data files at load time."],"tags":["polars","timezone","datetime","parsing"],"backgroundTag":"invalid-date-format","analyzedSha":"fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e","analyzedAt":"2026-09-18T22:14:11.667Z","contentChangedAt":"2026-09-18T22:14:11.667Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}