Last updated: Apr 11, 2024
Reading time·5 min
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.
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')
Make sure you have the pytz module installed to be able to use it.
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.
tzinfo
of both datetimes to utc
before the comparisonOne way to solve the error is to set the timezone info of both datetimes to
utc
.
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')
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.
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')
The replace()
method makes both datetime
objects offset-naive before the
comparison, so everything works as expected.
localize()
method to make both datetime
objects offset-awareYou can also use the localize()
method from the pytz
module to make both
datetime
objects offset-aware.
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')
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.
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.
You might also get the error when working with a timestamp.
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 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.
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')
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:
tzinfo
attribute on the timezone-aware datetime
object.tzinfo
value as an argument to the datetime.datetime.now()
method.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')
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.
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.
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.
If you'd rather not install third-party libraries to solve the error, import and
use the timezone
and timedelta
classes.
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 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.
You can learn more about the related topics by checking out the following tutorials: