繁体   English   中英

不能减去偏移天真和偏移感知日期时间

[英]Can't subtract offset-naive and offset-aware datetimes

我在 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(即使这样做更烦人)。

您是否尝试删除时区意识?

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

naive = dt.replace(tzinfo=None)

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

编辑:请注意这个答案的年龄。 下面是一个涉及添加时区信息而不是在 python 3 中删除它的答案。 https://stackoverflow.com/a/25662061/93380

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

from datetime import datetime, timezone

now = datetime.now(timezone.utc)

在较旧的 Python 版本上,您可以自己定义utc tzinfo 对象(来自日期时间文档的示例):

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)

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

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

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

就其本身而言,这可能远远不足以激励您使用 Django 作为界面,但还有许多其他好处。 另一方面,如果您因为正在破坏 Django 应用程序(就像我所做的那样)而偶然发现这里,那么这可能会有所帮助...

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

# 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

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

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

问题是,当我们从模型或表单中获取 datetime 对象时,它是偏移感知的,如果我们通过系统获取时间,它就是偏移 naive

所以我所做的是使用timezone.now()获取当前时间并通过from django.utils import timezone 导入时区并将USE_TZ = True放在您的项目设置文件中。

你不需要标准库之外的任何东西

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

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

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

只要您需要当前时间与 PostgreSQL timestamptz进行比较,就使用pg_utcnow

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

import datetime

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

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

我发现timezone.make_aware(datetime.datetime.now())在 Django 中很有帮助(我在 1.9.1)。 不幸的是,您不能简单地使datetime对象偏移感知,然后timetz()它。 您必须制作一个datetime并基于此进行比较。

标题中问题的最直接的答案似乎缺失了:

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

是否有一些紧迫的原因导致您无法处理 PostgreSQL 本身的年龄计算? 就像是

select *, age(timeStampField) as timeStampAge from myTable

我知道这是旧的,但只是想我会添加我的解决方案,以防有人发现它有用。

我想将本地朴素的日期时间与来自时间服务器的已知日期时间进行比较。 我基本上使用感知日期时间对象创建了一个新的原始日期时间对象。 这有点像黑客,看起来不是很漂亮,但可以完成工作。

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 :-(')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM