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

Modified: | Tags: Python, 日時処理

Pythonで日時(日付・時刻)の情報を扱うには標準ライブラリのdatetimeモジュールを使う。ISOフォーマット(ISO 8601)の文字列とdatetimeオブジェクトなどを相互に変換するisoformat()fromisoformat()が用意されている。

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

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

日時(日付・時刻)をISOフォーマット(ISO 8601)の文字列に変換するには、date, time, datetimeオブジェクトのisoformat()メソッドを使う。

dateオブジェクトを変換

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

import datetime

d = datetime.date(2023, 4, 1)
print(d)
# 2023-04-01

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

isoformat()でISOフォーマット(ISO 8601)の文字列に変換できる。

print(d.isoformat())
# 2023-04-01

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()でISOフォーマット(ISO 8601)の文字列に変換できる。

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

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

第一引数timespecで結果に含める要素を指定できる。デフォルトは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(2023, 4, 1, 5, 0, 30, 1000)
print(dt)
# 2023-04-01 05:00:30.001000

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

isoformat()でISOフォーマット(ISO 8601)の文字列に変換できる。

print(dt.isoformat())
# 2023-04-01T05:00:30.001000

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

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

print(dt.isoformat(' '))
# 2023-04-01 05:00:30.001000

print(dt.isoformat('x'))
# 2023-04-01x05:00:30.001000

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

timeの場合と同様に、第二引数timespecで結果に含める時刻の要素を指定できる。デフォルトはtimespec='auto'で、マイクロ秒が0のときは'seconds'、そうでない場合は'microsecond'となる。

print(dt.isoformat(timespec='hours'))
# 2023-04-01T05

print(dt.isoformat(timespec='minutes'))
# 2023-04-01T05:00

print(dt.isoformat(timespec='seconds'))
# 2023-04-01T05:00:30

print(dt.isoformat(timespec='milliseconds'))
# 2023-04-01T05:00:30.001

print(dt.isoformat(timespec='microseconds'))
# 2023-04-01T05:00:30.001000

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

print(dt.date().isoformat())
# 2023-04-01

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

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

ISOフォーマット(ISO 8601)の文字列を日時(日付・時刻)に変換するには、date, time, datetimeクラスのクラスメソッドであるfromisoformat()を使う。

fromisoformat()はPython3.7で追加された。また、後述のように、Python3.11からISO 8601の基本形式にも対応するようになった。

dateオブジェクトに変換

fromisoformat()でISO 8601形式の文字列をdateオブジェクトに変換できる。

d = datetime.date.fromisoformat('2023-04-01')
print(d)
# 2023-04-01

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

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

# print(datetime.date.fromisoformat('2023-04'))
# ValueError: Invalid isoformat string: '2023-04'

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

timeオブジェクトに変換

fromisoformat()でISO 8601形式の文字列をtimeオブジェクトに変換できる。

t = datetime.time.fromisoformat('05:00:30.001000')
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オブジェクトに変換

fromisoformat()でISO 8601形式の文字列をdatetimeオブジェクトに変換できる。

dt = datetime.datetime.fromisoformat('2023-04-01T05:00:30.001000')
print(dt)
# 2023-04-01 05:00:30.001000

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

日付と時刻の間の文字はT以外でも一文字であればOK。二文字以上はエラー。

print(datetime.datetime.fromisoformat('2023-04-01x05:00:30.001000'))
# 2023-04-01 05:00:30.001000

# print(datetime.datetime.fromisoformat('2023-04-01xx05:00:30.001000'))
# ValueError: Invalid isoformat string: '2023-04-01xx05:00:30.001000'

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

print(datetime.datetime.fromisoformat('2023-04-01T05'))
# 2023-04-01 05:00:00

print(datetime.datetime.fromisoformat('2023-04-01'))
# 2023-04-01 00:00:00

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

