如何使用pytz检查datetime对象是否本地化?

102

我想要存储一个带有本地化的UTC时区的日期时间对象。存储日期时间对象的方法可以接收非本地化的日期时间(无时区)对象或者已经本地化的对象。我如何确定是否需要进行本地化?

缺少if条件的代码:

class MyClass:
  def set_date(self, d):
    # what do i check here?
    # if(d.tzinfo):
      self.date = d.astimezone(pytz.utc)
    # else:
      self.date = pytz.utc.localize(d)
4个回答

177

如何判断是否需要本地化?

来自datetime文档:

  • 若 datetime 对象 d 是“aware”,则:

d.tzinfo is not None and d.tzinfo.utcoffset(d) is not None
  • d是天真的当且仅当:

    d.tzinfo is None or d.tzinfo.utcoffset(d) is None
    
  • 如果d是一个代表UTC时区时间的日期时间对象,则两种情况下都可以使用:

    self.date = d.replace(tzinfo=pytz.utc)
    

    不管d是否有时区信息,它都能正常工作。

    注意:不要使用datetime.replace()方法和一个带有非固定UTC偏移量的时区(在UTC时区中使用它是可以的,但否则应该使用tz.localize()方法)。


    1
    @Geekfish:我不知道。所有正常的实现都应该返回非None值或引发异常。 - jfs
    3
    自 Python 3.6 开始,可以在不带时区信息的实例上调用 astimezone 方法,它会假定使用系统时区。 - Mitar
    2
    @Mitar:是的,这很不幸。 - jfs
    1
    @pablete:这是一个有效的问题。如果你对此有强烈的感觉,你可以提交一个补丁,例如:def is_naive(self): return self.tzinfo is None or self.tzinfo.utcoffset(self) is None https://devguide.python.org/ - jfs
    1
    @ryanjdillon:tzinfo上的utcoffset方法(与datetime不同)需要一个日期时间参数,因此答案中的代码是正确的。 - JKillian
    显示剩余5条评论

    26

    如果您想检查日期时间对象“d”是否本地化,请检查d.tzinfo,如果为None,则没有本地化。


    但是如果它有一个不来自pytz的tzinfo对象呢? - chiborg
    10
    注意:仅检查d.tzinfo is not None是不够的。此外,对于称为明确时间对象的dd.tzinfo.utcoffset(d)不应为None - jfs
    3
    这个回答是不正确的。以下是“naive(朴素)”或“aware(意识到)”的定义,摘自datetime官方文档:“类型为time或datetime的对象可能是naive或aware。如果一个datetime对象d的d.tzinfo不为None且d.tzinfo.utcoffset(d)不返回None,则d是aware的。如果d.tzinfo为None,或者d.tzinfo不为None但d.tzinfo.utcoffset(d)返回None,则d是naive的。如果一个time对象t的t.tzinfo不为None且t.tzinfo.utcoffset(None)不返回None,则t是aware的。否则,t是naive的。” - Gonzalo

    14

    这里是一个包装最佳答案的函数。

    def tz_aware(dt):
        return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None
    

    2
    而且其余时间返回“无”。 - Stephen Rauch
    1
    只需简单地说“return dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None”即可。 - gimboland

    3
    这是一个更完整的函数,用于将时间戳对象转换或强制转换为UTC。如果出现异常,这意味着时间戳未本地化。由于在代码中始终使用UTC是一种良好的实践,因此该函数在持久性输入级别非常有用。
    def convert_or_coerce_timestamp_to_utc(timeobj):
            out = timeobj
            try:
                out = timeobj.astimezone(pytz.utc) # aware object can be in any timezone
            except (ValueError,TypeError) as exc: # naive
                out = timeobj.replace(tzinfo=pytz.utc)
            return out
    

    J.F. Sebastian提供的答案中的“try catch”的小改动是额外的catch条件,没有它,函数将无法捕获所有的naive情况。


    为什么你也捕获 TypeError?我没有在其他答案中看到这一点被提到。 - Daenyth
    正如我所写的,如果没有额外的catch,就不会捕获所有天真的情况。这只是基于直接经验的补充。我最初只使用了第一个catch,一些天真的时间戳通过了 :) - eiTan LaVi
    如果您展示一个在这里引发TypeError的天真对象的具体示例,答案可能会得到改进。 - Daenyth
    正如已经提到的那样 - 自 Python 3.6 起,astimezone 可以在 naive 实例上调用,并且它会假定系统时区。因此,在没有特定检查的情况下,您的实现是危险的。 - Nikolay Prokopyev

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