如何添加关于夏令时时区的每周时间增量

Posted

技术标签:

【中文标题】如何添加关于夏令时时区的每周时间增量【英文标题】:How to add weekly timedeltas with regards to daylight saving timezones 【发布时间】:2014-08-31 15:19:51 【问题描述】:

我想为本地化的日期时间对象增加或减少几周(或几天、一个月或几年)。问题是,由于夏令时时区,这种天真的方法将导致 1 小时轮班。

2014-03-27 12:00 就在从冬季时间切换到夏季时间之前。例如,如果我在欧洲/柏林时区本地化的该日期添加一周的时间增量,结果将是 2014-04-03 13:00。我想要一天中的同一个小时,2014-04-03 12:00。我找到了解决方案:

from datetime import datetime, timedelta
import pytz
my_tz = pytz.timezone("Europe/Berlin")

def add_relativedelta(date, delta):
    """
    Adds the given timedelta to the given date. Shifts in timezone offsets
    will be removed.
    """
    tz = date.tzinfo
    result = tz.normalize(date + delta)
    if result.utcoffset() != date.utcoffset():
        result = tz.normalize(date.utcoffset() - result.utcoffset() + result)
    return result

date = my_tz.localize(datetime(year=2014, month=3, day=27, hour=12, minute=0))
print """ Original localized date (winter time)
 One week later (summer time)
 Date one week later preserving hour of day (summer time)""".format(date,
                     my_tz.normalize(date + timedelta(days=7)),
                     add_relativedelta(date, timedelta(days=7)))


2014-03-27 12:00:00+01:00 Original localized date (winter time)
2014-04-03 13:00:00+02:00 One week later (summer time)
2014-04-03 12:00:00+02:00 Date one week later preserving hour of day (summer time)

我想知道是否有更通用/更好的解决方案。有没有可以解决这个问题的库?这似乎是一个很常见的问题。

【问题讨论】:

相关;如果您必须包括 DST 更改:***.com/q/63579796/10197418 【参考方案1】:

timedelta(days=7) 表示 7 天,如 7*24 小时 - 而不是“太阳日”。 如果您将 7 天添加到可识别时区的日期时间,您将获得 7 天后的日期时间 - 与该日期时间在时区中表示的方式无关。

看来您真正想要的是将增量应用于您指定的时间,忽略时区详细信息。注意区别:

In [13]: print my_tz.normalize( my_tz.localize( dt ) + delta )
2014-04-03 13:00:00+02:00

In [14]: print my_tz.normalize( my_tz.localize( dt + delta ) )
2014-04-03 12:00:00+02:00

因此,如果可能,请在本地化之前将增量应用于日期时间。

【讨论】:

【参考方案2】:

我使用这个简单的代码而不需要其他库:

from datetime import datetime, timedelta
from pytz import timezone

tz = timezone('Europe/Berlin')
dt = tz.localize(datetime(2014, 3, 27, 12))

week_naive = datetime.combine(dt.date() + timedelta(days=7), dt.time())
week_local = dt.tzinfo.localize(week_naive)

print(dt, "Original datetime")
print(week_local, "Next week datetime")

输出:

2014-03-27 12:00:00+01:00 Original datetime
2014-04-03 12:00:00+02:00 Next week datetime

【讨论】:

【参考方案3】:

我试图从给定日期查找一周的开始和结束,但遇到了这个问题。我能够使用 arrow 包实现 DST 感知时间增量。我是这样做的:

from dataclasses import dataclass
from datetime import datetime

import arrow


@dataclass(frozen=True)
class Week:
    start: datetime
    end: datetime


def get_week_range(dt: datetime, tz: str) -> Week:
    """Returns Week instance with localized, time-aware start and end datetimes"""
    dt = arrow.get(dt, tz)
    start = dt.shift(days=-dt.weekday())
    end = start.shift(days=7).shift(seconds=-1)

    return Week(start=start, end=end)


>>> get_week_range(datetime(2020, 10, 27), "America/Chicago")
Week(start=<Arrow [2020-10-26T00:00:00-05:00]>, end=<Arrow [2020-11-01T23:59:59-06:00]>)

请注意此示例中返回的结束日期如何正确调整为 -06:00 偏移量,因为 America/Chicago 时区的夏令时于 11 月 1 日凌晨 2 点结束。

【讨论】:

以上是关于如何添加关于夏令时时区的每周时间增量的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript、时区和夏令时

C# 英国夏令时 (BST) 时区缩写

如何在 Dynamics CRM 中获取时区的夏令时开始和结束?

如何获取另一个时区的夏令时状态

如何在android中处理时区和夏令时?

带有时区和夏令时的纪元的日期和时间(以秒为单位)