如何在python中识别未知的日期时区时区

Mar*_*zzi 446 python timezone datetime

我需要做什么

我有一个时区不知道的日期时间对象,我需要添加一个时区,以便能够将其与其他时区感知日期时间对象进行比较.我不想将我的整个应用程序转换为时区,而不是因为这个遗留案例.

我试过的

首先,要证明问题:

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> import pytz
>>> unaware = datetime.datetime(2011,8,15,8,15,12,0)
>>> unaware
datetime.datetime(2011, 8, 15, 8, 15, 12)
>>> aware = datetime.datetime(2011,8,15,8,15,12,0,pytz.UTC)
>>> aware
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> aware == unaware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
Run Code Online (Sandbox Code Playgroud)

首先,我尝试了astimezone:

>>> unaware.astimezone(pytz.UTC)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: astimezone() cannot be applied to a naive datetime
>>>
Run Code Online (Sandbox Code Playgroud)

这失败并不令人惊讶,因为它实际上是在尝试进行转换.替换似乎是一个更好的选择(根据Python:如何获得"timezone aware"的datetime.today()值?):

>>> unaware.replace(tzinfo=pytz.UTC)
datetime.datetime(2011, 8, 15, 8, 15, 12, tzinfo=<UTC>)
>>> unaware == aware
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't compare offset-naive and offset-aware datetimes
>>> 
Run Code Online (Sandbox Code Playgroud)

但正如你所看到的,replace似乎设置了tzinfo,但没有让对象知道.我正准备在解析它之前回过头来修改输入字符串以获得时区(我正在使用dateutil进行解析,如果这很重要),但这看起来非常糟糕.

另外,我在python 2.6和python 2.7中都尝试了这个,结果相同.

上下文

我正在为一些数据文件编写解析器.我需要支持一种旧格式,其中日期字符串没有时区指示符.我已经修复了数据源,但我仍然需要支持旧数据格式.遗留数据的一次转换不是针对各种商业BS原因的选项.虽然一般来说,我不喜欢硬编码默认时区的想法,在这种情况下,它似乎是最好的选择.我非常有信心地知道所有遗留数据都是UTC格式的,所以我准备接受在这种情况下违约的风险.

unu*_*tbu 528

通常,要使天真的日期时间可识别,请使用localize方法:

import datetime
import pytz

unaware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0)
aware = datetime.datetime(2011, 8, 15, 8, 15, 12, 0, pytz.UTC)

now_aware = pytz.utc.localize(unaware)
assert aware == now_aware
Run Code Online (Sandbox Code Playgroud)

对于UTC时区,没有必要使用,localize因为没有夏令时计算来处理:

now_aware = unaware.replace(tzinfo=pytz.UTC)
Run Code Online (Sandbox Code Playgroud)

作品.(.replace返回一个新的日期时间;它不会修改unaware.)

  • 好吧,我觉得很傻.替换返回新的日期时间.它说文档也在那里,我完全错过了.谢谢,这正是我想要的. (9认同)
  • putz 有一个错误,它将阿姆斯特丹时区设置为 UTC 的 + 20 分钟,这是 1937 年以来的一些古老时区。你有一份工作 pytz。 (5认同)
  • 如果时区不是UTC,那么不要直接使用构造函数:`aware = datetime(...,tz)`,改为使用`.localize()`. (4认同)
  • "替换返回一个新的日期时间." 是的.REPL给你的提示是它显示返回的值.:) (2认同)
  • 值得一提的是,当地时间可能存在模糊性。`tz.localize(..., is_dst=None)` 断言它不是。 (2认同)

小智 124

所有这些示例都使用外部模块,但您只需使用datetime模块即可获得相同的结果,如本SO答案中所示:

from datetime import datetime
from datetime import timezone

dt = datetime.now()
dt.replace(tzinfo=timezone.utc)

