Can't compare offset-naive and offset-aware datetimes [Fix]

avatar
Borislav Hadzhiev

Last updated: Apr 11, 2024
5 min

banner

# Can't compare offset-naive and offset-aware datetimes

The Python "TypeError: can't compare offset-naive and offset-aware datetimes" occurs when you try to compare a datetime that is unaware of the timezone with one that is aware of the timezone.

To solve the error, either make both datetime objects aware of the timezone or make them naive.

Here is an example of how the error occurs.

main.py
from datetime import datetime import pytz tz = pytz.timezone('America/New_York') dt_aware = datetime(2024, 9, 20, 12, 0, 0, tzinfo=tz) dt_naive = datetime(2024, 9, 30, 9, 30, 0) # ⛔️ TypeError: can't compare offset-naive and offset-aware datetimes if dt_aware > dt_naive: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_naive')

typeerror cant compare offset naive and offset aware datetimes

The code for this article is available on GitHub

Make sure you have the pytz module installed to be able to use it.

shell
pip install pytz # or with pip3 pip3 install pytz

Notice that the first datetime is offset-aware and the second datetime is offset-naive.

The timezone of the first datetime is set to America/New_York.

Python is telling us that we cannot compare a datetime that has its timezone set with one that doesn't.

# Setting the tzinfo of both datetimes to utc before the comparison

One way to solve the error is to set the timezone info of both datetimes to utc.

main.py
from datetime import datetime import pytz tz = pytz.timezone('America/New_York') utc = pytz.UTC dt_aware = datetime(2024, 9, 20, 12, 0, 0, tzinfo=tz).replace(tzinfo=utc) dt_naive = datetime(2024, 9, 30, 9, 30, 0).replace(tzinfo=utc) if dt_aware > dt_naive: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_naive')

set timezone info of both dates to utc

The code for this article is available on GitHub

Initially, the first datetime has its timezone set to America/New_York and the timezone wasn't set on the second datetime.

We used the datetime.replace method to set the timezone of both datetimes to UTC.

Now that the datetimes have the same offset, the comparison is allowed.

The datetime.replace method returns a datetime with the same attributes, except for those attributes that were passed as keyword arguments to the method.

You can set the tzinfo keyword argument to None to create a naive datetime from an aware datetime without converting the date and time data.

main.py
from datetime import datetime import pytz tz = pytz.timezone('America/New_York') dt_aware = datetime(2024, 9, 20, 12, 0, 0, tzinfo=tz).replace(tzinfo=None) dt_naive = datetime(2024, 9, 30, 9, 30, 0).replace(tzinfo=None) if dt_aware > dt_naive: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_naive')

making both dates offset naive before comparing

The code for this article is available on GitHub

The replace() method makes both datetime objects offset-naive before the comparison, so everything works as expected.

# Using the localize() method to make both datetime objects offset-aware

You can also use the localize() method from the pytz module to make both datetime objects offset-aware.

main.py
from datetime import datetime import pytz utc = pytz.UTC dt_aware = utc.localize(datetime(2024, 9, 20, 12, 0, 0)) dt_naive = utc.localize(datetime(2024, 9, 30, 9, 30, 0)) if dt_aware > dt_naive: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_naive')
The code for this article is available on GitHub

Both datetime objects are now offset-aware, so the comparison is allowed.

However, this approach cannot be used when the tzinfo of the datetime object is already set.

main.py
from datetime import datetime import pytz tz = pytz.timezone('America/New_York') utc = pytz.UTC # ⛔️ ValueError: Not naive datetime (tzinfo is already set) dt_aware = utc.localize(datetime(2024, 9, 20, 12, 0, 0, tzinfo=tz))

Trying to call the localize() method with a datetime object that has its timezone already set raises a ValueError.

In this case, you have the use the datetime.replace() method as shown in the previous subheading.

# Solving the error when working with timestamps

You might also get the error when working with a timestamp.

main.py
from datetime import datetime import pytz dt_aware = datetime(2024, 9, 20, 12, 0, 0, tzinfo=pytz.UTC) dt_naive = datetime.now() # ⛔️ TypeError: can't compare offset-naive and offset-aware datetimes if dt_aware > dt_naive: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_naive')
The code for this article is available on GitHub

The datetime.datetime.now() method is not timezone-aware, so trying to compare its output with a timezone-aware datetime object is not allowed.

One way to solve the error is to use the datetime() class to convert the timestamp to a timezone-aware datetime object.

main.py
from datetime import datetime import pytz dt_aware = datetime(2024, 9, 20, 12, 0, 0, tzinfo=pytz.UTC) dt_naive = datetime.now() dt_aware_now = datetime( year=dt_naive.year, month=dt_naive.month, day=dt_naive.day, hour=dt_naive.hour, minute=dt_naive.minute, second=dt_naive.second, tzinfo=pytz.UTC ) if dt_aware > dt_aware_now: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_naive')

convert timestamp to datetime aware object

We used the datetime() class to construct a datetime aware object from the timestamp.

Notice that we set the tzinfo parameter to keyword argument to pytz.UTC.

Now both datetime objects are offset-aware, so the comparison is allowed.

You can also:

  1. Access the tzinfo attribute on the timezone-aware datetime object.
  2. Pass the tzinfo value as an argument to the datetime.datetime.now() method.
main.py
from datetime import datetime import pytz dt_aware = datetime(2024, 9, 20, 12, 0, 0, tzinfo=pytz.UTC) timezone = dt_aware.tzinfo dt_aware_now = datetime.now(timezone) if dt_aware > dt_aware_now: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_naive')

convert timestamp to datetime aware object

The code for this article is available on GitHub

The tzinfo attribute returns the timezone info object of the datetime.

We passed the same timezone info object to the datetime.datetime.now() method, so both datetime objects are offset-aware.

# Solving the error when using Django

As shown in the previous subheading, the datetime.datetime.now() method is not timezone-aware.

However, if you use Django, you can call the timezone.now() method instead.

main.py
from datetime import datetime import pytz from django.utils import timezone dt_aware = datetime(2024, 9, 20, 12, 0, 0, tzinfo=pytz.UTC) now = timezone.now() if dt_aware > now: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_naive')

The timezone.now() method is offset-aware.

Note that this approach can only be used in a Django application.

If the USE_TZ setting is set to True, then timezone.now() returns a timezone-aware datetime object even if the pytz module is not installed.

# Solving the error without third-party libraries

If you'd rather not install third-party libraries to solve the error, import and use the timezone and timedelta classes.

main.py
from datetime import datetime, timezone, timedelta dt_aware = datetime.strptime( '2024-09-30T09:30:05+0000', '%Y-%m-%dT%H:%M:%S%z' ) print(dt_aware) # 👉️ 2024-09-30 09:30:05+00:00 dt_aware_2 = datetime( 2025, 9, 24, tzinfo=timezone(offset=timedelta()) ) print(dt_aware_2) # 👉️ 2025-09-24 00:00:00+00:00 if dt_aware > dt_aware_2: print('dt_aware is greater than dt_naive') else: print('dt_aware is not greater than dt_aware_2')
The code for this article is available on GitHub

The timedelta class represents the difference between two datetime objects.

We set the tzinfo keyword argument when instantiating the second datetime object, so both objects are offset-aware.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.