问题:如何在Python中获取“时区感知”的datetime.today()值?

我正在尝试从的值中减去一个日期值,datetime.today()以计算某物是多久以前的。但它抱怨:

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

该值datetime.today()似乎不是“时区感知”的,而我的其他日期值是。如何获得datetime.today()时区感知的值?

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

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

I am trying to subtract one date value from the value of datetime.today() to calculate how long ago something was. But it complains:

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

The value datetime.today() doesn’t seem to be “timezone aware”, while my other date value is. How do I get a value of datetime.today() that is timezone aware?

Right now, it’s giving me the time in local time, which happens to be PST, i.e. UTC – 8 hours. Worst case, is there a way I can manually enter a timezone value into the datetime object returned by datetime.today() and set it to UTC-8?

Of course, the ideal solution would be for it to automatically know the timezone.


回答 0

在标准库中,没有跨平台的方法来创建感知时区而不创建自己的时区类。

在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:

In the standard library, there is no cross-platform way to create aware timezones without creating your own timezone class.

On Windows, there’s win32timezone.utcnow(), but that’s part of pywin32. I would rather suggest to use the pytz library, which has a constantly updated database of most timezones.

Working with local timezones can be very tricky (see “Further reading” links below), so you may rather want to use UTC throughout your application, especially for arithmetic operations like calculating the difference between two time points.

You can get the current date/time like so:

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

Mind that datetime.today() and datetime.now() return the local time, not the UTC time, so applying .replace(tzinfo=pytz.utc) to them would not be correct.

Another nice way to do it is:

datetime.now(pytz.utc)

which is a bit shorter and does the same.


Further reading/watching why to prefer UTC in many cases:


回答 1

获取特定时区的当前时间:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))

Get the current time, in a specific timezone:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))

回答 2

在Python 3中,标准库使将UTC指定为时区变得容易得多:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2016, 8, 26, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

如果您想要一个仅使用标准库并且在Python 2和Python 3中均可使用的解决方案,请参见jfs的答案

如果您需要当地时区而不是UTC,请参见MihaiCapotă的答案

In Python 3, the standard library makes it much easier to specify UTC as the timezone:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2016, 8, 26, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

If you want a solution that uses only the standard library and that works in both Python 2 and Python 3, see jfs’ answer.

If you need the local timezone, not UTC, see Mihai Capotă’s answer


回答 3

这是一个适用于Python 2和3的stdlib解决方案:

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偏移量的时区的午夜更为复杂。

Here’s a stdlib solution that works on both Python 2 and 3:

from datetime import datetime

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

where today is an aware datetime instance representing the beginning of the day (midnight) in UTC and utc is a tzinfo object (example from the documentation):

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

Related: performance comparison of several ways to get midnight (start of a day) for a given UTC time. Note: it is more complex, to get midnight for a time zone with a non-fixed UTC offset.


回答 4

构造表示当前时间的时区感知日期时间对象的另一种方法:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  

Another method to construct time zone aware datetime object representing current time:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  

回答 5

从Python 3.3开始,仅使用标准库的单行代码就可以使用。您可以datetime使用来获取本地时区感知对象astimezone(如johnchen902建议):

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))

A one-liner using only the standard library works starting with Python 3.3. You can get a local timezone aware datetime object using astimezone (as suggested by johnchen902):

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))

回答 6

如果您使用的是Django,则可以将日期设置为非tz感知(仅UTC)。

在settings.py中注释以下行:

USE_TZ = True

If you are using Django, you can set dates non-tz aware (only UTC).

Comment the following line in settings.py:

USE_TZ = True

回答 7

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

使用stdlib,这是不可能的。

SO上看到类似的问题。

pytz is a Python library that allows accurate and cross platform timezone calculations using Python 2.3 or higher.

With the stdlib, this is not possible.

See a similar question on SO.


回答 8

这是使用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'

编辑

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

Here is one way to generate it with the 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 will store the local date and the offset from UTC, not the date at UTC timezone, so you can use this solution if you need to identify which timezone the date is generated at. In this example and in my local timezone:

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

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

The key is adding the %z directive to the representation FORMAT, to indicate the UTC offset of the generated time struct. Other representation formats can be consulted in the datetime module docs

