Python
datetime
timezone
timezone-aware
programming

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:

python
1from datetime import datetime
2
3now = datetime.today()
4print(now)
5print(now.tzinfo)

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.

python
1from datetime import datetime, timezone
2
3now_utc = datetime.now(timezone.utc)
4print(now_utc)
5print(now_utc.tzinfo)

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.

python
1from datetime import datetime
2from zoneinfo import ZoneInfo
3
4now_toronto = datetime.now(ZoneInfo("America/Toronto"))
5print(now_toronto)
6print(now_toronto.tzinfo)

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:

python
1from datetime import datetime
2
3local_aware = datetime.now().astimezone()
4print(local_aware)
5print(local_aware.tzinfo)

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:

python
from datetime import datetime, timezone

wrong = datetime.today().replace(tzinfo=timezone.utc)

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:

python
1from datetime import datetime, timezone
2from zoneinfo import ZoneInfo
3
4stored = datetime.now(timezone.utc)
5shown = stored.astimezone(ZoneInfo("America/Toronto"))
6
7print(stored)
8print(shown)

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 pytz in new code when zoneinfo is 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.

Course illustration
Course illustration

All Rights Reserved.