我试图从datetime.datetime.today()的值中减去一个日期值,以计算多长时间以前的事情。但它抱怨道:

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

datetime.datetime.today()的返回值似乎不是“时区感知”,而我的其他日期值是。我如何从datetime.datetime.today()得到一个时区感知的返回值?

理想的解决方案是让它自动知道时区。

现在,它给我的时间是当地时间,恰好是PST,即UTC - 8小时。最坏的情况下,是否有一种方法我可以手动输入一个时区值到datetime.datetime.today()返回的datetime对象,并将其设置为UTC-8?


当前回答

下面是一个适用于Python 2和Python 3的标准库解决方案:

from datetime import datetime

now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight

其中today是一个感知的datetime实例,表示UTC中的一天的开始(午夜),UTC是一个tzinfo对象(示例来自文档):

from datetime import tzinfo, timedelta

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

相关:几种获得给定UTC时间的午夜(一天的开始)的方法的性能比较。 注意:要获得具有非固定UTC偏移量的时区的午夜,这比较复杂。

其他回答

在标准库中,如果不创建自己的时区类,就没有跨平台的方法来创建感知时区。(编辑:Python 3.9在提供此功能的标准库中引入了zoneinfo。)

在Windows上,有win32timezone.utcnow(),但这是pywin32的一部分。我宁愿建议使用pytz库,它有一个不断更新的大多数时区的数据库。

使用本地时区可能非常棘手(请参阅下面的“进一步阅读”链接),因此您可能更希望在整个应用程序中使用UTC,特别是用于计算两个时间点之间的差值这样的算术操作。

你可以像这样得到当前的日期/时间:

import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)

注意,datetime.today()和datetime.now()返回本地时间,而不是UTC时间,因此对它们应用.replace(tzinfo=pytz.utc)是不正确的。

另一种很好的方法是:

datetime.now(pytz.utc)

它更短一点,做同样的事情。


进一步阅读/观看为什么在许多情况下更喜欢UTC:

pytz文档 关于时间,每个开发人员都应该知道的——许多现实生活用例的开发提示 时间和时区的问题-电脑爱好者-关于使用时区的复杂性的有趣的,令人大开眼界的解释(视频)

如果你使用的是Django,你可以设置不受tz影响的日期(只有UTC)。

在settings.py中注释下面的行:

USE_TZ = True

在utc时区中获取一个支持时区的日期就足以让日期减法工作了。

但是如果你想要一个当前时区的时区感知日期,tzlocal是最好的方法:

from tzlocal import get_localzone  # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())

PS dateutil具有类似的函数(dateutil.tz.tzlocal)。但是尽管共享名称,但它有一个完全不同的代码库,正如J.F.塞巴斯蒂安所指出的那样,这可能会给出错误的结果。

下面是用stdlib生成它的一种方法:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

date将存储本地日期和与UTC的偏移量,而不是UTC时区的日期,因此如果需要确定日期是在哪个时区生成的,可以使用此解决方案。在这个例子中,在我的本地时区:

date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))

date.tzname()
'UTC+02:00'

关键是将%z指令添加到表示FORMAT中,以指示生成的时间结构的UTC偏移量。其他表示格式可以参考datetime模块文档

如果需要UTC时区的日期,可以将time.localtime()替换为time.gmtime()

date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)

date    
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)

date.tzname()
'UTC'

Edit

这只适用于python3。z指令在python 2 _strptime.py代码中不可用

pytz是一个Python库,允许使用Python 2.3或更高版本进行精确的跨平台时区计算。

对于stdlib,这是不可能的。

在SO上可以看到类似的问题。