【问题标题】:How do I find the next 7am in a timezone [duplicate]如何在时区找到下一个早上 7 点 [重复]
【发布时间】:2016-08-03 20:55:56
【问题描述】:
current_datetime = datetime.now(tz)
next_hour = datetime(current_datetime.year, current_datetime.month, current_datetime.day, 7, 0, 0, 0, tz)
timedelta_until_next_hour = next_hour - current_datetime
if timedelta_until_next_hour.total_seconds() < 0:
    timedelta_until_next_hour += timedelta(days=1)
return timedelta_until_next_hour.total_seconds()

我正在尝试查找下一次当地时间上午 7 点的时间,并返回直到该时间的秒数。

我遇到了一些夏令时问题。例如:America/New_York current_datetime 的 utcoffset 为 -4 小时 而next_hour 的偏移量为 -5 小时,因此两者的减法相差一个小时

【问题讨论】:

标签: python python-2.7 date datetime


【解决方案1】:

寻找下一个早上 7 点

您可以使用python-dateutilrelativedelta module 轻松做到这一点:

from dateutil.relativedelta import relativedelta
def next_7am(dt):
    relative_days = (dt.hour >= 7)
    absolute_kwargs = dict(hour=7, minute=0, second=0, microsecond=0)
    return dt + relativedelta(days=relative_days, **absolute_kwargs)

它的工作方式是relativedelta 采用absolute 参数(表示为单数,例如monthyearday)和相对 em> 参数(表示为复数形式,例如monthsyearsdays)。如果将relativedelta 对象添加到datetime,它将替换datetime 中的absolute 值,然后添加relative 值,所以我在上面所做的是指定relative_days 应该是 1 如果它已经是早上 7 点,否则它应该是 0,并且绝对参数说“用早上 7 点替换时间”。将其添加到您的日期时间,它将为您提供下一个早上 7 点。

处理时区

下一步取决于您使用的时区。如果您使用的是dateutil 时区,那么您可以使用上面定义的函数:

dt_next_7am = next_7am(dt)

如果您使用pytz 时区,则应将其剥离并作为原始日期时间进行计算,然后重新本地化时区,如下所示:

dt_next_7am = tz.localize(next_7am(dt.replace(tzinfo=None)))

如果你想得到这两次之间的绝对小时数,你应该用 UTC 做算术:

time_between = dt_next_7am.astimezone(tz=UTC) - dt.astimezone(tz=UTC)

其中UTC 已定义为dateutil.tz.tzutc()pytz.UTC 或等效项。

DST 转换示例

这是一个使用dateutil 的示例(结果在评论中):

from datetime import datetime
from dateutil.tz import gettz, tzutc

LA = gettz('America/Los_Angeles')
dt = datetime(2011, 11, 5, 12, 30, tzinfo=LA)
dt7 = next_7am(dt)

print(dt7.astimezone(tzutc()) - dt.astimezone(tzutc()))  # 19:30:00

还有一个示例显示了使用pytz 执行此操作的正确方法:

from datetime import datetime
import pytz
LA = pytz.timezone('America/Los_Angeles')
UTC = pytz.UTC

dt = LA.localize(datetime(2011, 11, 5, 12, 30))
dt7_bad = next_7am(dt)      # pytz won't like this
dt7_good = LA.localize(next_7am(dt.replace(tzinfo=None)))

dt_utc = dt.astimezone(pytz.UTC)
print(dt7_bad.astimezone(pytz.UTC) - dt_utc)   # 18:30:00 (Wrong)
print(dt7_good.astimezone(pytz.UTC) - dt_utc)  # 19:30:00 (Right)

模糊/不存在 7 AM

如果您要处理某些区域的某些日期,特别是那些会导致时间不明确的日期,请参见以下列表(截至 2016 年 4 月):

