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

Ian*_*Ian 271 python postgresql timezone datetime

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

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

TypeError: can't subtract offset-naive and offset-aware datetimes 
Run Code Online (Sandbox Code Playgroud)

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

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

NOW() AT TIME ZONE 'UTC'
Run Code Online (Sandbox Code Playgroud)

这样我的所有时间戳都默认为UTC(尽管这样做更烦人).

phi*_*llc 283

你试图删除时区意识吗?

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

naive = dt.replace(tzinfo=None)
Run Code Online (Sandbox Code Playgroud)

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

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

  • (仅供记录)实际上添加有关时区的信息可能是一个更好的主意:http://stackoverflow.com/a/4530166/548696 (31认同)
  • 这似乎是唯一的方法.看起来相当蹩脚的是,python对时区有如此蹩脚的支持,它需要第三方模块才能正确处理时间戳. (26认同)
  • python 3000应该没问题. (21认同)
  • 天真的日期时间对象本质上是模棱两可的,因此应该避免它们.它[很容易添加tzinfo](http://stackoverflow.com/a/25662061/4279) (6认同)
  • 根据定义,UTC 不需要时区。如果另一个 datetime 对象是时区感知的,那么肯定会提供相对于 UTC 的偏移量。UTC 时间可能没有时区,但它们并不“幼稚”,Python 处理错误。 (2认同)
  • @Kylotan:UTC 是此上下文中的时区(由 tzinfo 类表示)。查看“datetime.timezone.utc”或“pytz.utc”。例如,“1970-01-01 00:00:00”是不明确的,您必须添加一个时区来消除歧义:“1970-01-01 00:00:00 UTC”。你看,你必须“添加”新信息;时间戳本身是不明确的。 (2认同)

jfs*_*jfs 176

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

from datetime import datetime, timezone

now = datetime.now(timezone.utc)
Run Code Online (Sandbox Code Playgroud)

在较旧的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()
Run Code Online (Sandbox Code Playgroud)

然后:

now = datetime.now(utc)
Run Code Online (Sandbox Code Playgroud)

  • 比删除tz好,因为接受的答案提倡恕我直言. (6认同)
  • 这是python时区的列表:/sf/ask/970684851/#13867319 (2认同)

sag*_*age 55

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

from django.utils import timezone
now_aware = timezone.now()
Run Code Online (Sandbox Code Playgroud)

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

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

  • 您需要“USE_TZ=True”,才能在此处了解日期时间。 (2认同)
  • 是的 - 我忘了提到你需要像 JFSebastian 描述的那样设置你的 settings.py(我猜这是一个“设置并忘记”的实例)。 (2认同)

ePi*_*314 19

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

# 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
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*ave 12

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

naive_date.replace(tzinfo=aware_date.tzinfo) - aware_date
Run Code Online (Sandbox Code Playgroud)


小智 6

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

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

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


Jef*_*ter 6

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

datetime.datetime.now().astimezone()
Run Code Online (Sandbox Code Playgroud)

如果您只是更换时区,则不会调整时间。如果您的系统已经是 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
Run Code Online (Sandbox Code Playgroud)


erj*_*ang 5

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

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

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


Joh*_*ley 5

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

import datetime

def calcEpochSec(dt):
    epochZero = datetime.datetime(1970,1,1,tzinfo = dt.tzinfo)
    return (dt - epochZero).total_seconds()
Run Code Online (Sandbox Code Playgroud)

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