我有一個接收常規 ping 的腳本,只要最后一次 ping 超過 10 分鐘,它就會引發錯誤并進入恐慌模式。簡化示例:
from datetime import datetime
import time
from random import randint
class Checker:
def __init__(self):
self.last_ping = datetime.now()
def ping(self):
self.last_ping = datetime.now()
def panic_if_stale(self):
if (datetime.now() - self.last_ping).total_seconds() > 600:
# no ping for longer than 10 minutes, we panic
raise Exception('PANIC')
if __name__ == '__main__':
checker = Checker()
while True:
if randint(0, 10) == 5:
# this is not actually random, just simulating
checker.ping()
checker.panic_if_stale()
time.sleep(60)
上周日夏令時在這里改變了,我遇到了一個錯誤(該.total_seconds()行是.seconds,這使得該錯誤實際上引發了恐慌,但即使沒有它,仍然存在奇怪的行為)。經過一些實驗,我發現正在發生以下情況:
from datetime import datetime, timedelta
if __name__ == '__main__':
first_date = datetime(2022, 10, 30, 2, 30)
# this should be 50 minutes later, but after the time change:
second_date = datetime(2022, 10, 30, 2, 20, fold=1)
# this prints out -600 seconds:
print((second_date - first_date).total_seconds())
# this prints out 3000 seconds(the correct answer in my opinion):
print(second_date.timestamp() - first_date.timestamp())
# this prints out 85800 seconds, but I used the wrong function so that's kind of whatever:
print((second_date - first_date).seconds)
# this prints out "False", even though it should be True:
print((second_date - first_date) > timedelta(minutes=5))
即使 datetime 顯然確實如此(如時間戳差異所見), timedelta 是否不會將 DST 考慮在內。在可能的情況下使用 timedelta 不是最佳做法嗎?我應該只使用時間戳(我覺得有點難看)嗎?最好我不想像 pytz 那樣向這個專案添加外部依賴項。
uj5u.com熱心網友回復:
避免 DST 問題的一種方法:使用 UTC。前任:
from datetime import datetime, timezone
class Checker:
def __init__(self):
self.last_ping = datetime.now(timezone.utc)
def ping(self):
self.last_ping = datetime.now(timezone.utc)
def panic_if_stale(self):
if (datetime.now(timezone.utc) - self.last_ping).total_seconds() > 600:
# no ping for longer than 10 minutes, we panic
raise Exception('PANIC')
一些背景知識,為什么 DST 轉換在與 timedelta 算術結合時會導致問題:timedelta 算術是墻上時間算術。它不考慮fold。持續時間與您在調整為 DST 轉換的時鐘上看到的相同。另請參閱時區感知日期時間演算法的語意。
在示例中使用有意識的日期時間可以更清楚地說明這一點:
from zoneinfo import ZoneInfo
first_date = datetime(2022, 10, 30, 2, 30, fold=0, tzinfo=ZoneInfo("Europe/Berlin"))
second_date = datetime(2022, 10, 30, 2, 20, fold=1, tzinfo=ZoneInfo("Europe/Berlin"))
print(first_date, second_date)
# 2022-10-30 02:30:00 02:00 2022-10-30 02:20:00 01:00
# WALL TIME: -600 seconds, 02:30:00 h -> 02:20:00 h
print((second_date - first_date).total_seconds())
# -600.0
# still correct; .timestamp considers fold attribute:
print(second_date.timestamp() - first_date.timestamp())
# 3000.0
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/526476.html
上一篇:處理“OutOfBoundsDatetime:越界納秒時間戳:”
下一篇:根據日期和小時獲取日期范圍
