关于python:不能减去offset naive和offset aware日期时间

Can't subtract offset-naive and offset-aware datetimes

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

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

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

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

编辑:谢谢您的建议,但是尝试调整时区似乎会给我带来错误。所以我将使用时区而不知道pg中的时间戳,并且总是使用以下方法插入:

1
NOW() AT TIME ZONE 'UTC'

这样,默认情况下,我所有的时间戳都是UTC(即使这样做更烦人)。


您是否尝试取消时区意识?

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

1
naive = dt.replace(tzinfo=None)

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

编辑:请注意这个答案的年龄。下面是python 3的答案。


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

1
2
3
from datetime import datetime, timezone

now = datetime.now(timezone.utc)

在旧的python版本上,您可以自己定义utctzinfo对象(来自datetime docs的示例):

1
2
3
4
5
6
7
8
9
10
11
12
13
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()

然后:

1
now = datetime.now(utc)


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

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

您确实需要设置一个基本的django设置基础结构,即使您只是使用这种类型的接口(在设置中,您需要包括USE_TZ=True以获得一个已知的日期时间)。

就其本身而言,这可能远不足以激励您使用django作为界面,但还有许多其他的好处。另一方面,如果你在这里绊倒是因为你弄坏了你的django应用程序(和我一样),那么也许这有助于…


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

1
2
3
4
5
6
7
8
9
# 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

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


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

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

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


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

问题是,当我们从模型或表单中获取日期时间对象时,它是可偏移的,并且如果我们通过系统获取时间,那么它是原始偏移。

所以我所做的是使用timezone.now()获取当前时间,并从django.utils import timezone导入时区,并将use_Tz=true放在项目设置文件中。


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

1
2
3
4
5
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,并在此基础上进行比较。


为什么PostgreSQL本身不能处理年龄计算,有什么紧迫的原因吗?类似的东西

1
select *, age(timeStampField) as timeStampAge from myTable


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

我想将本地的naive datetime与来自时间服务器的aware datetime进行比较。我基本上使用aware-datetime对象创建了一个新的naive-datetime对象。这是一个有点黑客,看起来不太漂亮,但能完成工作。

1
2
3
4
5
6
7
8
9
10
11
12
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()

……软糖来了……

1
2
3
4
    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 :-(')