PythonでISO 8601形式の文字列と日時datetimeを相互変換

Pythonで日時(日付・時刻)の情報を扱う場合は標準ライブラリのdatetimeモジュールを使います。 datetimeモジュールには、広く使われているISOフォーマット(ISO 8601)の文字列とdatetimeオブジェクトなどとの相互変換のためのメソッドが用意されている(文字列をdatetime等に変換するfromisoformat()はPython3.7で追加)。

datetime --- 基本的な日付型および時間型 — Python 3.7.2 ドキュメント ISO 8601 - Wikipedia

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

日時(日付・時刻)を文字列に変換: isoformat() dateオブジェクトを変換 timeオブジェクトを変換 datetimeオブジェクトを変換 文字列を日時(日付・時刻)に変換: fromisoformat() dateオブジェクトに変換 timeオブジェクトに変換 datetimeオブジェクトに変換 ISO 8601の基本形式と拡張形式の変換 タイムゾーン情報を含む場合

datetime, date, timeオブジェクトの基本や、ISO形式ではない任意の形式の文字列との相互変換については

日時(日付・時刻)を文字列に変換: isoformat()

日時(日付・時刻)を文字列に変換するにはisoformat()を使います。

dateオブジェクトを変換

以下のdateオブジェクトを例とします。

import datetime

d = datetime.date(2018, 12, 31)

print(d)
# 2018-12-31

print(type(d))
# <class 'datetime.date'>

isoformat()で文字列に変換できます。

print(d.isoformat())
# 2018-12-31

print(type(d.isoformat()))
# <class 'str'>

timeオブジェクトを変換

以下のtimeオブジェクトを例とします。

t = datetime.time(5, 0, 30, 1000)

print(t)
# 05:00:30.001000

print(type(t))
# <class 'datetime.time'>

isoformat()で文字列に変換できます。

print(t.isoformat())
# 05:00:30.001000

print(type(t.isoformat()))
# <class 'str'>

引数timespecに以下のような文字列を指定することで結果に含める要素を指定できます。引数timespecはPython3.6で追加されたもの。それより前のバージョンでは使えないので気をつけてください。 デフォルトはtimespec='auto'で、マイクロ秒が0のときは'seconds'に等しく、そうでない場合は'microsecond'と等しくなります。

print(t.isoformat('hours'))
# 05

print(t.isoformat('minutes'))
# 05:00

print(t.isoformat('seconds'))
# 05:00:30

print(t.isoformat('milliseconds'))
# 05:00:30.001

print(t.isoformat('microseconds'))
# 05:00:30.001000

datetimeオブジェクトを変換

以下のdatetimeオブジェクトを例とします。
dt = datetime.datetime(2018, 12, 31, 5, 0, 30, 1000)

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

print(type(dt))
# <class 'datetime.datetime'>

isoformat()で文字列に変換できます。

print(dt.isoformat())
# 2018-12-31T05:00:30.001000

print(type(dt.isoformat()))
# <class 'str'>

デフォルトでは日付と時刻の間はTとなります。第一引数sepで任意の一文字を指定できます。二文字以上の文字列を指定するとエラー。

print(dt.isoformat(' '))
# 2018-12-31 05:00:30.001000

print(dt.isoformat('x'))
# 2018-12-31x05:00:30.001000

# print(dt.isoformat('xx'))
# TypeError: isoformat() argument 1 must be a unicode character, not str

timeの場合と同様に、第二引数timespecで結果に含める時刻の要素を指定できます。引数timespecはPython3.6で追加されたもの。それより前のバージョンでは使えないので気をつけてください。 デフォルトはtimespec='auto'で、マイクロ秒が0のときは'seconds'に等しく、そうでない場合は'microsecond'と等しくなります。

print(dt.isoformat(timespec='hours'))
# 2018-12-31T05

print(dt.isoformat(timespec='minutes'))
# 2018-12-31T05:00

