无法减去offset-naive和offset-aware datetimes

时间:2009-04-28 02:28:09

标签: python postgresql datetime timezone

我在PostgreSQL中有一个时区感知timestamptz字段。当我从表中提取数据时,我想立即减去时间,这样我就可以得到它的年龄。

我遇到的问题是,datetime.datetime.now()datetime.datetime.utcnow()似乎都会返回时区不知道的时间戳,这会导致我收到此错误:

TypeError: can't subtract offset-naive and offset-aware datetimes 

有没有办法避免这种情况(最好不使用第三方模块)。

编辑:感谢您的建议,但是尝试调整时区似乎给了我错误..所以我只是在PG中使用时区不知道的时间戳并始终使用:

NOW() AT TIME ZONE 'UTC'

这样我的所有时间戳都默认为UTC(即使这样做更烦人)。

12 个答案:

答案 0 :(得分:254)

你试图删除时区意识吗?

来自http://pytz.sourceforge.net/

naive = dt.replace(tzinfo=None)

也可能需要添加时区转换。

编辑:请注意这个答案的年龄。 Python 3答案如下。

答案 1 :(得分:141)

正确的解决方案是添加时区信息,例如,将当前时间作为Python 3中的感知日期时间对象获取:

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)

答案 2 :(得分:48)

我知道有些人专门使用Django作为抽象这种类型的数据库交互的接口。 Django提供了可用于此的实用程序:

from django.utils import timezone
now_aware = timezone.now()

您确实需要设置一个基本的Django设置基础架构,即使您只是使用这种类型的界面(在设置中,您需要包含USE_TZ=True才能获得有意识的日期时间。)

就其本身而言,这可能远远不足以激励您将Django用作接口,但还有许多其他特权。另一方面,如果你因为你正在修改你的Django应用程序(就像我一样)而在这里跌跌撞撞,那么也许这会有所帮助......

答案 3 :(得分:14)

这是一个非常简单明了的解决方案
两行代码

# 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

您必须使用相同的时间信息管理日期时间变量

答案 4 :(得分:5)

psycopg2模块有自己的时区定义,所以我最终在utcnow周围编写了自己的包装器:

def pg_utcnow():
    import psycopg2
    return datetime.utcnow().replace(
        tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=0, name=None))

只要您需要当前时间与PostgreSQL pg_utcnow

进行比较,就可以使用timestamptz

答案 5 :(得分:4)

我也遇到了同样的问题。然后我经过大量的搜索后找到了解决方案。

问题在于,当我们从模型或表单获取datetime对象时,它是偏移识别,如果我们按系统获得时间,则偏移

所以我做的是使用 timezone.now()获取当前时间,然后通过从django.utils导入时区导入时区并输入项目设置文件中的USE_TZ = True

答案 6 :(得分:2)

我提出了一个超简单的解决方案:

import datetime

def calcEpochSec(dt):
    epochZero = datetime.datetime(1970,1,1,tzinfo = dt.tzinfo)
    return (dt - epochZero).total_seconds()

它适用于时区感知和时区初始日期时间值。并且不需要额外的库或数据库解决方法。

答案 7 :(得分:1)

为什么你无法处理PostgreSQL本身的年龄计算?像

这样的东西
select *, age(timeStampField) as timeStampAge from myTable

答案 8 :(得分:1)

我发现timezone.make_aware(datetime.datetime.now())对django有帮助(我在1.9.1上)。很遗憾,您无法简单地使datetime对象偏移识别,然后timetz()。你必须制作一个datetime并根据它进行比较。

答案 9 :(得分:1)

似乎缺少对标题中问题最直接的回答:

naive_date.replace(tzinfo=aware_date.tzinfo) - aware_date

答案 10 :(得分:0)

我知道这已经过时了,但我想我会添加我的解决方案以防有人发现它有用。

我想将本地天真日期时间与来自时间服务器的识别日期时间进行比较。我基本上使用知道的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 :-(')

答案 11 :(得分:0)

除了标准库外,您什么都不需要

datetime.datetime.now().astimezone()

如果仅更换时区,则不会调整时间。如果您的系统已经是UTC,那么.replace(tz ='UTC')很好。

>>> 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