【问题标题】:Losing DST information using pytz and UTC conversions使用 pytz 和 UTC 转换丢失 DST 信息
【发布时间】:2013-06-12 11:57:25
【问题描述】:

也许这是凌晨 4 点的错误,但我认为我做的一切都正确,但 DST 似乎没有从 UTC 时间戳转换为本地化日期时间。

>>> from datetime import datetime
>>> import pytz
>>> eastern = pytz.timezone("US/Eastern")
>>> utc = pytz.utc
>>> local_now = eastern.localize(datetime.now())
>>> utc_now = local_now.astimezone(utc)
>>> seconds = int(utc_now.strftime("%s"))
>>> utc_then = utc.localize(datetime.fromtimestamp(seconds))
>>> local_then = utc_then.astimezone(eastern)
>>> print utc_now, utc_then
2013-06-16 10:05:27.893005+00:00 2013-06-16 11:05:27+00:00
>>> print local_now, local_then
2013-06-16 06:05:27.893005-04:00 2013-06-16 07:05:27-04:00

【问题讨论】:

    标签: python datetime timezone utc pytz


    【解决方案1】:

    要将您的本地时区作为pytz.timezone 对象,您可以使用tzlocal 模块:

    #!/usr/bin/env python
    from datetime import datetime
    import pytz # pip install pytz
    from tzlocal import get_localzone # pip install tzlocal
    
    local_tz = get_localzone()
    local_now = datetime.now(local_tz)
    utc_now = local_now.astimezone(pytz.utc)
    seconds = (utc_now - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
    
    utc_then = datetime.fromtimestamp(seconds, pytz.utc)
    local_then = utc_then.astimezone(local_tz)
    print("%s %s" % (utc_now, utc_then))
    print("%s %s" % (local_now, local_then))
    
    • 不要使用datetime.now()——它可能会模棱两可,例如,在 DST 更改期间。要么像我的例子那样显式传递 tzinfo,要么使用 datetime.utcnow()
    • 不要使用utc_now.strftime('%s') -- it ignores timezone info (it uses the current local timezone),它是不可移植的。使用datetime.timestamp() method or its analogs instead
    • 不要使用utc.localize(datetime.fromtimestamp(seconds)) -- .fromtimestamp() 在本地时区返回一个可能与 UTC 不同的天真日期时间对象。要么像我的示例中那样显式传递 tzinfo,要么使用 datetime.utcfromtimestamp() 来获取代表 UTC 时间的天真日期时间对象
    • 不要将datetime.utctimetuple() 与天真的日期时间对象一起使用——它不会将它们转换为UTC。如果对象已经是 UTC:utc_now.timetuple() 返回相同的时间。

    要为不明确的当地时间引发异常,请使用localize(is_dst=None)

    aware_dt = tz.localize(naive_dt, is_dst=None)
    

    没有is_dst=None,在更改夏令时期间tz.localize() 有50% 的机会返回错误结果。如果在您的特定情况下最好得到一个可能错误的结果而不是异常,那么您可以通过显式的is_dst=False 来提醒自己。

    一般来说,如果源时区和目标时区都不是 UTC,pytz documentation 建议在调用 .astimezone() 之后使用 tz.normalize()

    【讨论】:

      【解决方案2】:
                   o------------o
                   |            |  DT.datetime.utcfromtimestamp (*)
                   |            |<-----------------------------------o
                   |            |                                    |
                   |  datetime  |                                    |
                   |            |  DT.datetime.fromtimestamp         |
                   |            |<----------------------------o      |
                   |            |                             |      |
                   o------------o                             |      |
                      |   ^                                   |      |
           .timetuple |   |                                   |      |
        .utctimetuple |   | DT.datetime(*tup[:6])             |      |
                      v   |                                   |      |
                   o------------o                          o------------o
                   |            |-- calendar.timegm (*) -->|            |
                   |            |                          |            |
                   |            |---------- time.mktime -->|            |
                   |  timetuple |                          |  timestamp |
                   |            |<-- time.localtime -------|            |
                   |            |                          |            |
                   |            |<-- time.gmtime (*)-------|            |
                   o------------o                          o------------o
      
      (*) Interprets its input as being in UTC and returns output in UTC
      

      如图所示,当您有一个 UTC 日期时间(例如 utc_now)时,要获取其时间戳,请使用

      seconds = calendar.timegm(utc_date.utctimetuple())
      

      如果您有时间戳,要获取 UTC 日期时间,请使用

      DT.datetime.utcfromtimestamp(seconds)
      

      import datetime as DT
      import pytz
      import calendar
      eastern = pytz.timezone("US/Eastern")
      utc = pytz.utc
      now = DT.datetime(2013, 6, 16, 10, 0, 0)
      local_now = eastern.localize(now)
      utc_now = local_now.astimezone(utc)
      seconds = calendar.timegm(utc_now.utctimetuple())
      
      print(seconds)
      # 1371391200
      
      utc_then = utc.localize(DT.datetime.utcfromtimestamp(seconds))
      local_then = utc_then.astimezone(eastern)
      
      print utc_now, utc_then
      # 2013-06-16 14:00:00+00:00 2013-06-16 14:00:00+00:00
      print local_now, local_then
      # 2013-06-16 10:00:00-04:00 2013-06-16 10:00:00-04:00
      

      PS。请注意,timetuple()utctimetuple() 方法会将日期时间缩短微秒。要以保留微秒的方式将日期时间转换为时间戳,请使用mata's solution

      【讨论】:

        【解决方案3】:

        如果你想编写可移植的代码,你应该避免使用datetime.now,因为它总是使用本地时区,所以local_now = eastern.localize(datetime.now()) 仅在本地计算机上的时区东部时才有效。始终尝试使用utcnow,出于同样的原因utcfromtimestamp

        另外,使用strftime("%s") 将日期时间转换为时间戳也不起作用。

        from datetime import datetime
        import pytz
        
        utc_now = pytz.utc.localize(datetime.utcnow())
        eastern = pytz.timezone("US/Eastern")
        local_now = utc_now.astimezone(eastern)
        
        # seconds = utc_now.timestamp()  python3
        seconds = (utc_now - pytz.utc.localize(datetime.utcfromtimestamp(0))).total_seconds()
        utc_then = pytz.utc.localize(datetime.utcfromtimestamp(seconds))
        
        local_then = utc_then.astimezone(eastern)
        
        print("%s - %s" % (utc_now, utc_then))
        print("%s - %s" % (local_now, local_then))
        

        【讨论】:

        • datetime(1970, 1, 1, tzinfo=pytz.utc) 可能比pytz.utc.localize(datetime.utcfromtimestamp(0)) 更明确。我不确定 stdlib 是否可以与其他纪元值一起使用。
        猜你喜欢
        • 2017-03-10
        • 2010-11-24
        • 2014-08-17
        • 2018-06-27
        • 2012-07-05
        • 2020-09-01
        • 1970-01-01
        • 2012-11-30
        • 2014-10-05
        相关资源
        最近更新 更多