如何让日期时间对象感知(而不是幼稚)?
我需要做什么
我有一个不支持时区的日期时间对象,我需要向其中添加一个时区,以便能够将其与其他支持时区的日期时间对象进行比较。我不想在不知道这一遗留情况的情况下将整个应用程序转换为时区。
我尝试过的
首先,为了演示问题:
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
首先,我尝试了 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
>>>
失败并不奇怪,因为它实际上是在尝试进行转换。替换似乎是一个更好的选择(根据 如何在 Python 中获取“时区感知”的 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
>>>
但如您所见,replace 似乎设置了 tzinfo
,但不制作对象 意识到的。我正准备在解析输入字符串之前对其进行修改以使其具有时区(如果重要的话,我正在使用 dateutil
进行解析),但这似乎非常混乱。
另外,我在 Python 2.6 和 Python 2.7 中都尝试过这一点,得到了相同的结果。
上下文
我正在为一些数据文件编写解析器。我需要支持一种旧格式,其中日期字符串没有时区指示器。我已经修复了数据源,但我仍然需要支持旧数据格式。由于各种商业BS原因,遗留数据的一次性转换不是一种选择。虽然总的来说,我不喜欢硬编码默认时区的想法,在这种情况下,它似乎是最好的选择。我有合理的信心知道所有有问题的遗留数据都是采用 UTC 格式的,因此我准备接受在这种情况下默认的风险。
What I need to do
I have a timezone-unaware datetime object, to which I need to add a time zone in order to be able to compare it with other timezone-aware datetime objects. I do not want to convert my entire application to timezone unaware for this one legacy case.
What I've Tried
First, to demonstrate the problem:
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
First, I tried 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
>>>
It's not terribly surprising this failed, since it's actually trying to do a conversion. Replace seemed like a better choice (as per How do I get a value of datetime.today() in Python that is "timezone aware"?):
>>> 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
>>>
But as you can see, replace seems to set the tzinfo
, but not make the object aware. I'm getting ready to fall back to doctoring the input string to have a timezone before parsing it (I'm using dateutil
for parsing, if that matters), but that seems incredibly kludgy.
Also, I've tried this in both Python 2.6 and Python 2.7, with the same results.
Context
I am writing a parser for some data files. There is an old format I need to support where the date string does not have a timezone indicator. I've already fixed the data source, but I still need to support the legacy data format. A one time conversion of the legacy data is not an option for various business BS reasons. While in general, I do not like the idea of hard-coding a default timezone, in this case it seems like the best option. I know with reasonable confidence that all the legacy data in question is in UTC, so I'm prepared to accept the risk of defaulting to that in this case.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
一般来说,要使日期时间时区感知,请使用 本地化方法:
对于 UTC 时区,实际上没有必要使用
localize
,因为没有夏令时计算需要处理:有效。 (
.replace
返回一个新的日期时间;它不会修改不知道
。)In general, to make a naive datetime timezone-aware, use the localize method:
For the UTC timezone, it is not really necessary to use
localize
since there is no daylight savings time calculation to handle:works. (
.replace
returns a new datetime; it does not modifyunaware
.)所有这些示例都使用外部模块,但您可以仅使用日期时间模块来实现相同的结果,如 this SO answer< /a>:
更少的依赖项并且没有 pytz 问题。
注意:如果您希望将其与 python3 和 python2 一起使用,您也可以将其用于时区导入(针对 UTC 进行硬编码):
All of these examples use an external module, but you can achieve the same result using just the datetime module, as also presented in this SO answer:
Fewer dependencies and no pytz issues.
NOTE: If you wish to use this with python3 and python2, you can use this as well for the timezone import (hardcoded for UTC):
我在 2011 年编写了这个 Python 2 脚本,但从未检查过它是否适用于 Python 3。
我已从 dt_aware 转移到 dt_unaware:
并从 dt_unware 转移到 dt_aware:
I wrote this Python 2 script in 2011, but never checked if it works on Python 3.
I had moved from dt_aware to dt_unaware:
and dt_unware to dt_aware:
Python 3.9 添加了
zoneinfo
模块< /strong> 所以现在只需要标准库!附加时区:
附加系统的本地时区:
随后正确转换为其他时区:
维基百科可用时区列表
Windows 没有系统时区数据库,因此这里需要一个额外的包:
有一个向后移植允许在Python 3.6到3.8<中使用
zoneinfo
/strong>:那么:
Python 3.9 adds the
zoneinfo
module so now only the standard library is needed!Attach a timezone:
Attach the system's local timezone:
Subsequently it is properly converted to other timezones:
Wikipedia list of available time zones
Windows has no system time zone database, so here an extra package is needed:
There is a backport to allow use of
zoneinfo
in Python 3.6 to 3.8:Then:
我在 Django 中使用此语句将无意识时间转换为有意识时间:
I use this statement in Django to convert an unaware time to an aware:
我同意之前的答案,如果您可以从 UTC 开始就可以了。但我认为,人们使用具有非 UTC 本地时区的 日期时间的 tz 感知值也是一种常见情况。
如果您只按名称进行操作,人们可能会推断出Replace() 将适用并生成正确的日期时间感知对象。事实并非如此。
replace( tzinfo=... ) 的行为似乎是随机的。因此它是无用的。不要使用这个!
localize 是正确使用的函数。示例:
或更完整的示例:
为我提供当前本地时间的时区感知日期时间值:
I agree with the previous answers, and is fine if you are ok to start in UTC. But I think it is also a common scenario for people to work with a tz aware value that has a datetime that has a non UTC local timezone.
If you were to just go by name, one would probably infer replace() will be applicable and produce the right datetime aware object. This is not the case.
the replace( tzinfo=... ) seems to be random in its behaviour. It is therefore useless. Do not use this!
localize is the correct function to use. Example:
Or a more complete example:
gives me a timezone aware datetime value of the current local time:
使用
dateutil.tz.tzlocal()
获取您使用datetime.datetime.now()
和datetime.datetime.astimezone()
:请注意,
datetime.astimezone
将首先转换您的datetime
反对然后将 UTC 转换为时区,这与调用datetime.replace
时原始时区信息为None
相同。Use
dateutil.tz.tzlocal()
to get the timezone in your usage ofdatetime.datetime.now()
anddatetime.datetime.astimezone()
:Note that
datetime.astimezone
will first convert yourdatetime
object to UTC then into the timezone, which is the same as callingdatetime.replace
with the original timezone information beingNone
.这整理了 @Sérgio 和 @unutbu 的答案。它将“仅适用于”
pytz.timezone
对象或 IANA 时区 字符串。这似乎是
datetime.localize()
(或.inform()
或.awarify()
)应该做的,接受字符串和时区tz 参数的对象,如果未指定时区,则默认为 UTC。This codifies @Sérgio and @unutbu's answers. It will "just work" with either a
pytz.timezone
object or an IANA Time Zone string.This seems like what
datetime.localize()
(or.inform()
or.awarify()
) should do, accept both strings and timezone objects for the tz argument and default to UTC if no time zone is specified.对于那些只想制作时区感知日期时间的人
对于那些想要具有非 utc 时区的日期时间的人 从 python 3.9 stdlib 开始
for those that just want to make a timezone aware datetime
for those that want a datetime with a non utc timezone starting in python 3.9 stdlib
另一种拥有
datetime
对象的方法并不简单:Yet another way of having a
datetime
object NOT naive:对 Python 很陌生,我遇到了同样的问题。我发现这个解决方案非常简单,对我来说它工作得很好(Python 3.6):
quite new to Python and I encountered the same issue. I find this solution quite simple and for me it works fine (Python 3.6):
在时区之间更改
Changing between timezones
采用unutbu答案的格式;我制作了一个实用程序模块,可以使用更直观的语法来处理此类事情。可以用pip安装。
In the format of unutbu's answer; I made a utility module that handles things like this, with more intuitive syntax. Can be installed with pip.
这是一个简单的解决方案,可以最大限度地减少对代码的更改:
Here is a simple solution to minimize changes to your code:
根据文档
datetime.utcnow< /代码>
:
警告:由于许多
datetime
方法将简单的datetime
对象视为本地时间,因此最好使用感知日期时间来表示时间以世界标准时间 (UTC) 表示。因此,创建表示 UTC 当前时间的对象的推荐方法是调用datetime.now(timezone.utc)
。根据文档
datetime.utcfromtimestamp< /代码>
警告:因为天真
datetime
对象...通过调用datetime.fromtimestamp(timestamp, tz=timezone.utc)
。在
python 3.11.2
中测试As per the documentation
datetime.utcnow
:As per the documentation
datetime.utcfromtimestamp
Tested in
python 3.11.2
在所有提到的方法中,当它是 Unix 时间戳 时,有一个使用 pandas 的非常简单的解决方案。
Above all mentioned approaches, when it is a Unix timestamp, there is a very simple solution using pandas.