[Python] AWSの日付時刻データをプログラムで扱う

AWSで取得した日付時刻データをPythonで処理する方法をケース別に見てみましょう。
2024.06.29

こんにちは。サービス開発室の武田です。

前回、AWSのAPIで取得できるデータに含まれる日付時刻データにはいくつか種類があることを見ました。

今回はそれらのデータをPythonで扱う例を確認しましょう。

Pythonのバージョンは 3.12.2 で確認しています。

Pythonで日付時刻データを扱う際のポイント

日付時刻データを扱う際に難しいのはタイムゾーンの存在です(諸説あり)。Pythonの日付、時刻データ(オブジェクト)は2種類あります。

  • native: タイムゾーンをもたないローカルな日付、時間
  • aware: タイムゾーンをもつ実際の日付、時間

どちらが優れているというよりかは違いを知って適切に使いましょう、というものです。たとえば日本時間しか扱わない使い捨てのプログラムなどであれば、nativeの方がシンプルに書けるでしょう。AWSの日付時刻データは、基本的にはUTCとなるためawareを意識して使用することで混乱が減るでしょう。「なんか時間が9時間ずれる」という現象にあたったら、 間違いなくタイムゾーン です。

ISO 8601 → Pythonオブジェクト

まずはISO 8601フォーマットの文字列からPythonオブジェクトを生成する方法を見てみます。これはPython 3.11以降かどうかで少し方法が異なります。

Python 3.11以降であれば、何も考えずにdatetime.fromisoformat()を使用すればOKです。秒でもミリ秒でも正しく変換してくれます。

>>> from datetime import datetime, timezone, timedelta
>>> datetime.fromisoformat('2024-06-27T00:00:00Z')
datetime.datetime(2024, 6, 27, 0, 0, tzinfo=datetime.timezone.utc)

>>> datetime.fromisoformat('2024-06-27T00:00:00.111Z')
datetime.datetime(2024, 6, 27, 0, 0, 0, 111000, tzinfo=datetime.timezone.utc)

Python 3.10以前ではタイムゾーンのZを解釈してくれません。そのためこれを+00:00に置換することでパースできるようにしてやります。

>>> datetime.fromisoformat('2024-06-27T00:00:00Z')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Invalid isoformat string: '2024-06-27T00:00:00Z'

>>> datetime.fromisoformat('2024-06-27T00:00:00Z'.replace('Z', '+00:00'))
datetime.datetime(2024, 6, 27, 0, 0, tzinfo=datetime.timezone.utc)

UNIX時間 → Pythonオブジェクト

変換自体はdatetime.fromtimestamp()を使用すれば簡単にできます。ポイントおよび注意点が2つあります。1個はタイムゾーンをきちんと指定すること。もう1個は秒なのかミリ秒なのかを把握することです。

まずこれはダメな例です。AWSのデータに含まれているUNIX時間はタイムゾーンがUTCです。そのため単に変換してしまうとnativeオブジェクトとなってしまい、時刻がずれてしまいます。またエポックミリ秒を渡してしまうとValueErrorとなります。

>>> datetime.fromtimestamp(1719446400)
datetime.datetime(2024, 6, 27, 9, 0)

>>> datetime.fromtimestamp(1719446400111)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: year 56457 is out of range

対応方法はそれぞれ、 tzを指定する秒に直す です。fromtimestampは浮動小数点のエポック秒表現を受け入れます。そのため 1000で割る と正しく評価されます。

>>> datetime.fromtimestamp(1719446400, tz=timezone.utc)
datetime.datetime(2024, 6, 27, 0, 0, tzinfo=datetime.timezone.utc)

>>> datetime.fromtimestamp(1719446400111 / 1000, tz=timezone.utc)
datetime.datetime(2024, 6, 27, 0, 0, 0, 111000, tzinfo=datetime.timezone.utc)

なおdatetime.utcfromtimestamp()というメソッドも存在しますが、3.12からは非推奨となっていますので、tzを指定する方法を使用してください。

Pythonオブジェクト → ISO 8601

PythonオブジェクトからISO 8601の文字列表現を取得する方法です。datetime#isoformat()というメソッドが提供されています。この際に注意することは2つです。1個は秒やミリ秒などの精度を意識すること。もう1個はタイムゾーンの表現です。

単に呼び出してみましょう。

>>> datetime.fromtimestamp(1719446400, tz=timezone.utc).isoformat()
'2024-06-27T00:00:00+00:00'

>>> datetime.fromtimestamp(1719446400.111, tz=timezone.utc).isoformat()
'2024-06-27T00:00:00.111000+00:00'

元のデータがミリ秒を持っているかで書式が自動で変わり、持っていた場合ミリ秒は6桁表記です。またタイムゾーンは+00:00表記です。

isoformat()はPython 3.6からtimespecという引数を取れるようになっており、これを指定することで時間の書式を制御できます。またタイムゾーンはZ表記の方がAWSの仕様と合っているため、これは置換して合わせてやりましょう。

>>> datetime.fromtimestamp(1719446400, tz=timezone.utc).isoformat(timespec='seconds').replace('+00:00', 'Z')
'2024-06-27T00:00:00Z'

>>> datetime.fromtimestamp(1719446400, tz=timezone.utc).isoformat(timespec='milliseconds').replace('+00:00', 'Z')
'2024-06-27T00:00:00.000Z'

>>> datetime.fromtimestamp(1719446400.111, tz=timezone.utc).isoformat(timespec='seconds').replace('+00:00', 'Z')
'2024-06-27T00:00:00Z'

>>> datetime.fromtimestamp(1719446400.111, tz=timezone.utc).isoformat(timespec='milliseconds').replace('+00:00', 'Z')
'2024-06-27T00:00:00.111Z'

Pythonオブジェクト → UNIX時間

PythonオブジェクトからUNIX時間を取得するのはdatetime#timestamp()メソッドを使用します。注意点として、値は浮動小数点数(float型)のエポック秒ということです。

>>> datetime.fromisoformat('2024-06-27T00:00:00Z').timestamp()
1719446400.0

>>> datetime.fromisoformat('2024-06-27T00:00:00.111Z').timestamp()
1719446400.111

もし 厳密に整数 を要求する場面があったなら、int()を使用して適切にキャストあげましょう。またエポックミリ秒が求められるケースでは1000倍すればOKです。

>>> int(datetime.fromisoformat('2024-06-27T00:00:00Z').timestamp())
1719446400

>>> int(datetime.fromisoformat('2024-06-27T00:00:00.111Z').timestamp() * 1000)
1719446400111

まとめ

PythonでAWSリソースを操作する場合はboto3を使用するのが一般的です。boto3で取得したデータは内部的にdatetimeに変換済みである場合もあります。その場合は考えることが半分で済みますね。

また今回タイムゾーンは標準ライブラリを使用しましたが、python-dateutilなどもよく使用されるライブラリです。基本的な使用感は変わりませんが、どちらにせよよっぽどの理由がない限りはawareなオブジェクトを扱った方が変なバグを作り込む心配が減ります。ぜひ気をつけてAWSライフを送ってください。

参考URL