PythonにおけるJSON読込み時の日付時刻文字列を datetime に

Java だったら、Jackson 使用で、jackson-datatype-jsr310 ライブラリを使った
シリアライズを設定するだろう。
Jackson の 日付時刻の読込み - Oboe吹きプログラマの黙示録
jackson-datatype-jsr310 を使う - Oboe吹きプログラマの黙示録
Pythonjson.loadsJSON文字列 ⇒ dict で、value が文字列か数値に限定されるのではなく
日付時刻の文字列は、datetime にしたい時がある。
 ( dict への変換ではなく任意クラスに変換する時は、前に
   JSONDecoder を継承してJSON→任意クラスへの変換 - Oboe吹きプログラマの黙示録
  を書いていました。)

dict に変換したいJSON文字列、日付時刻が、%Y-%m-%d %H:%M:%S.%f の形式である。

jsonstr = """
{ "date": "2023-11-03 16:27:26.015720"  }
"""

必要なインポート

import json
import datetime
import re

json.loads で指定する object_hook を、日付時刻の書式 %Y-%m-%d %H:%M:%S.%f に、
正規表現で一致している値に限り datetime.strptime で変換する処理として用意する。
日付の正規表現https://oboe2uran.hatenablog.com/entry/2015/04/14/225042
時刻の正規表現https://oboe2uran.hatenablog.com/entry/2017/09/03/000539
を使う。

def dateTimeParser(dct):
    for k, v in dct.items():
        if isinstance(v, str):
            if re.fullmatch(r'^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01]) (0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[0-5][0-9]):(0[0-9]|[0-5][0-9])\.[0-9]{6}$', v):
                try:
                    dct[k] = datetime.datetime.strptime(v, "%Y-%m-%d %H:%M:%S.%f")
                except:
                    pass
    return dct

正規表現だけでは、存在しない日付、11月31日などはエラー
 ValueError: day is out of range for month
となってしまうので、try~catch で捕捉する。

json.loads 実行

a = json.loads(jsonstr, object_hook=dateTimeParser)

print(type(a.get('date')))
print(a.get('date'))

print

<class 'datetime.datetime'>
2023-11-03 16:27:26.015720

これには、欠点がある、1つの書式にしか対応できていない。
そこで、数種類の書式でも datetime に変換するようにする。

  %Y-%m-%d %H:%M:%S.%f
  %Y-%m-%d %H:%M:%S
  %Y-%m-%d

この3つに対応させるようにすると、、

def dateTimeParser(dct):
    ptns = [
        (r'^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01]) (0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[0-5][0-9]):(0[0-9]|[0-5][0-9])\.[0-9]{6}$', '%Y-%m-%d %H:%M:%S.%f'),
        (r'^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01]) (0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[0-5][0-9]):(0[0-9]|[0-5][0-9])$', '%Y-%m-%d %H:%M:%S'),
        (r'^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01])$', '%Y-%m-%d'),
    ]
    for k, v in dct.items():
        if isinstance(v, str):
            r = [t for t in ptns if re.fullmatch(t[0], v)]
            if len(r)==1:
                try:
                    dct[k] = datetime.datetime.strptime(v, r[0][1])
                except:
                    pass
    return dct