Skip to content

datetime utcnow deprecation leads to type confusion #105544

Closed
@bdarnell

Description

@bdarnell

Bug report

"Naive" and "aware" datetimes are functionally two distinct types, although they are not represented as such in the typeshed annotations. Specifically, operations such as subtraction and inequality comparisons fail if one operand is naive and the other is aware.

Python 3.12b2 deprecates many datetime methods including utcnow and utcfromtimestamp, which return naive datetimes. The suggested alternative is to use e.g. datetime.datetime.fromtimestamp(datetime.UTC), which returns an aware datetime. Applying this suggestion without breaking existing code is tricky.

For example, consider a function that takes a datetime and compares it to the current time:

def is_expired(d: datetime) -> bool:
    now = datetime.datetime.utcnow()
    return (now - d) > datetime.timedelta(days=30)

This function only accepts naive datetimes, but even though it has type annotations this is not obvious, and indeed its author may not have been aware of this concern. Applying the suggestion from the deprecation warning turns it into a function that only accepts aware datetimes, breaking every existing caller.

This could be a problem if the function occurs in a library as part of a public interface: if the library author does a search-and-replace to convert the utc-prefixed naive methods with their aware counterparts, the library may no longer have any test coverage that uses naive datetimes to detect this breakage. And even if the author is aware of this as a potential issue, the error occurs at the subtraction operation, not at the utcnow() call, which may be some distance away. I have a situation like this in Tornado and I don't know how to move away from deprecated methods while being confident that I'm not breaking application code. (I gather that the recommendation is to use if d.tzinfo is None: d = d.replace(tzinfo=datetime.timezone.utc), but how can I be sure that I have this incantation everywhere it needs to be?)

(Other minor gripes about this transition are that datetime.UTC, which is mentioned in the deprecation warning, is relatively new (3.11) and I need to use datetime.timezone.utc for compatibility with older versions of Python. And the whole thing is just awkwardly verbose.)

I would like to ask that this deprecation be reconsidered. The recommended fix is not semantically equivalent to the deprecated code, and type checkers are not sufficient to detect incorrect usage. In adapting to this deprecation in Python 3.12, libraries may be tempted to make changes that will adversely affect users of all python versions.

I think my ideal solution would be to make "naive" and "aware" datetimes into real types (at the level of type annotations if not actual classes), so that mixing the two is a type-checkable error. (I think that's mostly doable with mypy's overload support, although you'd probably end up falling back to an ambiguous case sometimes, effectively a Union[AwareDateTime, NaiveDateTime]. The type checker would then force you to check the status of any datetimes you use before subtracting or comparing them).

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12only security fixes3.13bugs and security fixesstdlibPython modules in the Lib dirtype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions