ほぼ雑記的メモ
pythonのdatetimeはnativeとawareという二つのものが存在します。端的にいうとnativeとは純粋に日付と時間を表したもので年月日時分秒を表したものということでよいかと思います。2023/4/1から2023/8/24まで何秒か?なんて計算をするときに有効です。一方awareはその地域における時間で例えば日本で9時、イギリスで1時、アメリカで何時のようなもので、その地域の壁時計時間が何時かなどという処理をするときに有効です。何も考えずにdatetimeオブジェクトを作成するとnativeになります。tzinfoにtimezoneを表すオブジェクトを引き渡して作成をするとawareになりますfrom datetime import datetime, timezonedatetime(2023,10,1) #nativedatetime(2023,10,1, tzinfo=timezone.utc) # aware nativeとawareはその用途や目的が違うので相互に演算をすることが出来ません。演算しようとするとエラーとなります。さておよそPCの時間というのは所謂epochで管理されています。epochというのは1970/1/1 00:00:00を起源としそこからの経過秒数です。これは閏秒を考慮していないUTCでtime()関数の戻り値でもあります。この閏秒を考慮していないというのが非常に快適で今現在のepochに3600*24を足せば必ず明日の同時刻になります。UTCとのずれに「分」がない地域では3600で割り切れる時間は必ず正時になります。日本はUTCから9時間進んでいるので3600*24で割ったあまりが3600*9ならばそれは夜中の0時ということになります。以下awareなdatetimeについて主に話を進めます。pythonのtimezoneはtzinfoというクラスで管理されているのですがこのクラスはインスタンス化できない仮想クラスで長らく実装されたクラスは標準で datetime.timezone.utcしかありませんでした。そのためpytzというモジュールを利用したりしていたのですが、最近ではzoneinfoというモジュールが追加されこの辺り使いやすくなっています。試しにAsia/Tokyoでawareな時刻を作成してそのepochを取得してみたりtimedeltaを足したりして時間を進めてみたりしてみます。datetimeからepochを得るにはtimestamp()メソッドを使います。datetimeにtimedelta(seconds=3600)を足すと1時間先の時刻になることがtimestamp()の結果からわかると思います。from zoneinfo import ZoneInfofrom datetime import datetime, timedeltaTZ = ZoneInfo('Asia/Tokyo')delta = timedelta(hours=1)d = datetime(2023, 3, 26, 0, 0, 0, tzinfo=TZ)print(d, d.timestamp())d += deltaprint(d, d.timestamp())d += deltaprint(d, d.timestamp())d += deltaprint(d, d.timestamp())実行結果2023-03-26 00:00:00+09:00 1679756400.02023-03-26 01:00:00+09:00 1679760000.02023-03-26 02:00:00+09:00 1679763600.02023-03-26 03:00:00+09:00 1679767200.0同じプログラムをAsia/TokyoではなくEurope/Zurichで動かしてみます。すると以下のような結果が表示されます。2023-03-26 00:00:00+01:00 1679785200.02023-03-26 01:00:00+01:00 1679788800.02023-03-26 02:00:00+01:00 1679792400.02023-03-26 03:00:00+02:00 1679792400.03/26の03:00から+02:00となっているのがわかるかと思います。これはチューリッヒでは2023/3/26の深夜に夏時間に移行するからです。そしてさらに詳しくみてみるとepochが1679792400.0の1時間後がまた1679792400.0になっています。つまりepoch的に全く進んでないことがわかります。サマールールへの移行のルールを詳しく書くと2023年は3/26の01:59の次は02:00ではなく03:00(そして時差+02:00)です。つまり上記の出力結果である2023-03-26 02:00:00+01:00 1679792400.0は存在しない時間です。ですがpythonでは出力されてしまいます。どうもpythonはawareであっても純粋にhourに1を足してしまうようで、その結果epochが進まないという事態になっているようです。これは結構トラップな気がします。個人的にはtimedelta(hours=1)をawareな時刻であっても足したらawareの時刻において1時間先になってほしいところですがpythonはそうはなっていないようです。なおこのプログラムを以下のように書き換えepochで管理をするように務めるとts = 1679785200d = datetime.fromtimestamp(ts, tz=TZ)print(d, d.timestamp())ts += 3600d = datetime.fromtimestamp(ts, tz=TZ)print(d, d.timestamp())ts += 3600d = datetime.fromtimestamp(ts, tz=TZ)print(d, d.timestamp())ts += 3600d = datetime.fromtimestamp(ts, tz=TZ)print(d, d.timestamp())以下のように期待した出力が得られます2023-03-26 00:00:00+01:00 1679785200.02023-03-26 01:00:00+01:00 1679788800.02023-03-26 03:00:00+02:00 1679792400.02023-03-26 04:00:00+02:00 1679796000.0これらの実験は全てpython3.9での実行結果です。
from datetime import datetime, timezone
datetime(2023,10,1) #native
datetime(2023,10,1, tzinfo=timezone.utc) # aware
from zoneinfo import ZoneInfo
from datetime import datetime, timedelta
TZ = ZoneInfo('Asia/Tokyo')
delta = timedelta(hours=1)
d = datetime(2023, 3, 26, 0, 0, 0, tzinfo=TZ)
print(d, d.timestamp())
d += delta
2023-03-26 00:00:00+09:00 1679756400.0
2023-03-26 01:00:00+09:00 1679760000.0
2023-03-26 02:00:00+09:00 1679763600.0
2023-03-26 03:00:00+09:00 1679767200.0
2023-03-26 00:00:00+01:00 1679785200.02023-03-26 01:00:00+01:00 1679788800.02023-03-26 02:00:00+01:00 1679792400.02023-03-26 03:00:00+02:00 1679792400.0
2023-03-26 02:00:00+01:00 1679792400.0
ts = 1679785200
d = datetime.fromtimestamp(ts, tz=TZ)
ts += 3600
2023-03-26 00:00:00+01:00 1679785200.0
2023-03-26 01:00:00+01:00 1679788800.0
2023-03-26 03:00:00+02:00 1679792400.0
2023-03-26 04:00:00+02:00 1679796000.0
Powered by Red Leaf ( Rev. c78c769f2 ), © Issei Numata, 2007-2021