1901-12-13 07:00:00 (/Pacific/Fakaofo)
1901-12-14 07:00:00 (/Asia/Kamchatka)
1901-12-14 07:00:00 (/Asia/Ust-Nera)
1901-12-14 07:00:00 (/Pacific/Bougainville)
1901-12-14 07:00:00 (/Pacific/Kosrae)
1901-12-14 07:00:00 (/Pacific/Majuro)
1917-03-25 07:00:00 (/Antarctica/Macquarie)
1918-03-31 07:00:00 (/EST5EDT)
1919-03-31 07:00:00 (/Antarctica/Macquarie)
1952-01-13 07:00:00 (/Antarctica/DumontDUrville)
1954-02-13 07:00:00 (/Antarctica/Mawson)
1957-01-13 07:00:00 (/Antarctica/Davis)
1969-01-01 07:00:00 (/Antarctica/Casey)
1969-02-01 07:00:00 (/Antarctica/Davis)
1969-09-29 07:00:00 (/Kwajalein)
1969-09-29 07:00:00 (/Pacific/Kwajalein)
1979-09-30 07:00:00 (/Pacific/Enderbury)
1979-09-30 07:00:00 (/Pacific/Kiritimati)
2009-10-18 07:00:00 (/Antarctica/Casey)
2011-09-23 07:00:00 (/Pacific/Apia)
2011-10-28 07:00:00 (/Antarctica/Casey)

那么生成的 7AM 值将不明确或不存在。如果您想处理这些边缘情况,请参阅this answer。可能值得注意的是,在实现PEP495 之后,处理模棱两可的时间可能会略有不同。

下面是使用python-dateutilrrule 模块生成重复规则和使用pytz 区域的方法的替代实现(请注意,这将适用于非pytz 区域,但它不会解决歧义/不存在的时间正确):

from datetime import datetime
from dateutil import rrule
import pytz

def next_real_7am(dt):
    tzi = dt.tzinfo

    dt_naive = dt.replace(tzinfo=None)
    rr = rrule.rrule(freq=rrule.DAILY, byhour=7, dtstart=dt_naive)

    for ndt in rr:
        localize = getattr(tzi, 'localize', None)
        if tzi is not None and localize is not None:
            try:
                ndt = localize(ndt, is_dst=None)
            except pytz.AmbiguousTimeError:
                return min([localize(ndt, is_dst=True),
                            localize(ndt, is_dst=False)])
            except pytz.NonExistentTimeError:
                continue
        else:
            ndt = ndt.replace(tzinfo=tzi)

        return ndt

KWA = pytz.timezone('Pacific/Kwajalein')

dtstart = KWA.localize(datetime(1969, 9, 29, 18))
dt7 = next_real_7am(dtstart)

print(dt7.tzname())         # Should be MHT, before the transition

dtstart = KWA.localize(datetime(1993, 8, 19, 18))  # There was no 8/20 in this zone
dt7 = next_real_7am(dtstart)
print(dt7)                  # Should be 1993-8-21 07:00:00

【讨论】:

  • dt.replace(hour=7, ...) 给出的是今天早上 7 点,而不是未来最接近的早上 7 点
  • 糟糕,我的错,正在调整。
  • 谢谢。是否有理由使用 relativedelta 而不是 timedelta?我明白了:P
  • @quikst3r 他们做了两件不同的事情。 timedelta 做纯粹的“挂钟”算术。 relativedelta 允许您在添加步骤中进行绝对替换。如果您有兴趣,可以在链接的文档中阅读它的许多其他功能。
  • 如果输入时间在给定时区不存在/不明确(例如,在 DST 转换期间),则代码错误。见how non-existent/ambiguous time can be handled
【解决方案2】:

我正在尝试查找下一次当地时间上午 7 点的时间,并返回直到该时间的秒数。

使用the same code as for dt6 (replace time(6) with time(7)) 查找dt7

那么距离那个秒数(dt7 - now).total_seconds()

the bullet points that explain when other solutions may fail

【讨论】:

  • 我不介意投反对票。但希望得到解释。如何改进答案?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-01
  • 1970-01-01
相关资源
最近更新 更多