DEV Community

rene
rene

Posted on

Python 3.12 warns that datetime.utcnow() is deprecated. Here is what to use instead

If you upgraded to Python 3.12 and your logs suddenly show this, you are not alone:

DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version.
Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
Enter fullscreen mode Exit fullscreen mode

The fix is one line, but it is worth understanding why, because the old call hides a real bug.

The problem: utcnow() returns a "naive" datetime

from datetime import datetime

a = datetime.utcnow()
print(a.tzinfo)   # None  -> naive: Python does not know this is UTC
Enter fullscreen mode Exit fullscreen mode

The value is UTC, but nothing in the object says so. Mix it with an aware datetime and you get a crash:

from datetime import datetime, timezone

naive = datetime.utcnow()
aware = datetime.now(timezone.utc)

print(aware > naive)
# TypeError: can't compare offset-naive and offset-aware datetimes
Enter fullscreen mode Exit fullscreen mode

Worse, calling .timestamp() on a naive datetime assumes your machine's local time zone, so datetime.utcnow().timestamp() can be off by hours on a laptop that is not set to UTC.

The fix

from datetime import datetime, timezone

now = datetime.now(timezone.utc)      # works on every Python 3 version
# Python 3.11+ also has the shorter alias: datetime.now(datetime.UTC)
print(now.tzinfo)       # UTC
print(now.isoformat())  # 2026-10-30T15:12:48.123456+00:00
Enter fullscreen mode Exit fullscreen mode

The same applies to utcfromtimestamp():

# old
datetime.utcfromtimestamp(1_700_000_000)
# new
datetime.fromtimestamp(1_700_000_000, tz=timezone.utc)
Enter fullscreen mode Exit fullscreen mode

Parsing timestamps from APIs

Many APIs return strings ending in Z. Since Python 3.11, fromisoformat accepts it and gives you an aware datetime:

from datetime import datetime

dt = datetime.fromisoformat("2026-10-29T13:02:03Z")
print(dt.tzinfo)  # UTC
Enter fullscreen mode Exit fullscreen mode

On older versions, replace the suffix first: s.replace("Z", "+00:00").

Quick migration checklist

  1. Search your code: grep -rn "utcnow\|utcfromtimestamp" .
  2. Replace with datetime.now(timezone.utc) and datetime.fromtimestamp(ts, tz=timezone.utc).
  3. Store and compare only aware datetimes; convert to a local zone only when showing a time to a person.
  4. Run your tests with python -W error::DeprecationWarning to catch leftovers.

Sources

This article was written with AI assistance and checked by the author.

Have you hit a time zone bug in production that only showed up on someone else's machine? What was the cause?

Top comments (0)