datetime
前回、PandasのデータフレームをHTML化する方法を紹介しました。
今回はdatetimeモジュールで日時を取得した際にたまに重要となるタイムゾーンのお話です。
私がタイムゾーンを気にし出したのは、Twitterのbotを作っている時でした。
色々なツイートをデータベースに保存するのですが、気にせずに「datetime.datetime.now()」で日時を取得し、そのままデータベースに放り込んでいました。
しかしある時、Twitter APIから得られた日時と比較しようとしたときにエラーとなってしまい、なぜかなと思って調べた結果、タイムゾーンが原因だったわけです。
その時の経験を記事にしてみようというのが今回のお話。
awareオブジェクトとnaiveオブジェクト
先ほどお話しした「datetime.datetime.now()」で得られた日時とTwitter APIから得られた日時でエラーとなったのは、「datetime.datetime.now()」で得られた日時にはタイムゾーンの情報がなく、Twitter APIから得られた日時ではタイムゾーンの情報があるという違いからでした。
タイムゾーンの情報がある日時のデータ(datetime型)をawareなオブジェクト、タイムゾーンの情報がない日時のデータをnaiveなオブジェクトといいます。
このawareオブジェクトとnaiveオブジェクトは直接計算ができず、そのためにエラーとなったわけです。
まず通常の「datetime.datetime.now()」で得られた日時と世界の時間の基準である世界協定時(Coordinated Universal Time、UTC)を作成してみます。
import datetime
timenow = datetime.datetime.now()
timenow_utc = datetime.datetime.now(datetime.timezone.utc)
print(timenow)
print(timenow.tzinfo)
print(timenow_utc)
print(timenow_utc.tzinfo)
2022-08-27 21:07:54.257376
None
2022-08-27 12:07:54.257502+00:00
UTC
UTCを取得するには「datetime.datetime.now()」のオプションに「datetime.timezone.utc」を追加します。
また得られたdatetime型のデータに対し、「.tzinfo」でタイムゾーンの情報が取得できます。
上の例では「2022-08-27 12:07:54.257502」が日時で「+00:00」が世界協定時からの時差です。
また「datetime.datetime.now()」で取得した日時では「.tzinfo」で取得したタイムゾーンの情報が「None」になっているのに対し、「datetime.datetime.now(datetime.timezone.utc)」では「UTC」になっています。
つまり「datetime.datetime.now()」にはタイムゾーンの情報がなく、「datetime.datetime.now(datetime.timezone.utc)」では「UTC」のタイムゾーンが使われていることになります。
そして前者がnaiveなオブジェクト、後者がawareなオブジェクトというわけです。
また日時に着目してみると「datetime.datetime.now()」で取得された日時は実行したPCの日時です。
それに対し「datetime.datetime.now(datetime.timezone.utc)」はUTCの時間なので、9時間引かれた日時になっています。
それではこれらを引き算してみましょう。
import datetime
timenow = datetime.datetime.now()
timenow_utc = datetime.datetime.now(datetime.timezone.utc)
print(timenow - timenow_utc)
実行結果
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [2], in <cell line: 6>()
3 timenow = datetime.datetime.now()
4 timenow_utc = datetime.datetime.now(datetime.timezone.utc)
----> 6 print(timenow - timenow_utc)
TypeError: can't subtract offset-naive and offset-aware datetimes
「can’t subtract offset-naive and offset-aware datetimes」とは「naiveなオブジェクトとawareなオブジェクトは引き算できませんよ」というエラーです。
ということで計算をしたい場合、どちらかの形式に揃える必要があります。
タイムゾーンの情報を変更:astimezoneとreplace
タイムゾーンの情報は「.astimezone(タイムゾーン)」、もしくは「.replace(tzinfo=タイムゾーン)」で変更します。
まずは「.astimezone(タイムゾーン)」を使って、「datetime.datetime.now()」で取得した日時のタイムゾーンをUTCに変更してみましょう。
import datetime
timenow = datetime.datetime.now()
timenow_utc = timenow.astimezone(datetime.timezone.utc)
print(timenow)
print(timenow.tzinfo)
print(timenow_utc)
print(timenow_utc.tzinfo)
実行結果
2022-08-27 21:14:46.076962
None
2022-08-27 12:14:46.076962+00:00
UTC
次に「.replace(tzinfo=タイムゾーン)」を使って、「datetime.datetime.now()」で取得した日時のタイムゾーンをUTCに変更してみましょう。
import datetime
timenow = datetime.datetime.now()
timenow_utc = timenow.replace(tzinfo=datetime.timezone.utc)
print(timenow)
print(timenow.tzinfo)
print(timenow_utc)
print(timenow_utc.tzinfo)
実行結果
2022-08-27 21:16:53.761349
None
2022-08-27 21:16:53.761349+00:00
UTC
ここでちょっと日時に注意してみてください。
実は「astimezone」で日時を変更した場合、時差分が自動で修正されます。
そのため「2022-08-27 21:14:46.076962」だった日時が、UTCのタイムゾーン情報変更後は「2022-08-27 12:14:46.076962+00:00」となっています。
逆に「replace」で日時を変更した場合、時差は修正されず、タイムゾーンの情報だけが変更されます。
そのため日時はUTCのタイムゾーン変更前は「2022-08-27 21:16:53.761349」で、変更後は「2022-08-27 21:16:53.761349+00:00」であり、日時は変わっていません。
タイムゾーンの作成:datetime.timezone
実際はUTCのタイムゾーンを使うよりも我々日本人としては日本のタイムゾーンが必要なことが多いでしょう。
それを簡単に扱うライブラリもあるようですが、今回は自分でタイムゾーンを作って使ってみます。
自分でタイムゾーンを作成するには「datetime.timezone(UTCからの時差, ‘名前’)」で作成できます。
ただしUTCからの時差もdatetime型で作成する必要があり、「datetime.timedelta(hours=時差)」を使います。
つまり「datetime.timezone(datetime.timedelta(hours=時差), ‘名前’)」と覚えておくのがいいかもしれません。
import datetime
JST_TZ = datetime.timezone(datetime.timedelta(hours=+9), 'JST')
timenow_utc = datetime.datetime.now(datetime.timezone.utc)
timenow_jst_astimezone = timenow_utc.astimezone(JST_TZ)
timenow_jst_replace = timenow_utc.replace(tzinfo=JST_TZ)
print(timenow_utc)
print(timenow_utc.tzinfo)
print(timenow_jst_astimezone)
print(timenow_jst_astimezone.tzinfo)
print(timenow_jst_replace)
print(timenow_jst_replace.tzinfo)
実行結果
2022-08-27 12:41:45.141586+00:00
UTC
2022-08-27 21:41:45.141586+09:00
JST
2022-08-27 12:41:45.141586+09:00
JST
これで日時を使いたい場所の時差さえ分かれば、タイムゾーンを設定できるようになりました。
次回はdatetime型のNan(欠損値)であるNaTをいじってみたので、その解説をしていきます。
ではでは今回はこんな感じで。
コメント