{"record":{"id":"4a609f8773b1d0a0","repo":"python/cpython","slug":"offset-must-be-a-timedelta","errorCode":null,"errorMessage":"offset must be a timedelta","messagePattern":"offset must be a timedelta","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/_pydatetime.py","lineNumber":2454,"sourceCode":"    THURSDAY = 3\n    firstday = _ymd2ord(year, 1, 1)\n    firstweekday = (firstday + 6) % 7  # See weekday() above\n    week1monday = firstday - firstweekday\n    if firstweekday > THURSDAY:\n        week1monday += 7\n    return week1monday\n\n\nclass timezone(tzinfo):\n    \"\"\"Fixed offset from UTC implementation of tzinfo.\"\"\"\n\n    __slots__ = '_offset', '_name'\n\n    # Sentinel value to disallow None\n    _Omitted = object()\n    def __new__(cls, offset, name=_Omitted):\n        if not isinstance(offset, timedelta):\n            raise TypeError(\"offset must be a timedelta\")\n        if name is cls._Omitted:\n            if not offset:\n                return cls.utc\n            name = None\n        elif not isinstance(name, str):\n            raise TypeError(\"name must be a string\")\n        if not cls._minoffset <= offset <= cls._maxoffset:\n            raise ValueError(\"offset must be a timedelta \"\n                             \"strictly between -timedelta(hours=24) and \"\n                             f\"timedelta(hours=24), not {offset!r}\")\n        return cls._create(offset, name)\n\n    def __init_subclass__(cls):\n        raise TypeError(\"type 'datetime.timezone' is not an acceptable base type\")\n\n    @classmethod\n    def _create(cls, offset, name=None):\n        self = tzinfo.__new__(cls)","sourceCodeStart":2436,"sourceCodeEnd":2472,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydatetime.py#L2436-L2472","documentation":"timezone.__new__ requires its first argument to be a timedelta instance; it represents a fixed UTC offset as an exact duration. Passing an int (minutes/seconds ambiguity), a string, or None is rejected immediately with TypeError.","triggerScenarios":"timezone(7200) intending seconds; timezone('-05:00'); timezone(None); timezone(5, 'EST') copying a textbook example in a language where ints worked.","commonSituations":"Porting code from dateutil.tz.tzoffset('name', seconds) which does accept ints; reading offset configs as numbers of seconds/minutes; timezone-aware ORM fields feeding ints into timezone().","solutions":["Wrap the numeric seconds: timezone(timedelta(seconds=7200))","For minute-based configs: timezone(timedelta(minutes=offset_minutes))","For named IANA zones use ZoneInfo('Region/City') instead of timezone()","Type-check config values at load time and convert to timedelta there"],"exampleFix":"// before\ntz = timezone(-5 * 3600)  # intended UTC-5\n\n# after\ntz = timezone(timedelta(hours=-5))","handlingStrategy":"type-guard","validationCode":"from datetime import timedelta, timezone\n\ndef make_timezone(offset) -> timezone:\n    if isinstance(offset, int):        # seconds, dateutil-style\n        offset = timedelta(seconds=offset)\n    if not isinstance(offset, timedelta):\n        raise TypeError('offset must be int seconds or timedelta')\n    return timezone(offset)","typeGuard":"from datetime import timedelta\n\ndef is_timedelta(v) -> bool:\n    return isinstance(v, timedelta)","tryCatchPattern":null,"preventionTips":["Convert numeric offsets to timedelta at config load","Prefer ZoneInfo for named zones","Annotate APIs as taking timedelta"],"tags":["python","datetime","timezone","type-error"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}