I have a timezone aware timestamptz
field in PostgreSQL. When I pull data from the table, I then want to subtract the time right now so I can get it's age.
The problem I'm having is that both datetime.datetime.now()
and datetime.datetime.utcnow()
seem to return timezone unaware timestamps, which results in me getting this error:
TypeError: can't subtract offset-naive and offset-aware datetimes
Is there a way to avoid this (preferably without a third-party module being used).
EDIT: Thanks for the suggestions, however trying to adjust the timezone seems to give me errors.. so I'm just going to use timezone unaware timestamps in PG and always insert using:
NOW() AT TIME ZONE 'UTC'
That way all my timestamps are UTC by default (even though it's more annoying to do this).
have you tried to remove the timezone awareness?
from http://pytz.sourceforge.net/
naive = dt.replace(tzinfo=None)
may have to add time zone conversion as well.
edit: Please be aware the age of this answer. An answer involving ADDing the timezone info instead of removing it in python 3 is below. https://stackoverflow.com/a/25662061/93380
The correct solution is to add the timezone info e.g., to get the current time as an aware datetime object in Python 3:
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
On older Python versions, you could define the utc
tzinfo object yourself (example from datetime docs):
from datetime import tzinfo, timedelta, datetime
ZERO = timedelta(0)
class UTC(tzinfo):
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
utc = UTC()
then:
now = datetime.now(utc)
I know some people use Django specifically as an interface to abstract this type of database interaction. Django provides utilities that can be used for this:
from django.utils import timezone
now_aware = timezone.now()
You do need to set up a basic Django settings infrastructure, even if you are just using this type of interface (in settings, you need to include USE_TZ=True
to get an aware datetime).
By itself, this is probably nowhere near enough to motivate you to use Django as an interface, but there are many other perks. On the other hand, if you stumbled here because you were mangling your Django app (as I did), then perhaps this helps...
USE_TZ=True
, to get an aware datetime here.
+ timedelta(hours=5, minutes=30)
for IST
This is a very simple and clear solution Two lines of code
# First we obtain de timezone info o some datatime variable
tz_info = your_timezone_aware_variable.tzinfo
# Now we can subtract two variables using the same time zone info
# For instance
# Lets obtain the Now() datetime but for the tz_info we got before
diff = datetime.datetime.now(tz_info)-your_timezone_aware_variable
Conclusion: You must mange your datetime variables with the same time info
diff = datetime.now(timezone.utc) - your_timezone_aware_variable
works (and the (a - b)
formula above is the explanation why (a - b)
can work even if a.tzinfo
is not b.tzinfo
).
You don't need anything outside the std libs
datetime.datetime.now().astimezone()
If you just replace the timezone it will not adjust the time. If your system is already UTC then .replace(tz='UTC') is fine.
>>> x=datetime.datetime.now()
datetime.datetime(2020, 11, 16, 7, 57, 5, 364576)
>>> print(x)
2020-11-16 07:57:05.364576
>>> print(x.astimezone())
2020-11-16 07:57:05.364576-07:00
>>> print(x.replace(tzinfo=datetime.timezone.utc)) # wrong
2020-11-16 07:57:05.364576+00:00
I also faced the same problem. Then I found a solution after a lot of searching .
The problem was that when we get the datetime object from model or form it is offset aware and if we get the time by system it is offset naive.
So what I did is I got the current time using timezone.now() and import the timezone by from django.utils import timezone and put the USE_TZ = True in your project settings file.
The psycopg2 module has its own timezone definitions, so I ended up writing my own wrapper around utcnow:
def pg_utcnow():
import psycopg2
return datetime.utcnow().replace(
tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=0, name=None))
and just use pg_utcnow
whenever you need the current time to compare against a PostgreSQL timestamptz
I came up with an ultra-simple solution:
import datetime
def calcEpochSec(dt):
epochZero = datetime.datetime(1970,1,1,tzinfo = dt.tzinfo)
return (dt - epochZero).total_seconds()
It works with both timezone-aware and timezone-naive datetime values. And no additional libraries or database workarounds are required.
The most straight forward answer to the question in the title seems to be missing:
naive_date.replace(tzinfo=aware_date.tzinfo) - aware_date
I've found timezone.make_aware(datetime.datetime.now())
is helpful in django (I'm on 1.9.1). Unfortunately you can't simply make a datetime
object offset-aware, then timetz()
it. You have to make a datetime
and make comparisons based on that.
Is there some pressing reason why you can't handle the age calculation in PostgreSQL itself? Something like
select *, age(timeStampField) as timeStampAge from myTable
I know this is old, but just thought I would add my solution just in case someone finds it useful.
I wanted to compare the local naive datetime with an aware datetime from a timeserver. I basically created a new naive datetime object using the aware datetime object. It's a bit of a hack and doesn't look very pretty but gets the job done.
import ntplib
import datetime
from datetime import timezone
def utc_to_local(utc_dt):
return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
try:
ntpt = ntplib.NTPClient()
response = ntpt.request('pool.ntp.org')
date = utc_to_local(datetime.datetime.utcfromtimestamp(response.tx_time))
sysdate = datetime.datetime.now()
...here comes the fudge...
temp_date = datetime.datetime(int(str(date)[:4]),int(str(date)[5:7]),int(str(date)[8:10]),int(str(date)[11:13]),int(str(date)[14:16]),int(str(date)[17:19]))
dt_delta = temp_date-sysdate
except Exception:
print('Something went wrong :-(')
utc_to_local()
from my answer returns local time as an aware datetime object (It is Python 3.3+ code)
delta = response.tx_time - time.time()
.
Success story sharing
datetime.timezone.utc
orpytz.utc
. For example,1970-01-01 00:00:00
is ambiguous and you have to add a timezone to disambiguate:1970-01-01 00:00:00 UTC
. You see, you have to add new information; the timestamp by itself is ambiguous.