print(dt.replace(tzinfo=timezone.utc).isoformat())
'2017-01-12T22:11:31+00:00'
Run Code Online (Sandbox Code Playgroud)

依赖性较少,没有pytz问题.

注意:如果您希望将它与python3和python2一起使用,您也可以使用它来进行时区导入(硬编码为UTC):

try:
    from datetime import timezone
    utc = timezone.utc
except ImportError:
    #Hi there python2 user
    class UTC(tzinfo):
        def utcoffset(self, dt):
            return timedelta(0)
        def tzname(self, dt):
            return "UTC"
        def dst(self, dt):
            return timedelta(0)
    utc = UTC()
Run Code Online (Sandbox Code Playgroud)

  • 防止`pytz`问题非常好的答案,我很高兴我向下滚动了一下!我不想在远程服务器上使用`pytz`来解决它:) (8认同)
  • 你应该注意``dt.replace(tzinfo = timezone.utc)`返回一个新的日期时间,它不会修改`dt`.(我将编辑以显示此内容). (8认同)
  • 请注意,`from datetime import timezone`在py3中工作,但不在py2.7中工作. (7认同)
  • @bumpkin 迟到总比不到好,我猜:`tz = pytz.timezone('America/Chicago')` (5认同)
  • 您如何不使用 timezone.utc,而是提供不同的时区作为字符串(例如“America/Chicago”)? (3认同)

Sér*_*gio 76

我曾经使用过dt_aware和dt_unware

dt_unaware = dt_aware.replace(tzinfo=None)
Run Code Online (Sandbox Code Playgroud)

和dt_unware到dt_aware

from pytz import timezone
localtz = timezone('Europe/Lisbon')
dt_aware = localtz.localize(dt_unware)
Run Code Online (Sandbox Code Playgroud)

但之前的回答也是一个很好的解决方案.

  • 如果`dt_unware`表示不存在或模糊的本地时间,你可以使用`localtz.localize(dt_unware,is_dst = None)`来引发异常(注意:你的答案的前一版本中没有这样的问题`localtz`是UTC,因为UTC没有DST转换 (2认同)
  • 感谢您展示转换的两个方向。 (2认同)

小智 40

我在Django中使用此语句将不知不觉的时间转换为意识到:

from django.utils import timezone

dt_aware = timezone.make_aware(dt_unaware, timezone.get_current_timezone())
Run Code Online (Sandbox Code Playgroud)

  • 你实际上并不是第二个参数.[default argument](https://docs.djangoproject.com/en/1.10/ref/utils/#django.utils.timezone.make_aware)(无)表示隐式使用本地时区.与DST相同(这是第三个参数_ (3认同)
  • 我喜欢这个解决方案(+1),但它依赖于Django,这不是他们想要的(-1).=) (2认同)

xjc*_*jcl 20

Python 3.9 添加了zoneinfo模块,所以现在只需要标准库!

from zoneinfo import ZoneInfo
from datetime import datetime
unaware = datetime(2020, 10, 31, 12)
Run Code Online (Sandbox Code Playgroud)

附上时区:

>>> unaware.replace(tzinfo=ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 12:00:00+09:00'
Run Code Online (Sandbox Code Playgroud)

附加系统的本地时区:

>>> unaware.replace(tzinfo=ZoneInfo('localtime'))
datetime.datetime(2020, 10, 31, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='localtime'))
>>> str(_)
'2020-10-31 12:00:00+01:00'
Run Code Online (Sandbox Code Playgroud)

随后它被正确转换为其他时区:

>>> unaware.replace(tzinfo=ZoneInfo('localtime')).astimezone(ZoneInfo('Asia/Tokyo'))
datetime.datetime(2020, 10, 31, 20, 0, tzinfo=backports.zoneinfo.ZoneInfo(key='Asia/Tokyo'))
>>> str(_)
'2020-10-31 20:00:00+09:00'
Run Code Online (Sandbox Code Playgroud)

可用时区的维基百科列表


Windows 没有系统时区数据库,所以这里需要一个额外的包:

pip install tzdata  
Run Code Online (Sandbox Code Playgroud)

有一个 backport 允许zoneinfoPython 3.6 到 3.8 中使用

pip install backports.zoneinfo
Run Code Online (Sandbox Code Playgroud)

然后:

from backports.zoneinfo import ZoneInfo
Run Code Online (Sandbox Code Playgroud)

  • 在 Windows 上,您还需要 `pip install tzdata` (2认同)

pao*_*lov 12

我同意之前的答案,如果你可以在UTC开始,那就没问题了.但我认为这也是人们使用具有非UTC本地时区日期时间的tz感知值的常见情况.

如果您只是按名称去,可能会推断replace()将适用并生成正确的日期时间感知对象.不是这种情况.

替换(tzinfo = ...)似乎在其行为中是随机的.因此没用.不要用这个!

localize是正确使用的功能.例:

localdatetime_aware = tz.localize(datetime_nonaware)
Run Code Online (Sandbox Code Playgroud)

或者更完整的例子:

import pytz
from datetime import datetime
pytz.timezone('Australia/Melbourne').localize(datetime.now())
Run Code Online (Sandbox Code Playgroud)

给我一个当前本地时间的时区感知日期时间值:

datetime.datetime(2017, 11, 3, 7, 44, 51, 908574, tzinfo=<DstTzInfo 'Australia/Melbourne' AEDT+11:00:00 DST>)
Run Code Online (Sandbox Code Playgroud)

  • 这需要更多的投票,尝试在非UTC的时区执行`replace(tzinfo = ...)`将会占用您的日期时间。我得到的是`-07:53`而不是`-08:00`。参见/sf/answers/979622801/ (2认同)
  • 您能否给出一个具有意外行为的“replace(tzinfo=...)”的可重现示例? (2认同)

Har*_*eno 10

对于那些只想了解时区的日期时间的人

import datetime

datetime.datetime(2019, 12, 7, tzinfo=datetime.timezone.utc)
Run Code Online (Sandbox Code Playgroud)

对于那些想要从 python 3.9 stdlib 开始的非 utc 时区的日期时间的人

import datetime
from zoneinfo import ZoneInfo

datetime.datetime(2019, 12, 7, tzinfo=ZoneInfo("America/Los_Angeles")) 
Run Code Online (Sandbox Code Playgroud)

  • 接受的答案中未提及 tzinfo 命名参数。 (2认同)

hob*_*obs 9

这编纂了@Sérgio和@ unutbu的答案.它将与pytz.timezone对象或IANA时区字符串"正常工作" .

def make_tz_aware(dt, tz='UTC', is_dst=None):
    """Add timezone information to a datetime object, only if it is naive."""
    tz = dt.tzinfo or tz
    try:
        tz = pytz.timezone(tz)
    except AttributeError:
        pass
    return tz.localize(dt, is_dst=is_dst) 
Run Code Online (Sandbox Code Playgroud)

这似乎是datetime.localize()(或.inform().awarify())应该做什么,接受tz参数的字符串和时区对象,如果没有指定时区,则默认为UTC.


Ahm*_*met 6

为了添加本地时区;(使用dateutil)

from dateutil import tz
import datetime

dt_unaware = datetime.datetime(2017, 6, 24, 12, 24, 36)

dt_aware = dt_unaware.replace(tzinfo=tz.tzlocal())
Run Code Online (Sandbox Code Playgroud)

  • 减少一次导入,只需: `.replace(tzinfo=datetime.timezone.utc)` (2认同)

iva*_*ncz 5

还有另一种让datetime对象不幼稚的方法:

>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc)
datetime.datetime(2021, 5, 1, 22, 51, 16, 219942, tzinfo=datetime.timezone.utc)
Run Code Online (Sandbox Code Playgroud)