【问题标题】:Getting number of seconds elapsed in `Wall Clock' time获取“挂钟”时间经过的秒数
【发布时间】:2015-10-20 04:03:14
【问题描述】:

我正在编写一个模块来计算给定一天的半小时交易时间。交易时段从半小时开始,从 1(从 00:00 开始)到 48(从 23:30 开始)连续编号。通常一天有 48 个交易时段,但夏令时开始当天有 46 个,结束当天有 50 个。

下面的代码适用于所有“正常”日期,但无法在夏令时开始或结束的日子给出正确的交易期数,因为下面代码中的 datetime.replace() 使用相同的 UTC 时间偏移作为开始那天。在夏令时改变的日子里,这个假设是不正确的。

datetime.replace() 是否可以将“挂钟”时间设置为 00:00,以便时差与您在 00:00 设置秒表时得到的时间相匹配,然后计算半小时间隔它会在所有日子里正确匹配吗?我还没有找到自动执行此操作的方法。

一个例子: 新西兰的夏令时于 2015 年 4 月 5 日 03:00 (2015-05-04 14:00Z) 结束。因此,小时 02:00-02:59 (2015-05-04 14:00Z - 2015-05-04 14:59Z) 在“挂钟”时间重复。因此,在 2015 年 4 月 5 日,新西兰用了 18000 秒才到达凌晨 4 点,因为夏令时结束了。 2014 年 9 月 28 日,夏令时开始用时 10800 秒。

@staticmethod
def half_hour_from_utc(time_utc = None):
    # Get the time as NZ civil time.
    time_nz = time_utc.astimezone(pytz.timezone('Pacific/Auckland'))



    # Get the time tuple for the start of the day. This is done by keeping
    # the date the same but setting the hours, minutes, seconds and
    # microseconds to zero.
    time_start_of_day = time_nz.replace(hour = 0, minute = 0, second = 0, microsecond = 0)

    # Get total number of seconds. The half hour period is the number of
    # 1800 second periods that have passed + 1
    total_secs = int((time_nz - time_start_of_day).total_seconds())
    half_hour = 1 + total_secs // 1800


    print('%s %s %s %s\n' % (time_nz, time_start_of_day, total_secs, half_hour))

【问题讨论】:

    标签: python datetime dst


    【解决方案1】:

    问题在于 .replace() 调用可能会返回非规范化的 datetime 值,即 tzinfo 在午夜可能是错误的。见How do I get the UTC time of “midnight” for a given timezone?

    from datetime import datetime, time as datetime_time, timedelta
    import pytz # $ pip install pytz
    
    def half_hour_from_utc(time_utc, tz=pytz.timezone('Pacific/Auckland')):
        time_nz = time_utc.astimezone(tz) # no need to call normalize() here
        midnight = datetime.combine(time_nz, datetime_time(0, 0)) # naive
        time_start_of_day = tz.localize(midnight, is_dst=None) # aware
    
        return 1 + (time_nz - time_start_of_day) // timedelta(minutes=30) # Python 3
    

    在 Python 2 上模拟 1 + td // timedelta(minutes=30)

    td = time_nz - time_start_of_day
    assert td.days == 0
    return 1 + td.seconds // 1800
    

    如果 DST 转换可能发生在给定时区的午夜,那么您可以在一天的开始使用最小值:

    time_start_of_day = min(tz.localize(midnight, is_dst=False),
                            tz.localize(midnight, is_dst=True))
    

    注意:即使在给定日期的给定时区中不存在 00:00 时间,它也可以工作:要找到差异,只有相应的 UTC 时间很重要。

    【讨论】:

    • 在新西兰,夏令时转换从不会在当地时间午夜发生,因此不会出现具体问题。能举个反例吗?
    • @bjem: 0。这就是我在主代码部分使用is_dst=None 的原因 1. 你不能保证它永远不会发生(规则可能会改变) 2. 有类似情况的人问题可能想使用不同的时区
    • @bjem:如果您的意思是.replace() 问题不会发生,那么您就错了。它发生在 DST 转换发生在 午夜和给定时间之间,例如,如果 time_nz 是 2015 年 4 月 5 日上午 9 点,那么您问题中基于 .replace() 的代码将产生错误的结果。跨度>
    • My original comment 是关于我的回答中的.localize() 调用(与.replace() 问题无关)。 localize(is_dst=None) 适用于当前的太平洋/奥克兰时区规则。您问题中的.replace() 调用不正确。你不应该使用它。
    【解决方案2】:

    在处理挂钟算术时,您应该使用normalize()localize()

    def half_hour_from_utc(time_utc = None):
        tz = pytz.timezone('Pacific/Auckland')
        time_nz = tz.normalize(time_utc)
        time_start_of_day = tz.localize(datetime.datetime(time_nz.year, time_nz.month, time_nz.day))
        total_secs = int((time_nz - time_start_of_day).total_seconds())
        half_hour = 1 + total_secs // 1800
        print('%s %s %s %s\n' % (time_nz, time_start_of_day, total_secs, half_hour))
    
    • normalize() = 将带有时区的日期时间转换为带有另一个时区的日期时间。
    • localize() = 将不带时区的日期时间转换为带时区的日期时间。

    这些方法考虑了使用时区计算正确日期时间的必要逻辑。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    相关资源
    最近更新 更多