print(dt.isoformat(timespec='seconds'))
# 2018-12-31T05:00:30

print(dt.isoformat(timespec='milliseconds'))
# 2018-12-31T05:00:30.001

print(dt.isoformat(timespec='microseconds'))
# 2018-12-31T05:00:30.001000

print(dt.isoformat(' ', 'seconds'))
# 2018-12-31 05:00:30

日付のみや時刻のみを文字列に変換したい場合はdate()やtime()でdateオブジェクトやtimeオブジェクトに変換してからisoformat()で文字列に変換すればOKです。

print(dt.date())
# 2018-12-31

print(type(dt.date()))
# <class 'datetime.date'>

print(dt.date().isoformat())
# 2018-12-31

print(type(dt.date().isoformat()))
# <class 'str'>

print(dt.time())
# 05:00:30.001000

print(type(dt.time()))
# <class 'datetime.time'>

print(dt.time().isoformat())
# 05:00:30.001000

print(type(dt.time().isoformat()))
# <class 'str'>

print(dt.time().isoformat('seconds'))
# 05:00:30

文字列を日時(日付・時刻)に変換: fromisoformat()

文字列を日時(日付・時刻)に変換するにはfromisoformat()を使います。fromisoformat()はPython3.7で追加されたもの。それより前のバージョンでは使えないので気をつけてください。

dateオブジェクトに変換

date.fromisoformat()で文字列をdateオブジェクトに変換できます。
s = '2018-12-31'

d = datetime.date.fromisoformat(s)

print(d)
# 2018-12-31

print(type(d))
# <class 'datetime.date'>

年月日が省略されていたり、桁数が合っていなかったりするとエラー。年は4桁、月日は2桁で0埋めが必要。

# print(datetime.date.fromisoformat('2018-12'))
# ValueError: Invalid isoformat string: '2018-12'

print(datetime.date.fromisoformat('2018-01-01'))
# 2018-01-01

# print(datetime.date.fromisoformat('2018-1-1'))
# ValueError: Invalid isoformat string: '2018-1-1'

timeオブジェクトに変換

time.fromisoformat()で文字列をtimeオブジェクトに変換できます。
s = '05:00:30.001000'

t = datetime.time.fromisoformat(s)

print(t)
# 05:00:30.001000

print(type(t))
# <class 'datetime.time'>

分や秒が省略されていると0とみなされます。

print(datetime.time.fromisoformat('05'))
# 05:00:00

時間、分、秒は0埋めされた2桁表記でないとエラー。

# print(datetime.time.fromisoformat('5:00:30'))
# ValueError: Invalid isoformat string: '5:00:30'

datetimeオブジェクトに変換

datetime.fromisoformat()で文字列をdatetimeオブジェクトに変換できます。
s = '2018-12-31T05:00:30.001000'

dt = datetime.datetime.fromisoformat(s)

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

print(type(dt))
# <class 'datetime.datetime'>

日付と時刻の間の文字はT以外でも一文字であれば問題ありません。二文字以上はエラー。

print(datetime.datetime.fromisoformat('2018-12-31x05:00:30.001000'))
# 2018-12-31 05:00:30.001000

# print(datetime.datetime.fromisoformat('2018-12-31xx05:00:30.001000'))
# ValueError: Invalid isoformat string: '2018-12-31xx05:00:30.001000'

分や秒の値を省略すると0とみなされます。

print(datetime.datetime.fromisoformat('2018-12-31T05'))
# 2018-12-31 05:00:00

print(datetime.datetime.fromisoformat('2018-12-31'))
# 2018-12-31 00:00:00

dateオブジェクト、timeオブジェクトと同様に、桁数が合っていないとエラー。0埋めが必要。

# print(datetime.datetime.fromisoformat('2018-12-31T5:00'))
# ValueError: Invalid isoformat string: '2018-12-31T5:00'

