簡體   English   中英

Python datetime:timedelta 和夏令時之間的意外依賴

[英]Python datetime: unexpected dependency between timedelta and daylight saving time

我想知道在接下來的幾個小時內是否有夏令時變化。 因此,我認識到 datetime.timedelta、astimezone 和夏令時之間存在意外的依賴關系。 在夏令時更改期間,timedelta 計算似乎不起作用。 這是一個簡單的例子:

import datetime
import pytz

format = '%Y-%m-%d %H:%M:%S %Z%z'
local_tz = pytz.timezone('Europe/Berlin')
time_diff = datetime.timedelta(hours = 1)

print((datetime.datetime(2023, 2, 26, 1, 0, 0) + time_diff).astimezone(local_tz).strftime(format))
# => 2023-02-26 02:00:00 CET+0100

print((datetime.datetime(2023, 3, 26, 1, 0, 0) + time_diff).astimezone(local_tz).strftime(format))
# => 2023-03-26 01:00:00 CET+0100

我希望最后打印的結果是“2023-03-26 02:00:00 CET+0100”或“2023-03-26 03:00:00 CEST+0200”。 有誰能解釋這種行為?

經過不同的嘗試后,我通過在將時區添加到時間戳后添加時間增量找到了解決方案。

print((datetime.datetime(2023, 3, 26, 1, 0, 0).astimezone(local_tz) + time_diff).strftime(format))

但是我仍然不明白我第一次使用的代碼中的錯誤。

我的版本:
- Python 3.10.2
- pytz 2022.7

另請參閱 Paul Ganssle 的這個答案- 原生 Python 的 timedelta 與時區相結合,必須預期不存在的日期時間(如 2023-02-26 02:00:00 CET+0100)。

這里有一個稍微擴展的比較供參考,+代碼中的注釋。 自 Python 3.9 的 zoneinfo 發布以來,pytz 已被棄用。

from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
import pandas as pd
import pytz

def absolute_add(dt: datetime, td: timedelta) -> datetime:
    utc_in = dt.astimezone(timezone.utc)  # Convert input to UTC
    utc_out = utc_in + td  # Do addition in UTC
    civil_out = utc_out.astimezone(dt.tzinfo)  # Back to original tzinfo
    return civil_out

# -----
# in vanilla Python, we can create non-existent datetime...

# I) tz already set
t = datetime(2023, 3, 26, 1, tzinfo=ZoneInfo("Europe/Berlin"))
print(t + timedelta(hours=1))
# 2023-03-26 02:00:00+01:00
# this datetime should not exist in that time zone since there is a DST transtion,
# 1 am UTC+1 plus one hour gives 3 am UTC+2

# II) tz set after timedelta addition
print(datetime(2023, 3, 26, 1) + timedelta(hours=1))
# 2023-03-26 02:00:00
# this is ok since no tz specified
print((datetime(2023, 3, 26, 1) + timedelta(hours=1)).replace(tzinfo=ZoneInfo("Europe/Berlin")))
# 2023-03-26 02:00:00+01:00
# again, we have a non-existent datetime
print((datetime(2023, 3, 26, 1) + timedelta(hours=1)).astimezone(ZoneInfo("Europe/Berlin")))
# 2023-03-26 01:00:00+01:00
# also a bit confusing; 2 am would be non-existing, so the hour is "corrected"
# backwards before setting the time zone
# -----
# adding the timedelta in UTC as "absolute duration" works as expected:
print(absolute_add(t, timedelta(hours=1)))
# 2023-03-26 03:00:00+02:00

# with pytz timezones, you can normalize to correct the non-existent datetime:
tz = pytz.timezone("Europe/Berlin")
t = tz.localize(datetime(2023, 3, 26, 1))
print(tz.normalize(t + timedelta(hours=1)))
# 2023-03-26 03:00:00+02:00

# this is correctly implemented in pandas for instance:
t = pd.Timestamp(2023, 3, 26, 1).tz_localize("Europe/Berlin")
print(t + pd.Timedelta(hours=1))
# 2023-03-26 03:00:00+02:00

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM