如何在考虑夏令时区时添加每周的时间差

14 投票
3 回答
5686 浏览
提问于 2025-04-18 12:46

我想给本地化的日期时间对象加上或减去几周(或者几天、几个月、几年)。问题是,简单的方法会因为夏令时的时区而导致时间偏移一个小时。

比如说,2014年3月27日12:00是冬季时间转为夏季时间的前一刻。如果我在这个日期上加上一周的时间(timedelta),比如在欧洲/柏林这个时区,结果会变成2014年4月3日13:00。我希望能保持同样的小时数,也就是2014年4月3日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)

我在想有没有更通用或更好的解决方案。有没有什么库可以解决这个问题?这似乎是一个相当常见的问题。

3 个回答

0

我在尝试从一个给定的日期找出一周的开始和结束时遇到了一些问题。我使用了一个叫做 arrow 的工具包,成功地得到了一个考虑夏令时的时间差。下面是我怎么做的:

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点结束。

3

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

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
20

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

所以,如果可以的话,最好在将日期时间转为本地时区之前就应用这个时间差。

撰写回答