日付と時刻を含む文字列をdateオブジェクトやtimeオブジェクトに変換したい場合、date.fromisoformat()やtime.fromisoformat()ではなく、datetime.fromisoformat()でdatetimeオブジェクトに変換してからdate()やtime()でdateオブジェクトやtimeオブジェクトに変換します。

s = '2018-12-31T05:00:30.001000'

# print(datetime.date.fromisoformat(s))
# ValueError: Invalid isoformat string: '2018-12-31T05:00:30.001000'

# print(datetime.time.fromisoformat(s))
# ValueError: Invalid isoformat string: '2018-12-31T05:00:30.001000'

d = datetime.datetime.fromisoformat(s).date()

print(d)
# 2018-12-31

print(type(d))
# <class 'datetime.date'>

t = datetime.datetime.fromisoformat(s).time()

print(t)
# 05:00:30.001000

print(type(t))
# <class 'datetime.time'>

ISO 8601の基本形式と拡張形式の変換

これまでの例のように、isoformat()もfromisoformat()もハイフン-やコロン:を区切り文字として含む拡張形式(拡張表記)のISO 8601を扱う。 区切り文字を含まない基本形式(基本表記・標準表記)に変換したい場合は文字列メソッドを使います。 例えばreplace()でハイフン-とコロン:を削除する方法が考えられます。

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

s_basic = s.replace('-', '').replace(':', '')

print(s_basic)
# 20181231T050030

ミリ秒以下を含む場合はsplit()で小数点(ピリオド).で分割した最初の要素を対象としてミリ秒の部分を無視すればOKです。

s = '2018-12-31T05:00:30.001000'

s_basic = s.split('.')[0].replace('-', '').replace(':', '')

print(s_basic)
# 20181231T050030

反対に基本形式を拡張形式に変換したい場合はstrptime()を使ってdatetimeオブジェクトとしてからisoformat()で再度文字列に戻す。

s_ex = datetime.datetime.strptime(s_basic, '%Y%m%dT%H%M%S').isoformat()

print(s_ex)
# 2018-12-31T05:00:30

タイムゾーン情報を含む場合

これまでの例のようにタイムゾーンの情報を含まない文字列をfromisoformat()で変換した場合、tzinfo属性はNoneとなります。

s = '2018-12-31T05:00:30.001000'

dt = datetime.datetime.fromisoformat(s)
print(dt)
# 2018-12-31 05:00:30.001000

print(dt.tzinfo)
# None

末尾に+09:00や-09:00を含む文字列の場合、tzinfo属性にタイムゾーンの情報が適宜格納されます。

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

dt = datetime.datetime.fromisoformat(s)

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

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

tzinfo属性に値が格納されていれば、isoformat()で文字列に変換する場合、その値に基づいて末尾にタイムゾーンを表す文字列が追加されます。

print(dt.isoformat())
# 2018-12-31T05:00:30.001000+09:00

UTC(協定世界時)を表す場合は末尾にZを付けるが、fromisoformat()はこれに対応していません。末尾にZが付いた文字列はエラーになります。

s = '2018-12-31T05:00:30.001000Z'

# dt = datetime.datetime.fromisoformat(s)
# ValueError: Invalid isoformat string: '2018-12-31T05:00:30.001000Z'

末尾のZを処理するには+00:00に置き換える方法があります。この場合、tzinfo属性に情報が付与されます。

print(s.replace('Z', '+00:00'))
# 2018-12-31T05:00:30.001000+00:00

dt_utc = datetime.datetime.fromisoformat(s.replace('Z', '+00:00'))

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

print(dt_utc.tzinfo)
# UTC

末尾のZを削除した場合はtzinfo属性がNoneとなります。タイムゾーンを考慮しない場合はこちらでもよい。

print(s.replace('Z', ''))
# 2018-12-31T05:00:30.001000

dt_none = datetime.datetime.fromisoformat(s.replace('Z', ''))

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

print(dt_none.tzinfo)
# None
Last Updated: 6/26/2019, 10:34:03 PM