ChatGPT解决这个技术问题 Extra ChatGPT

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

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


p
phillc

您是否尝试过消除时区意识?

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

naive = dt.replace(tzinfo=None)

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

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


这似乎是唯一的方法。 python 对时区的支持如此糟糕,以至于它需要第三方模块才能正确处理时间戳,这似乎很蹩脚..
(仅作记录)实际上添加有关时区的信息可能是一个更好的主意:stackoverflow.com/a/4530166/548696
天真的日期时间对象本质上是模棱两可的,因此应该避免使用它们。它是easy to add tzinfo instead
根据定义,UTC 不需要时区。如果另一个 datetime 对象是时区感知的,那么肯定会提供相对于 UTC 的偏移量。 UTC 时间可能没有时区,但它们并不“幼稚”,Python 处理错误。
@Kylotan:UTC 在这种情况下是一个时区(由 tzinfo 类表示)。查看 datetime.timezone.utcpytz.utc。例如,1970-01-01 00:00:00 有歧义,您必须添加一个时区来消除歧义:1970-01-01 00:00:00 UTC。你看,你必须添加新信息;时间戳本身是不明确的。
j
jfs

正确的解决方案是添加时区信息,例如,将当前时间作为 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)

比删除 tz 更好,因为公认的答案提倡恕我直言。
以下是 Python 时区列表:stackoverflow.com/questions/13866926/…
s
sage

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

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

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

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


您需要 USE_TZ=True,才能在此处获得可感知的日期时间。
是的 - 我忘了提到您需要按照 JFSebastian 的描述设置您的 settings.py(我猜这是“设置并忘记”的一个实例)。
这也可以轻松转换为其他时区,例如 IST 的 + timedelta(hours=5, minutes=30)
e
ePi272314

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

# 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

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


不正确?我编写的代码已经过测试,我正在一个 django 项目中使用它。它非常清晰和简单
“不正确”是指您回答中的最后一句话:“...必须添加...不是 UTC”——UTC 时区在这里有效,因此该语句不正确。
要清楚,我的意思是:diff = datetime.now(timezone.utc) - your_timezone_aware_variable 有效(上面的 (a - b) 公式解释了为什么即使 a.tzinfo 不是 b.tzinfo(a - b) 也可以工作)。
J
JeffCharter

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

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

A
Ashok Joshi

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

问题在于,当我们从模型或表单中获取日期时间对象时,它是偏移感知的,如果我们通过系统获取时间,它是偏移天真的。

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


e
erjiang

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

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

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


任何返回零 UTC 偏移量的 tzinfo 对象都可以,for example
J
John L. Stanley

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

import datetime

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

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


D
Dave

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

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

J
Joseph Coco

我发现 timezone.make_aware(datetime.datetime.now()) 在 django 中很有帮助(我在 1.9.1 上)。不幸的是,您不能简单地让 datetime 对象偏移感知,然后 timetz() 它。您必须创建一个 datetime 并以此为基础进行比较。


N
Nic Gibson

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

select *, age(timeStampField) as timeStampAge from myTable

是的,有..但我主要是在问,因为我想避免在 postgre 中进行所有计算。
I
I_do_python

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

我想将本地天真的日期时间与来自时间服务器的有意识的日期时间进行比较。我基本上使用有意识的日期时间对象创建了一个新的天真的日期时间对象。这有点小技巧,看起来不是很漂亮,但可以完成工作。

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

仅供参考,utc_to_local() from my answeraware 日期时间对象的形式返回本地时间(它是 Python 3.3+ 代码)
目前尚不清楚您的代码试图做什么。您可以将其替换为 delta = response.tx_time - time.time()