如何在Python中为一个naive datetime实例添加时区

65

我有一个没有时区信息的datetime对象。现在我获取了时区信息,想把时区信息加入到已存在的datetime实例中,应该怎么办?

d = datetime.datetime.now()
tz = pytz.timezone('Asia/Taipei')

如何将时区信息tz添加到日期时间a

2个回答

94

使用 tz.localize(d) 将实例本地化。来自文档

第一种方法是使用 pytz 库提供的 localize() 方法。这用于将无时区信息的 naive datetime(datetime)本地化:

>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500
如果您不使用`tz.localize()`,而是使用`datetime.replace()`,则很有可能会使用历史偏移量。`tz.localize()`将选择对于给定日期有效的正确偏移量。例如,美国东部时区的夏令时开始和结束日期随时间改变。
当您尝试本地化一个日期时间值时,如果该值跨越了夏令时与冬令时转换期间,时区将被咨询以确定结果日期时间对象是否应该返回True或False的`.dst()`。您可以使用`.localize()`的`is_dst`关键字参数覆盖时区的默认设置。
dt = tz.localize(naive, is_dst=True)

通过设置is_dst=None,甚至可以完全关闭选择。在这种情况下,或者在极少数情况下没有为时区设置默认值的情况下,模糊的日期时间值将导致引发AmbiguousTimeError异常。只有模糊的日期时间值才会查询is_dst标志,否则会被忽略。

要将时区感知对象转换回本地时间对象,请使用.replace(tzinfo=None)

naivedt = awaredt.replace(tzinfo=None)

6
在datetime对象上调用.replace(tzinfo=None)方法,返回一个不带时区信息的datetime实例。 - Martijn Pieters
1
@hobs:我不会期望本地化适用于“时间”对象,因为本地化需要一个日期才能正确执行其工作。时区偏移对于仅有的时间组件没有意义,您缺少DST和历史上下文的信息。 - Martijn Pieters
@MartjinPieters 没错。但是如果你需要一个带时区的时间(只有与同一时区中的其他带时区的时间一起使用才有用),则可以使用.replace()。我同意这通常没有意义...但是如果您告知一个带有时区的time,那么在形成日期时间时稍后可以使用tz。 - hobs
3
使用 .replace() 函数替换 pytz 可能具有多个 UTC 偏移量的时区是错误的(许多时区都是如此)。默认的 tzinfo 对象通常对应于 LMT(太阳时间),这在大多数情况下并不是您想要的(我认为默认值的原因是帮助揭示不正确的 .replace() 使用方法)。 - jfs
@J.F.Sebastian 这么说是“错”的也有道理。但对于我这种情况(只有 datetime.time 实例,没有日期信息),这可能是可以接受的。我处理来自 wunderground.com 的 CSV 时遇到了这种情况。TZ 信息可用于列标题中(有时还在字段字符串中),但日期却可从其他地方获取。因此,我只需将时间标记为 tzinfo,并使用该 tzinfo 在稍后与日期进行本地化。我知道这是错误且不规范的方法,但它对我起作用了。对我错误答案的投票将希望鼓励其他人认真考虑如何正确解决这种边缘情况。 - hobs
显示剩余7条评论

28

如果您知道原始日期时间是在您正在尝试添加到的时区中“测量”的,那么您可以(但可能不应该)使用replace而不是localize

# d = datetime.datetime.now()
# tz = pytz.timezone('Asia/Taipei')
d = d.replace(tzinfo=tz)

我能想象出两种情况下这么做会有意义(第二种情况正是我遇到的):

  1. 如果您的服务器区域设置为错误的时区,并且您正在尝试通过使其感知此错误时区来更正datetime实例(并且后续将其本地化到“正确”的时区,以使now()的值与其他您要进行比较的时间(例如您的手表)相匹配)。
  2. 您希望为一个time实例(而不是datetime)添加一个时区(tzinfo)属性,以便稍后可以使用该属性形成完整的datetime实例。

5
你从数据库中检索到一个naive类型的日期时间值,但你知道它存储时所在的时区,现在你想在获取后对其进行操作。 - Marc
3
你使用utcnow生成了一个时区无关的UTC时间,现在需要将其与一个时区感知的时间进行比较。 - Marc
1
如上面的答案所指出的,这里可能会遇到问题。快速示例: parse('2022-09-03 10:00').replace(tzinfo=pytz.timezone('Europe/Berlin')) - parse('2022-09-03 10:00:00+02:00') -> datetime.timedelta(seconds=4020), pytz.timezone('Europe/Berlin').localize(parse('2022-09-03 10:00')) - parse('2022-09-03 10:00:00+02:00') -> datetime.timedelta(0) - nuts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接