Python, datetime, pytzでタイムゾーンを設定・取得・変換・削除
Pythonで日時(日付・時刻)を処理するdatetimeモジュールではタイムゾーンを扱うことができる。
UTC(協定世界時)からの時間差を指定して処理するだけならdatetimeモジュールで十分だが、サードパーティライブラリのpytzを導入すると、Asia/Tokyo
のような表記を使ったりDST(夏時間、サマータイム)を簡単に扱ったりできる。
datetime
, date
, time
オブジェクトの基本や、文字列との相互変換については以下の記事を参照。
本記事のサンプルコードでは、以下のようにdatetime
モジュールをインポートしている。
import datetime
naiveオブジェクトとawareオブジェクト
datetime
オブジェクト(日時=日付と時刻)とtime
オブジェクト(時刻)は、naiveとawareの2種類に分類される。以降のサンプルコードではdatetime
オブジェクトを例とする。
タイムゾーンについての情報を格納するtzinfo
属性を持っていないのがnaiveなオブジェクトで、持っているのがawareなオブジェクト。
例えば、コンストラクタdatetime()
で引数tzinfo
を省略するとtzinfo
属性がNone
のnaiveなオブジェクトとなり、何らかの値を指定するとawareなオブジェクトとなる。詳細は後述。
dt_naive = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000)
print(dt_naive)
# 2022-12-31 05:00:30.001000
print(dt_naive.tzinfo)
# None
dt_aware = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000,
tzinfo=datetime.timezone.utc)
print(dt_aware)
# 2022-12-31 05:00:30.001000+00:00
print(dt_aware.tzinfo)
# UTC
print(type(dt_aware.tzinfo))
# <class 'datetime.timezone'>
タイムゾーンを表すtimezoneオブジェクトの生成
上の例で引数tzinfo
に指定したdatetime.timezone.utc
はtzinfo
クラスのサブクラスであるtimezone
クラスのオブジェクトで、UTC(協定世界時)を表す。
print(datetime.timezone.utc)
# UTC
print(type(datetime.timezone.utc))
# <class 'datetime.timezone'>
print(issubclass(datetime.timezone, datetime.tzinfo))
# True
timezone
クラスはUTCから±24時間の差分を表すシンプルなクラス。夏時間などの複雑な処理を行いたい場合は後述のpytzを使う。
任意の時差を表すtimezone
オブジェクトは、コンストラクタtimezone()
にtimedelta
オブジェクトを指定して生成する。
tz_jst = datetime.timezone(datetime.timedelta(hours=9))
print(tz_jst)
# UTC+09:00
print(type(tz_jst))
# <class 'datetime.timezone'>
timedelta
オブジェクトについては以下の記事を参照。
デフォルトでは上の例のようにUTC+HH:MM
やUTC-HH:MM
といった名前がつくが、コンストラクタtimezone()
の第二引数name
に任意の名前を指定することもできる。
tz_jst_name = datetime.timezone(datetime.timedelta(hours=9), name='JST')
print(tz_jst_name)
# JST
タイムゾーンを設定したawareなオブジェクトを生成
ここでは、タイムゾーンを設定したawareなdatetime
オブジェクトを生成する方法を説明する。既存のオブジェクトのタイムゾーンの変換や削除については後述。
コンストラクタ: datetime()
コンストラクタdatetime()
の引数tzinfo
にtimezone
オブジェクトを指定すると、タイムゾーンが設定されたdatetime
オブジェクトが生成される。
dt_utc = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000,
tzinfo=datetime.timezone.utc)
print(dt_utc)
# 2022-12-31 05:00:30.001000+00:00
print(dt_utc.tzinfo)
# UTC
dt_jst = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000,
tzinfo=datetime.timezone(datetime.timedelta(hours=9)))
print(dt_jst)
# 2022-12-31 05:00:30.001000+09:00
print(dt_jst.tzinfo)
# UTC+09:00
現在時刻: now(), utcnow()
現在時刻のdatetime
オブジェクトはdatetime.now()
で生成できる。
- 関連記事: Pythonで現在時刻・日付・日時を取得
デフォルトはtzinfo
属性がNone
で、タイムゾーンは設定されない。日時自体は実行環境のタイムゾーンの日時となる。
dt_now = datetime.datetime.now()
print(dt_now)
# 2023-10-03 21:12:49.683787
print(dt_now.tzinfo)
# None
引数にtimezone
オブジェクトを指定すると、そのタイムゾーンに変換されtzinfo
属性が設定されたdatetime
オブジェクトが生成される。
UTCの場合。
dt_now_utc = datetime.datetime.now(datetime.timezone.utc)
print(dt_now_utc)
# 2023-10-03 12:12:49.689641+00:00
print(dt_now_utc.tzinfo)
# UTC
JST(日本標準時)の場合。+9時間の差分を指定する。この例はJST環境で実行しているので、引数なしの場合と日時の値は同じ。
dt_now_jst = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9)))
print(dt_now_jst)
# 2023-10-03 21:12:49.693979+09:00
print(dt_now_jst.tzinfo)
# UTC+09:00
datetime.utcnow()
という関数もある。現在のUTCの日時でtzinfo
属性がNone
のdatetime
オブジェクトが生成される。
dt_utcnow = datetime.datetime.utcnow()
print(dt_utcnow)
# 2023-10-03 12:12:49.697851
print(dt_utcnow.tzinfo)
# None
現在のUTCの日時でtzinfo
属性がUTCに設定されたdatetime
オブジェクトは上記のようにdatetime.now()
の引数にdatetime.timezone.utc
を指定して生成する。
文字列から変換: strptime(), fromisoformat()
文字列からdatetime
オブジェクトを生成するにはstrptime()
を使う。
タイムゾーンを表す書式化コードは%z
。+HHMM
または-HHMM
の形式でUTCとの差分を示す。
s = '2022/12/31 05:00:30+0900'
dt = datetime.datetime.strptime(s, '%Y/%m/%d %H:%M:%S%z')
print(dt)
# 2022-12-31 05:00:30+09:00
print(dt.tzinfo)
# UTC+09:00
ISO 8601形式では末尾に+HH:MM
または-HH:MM
の形式でタイムゾーン情報を含む。fromisoformat()
を使って、ISO 8601形式の文字列からタイムゾーンを含むdatetime
オブジェクトを生成できる。
s = '2022-12-31T05:00:30+09:00'
dt = datetime.datetime.fromisoformat(s)
print(dt)
# 2022-12-31 05:00:30+09:00
print(dt.tzinfo)
# UTC+09:00
タイムゾーンの情報を取得: tzinfo属性, utcoffset()
タイムゾーンが設定されたdatetime
オブジェクトは、tzinfo
属性とutcoffset()
メソッドでタイムゾーンの情報を取得できる。utcoffset()
はUTCとの差分(時差)を示すtimedelta
オブジェクトを返す。
dt_utc = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000,
tzinfo=datetime.timezone.utc)
print(dt_utc.tzinfo)
# UTC
print(type(dt_utc.tzinfo))
# <class 'datetime.timezone'>
print(dt_utc.utcoffset())
# 0:00:00
print(type(dt_utc.utcoffset()))
# <class 'datetime.timedelta'>
dt_jst = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000,
tzinfo=datetime.timezone(datetime.timedelta(hours=9)))
print(dt_jst.tzinfo)
# UTC+09:00
print(type(dt_jst.tzinfo))
# <class 'datetime.timezone'>
print(dt_jst.utcoffset())
# 9:00:00
print(type(dt_jst.utcoffset()))
# <class 'datetime.timedelta'>
タイムゾーンを変換・削除 ・追加: astimezone(), replace()
既存のdatetime
オブジェクトのタイムゾーンを変換・削除・追加するにはastimezone()
およびreplace()
メソッドを使う。
astimezone()
メソッドはタイムゾーン情報をもとに日時の値を変換し、replace()
メソッドは日時の値はそのままで単にtzinfo
属性を置き換える。
- datetime.astimezone() --- 基本的な日付型および時間型 — Python 3.11.5 ドキュメント
- datetime.replace() --- 基本的な日付型および時間型 — Python 3.11.5 ドキュメント
awareなオブジェクトのタイムゾーンを変換・削除
UTC+9時間の時差が設定された以下のdatetime
オブジェクトを例とする。
dt_jst = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000,
tzinfo=datetime.timezone(datetime.timedelta(hours=9)))
print(dt_jst)
# 2022-12-31 05:00:30.001000+09:00
print(dt_jst.tzinfo)
# UTC+09:00
タイムゾーンを変換するにはastimezone()
メソッドを使う。引数にtimezone
オブジェクトを指定する。タイムゾーンが考慮された同時刻のdatetime
オブジェクトが生成される。
dt_jst_to_utc = dt_jst.astimezone(datetime.timezone.utc)
print(dt_jst_to_utc)
# 2022-12-30 20:00:30.001000+00:00
print(dt_jst_to_utc.tzinfo)
# UTC
dt_jst_to_m5h = dt_jst.astimezone(datetime.timezone(datetime.timedelta(hours=-5)))
print(dt_jst_to_m5h)
# 2022-12-30 15:00:30.001000-05:00
print(dt_jst_to_m5h.tzinfo)
# UTC-05:00
シンプルにタイムゾーンを置き換えるにはreplace()
メソッドを使う。引数tzinfo
にtimezone
オブジェクトを指定する。元のdatetime
オブジェクトの日時の値はそのままでtzinfo
属性が置き換わったdatetime
オブジェクトが生成される。
dt_jst_to_utc_replace = dt_jst.replace(tzinfo=datetime.timezone.utc)
print(dt_jst_to_utc_replace)
# 2022-12-31 05:00:30.001000+00:00
print(dt_jst_to_utc_replace.tzinfo)
# UTC
astimezone()
メソッドの場合は元のオブジェクトと生成されたオブジェクトが同時刻を示すが、replace()
メソッドの場合は別の時刻となるので注意。
タイムゾーンの設定を表すtzinfo
属性を削除したい場合はreplace()
メソッドの引数にNone
を指定すればよい。
dt_jst_to_naive = dt_jst.replace(tzinfo=None)
print(dt_jst_to_naive)
# 2022-12-31 05:00:30.001000
print(dt_jst_to_naive.tzinfo)
# None
naiveなオブジェクトにタイムゾーンを追加
タイムゾーンが設定されていない以下のdatetime
オブジェクトを例とする。
dt_naive = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000)
print(dt_naive)
# 2022-12-31 05:00:30.001000
print(dt_naive.tzinfo)
# None
replace()
メソッドの引数tzinfo
にtimezone
オブジェクト(tzinfo
クラスのサブクラス)を指定すると、単純にそのタイムゾーンの情報が付与される。
dt_naive_to_utc_replace = dt_naive.replace(tzinfo=datetime.timezone.utc)
print(dt_naive_to_utc_replace)
# 2022-12-31 05:00:30.001000+00:00
print(dt_naive_to_utc_replace.tzinfo)
# UTC
dt_naive_to_jst_replace = dt_naive.replace(
tzinfo=datetime.timezone(datetime.timedelta(hours=9))
)
print(dt_naive_to_jst_replace)
# 2022-12-31 05:00:30.001000+09:00
print(dt_naive_to_jst_replace.tzinfo)
# UTC+09:00
naiveなオブジェクトからastimezone()
メソッドを呼ぶと、システムのローカルなタイムゾーンを前提に変換される。
以下の例はJST(+9時間)の環境で実行したので、タイムゾーンが設定されていないnaiveなオブジェクトもJSTであるという前提で変換されている。
dt_naive_to_utc = dt_naive.astimezone(datetime.timezone.utc)
print(dt_naive_to_utc)
# 2022-12-30 20:00:30.001000+00:00
print(dt_naive_to_utc.tzinfo)
# UTC
pytzの利用
これまでの例のように、単純にUTCからの時差を加えたり引いたりするだけであれば標準ライブラリのdatetimeモジュールを使えば問題ないが、サードパーティライブラリのpytzを導入すると、Asia/Tokyo
のような表記を使ったりDST(夏時間、サマータイム)を簡単に扱ったりできる。
pytzのインストール
pip
(環境によってはpip3
)でインストールできる。
$ pip install pytz
タイムゾーンを生成: pytz.timezone()
pytz.timezone()
でタイムゾーンを表すオブジェクトを生成できる。引数には'Asia/Tokyo'
や'US/Eastern'
などの文字列を指定する。
import datetime
import pytz
jst = pytz.timezone('Asia/Tokyo')
print(jst)
# Asia/Tokyo
eastern = pytz.timezone('US/Eastern')
print(eastern)
# US/Eastern
生成されたオブジェクトのクラスはdatetime.tzinfo
のサブクラスであり、これまでの例のdatetime.timezone
オブジェクトの代わりに引数に指定できるが、使用には注意が必要。
print(type(jst))
# <class 'pytz.tzfile.Asia/Tokyo'>
print(issubclass(type(jst), datetime.tzinfo))
# True
pytzを使う注意点: replace()などでの使用
タイムゾーン情報が設定された以下のawareなオブジェクトを例とする。
dt_aware = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000,
tzinfo=datetime.timezone.utc)
print(dt_aware)
# 2022-12-31 05:00:30.001000+00:00
上述のように、astimezone()
メソッドの引数にpytz.timezone()
で生成したオブジェクトを設定できる。'Asia/Tokyo'
といった名称を使って生成できるので非常に便利。
print(dt_aware.astimezone(jst))
# 2022-12-31 14:00:30.001000+09:00
print(dt_aware.astimezone(eastern))
# 2022-12-31 00:00:30.001000-05:00
astimezone()
メソッドは問題ないが、replace()
メソッドにpytz.timezone()
で生成したオブジェクトを設定すると+09:19
や-04:56
のように時差がずれる。
print(dt_aware.replace(tzinfo=jst))
# 2022-12-31 05:00:30.001000+09:19
print(dt_aware.replace(tzinfo=eastern))
# 2022-12-31 05:00:30.001000-04:56
これはpytzが厳格にタイムゾーンを処理しているため。例えば東京のタイムゾーンは1887年から+9:00
でそれ以前は+09:19
であり、時刻情報がない場合は+09:19
が使用される。
以下の記事などを参照。
astimezone()
メソッドは時刻情報をもとに変換が行われるため、1887年以降の時刻を変換する場合は+9:00
となるが、それ以前は+09:19
となる。一方、replace()
メソッドはawareなオブジェクトであっても時刻情報を使わずに変換するため、常に+09:19
が使われる。
pytzのタイムゾーンを使ってreplace()
のような処理を行うには、pytz.timezone()
で生成したオブジェクトのlocalize()
メソッドを用いる。localize()
メソッドの引数にはnaiveなオブジェクトのみ指定可能なので、awareなオブジェクトのtzinfo
属性をreplace(tzinfo=None)
で削除してから指定する。
print(jst.localize(dt_aware.replace(tzinfo=None)))
# 2022-12-31 05:00:30.001000+09:00
print(eastern.localize(dt_aware.replace(tzinfo=None)))
# 2022-12-31 05:00:30.001000-05:00
naiveなオブジェクトからreplace()
メソッドを呼ぶ場合も同様。pytzのタイムゾーンを引数に指定するとタイムゾーンがずれる場合があるので、localize()
メソッドを用いる。
dt_naive = datetime.datetime(2022, 12, 31, 5, 0, 30, 1000)
print(dt_naive)
# 2022-12-31 05:00:30.001000
print(dt_naive.replace(tzinfo=jst))
# 2022-12-31 05:00:30.001000+09:19
print(dt_naive.replace(tzinfo=eastern))
# 2022-12-31 05:00:30.001000-04:56
print(jst.localize(dt_naive))
# 2022-12-31 05:00:30.001000+09:00
print(eastern.localize(dt_naive))
# 2022-12-31 05:00:30.001000-05:00
コンストラクタdatetime()
の引数tzinfo
を指定する場合も同様。コンストラクタの時点では時刻情報が存在しないので+09:19
となってしまう。コンストラクタではtzinfo
を指定せずnaiveなオブジェクトを生成し、それをlocalize()
メソッドの引数に指定すればよい。
print(datetime.datetime(2022, 12, 31, 5, 0, 30, 1000,
tzinfo=jst))
# 2022-12-31 05:00:30.001000+09:19
print(jst.localize(datetime.datetime(2022, 12, 31, 5, 0, 30, 1000)))
# 2022-12-31 05:00:30.001000+09:00
datetime.now()
の引数にはpytz.timezone()
で生成したオブジェクトを指定しても問題ない。
print(datetime.datetime.now(jst))
# 2023-10-03 21:22:22.497686+09:00
pytzを使う注意点: サマータイム
サマータイムの切替時刻にも注意が必要。
localize()
だと存在しない時刻を生成する可能性がある。normalize()
メソッドを使うと正しく扱われる。
dt_dst = datetime.datetime(2023, 3, 12, 2, 0, 0, 0)
print(eastern.localize(dt_dst))
# 2023-03-12 02:00:00-05:00
print(eastern.normalize(eastern.localize(dt_dst)))
# 2023-03-12 03:00:00-04:00