# print(datetime.datetime.fromisoformat('2023-4-1T5:00'))
# ValueError: Invalid isoformat string: '2023-4-1T5:00'

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

s = '2023-04-01T05:00:30.001000'

# print(datetime.date.fromisoformat(s))
# ValueError: Invalid isoformat string: '2023-04-01T05:00:30.001000'

d = datetime.datetime.fromisoformat(s).date()
print(d)
# 2023-04-01

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

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

ISO 8601には、ハイフン-やコロン:を区切り文字として含む拡張形式(拡張表記)と、含まない基本形式(基本表記・標準表記)がある。

  • 拡張形式: 2023-04-01T05:00:30.001000
  • 基本形式: 20230401T050030.001000

Python 3.11からfromisoformat()が基本形式に対応

Python 3.11からfromisoformat()が基本形式に対応するようになった。

print(datetime.date.fromisoformat('20230401'))
# 2023-04-01

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

print(datetime.datetime.fromisoformat('20230401T050030.001000'))
# 2023-04-01 05:00:30.001000

それ以前のバージョンではstrptime()に適切なフォーマット文字列を指定する必要がある。

print(datetime.datetime.strptime('20230401T050030.001000', '%Y%m%dT%H%M%S.%f'))
# 2023-04-01 05:00:30.001000

基本形式と拡張形式の文字列の変換

これまでの例のように、isoformat()は日時を拡張形式の文字列に変換する。拡張形式の文字列を区切り文字を含まない基本形式に変換したい場合は文字列メソッドを使う。

例えばreplace()でハイフン-とコロン:を削除する方法が考えられる。

s = '2023-04-01T05:00:30.001000'
print(s.replace('-', '').replace(':', ''))
# 20230401T050030.001000

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

print(s.split('.')[0].replace('-', '').replace(':', ''))
# 20230401T050030

基本形式を拡張形式に変換したい場合はdatetimeオブジェクトに変換してからisoformat()で再度文字列に戻す。上述のように、基本形式の文字列からdatetimeオブジェクトへの変換には、Python 3.11以降はfromisoformat()、それより前のバージョンではstrptime()を使う。

s_basic = '20230401T050030.001000'
print(datetime.datetime.fromisoformat(s_basic).isoformat())
# 2023-04-01T05:00:30.001000

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

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

s = '2023-04-01T05:00:30.001000'
dt = datetime.datetime.fromisoformat(s)
print(dt)
# 2023-04-01 05:00:30.001000

print(dt.tzinfo)
# None

末尾に+09:00のようなタイムゾーン情報を含む場合、tzinfo属性に値が格納される。

s = '2023-04-01T05:00:30.001000+09:00'
dt = datetime.datetime.fromisoformat(s)
print(dt)
# 2023-04-01 05:00:30.001000+09:00

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

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

print(dt.isoformat())
# 2023-04-01T05:00:30.001000+09:00

UTC(協定世界時)を表す末尾のZ

UTC(協定世界時)を表す場合は末尾にZを付ける。

Python 3.11以降

Python 3.11からfromisoformat()は末尾のZを正しく処理するようになった。

s = '2023-04-01T05:00:30.001000Z'
dt = datetime.datetime.fromisoformat(s)
print(dt)
# 2023-04-01 05:00:30.001000+00:00

print(dt.tzinfo)
# UTC

Python 3.11より前

Python 3.11より前のバージョンのfromisoformat()では末尾にZが付いた文字列はエラーになる。

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

s = '2023-04-01T05:00:30.001000Z'
dt_utc = datetime.datetime.fromisoformat(s.replace('Z', '+00:00'))
print(dt_utc)
# 2023-04-01 05:00:30.001000+00:00

print(dt_utc.tzinfo)
# UTC

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

dt_none = datetime.datetime.fromisoformat(s.replace('Z', ''))
print(dt_none)
# 2023-04-01 05:00:30.001000

print(dt_none.tzinfo)
# None

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

関連カテゴリー

関連記事