note.nkmk.me

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

Date: 2019-01-21 / tags: Python

Pythonで日時(日付・時刻)の情報を扱う場合は標準ライブラリのdatetimeモジュールを使う。

datetimeモジュールには、広く使われているISOフォーマット(ISO 8601)の文字列とdatetimeオブジェクトなどとの相互変換のためのメソッドが用意されている(文字列をdatetime等に変換するfromisoformat()はPython3.7で追加)。

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

  • 日時(日付・時刻)を文字列に変換: 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()で文字列に変換すればよい。

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以外でも一文字であればOK。二文字以上はエラー。

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()で小数点(ピリオド).で分割した最初の要素を対象としてミリ秒の部分を無視すればよい。

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

datetimeやpytzを使ったタイムゾーンの処理についての詳細は以下の記事を参照。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事