问题:无法减去天真偏移和可感知偏移的日期时间
我timestamptz
在PostgreSQL中有一个时区识别字段。当我从表中提取数据时,我想现在减去时间,以便确定时间。
我遇到的问题是,无论是datetime.datetime.now()
和datetime.datetime.utcnow()
似乎回到时区不知道时间戳,这导致我得到这个错误:
TypeError: can't subtract offset-naive and offset-aware datetimes
有没有一种方法可以避免这种情况(最好不要使用第三方模块)。
编辑:感谢您的建议,但是尝试调整时区似乎给了我错误..所以我只打算在PG中使用不知道时区的时间戳,并始终使用以下命令插入:
NOW() AT TIME ZONE 'UTC'
这样,默认情况下,我所有的时间戳都是UTC(即使这样做比较烦人)。
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).
回答 0
回答 1
正确的解决方案是添加时区信息,例如,将当前时间作为Python 3中已知的datetime对象获取:
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
在较旧的Python版本上,您可以utc
自己定义tzinfo对象(例如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()
然后:
now = datetime.now(utc)
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)
回答 2
我知道有人专门使用Django作为抽象此类数据库交互的接口。Django提供了可用于此目的的实用程序:
from django.utils import timezone
now_aware = timezone.now()
您确实需要设置基本的Django设置基础结构,即使您只是使用这种类型的界面(在设置中,也需要包括在内USE_TZ=True
以获取已知的日期时间)。
就其本身而言,这可能还远远不足以激发您使用Django作为界面,但是还有许多其他好处。另一方面,如果您是因为要破坏Django应用(如我所做的那样)而在这里偶然发现的,那么这可能会有所帮助…
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…
回答 3
这是一个非常简单明了的解决方案
两行代码
# 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
结论:您必须使用相同的时间信息来管理日期时间变量
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
回答 4
psycopg2模块具有自己的时区定义,因此我最终围绕utcnow编写了自己的包装器:
def pg_utcnow():
import psycopg2
return datetime.utcnow().replace(
tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=0, name=None))
并且只pg_utcnow
在需要当前时间与PostgreSQL比较时使用timestamptz
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
回答 5
我也面临同样的问题。经过大量搜索之后,我找到了解决方案。
问题是,当我们从模型或表单获取datetime对象时,它是偏移量感知的;如果通过系统获取时间,则它是偏移量天真的。
所以我要做的是使用timezone.now()获得当前时间,并从django.utils import timezone导入时区,并将USE_TZ = True放入项目设置文件中。
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.
回答 6
我想出了一个超简单的解决方案:
import datetime
def calcEpochSec(dt):
epochZero = datetime.datetime(1970,1,1,tzinfo = dt.tzinfo)
return (dt - epochZero).total_seconds()
它适用于时区感知和时区原始日期时间值。并且不需要其他库或数据库解决方法。
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.
回答 7
我发现timezone.make_aware(datetime.datetime.now())
在Django中很有帮助(我在1.9.1上)。不幸的是,您不能简单地使datetime
对象具有偏移意识timetz()
。您必须做一个,datetime
并在此基础上进行比较。
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.
回答 8
有一些紧迫的原因导致您无法在PostgreSQL本身中处理年龄计算吗?就像是
select *, age(timeStampField) as timeStampAge from myTable
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
回答 9
我知道这很旧,但只是想我会添加我的解决方案,以防万一有人觉得有用。
我想将本地原始日期时间与时间服务器的已知日期时间进行比较。我基本上使用感知的datetime对象创建了一个新的朴素的datetime对象。这有点骇人听闻,看起来并不漂亮,但是可以完成工作。
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()
…软糖来了…
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 :-(')
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 :-(')