【问题标题】:getmtime() vs datetime.now():getmtime() 与 datetime.now():
【发布时间】:2020-02-23 12:57:32
【问题描述】:

此代码每年在时钟转换的晚上(欧洲中部夏令时间到欧洲中部时间)打印一次错误警告:

import os
import datetime

now = datetime.datetime.now()
age = now - datetime.datetime.fromtimestamp(os.path.getmtime(file_name))
if (age.seconds + age.days * 24 * 3600) < -180:
    print('WARN: file has timestap from future?: %s' % age)

即使在每年一小时的时钟轮班期间,如何使此代码正常工作?

更新

我只关心年龄,而不关心日期时间。

【问题讨论】:

    标签: python clock gmt


    【解决方案1】:

    通过从本地时间切换到 UTC 时间可以轻松改进发布的片段。 UTC 没有夏季(夏令时)时间变化。只需替换这两个日期时间函数now() -> utcnow() (docs) 和 fromtimestamp() -> utcfromtimestamp() (docs)。

    但是,如果唯一的预期输出是以秒为单位的文件年龄,我们可以直接使用时间戳(距“纪元”的秒数)而无需任何转换:

    import time
    import os.path
    
    ...
    age = time.time() - os.path.getmtime(file_name)
    

    【讨论】:

    • 首先使用 UTC 是普遍正确的方法。
    • @konstantin 为什么是正确的方法?我喜欢这个简单的解决方案,因为我(在这种情况下)只关心年龄(timedelta)而不是日期时间。
    • @guettli,我想说这可能是您用例的最佳和最简单的答案。比较时间时最重要的事情是您正在比较同类,在此示例中,它是 UTC 时间戳与 UTC 时间戳,因此始终有效。您的代码最初不起作用的原因是因为您正在比较不再与 UTC 时间戳相关的对象,因为它们不知道时区。如果您打算做更复杂的事情,那么我的回答可能会更有用,因为使用 datetime 对象更容易,但对于简单的比较来说,这是可行的。
    • @guettli 我将其标记为“非常正确的方法”,因为我花了太多时间(如果不是几天的话)调试系统和接口,这些系统和接口仅适用于作为输入接收到的关于日期时间和时区的一些先验假设。如果例如您的服务器与客户端不在同一时区运行,并且日期时间在没有明确的 UTC 偏移量的情况下传递并被解释为本地日期时间,事情可能仍会以某种方式解决(例如,在计算增量时),但调试很痛苦,如果可以很容易地避免每个人都只在第一时间/尽快坚持使用 UTC。
    • @guettli 感谢您接受我的回答。我希望它会有所帮助,因为我有点担心我的简短回答不值得如此慷慨的赏金,而且你多付了我。最好的问候(Schöne Grüße nach Chemnitz)
    【解决方案2】:

    您的两个日期时间对象都是“幼稚的”,这意味着它们不知道 DST。 datetime.now() 返回您的机器运行的当前时间,其中可能包括 DST。 datetime.fromtimestamp(os.path.getmtime()) 也是如此。

    #1 - 本地化您的日期时间对象可能是一种选择;像

    from datetime import datetime
    import tzlocal
    now_aware = tzlocal.get_localzone().localize(datetime.now())
    file_mtime = datetime.fromtimestamp(os.path.getmtime(file))
    # assuming the file was created on a machine in the same timezone (!):
    file_mtime_aware = now_aware.tzinfo.localize(file_mtime)
    age = now_aware - file_mtime_aware
    

    #2 - 另一个选项,使用 datetime 的 UTC 转换:

    now = datetime.utcnow()
    age = now - datetime.utcfromtimestamp(os.path.getmtime(file_name))
    if (age.seconds + age.days * 24 * 3600) < -180:
        print(f'WARN: file has timestamp from future?: {age} s')
    

    #3 -正如VPfB 在他的回答中指出的那样,os.path.getmtime 返回一个UTC 时间戳(检查os module docstime module docs)。因此,最简单的解决方案可能是首先跳过转换为 datetime 并仅使用 UTC 时间戳;例如获取当前 UTC 时间戳为time.time()

    使用时区可能会让您发疯...但是那里有一些很好的资源,例如this medium post.

    【讨论】:

      【解决方案3】:

      您的问题是您在不了解时区的情况下获得时间。因此,当时钟更改时,您将结束比较时钟更改之前的一个时间戳和时钟更改之后的另一个时间戳,而您的代码看不到这一点。

      你应该让你的 datetime 对象基于一个特定的时区,这样你就不会有时钟变化的问题,我建议使用 pytz 模块来帮助你。您可以在此答案中查看可用时区列表:Is there a list of Pytz Timezones?

      这是一个简单的代码示例,说明如何使用时区感知对象执行此操作:

      import os
      from datetime import datetime
      import pytz
      
      
      def get_datetime_now(timezone):
          """
          Returns timezone aware datetime object for right now
          """
          if timezone not in pytz.all_timezones:
              return None
          tz = pytz.timezone(timezone)
          dt = datetime.now().astimezone()
          return dt.astimezone(tz)
      
      
      def timestamp_to_datetime(timestamp, timezone):
          """
          Returns a datetime object from a timestamp
          """
          if timezone not in pytz.all_timezones:
              return None
          tz = pytz.timezone(timezone)
          dt = datetime.fromtimestamp(timestamp).astimezone()
          return dt.astimezone(tz)
      
      
      timezone = 'CET'
      
      file_timestamp = os.path.getmtime(file_name)
      
      now = get_datetime_now(timezone)
      file_datetime = timestamp_to_datetime(file_timestamp, timezone)
      age = now - file_datetime
      
      if (age.seconds + age.days * 24 * 3600) < -180:
          print('WARN: file has timestap from future?: %s' % age)
      

      【讨论】:

      • 为什么你的解决方案比age = time.time() - os.path.getmtime(file_name)更好。我只对年龄(时间增量)感兴趣,而不是日期时间。
      • 如果您只对时间增量感兴趣,那么它不是。我以这种方式接近它的原因是因为您提到它位于 CET 时区并表明您正在使用 datetime 对象,如果您要比较两个不同时区之间的时间,这种方法会很有用。如果您的时区相同,那么只需比较时间戳就足够了。唯一的其他考虑是确保您的系统时间与 NTP 服务器同步。
      猜你喜欢
      • 2010-09-19
      • 2010-09-08
      • 2015-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多