Pythonでタイムゾーンを意識して日付時刻を操作する方法として、pytz
, dateutil.tz
やzoneinfo
を使う方法が一般的です。
zoneinfo
はpython3.9以降ではないと使えないので、現在主流なのはpytz
とdateutil.tz
です。
pytz
とdateutil.tz
は癖がちょっと違うので、自分の備忘録のためにもそれぞれの時間操作方法をまとめます。
公式ドキュメントによるとpytzではなくdateutil.tzを推奨しているのに加えて、dateutil.tz
のほうが直感的にタイムゾーンを扱えるので、個人的にはdateutil.tz
がおすすめです!
- Python 3.8
- dateutil 2.8.2
- pytz 2021.3
Pythonの環境設定
dateutilとpytz
をpipでインストールします。
$ pip install python-dateutil
$ pip install pytz
これからのソースコードでは、下記のパッケージをimportしている前提です。
from dateutil import tz, parser
import datetime
import pytz
必ず知っておくべきnaiveとawareの概念について
datetimeクラスはPythonで日時を扱うことが可能なモジュールですが、これを使う上で必ず知っておかなければならない概念がnaive
とaware
です。
違いの詳細はpythonの公式ドキュメント(datetime)を参照してほしいですが、簡単に違いを説明すると下記です。
- naive: タイムゾーン情報を持たない
- aware: タイムゾーン情報を持つ
具体的にソースコードで例を紹介します。
dt_naive1 = parser.parse("2022-03-11 08:00:00") # 文字列からの初期化
dt_naive2 = datetime.datetime(2022, 3, 11, 8) # datetime()を利用した初期化
print('naive1: ', dt_naive1)
print('naive2: ', dt_naive2)
# naive1: 2022-03-11 08:00:00
# naive2: 2022-03-11 08:00:00
dt_aware_utc1 = parser.parse("2022-03-11 08:00:00 UTC") # 文字列からの初期化
dt_aware_utc2 = datetime.datetime(2022, 3, 11, 8, tzinfo=tz.UTC) # datetime()を利用した初期化
# aware1: 2022-03-11 08:00:00+00:00
# aware2: 2022-03-11 08:00:00+00:00
注目する違いは、出力された文字列のオフセット+00:00
の有無です。
時差が書いてある場合、awareのオブジェクトとなります。
dateutil.tzによる日付時刻変換操作
dateutil.tz
を利用したタイムゾーンを考慮した日付日時変換方法を紹介します。
タイムゾーンの付与と変換
まず、タイムゾーンの「付与」の方法です。あくまで付与なので、naive
オブジェクトに対する操作です。
dt_naive = parser.parse("2022-03-11 08:00:00") # timezone情報は持たない初期化
dt_aware = dt_naive.astimezone(tz.gettz('Asia/Tokyo'))
print('naive :', dt_naive)
print('aware :', dt_aware)
# naive : 2022-03-11 08:00:00
# aware : 2022-03-11 08:00:00+09:00
タイムゾーン情報が付与されていることがわかりますね。
次に、タイムゾーンの「変換」です。つまり、aware
オブジェクトに対する操作です。
dt_aware_utc = parser.parse("2022-03-11 08:00:00 UTC") # utc時刻
dt_aware_tky = dt_aware_utc.astimezone(tz.gettz('Asia/Tokyo')) # Asia/Tokyo時刻に変換
print('utc: ', dt_aware_utc)
print('tky: ', dt_aware_tky)
# utc: 2022-03-11 08:00:00+00:00
# tky: 2022-03-11 17:00:00+09:00
注目するのは、UTC時刻で同じ時間を示す変換が行われている点です。
naiveオブジェクトに対する操作と異なり、時差分を考慮して時間変換が行われています。
ここは非常に重要なので必ず理解してください!naiveオブジェクトへの操作なのか、awareオブジェクトに対する操作なのかをしっかりと意識する必要があります。
日付日時の操作
時間操作を行ってみます。日時変換は、datetime.timedelta()関数を使います(ドキュメント)。
dt_aware = datetime.datetime(2022, 3, 11, 8, tzinfo=tz.gettz('Asia/Tokyo')) # datetime()を利用したawareオブジェクトの初期化
dt_delta = datetime.timedelta(days=1, hours=2, minutes=30) # 1日と2時間30分を表す
dt_converted = dt_aware + dt_delta
print('aware :', dt_aware)
print('converted:', dt_converted)
# aware : 2022-03-11 08:00:00+09:00
# converted: 2022-03-12 10:30:00+09:00
DSTを考慮した時間変換
datetime.tz
を利用する場合、特別な処理はいらずにDST(Daylight Saving Time:夏時間)を考慮できます。
下記は、夏時間が存在するタイムゾーンである、America/New_Yorkでの処理例です。
dt_aware_utc = parser.parse("2022-11-06 05:00:00 UTC")
dt_aware_nyc = dt_aware_utc.astimezone(tz.gettz('America/New_York'))
dt_converted = dt_aware_nyc + datetime.timedelta(hours=1)
print('aware utc:', dt_aware_utc, ',', dt_aware_utc.astimezone(tz.UTC))
print('aware nyc:', dt_aware_nyc, ',', dt_aware_nyc.astimezone(tz.UTC))
print('converted:', dt_converted, ',', dt_converted.astimezone(tz.UTC))
# aware utc: 2022-11-06 05:00:00+00:00 , 2022-11-06 05:00:00+00:00
# aware nyc: 2022-11-06 01:00:00-04:00 , 2022-11-06 05:00:00+00:00
# converted: 2022-11-06 02:00:00-05:00 , 2022-11-06 07:00:00+00:00
現地時刻とそれをUTCに変換した時刻を出力しています。
ニューヨークでは、現地時間の2022-11-06 02:00に夏時間が終わり、通常の時刻に戻ります(UTC時刻では1時間分の時間が進みます)。
出力結果からわかるように、ローカル時間に1時間分を足すことで、ローカル時間では1時間分進み、UTC時刻では2時間分すすんでいることが確認できます。
DSTは頭が混乱してしまいがちですが、現地時間での処理なのか、UTC時刻での処理なのかを正しく理解したうえでdatetime.tzを使えば難しくありません。
pytzを利用した時間変換
pytz
を使ってタイムゾーンを扱う記事も多いので、こちらの方法も紹介します。
pytz
は使い方に一癖あるので注意が必要です。
awareオブジェクトの初期化
datetilme.tzのような初期化を行うと失敗してしまいます。必ずlocalize()
関数を使いましょう。
nyc = pytz.timezone('America/New_York')
dt_aware1 = datetime.datetime(2022, 3, 11, 8, tzinfo=nyc) # 期待通りではない結果になる
dt_aware2 = nyc.localize(datetime.datetime(2022, 3, 11, 8)) # 期待通りの結果になる
print("not correct:", dt_aware1)
print("correct :", dt_aware2)
# not correct: 2022-03-11 08:00:00-04:56
# correct : 2022-03-11 08:00:00-05:00
-04:56
は標準時刻が決まる前のニューヨークの太陽時刻で、昔の標準時刻です。
pytz
は計算の効率化のために、標準的なライブラリが利用するタイムゾーン情報を一部で利用していません。
DSTの利用方法
pytz
でDSTを扱うときも注意が必要です。normalized()
関数を使う必要があります。
nyc = pytz.timezone('America/New_York')
dt_nyc = nyc.localize(datetime.datetime(2022, 2, 14, 12))
dt_nyc_dst1 = dt_nyc + datetime.timedelta(days=180) # これだけではDSTを考慮できない
dt_nyc_dst2 = nyc.normalize(dt_nyc_dst1) # normalize()によって、DST変換する
print('nyc : ', dt_nyc)
print('not correct dst: ', dt_nyc_dst1)
print('correct dst : ', dt_nyc_dst2)
# nyc : 2022-02-14 12:00:00-05:00
# not correct dst: 2022-08-13 12:00:00-05:00
# correct dst : 2022-08-13 13:00:00-04:00
2022-08-13はニューヨークでは夏時間のため、UTCに対して-04:00
となります。
ソースコードを見ていただくとわかりますが、夏時間を考慮するためにはnormalize()関数を利用する必要があります。
まとめ:datetime.tzを使う方が簡単
この記事ではdateutil.tz
とpytz
を使ったPythonでの日付日時変換処理をまとめました。
コードを見ていただければわかるかと思いますが、dateutil.tz
のほうが難しいことを考えずに変数の初期化とDSTの考慮が可能となります。
バグの少ないコードを書くためにはdatetime.tzを利用した方が良いです。
また、この記事では紹介しませんでしたが、python3.9以降では標準ライブラリにzoneinfoと呼ばれるタイムゾーンを扱うモジュールが追加されています。
pytzやdatetime.tz
のようなサードパーティ製よりは、標準ライブラリを利用した方が簡単だと思うので、もしpython3.9以降を使っている場合はzoneinfoも調べてみてください。