Python, datetime, pytzでタイムゾーンを設定・取得・変換・削除

Pythonで日時(日付・時刻)を処理するdatetimeモジュールではタイムゾーンを扱うことができます。 UTC(協定世界時)からの時間差を指定して処理するだけならdatetimeモジュールのみで十分だが、サードパーティライブラリのpytzを導入すると、Asia/Tokyoのような表記を使ったりDST(夏時間、サマータイム)を簡単に扱ったりできます。

datetime --- 基本的な日付型および時間型 — Python 3.7.2 ドキュメント pytz · PyPI

ここでは以下の内容について説明します。

naiveなオブジェクトとawareなオブジェクト タイムゾーンを表すtimezoneオブジェクトの生成 タイムゾーンを設定したオブジェクトを生成 コンストラクタ: datetime() 現在時刻: now(), utcnow() 文字列から変換: strptime(), fromisoformat() タイムゾーンの情報を取得: tzinfo属性, utcoffset() タイムゾーンを変換・削除 ・追加: astimezone(), replace() awareなオブジェクトのタイムゾーンを変換・削除 naiveなオブジェクトにタイムゾーンを追加 pytzの利用 pytzのインストール タイムゾーンを生成: pytz.timezone() pytzを使う注意点: replace()などの使用 pytzを使う注意点: サマータイムdatetime, date, timeオブジェクトの基本や、ISO形式ではない任意の形式の文字列との相互変換については

naiveオブジェクトとawareオブジェクト

datetimeオブジェクト(日時=日付と時刻を扱う)とtimeオブジェクト(時刻を扱う)はnaiveとawareの2種類に分類されます。以降のサンプルコードではdatetimeオブジェクトを例とします。 タイムゾーンについての情報を格納するtzinfo属性を持っていないのがnaiveなオブジェクトで、持っているのがawareなオブジェクト。 コンストラクタdatetime()でオブジェクトを生成する場合、引数tzinfoを省略するとtzinfo属性が空(None)のnaiveなオブジェクトとなり、何らかの値を指定するとawareなオブジェクトとなります。詳細は後述します。

import datetime

dt_naive = datetime.datetime(2018, 12, 31, 5, 0, 30, 1000)

print(dt_naive)
# 2018-12-31 05:00:30.001000

print(dt_naive.tzinfo)
# None

dt_aware = datetime.datetime(2018, 12, 31, 5, 0, 30, 1000,
                             tzinfo=datetime.timezone.utc)

print(dt_aware)
# 2018-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(type(datetime.timezone.utc), 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

タイムゾーンを設定したオブジェクトを生成

タイムゾーンを設定したdatetimeオブジェクトを生成する方法を説明します。既存のオブジェクトのタイムゾーンを変換したり削除したりする方法については後述します。

コンストラクタ: datetime()

上の例でもあるように、コンストラクタdatetime()の引数tzinfoにtimezoneオブジェクト(tzinfoクラスのサブクラス)を指定すると、タイムゾーンが設定されたdatetimeオブジェクトが生成されます。

dt_utc = datetime.datetime(2018, 12, 31, 5, 0, 30, 1000,
                           tzinfo=datetime.timezone.utc)

print(dt_utc)
# 2018-12-31 05:00:30.001000+00:00

print(dt_utc.tzinfo)
# UTC


dt_jst = datetime.datetime(2018, 12, 31, 5, 0, 30, 1000,
                           tzinfo=datetime.timezone(datetime.timedelta(hours=9)))

print(dt_jst)
# 2018-12-31 05:00:30.001000+09:00

print(dt_jst.tzinfo)
# UTC+09:00

現在時刻: now(), utcnow()

現在時刻のdatetimeオブジェクトはdatetime.now()で生成できます。 デフォルトはtzinfo属性がNoneで、タイムゾーンは設定されていません。

dt_now = datetime.datetime.now()

print(dt_now)
# 2019-01-20 23:53:16.711783

print(dt_now.tzinfo)
# None

引数にtimezoneオブジェクト(tzinfoクラスのサブクラス)を指定すると、そのタイムゾーンに変換されtzinfo属性が設定されたdatetimeオブジェクトが生成されます。 UTCの場合です。

dt_now_utc = datetime.datetime.now(datetime.timezone.utc)

print(dt_now_utc)
# 2019-01-20 14:53:16.735097+00:00

print(dt_now_utc.tzinfo)
# UTC

JST(日本標準時)の場合です。+9時間の差分を指定します。この例はJST環境で実行しているので、引数なしの場合と日時の値は同じだが、引数を指定することでtzinfo属性が設定されます。

