zaki work log

作業ログやら生活ログやらなんやら

[Python] 標準ライブラリで"2023-09-27T12:43:39.123456Z"のようなISO 8601拡張形式の文字列のdatetime変換とタイムゾーン

"2023-09-27T12:43:39.123456Z" のようなISO 8601拡張形式の文字列をPythonでdatetimeオブジェクトとして扱ったり、JSTYYYY/mm/dd HH:MM:SS文字列に変換するサンプルコードについて。
最小限の環境で動くように、標準ライブラリで実装している。(pip installによる追加パッケージ不要)

ISO 8601形式についてはWikipediaなどを参照。

ja.wikipedia.org

動作確認はFedora 37で、Python 3.11.5、タイムゾーン設定はAsia/Tokyo

fromisoformat()でdatetimeオブジェクトの取得

$ python
Python 3.11.5 (main, Aug 28 2023, 00:00:00) [GCC 12.3.1 20230508 (Red Hat 12.3.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> from datetime import datetime
>>> datetime.fromisoformat('2023-09-27T12:43:39.123456Z')
datetime.datetime(2023, 9, 27, 12, 43, 39, 123456, tzinfo=datetime.timezone.utc)
>>> 

datetimefromisoformat()メソッドを使うと、UTCタイムゾーンを持つdatetimeオブジェクトを得られる。

strftime()で任意の書式の日付文字列への変換

書式を指定して文字列へ変換するにはstrftime()を使う。
ただし、上記のUTCのdatetimeオブジェクトを単に変換すると、UTCのままの文字列になる。

>>> d = datetime.fromisoformat('2023-09-27T12:43:39.123456Z')
>>> print(d.strftime('%Y/%m/%d %H:%M:%S'))
2023/09/27 12:43:39

タイムゾーンを考慮して文字列変換するにはastimezone()を使用する。

>>> print(d.astimezone().strftime('%Y/%m/%d %H:%M:%S'))
2023/09/27 21:43:39

astimezone()に引数を指定しない場合はシステムのタイムゾーンが使用される。(上記はTokyo/Asia設定の環境)

任意のタイムゾーンを指定するには、ZoneInfoを使用する。
例えば+0800の中国であれば、この通り。

>>> from zoneinfo import ZoneInfo
>>> print(d.astimezone(ZoneInfo('Asia/Shanghai')).strftime('%Y/%m/%d %H:%M:%S'))
2023/09/27 20:43:39
>>> print(d.astimezone(ZoneInfo('Asia/Tokyo')).strftime('%Y/%m/%d %H:%M:%S'))
2023/09/27 21:43:39

これはシステムのタイムゾーン設定がJSTでない場合にJSTで出力したい場合は明示的に指定が必要。クラウド上のコンピュートインスタンスのように。

Python3.10以前の場合

※ 以下はUbuntu 22.04で確認

実行環境のPythonバージョンが3.10の場合、入力の"2023-09-27T12:43:39.123456Z"の最後UTCを表すのZを認識しない。
更に+HHMM形式のタイムゾーン情報の書式も認識しない。

$ python3
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import datetime
>>> datetime.fromisoformat('2023-09-27T12:43:39.123456Z')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid isoformat string: '2023-09-27T12:43:39.123456Z'
>>> datetime.fromisoformat('2023-09-27T21:43:39.123456+0900')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid isoformat string: '2023-09-27T21:43:39.123456+0900'
>>> 

3.10のドキュメントを見る感じでは、間にコロンを入れて+HH:MM形式であれば動作する。
よって、末尾のZ00:00に変換してから処理してあげればOK

>>> datetime.fromisoformat('2023-09-27T12:43:39.123456+00:00')
datetime.datetime(2023, 9, 27, 12, 43, 39, 123456, tzinfo=datetime.timezone.utc)
>>> 
>>> d = datetime.fromisoformat('2023-09-27T12:43:39.123456+00:00')
>>> print(d.astimezone().strftime('%Y/%m/%d %H:%M:%S'))
2023/09/27 21:43:39

参考

docs.python.org

docs.python.org

python.civic-apps.com

dev.classmethod.jp

任意の書式からのdatetime変換は以下

zaki-hmkc.hatenablog.com