有没有办法在 Python 中推断日期是否是 DST(夏令时)更改的实际日期?

Is there a way to infer in Python if a date is the actual day in which the DST (Daylight Saving Time) change is made?

我想在 Python 中推断日期是否是一年中由于 DST(夏令时)而改变小时的实际日期。

使用库 pytz,您可以本地化日期时间并正确完成实际的 DST 更改。此外,库 datetime 的方法 dst() 允许您推断实际日期是夏令时还是冬令时 ()。但是,我想推断出进行 DST 更改的实际日期。

具体来说,我需要一个函数(例如 is_dst_change(date, timezone))来接收日期并且 returns True 仅适用于一年中确实有小时变化的那些日子。例如:

import pytz
import datetime

def is_dst_change(day, timezone):
    # Localize
    loc_day = pytz.timezone(timezone).localize(day)

    # Pseudocode: Infer if a date is the actual day in which the dst hour change is made
    if loc_day is dst_change_day:
        return True
    else:
        return False

# In the timezone 'Europe/Madrid', the days in which the DST change is made in 2021 are 28/03/2021 and 31/10/2021
is_dst_change(day=datetime.datetime(year=2021, month=3, day=28), timezone = 'Europe/Madrid')  # This should return True
is_dst_change(day=datetime.datetime(year=2021, month=10, day=31), timezone = 'Europe/Madrid')  # This should return True
is_dst_change(day=datetime.datetime(year=2021, month=2, day=1), timezone = 'Europe/Madrid')  # This should return False
is_dst_change(day=datetime.datetime(year=2021, month=7, day=1), timezone = 'Europe/Madrid')  # This should return False

因此,在上面的示例中,2021 年函数 is_dst_change(day, timezone='Europe/Madrid') 将 return True 的唯一日期是 28/03/2021 和 31/10/2021。对于 2021 年剩下的日子,它必须 return False。有没有办法用 Python 来推断这个?

如果今天的 UTC 偏移量与明天的不同,那么今天是 DST 变化。

def is_dst_change(day: datetime.datetime, timezone):
    # Override time to midnight
    day = day.replace(hour=0, minute=0, second=0, microsecond=0)
    tz = pytz.timezone(timezone)
    return(tz.utcoffset(day+datetime.timedelta(days=1)) != 
            tz.utcoffset(day))

“今天”在此代码中定义为午夜至午夜。

您可以使用 datetime.dst()(UTC 偏移量的变化不一定是 DST 转换):

from datetime import datetime, time, timedelta
from zoneinfo import ZoneInfo # Python 3.9+

def is_date_of_DSTtransition(dt: datetime, zone: str) -> bool:
    """
    check if the date part of a datetime object falls on the date
    of a DST transition.
    """
    _d = datetime.combine(dt.date(), time.min).replace(tzinfo=ZoneInfo(zone))
    return _d.dst() != (_d+timedelta(1)).dst()

例如对于 tz Europe/Berlin:

for d in range(366):
    if is_date_of_DSTtransition(datetime(2021, 1, 1) + timedelta(d), "Europe/Berlin"):
        print((datetime(2021, 1, 1) + timedelta(d)).date())
# 2021-03-28
# 2021-10-31

注意:我正在使用 zoneinfo here instead of pytz; for legacy code, there is a pytz deprecation shim。这里有一个 pytz 版本(需要额外的 normalize):

import pytz
def is_date_of_DSTtransition(dt: datetime, zone: str) -> bool:
    _d = pytz.timezone(zone).localize(datetime.combine(dt.date(), time.min))
    return _d.dst() != pytz.timezone(zone).normalize(_d+timedelta(1)).dst()