dt_now_jst = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9)))

print(dt_now_jst)
# 2019-01-20 23:53:16.758037+09:00

print(dt_now_jst.tzinfo)
# UTC+09:00

datetime.utcnow()という関数もあります。現在のUTCの日時でtzinfo属性がNoneのdatetimeオブジェクトが生成されます。

dt_utcnow = datetime.datetime.utcnow()

print(dt_utcnow)
# 2019-01-20 14:53:16.802029

print(dt_utcnow.tzinfo)
# None

現在のUTCの日時でtzinfo属性がUTCに設定されたdatetimeオブジェクトは上記のようにdatetime.now()の引数にdatetime.timezone.utcを指定して生成します。

文字列から変換: strptime(), fromisoformat()

文字列からdatetimeオブジェクトを生成するにはstrptime()を使います。詳細は

タイムゾーンを表す書式化コードは%z。+HHMMまたは-HHMMの形式でUTCとの差分を示す。 例えば以下のような文字列は%zを使ってタイムゾーンを含んだdatetimeオブジェクトを生成できます。

s = '2018/12/31 05:00:30+0900'

dt = datetime.datetime.strptime(s, '%Y/%m/%d %H:%M:%S%z')

print(dt)
# 2018-12-31 05:00:30+09:00

print(dt.tzinfo)
# UTC+09:00

Python3.7からISO 8601形式の文字列をdatetimeオブジェクトなどに変換するfromisoformat()が追加されました。詳細は

ISO 8601形式では末尾に+HH:MMまたは-HH:MMの形式でタイムゾーン情報を含む。ISO 8601形式に準ずる文字列であれば、fromisoformat()を使ってタイムゾーンを含んだdatetimeオブジェクトを生成できます。

s = '2018-12-31T05:00:30+09:00'

dt = datetime.datetime.fromisoformat(s)

print(dt)
# 2018-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(2018, 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(2018, 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()を使います。タイムゾーンが設定されたawareなオブジェクトか設定されていないnaiveなオブジェクトかによって振る舞いが異なります。

awareなオブジェクトのタイムゾーンを変換・削除

+9時間の時差が設定された以下のdatetimeオブジェクトを例とします。

dt_jst = datetime.datetime(2018, 12, 31, 5, 0, 30, 1000,
                           tzinfo=datetime.timezone(datetime.timedelta(hours=9)))

print(dt_jst)
# 2018-12-31 05:00:30.001000+09:00

print(dt_jst.tzinfo)
# UTC+09:00

タイムゾーンを変換するにはastimezone()メソッドを使います。引数にtimezoneオブジェクト(tzinfoクラスのサブクラス)を指定します。 タイムゾーンが考慮された同時刻のdatetimeオブジェクトが生成されます。

dt_jst_to_utc = dt_jst.astimezone(datetime.timezone.utc)

print(dt_jst_to_utc)
# 2018-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)
# 2018-12-30 15:00:30.001000-05:00

print(dt_jst_to_m5h.tzinfo)
# UTC-05:00

タイムゾーンを置き換えるにはreplace()メソッドを使います。引数tzinfoにtimezoneオブジェクト(tzinfoクラスのサブクラス)を指定します。 元のdatetimeオブジェクトの日時の値はそのままでtzinfo属性が置き換わったdatetimeオブジェクトが生成されます。

dt_jst_to_utc_replace = dt_jst.replace(tzinfo=datetime.timezone.utc)

print(dt_jst_to_utc_replace)
# 2018-12-31 05:00:30.001000+00:00

print(dt_jst_to_utc_replace.tzinfo)
# UTC

astimezone()メソッドの場合は元のオブジェクトと生成されたオブジェクトが同時刻を示すが、replace()メソッドの場合は別の時刻となるので気をつけてください。 タイムゾーンの設定を表すtzinfo属性を削除したい場合はreplace()メソッドの引数にNoneを指定すればOKです。

dt_jst_to_naive = dt_jst.replace(tzinfo=None)

print(dt_jst_to_naive)
# 2018-12-31 05:00:30.001000

print(dt_jst_to_naive.tzinfo)
# None

naiveなオブジェクトにタイムゾーンを追加

タイムゾーンが設定されていない以下のdatetimeオブジェクトを例とします。

dt_naive = datetime.datetime(2018, 12, 31, 5, 0, 30, 1000)

print(dt_naive)
# 2018-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)
# 2018-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)
# 2018-12-31 05:00:30.001000+09:00

print(dt_naive_to_jst_replace.tzinfo)
# UTC+09:00

