我在 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 应用程序(就像我一样),那么也许这会有所帮助......
USE_TZ=True
,才能在此处获得可感知的日期时间。
+ timedelta(hours=5, minutes=30)
这是一个非常简单明了的解决方案 两行代码
# 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
结论:您必须使用相同的时间信息管理您的日期时间变量
diff = datetime.now(timezone.utc) - your_timezone_aware_variable
有效(上面的 (a - b)
公式解释了为什么即使 a.tzinfo
不是 b.tzinfo
,(a - b)
也可以工作)。
您不需要标准库之外的任何东西
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
我也面临同样的问题。然后我经过大量搜索找到了解决方案。
问题在于,当我们从模型或表单中获取日期时间对象时,它是偏移感知的,如果我们通过系统获取时间,它是偏移天真的。
所以我所做的是我使用 timezone.now() 获取当前时间并通过 from django.utils import timezone 导入时区并将 USE_TZ = True 放入项目设置文件中。
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()
它适用于时区感知和时区原始日期时间值。并且不需要额外的库或数据库解决方法。
似乎缺少对标题中问题的最直接答案:
naive_date.replace(tzinfo=aware_date.tzinfo) - aware_date
我发现 timezone.make_aware(datetime.datetime.now())
在 django 中很有帮助(我在 1.9.1 上)。不幸的是,您不能简单地让 datetime
对象偏移感知,然后 timetz()
它。您必须创建一个 datetime
并以此为基础进行比较。
是否有一些紧迫的原因导致您无法在 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 :-(')
utc_to_local()
from my answer 以 aware 日期时间对象的形式返回本地时间(它是 Python 3.3+ 代码)
delta = response.tx_time - time.time()
。
datetime.timezone.utc
或pytz.utc
。例如,1970-01-01 00:00:00
有歧义,您必须添加一个时区来消除歧义:1970-01-01 00:00:00 UTC
。你看,你必须添加新信息;时间戳本身是不明确的。