If you need the date at the UTC timezone, you can replace time.localtime() with 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

This works only on python3. The z directive is not available on python 2 _strptime.py code


回答 9

使用可识别时区的Python datetime.datetime.now()中所述的dateutil :

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())

Use dateutil as described in Python datetime.datetime.now() that is timezone aware:

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())

回答 10

在时区中获取可识别时区的日期utc足以使日期减法起作用。

但是,如果您想在当前时区使用时区感知日期,tzlocal则可以采用以下方法:

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

PS dateutil具有类似的功能(dateutil.tz.tzlocal)。但是,尽管共享名称,但它具有完全不同的代码库,正如JF Sebastian 指出的那样,可能会产生错误的结果。

Getting a timezone-aware date in utc timezone is enough for date subtraction to work.

But if you want a timezone-aware date in your current time zone, tzlocal is the way to go:

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

PS dateutil has a similar function (dateutil.tz.tzlocal). But inspite of sharing the name it has a completely different code base, which as noted by J.F. Sebastian can give wrong results.


回答 11

这是一个使用可读时区的解决方案,该解决方案适用于today():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

您可以列出所有时区,如下所示:

import pytz

pytz.all_timezones
pytz.common_timezones # or

Here is a solution using a readable timezone and that works with today():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

You can list all timezones as follows:

import pytz

pytz.all_timezones
pytz.common_timezones # or

回答 12

特别是对于非UTC时区:

唯一具有自己方法的时区是timezone.utc,但是如果需要,您可以使用timedeltatimezone,并使用强制使用UTC偏移量来伪装时区.replace

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

在这里使用timezone(timedelta(hours=n))时区是真正的灵丹妙药,它还有许多其他有用的应用程序。

Especially for non-UTC timezones:

The only timezone that has its own method is timezone.utc, but you can fudge a timezone with any UTC offset if you need to by using timedelta & timezone, and forcing it using .replace.

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

Using timezone(timedelta(hours=n)) as the time zone is the real silver bullet here, and it has lots of other useful applications.


回答 13

如果您在python中获得了当前时间和日期,则在导入日期和时间后,您将在python中获得当前日期和时间,如下所示。

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))

If you get current time and date in python then import date and time,pytz package in python after you will get current date and time like as..

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))

回答 14

在我看来,另一种替代方法是使用Pendulum代替pytz。考虑以下简单代码:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

要安装Pendulum并查看其文档,请转到此处。它具有大量选项(例如简单的ISO8601,RFC3339和许多其他格式支持),更好的性能并倾向于产生更简单的代码。

Another alternative, in my mind a better one, is using Pendulum instead of pytz. Consider the following simple code:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

To install Pendulum and see their documentation, go here. It have tons of options (like simple ISO8601, RFC3339 and many others format support), better performance and tend to yield simpler code.


回答 15

如下所示,将时区用于可识别时区的日期时间。默认为UTC:

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

Use the timezone as shown below for a timezone-aware date time. The default is UTC:

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

回答 16

来自“ howchoo”的Tyler撰写了一篇非常出色的文章,帮助我对Datetime Objects有了更好的了解,请点击以下链接

使用日期时间

本质上,我只是在两个datetime对象的末尾添加了以下内容

.replace(tzinfo=pytz.utc)

例:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)

Tyler from ‘howchoo’ made a really great article that helped me get a better idea of the Datetime Objects, link below

Working with Datetime

essentially, I just added the following to the end of both my datetime objects

.replace(tzinfo=pytz.utc)

Example:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)

回答 17

尝试pnp_datetime,所有使用和返回的时间都是带时区的,不会造成任何天真偏移和可感知偏移的问题。

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)

try pnp_datetime, all the time been used and returned is with timezone, and will not cause any offset-naive and offset-aware issues.

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)

回答 18

应该强调的是,从Python 3.6开始,您只需要标准的lib即可获取表示本地时间(操作系统的设置)的时区感知日期时间对象。使用astimezone()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(请参阅@ johnchen902的评论)。

It should be emphasized that since Python 3.6, you only need the standard lib to get a timezone aware datetime object that represents local time (the setting of your OS). Using astimezone()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(see @johnchen902’s comment). Note there’s a small caveat though, astimezone(None) gives aware datetime, unaware of DST.


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。