Python3.6から、naiveなオブジェクトからastimezone()メソッドを呼べるようになりました。システムのローカルなタイムゾーンを前提に変換されます。 以下の例はJST(+9時間)の環境で実行したので、タイムゾーンが設定されていないnaiveなオブジェクトもJSTであるという前提で変換されています。

dt_naive_to_utc = dt_naive.astimezone(datetime.timezone.utc)

print(dt_naive_to_utc)
# 2018-12-30 20:00:30.001000+00:00

print(dt_naive_to_utc.tzinfo)
# UTC

pytzの利用

これまでの例のように、単純にUTCからの時差を加えたり引いたりするだけであれば標準ライブラリのdatetimeモジュールを使えば問題ないが、サードパーティライブラリのpytzを導入するとAsia/Tokyoのような表記を使ったりDST(夏時間、サマータイム)を簡単に扱ったりできます。

pytz · PyPI

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(2018, 12, 31, 5, 0, 30, 1000,
                             tzinfo=datetime.timezone.utc)

print(dt_aware)
# 2018-12-31 05:00:30.001000+00:00

上述のように、astimezone()メソッドの引数にdatetime.timezoneオブジェクトの代わりにpytz.timezone()で生成したオブジェクトを設定できます。'Asia/Tokyo'といった名称を使って生成できるので非常に便利です。

print(dt_aware.astimezone(jst))
# 2018-12-31 14:00:30.001000+09:00

print(dt_aware.astimezone(eastern))
# 2018-12-31 00:00:30.001000-05:00

astimezone()メソッドは問題ないが、replace()メソッドにpytz.timezone()で生成したオブジェクトを設定すると+09:19や-04:56のように時差がずれる。

print(dt_aware.replace(tzinfo=jst))
# 2018-12-31 05:00:30.001000+09:19

print(dt_aware.replace(tzinfo=eastern))
# 2018-12-31 05:00:30.001000-04:56

これはpytzが厳格にタイムゾーンを処理しているため。例えば東京のタイムゾーンは1887年から+9:00でそれ以前は+09:19であり、時刻情報がない場合は+09:19が使用されます。 以下の記事などを参照。

pytzの仕様が変わっている - Qiita

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)))
# 2018-12-31 05:00:30.001000+09:00

print(eastern.localize(dt_aware.replace(tzinfo=None)))
# 2018-12-31 05:00:30.001000-05:00

naiveなオブジェクトからreplace()メソッドを呼ぶ場合も同じです。pytzのタイムゾーンを引数に指定するとタイムゾーンがずれる場合があるので、localize()メソッドを使います。

dt_naive = datetime.datetime(2018, 12, 31, 5, 0, 30, 1000)

print(dt_naive)
# 2018-12-31 05:00:30.001000

print(dt_naive.replace(tzinfo=jst))
# 2018-12-31 05:00:30.001000+09:19

print(dt_naive.replace(tzinfo=eastern))
# 2018-12-31 05:00:30.001000-04:56

print(jst.localize(dt_naive))
# 2018-12-31 05:00:30.001000+09:00

print(eastern.localize(dt_naive))
# 2018-12-31 05:00:30.001000-05:00

コンストラクタdatetime()の引数tzinfoを指定する場合も同じです。コンストラクタの時点では時刻情報が存在しないので+09:19となってしまいます。コンストラクタではtzinfoを指定せずnaiveなオブジェクトを生成し、それをlocalize()メソッドの引数に指定すれば問題ありません。

print(datetime.datetime(2018, 12, 31, 5, 0, 30, 1000,
                        tzinfo=jst))
# 2018-12-31 05:00:30.001000+09:19

print(jst.localize(datetime.datetime(2018, 12, 31, 5, 0, 30, 1000)))
# 2018-12-31 05:00:30.001000+09:00

datetime.now()の引数にはpytz.timezone()で生成したオブジェクトを指定しても問題ありません。

print(datetime.datetime.now(jst))
# 2019-01-21 00:00:42.540529+09:00

pytzを使う注意点: サマータイム

サマータイムの切替時刻にも注意が必要。 localize()だと存在しない時刻を生成する可能性があります。normalize()メソッドを使うと正しく扱われる。

dt_dst = datetime.datetime(2018, 3, 11, 2, 0, 0, 0)

print(eastern.localize(dt_dst))
# 2018-03-11 02:00:00-05:00

print(eastern.normalize(eastern.localize(dt_dst)))
# 2018-03-11 03:00:00-04:00
Last Updated: 6/26/2019, 10:34:03 PM