datetime.UTC / timezone.utc

The tzinfo you should reach for first. UTC is timezone.utc (the same object), its offset is zero, its name is "UTC", and fromisoformat returns it for Z, +00:00 and -00:00.

datetime constantPython 3.2+ (UTC alias 3.11+)Live demo
Common call
datetime.now(UTC)
Returns
an aware datetime with tzinfo=datetime.timezone.utc
Replaces
datetime.utcnow() (naive, deprecated) and pytz.utc
Watch out
UTC needs Python 3.11+; use timezone.utc for older versions
UTC
→ timezone

Demo

Live evaluation
Parse a timestamp and express it in UTC. Naive input is taken as UTC already (replace), aware input is converted (astimezone).
Try:
Inputs
whenstran ISO 8601 timestamp
Code
from datetime import datetime, UTC
dt = datetime.fromisoformat('2026-09-29T17:30+05:30')
dt = dt.replace(tzinfo=UTC) if dt.tzinfo is None else dt.astimezone(UTC)
(dt.isoformat(), dt.tzinfo is UTC)
Result
('2026-09-29T12:00:00+00:00', True)

Every path ends at the same singleton, so dt.tzinfo is UTC is True. isoformat writes the zero offset as +00:00, never Z.

Common patterns

Current time in UTC
The replacement for utcnow().
from datetime import datetime, UTC
now = datetime.now(UTC)
Works on 3.2 through 3.13
timezone.utc is the same object under its older name.
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
Normalize any aware value to UTC for storage
astimezone(UTC) keeps the instant and makes offsets uniform.
from datetime import UTC
stored = aware_dt.astimezone(UTC)

Examples

1. One object, two names
from datetime import UTC, timezone (UTC, UTC is timezone.utc)
Returns
(datetime.timezone.utc, True)
2. Name and offset
from datetime import UTC (str(UTC), UTC.utcoffset(None))
Returns
('UTC', datetime.timedelta(0))
3. An aware UTC datetime
from datetime import datetime, UTC datetime(2026, 9, 29, 12, tzinfo=UTC).isoformat()
Returns
'2026-09-29T12:00:00+00:00'
4. Z parses to this object
from datetime import datetime, UTC datetime.fromisoformat('2026-09-29T12:00Z').tzinfo is UTC
Returns
True
5. So does timezone(timedelta(0))
from datetime import timedelta, timezone, UTC timezone(timedelta(0)) is UTC
Returns
True

Pitfalls

1. Confusing "UTC fields" with "aware UTC"
A naive datetime holding UTC numbers is still naive: comparing it with an aware value fails. Attach UTC explicitly.
naive
from datetime import datetime, UTC
datetime(2026, 9, 29, 12) < datetime(2026, 9, 29, 13, tzinfo=UTC)
TypeError: can't compare offset-naive and offset-aware datetimes
replace(tzinfo=UTC)
from datetime import datetime, UTC
datetime(2026, 9, 29, 12).replace(tzinfo=UTC) < datetime(2026, 9, 29, 13, tzinfo=UTC)
True
2. replace(tzinfo=UTC) on a value from another zone
replace relabels without converting — the instant moves. For an aware value use astimezone.
replace
from datetime import datetime, UTC
datetime.fromisoformat('2026-09-29T17:30+05:30').replace(tzinfo=UTC).isoformat()
'2026-09-29T17:30:00+00:00'
astimezone
from datetime import datetime, UTC
datetime.fromisoformat('2026-09-29T17:30+05:30').astimezone(UTC).isoformat()
'2026-09-29T12:00:00+00:00'

When to use

Use it
  • Timestamps you store, log, compare or send over a network
  • The tz argument of now(), fromtimestamp() and astimezone()
Reach for something else
  • Displaying local time to a user → convert with astimezone(their zone) at the edge

Notes

CPython impl
A static singleton created when _datetime is imported; datetime.UTC was added in 3.11 as a plain alias
Identity
timezone(timedelta(0)) without a name, and fromisoformat for Z / +00:00 / -00:00, all return this same object
Named zero
timezone(timedelta(0), "UTC") is a DIFFERENT object that compares equal

FAQ

None — UTC is an alias added in Python 3.11, and UTC is timezone.utc is True. Use timezone.utc if your code must run on 3.10 or older.