How do I get a value of datetime.today in Python that is timezone aware?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
datetime.today() returns a naive datetime, so it is the wrong starting point if you need timezone-aware values. In modern Python, the direct solution is datetime.now(tz=...). If you want UTC, use datetime.now(timezone.utc). If you want a named local zone, use ZoneInfo.
Why datetime.today() Is Not Enough
This returns a naive datetime:
Output shows tzinfo is None. That means the object does not know what timezone it belongs to, even if it represents the current local wall-clock time.
Naive datetimes are risky because they look valid but lose crucial context during storage, comparison, and conversion.
The Correct Modern Pattern
Use datetime.now() with an explicit timezone.
This produces a timezone-aware UTC datetime immediately. It is usually the best default for storage, logging, and APIs.
Using A Named Time Zone
If you want a specific region such as Toronto or Berlin, use zoneinfo, which is part of the standard library in modern Python.
This is the modern standard-library approach. It is preferable to building timezone awareness manually.
If You Really Want "Local Time But Aware"
If your goal is the machine's current local time with timezone information attached, a common pattern is:
astimezone() with no argument converts the naive current local time into an aware datetime in the local system timezone.
This is often the closest practical equivalent to "timezone-aware datetime.today()".
Do Not Use replace(tzinfo=...) As A General Conversion Tool
A frequent mistake is:
That does not convert local time into UTC. It simply relabels the naive clock value as if it had already been UTC. In other words, it changes the metadata without changing the instant correctly.
Use datetime.now(timezone.utc) when you want the current UTC time. Use astimezone when you want proper timezone conversion.
pytz Versus zoneinfo
Older examples often use pytz. That still exists in legacy code, but modern Python code should prefer zoneinfo when possible because it is part of the standard library and integrates with datetime directly.
If you are maintaining old code, you may still see pytz.timezone(...).localize(...). For new code, ZoneInfo is usually the cleaner choice.
Store In UTC, Display In Local Time
A practical rule for many systems is:
- create and store aware UTC datetimes
- convert to local zones only when presenting to users
That keeps the internal representation consistent and avoids many daylight-saving and cross-region bugs.
Example:
This is usually safer than storing local wall-clock timestamps directly.
Common Pitfalls
- Using
datetime.today()and assuming it is timezone-aware. - Using
replace(tzinfo=...)as though it performs a real timezone conversion. - Mixing naive and aware datetimes in comparisons or arithmetic.
- Reaching for
pytzin new code whenzoneinfois available. - Storing local-time aware values when the system really should standardize on UTC internally.
Summary
- '
datetime.today()returns a naive datetime.' - Use
datetime.now(timezone.utc)for a timezone-aware UTC value. - Use
datetime.now(ZoneInfo("Region/City"))for a specific named timezone. - Use
datetime.now().astimezone()if you want an aware value in the machine's local timezone. - Avoid
replace(tzinfo=...)for real timezone conversion.

