Python標準ライブラリで時間を扱う:datetimeを徹底攻略

シリーズ 03 – 日付/時間演算、タイムゾーン、フォーマット変換まで一挙に

時間は単なる「文字列」ではありません。日や月が変わり、サマータイムが導入され、地域ごとに基準も異なります。 そのため、日付/時間を扱う際は「表示用文字列」と「計算可能な時間値」を区別し、タイムゾーンまで含めて扱う習慣を身につけることが重要です。

時計職人の魔法の作業室

この記事では datetime モジュールを中心に次を整理します。

  • 現在時刻と日付の作成
  • 期間計算(timedelta)
  • 文字列フォーマット/パース(strftime, strptime)
  • タイムゾーン処理(timezone, zoneinfo)
  • よく直面する 注意点 と安定した実装パターン

1. datetime が提供する機能

datetime には、似ているようで役割が異なる型が存在します。

  • date : 年-月-日だけが必要なとき
  • time : 時:分:秒だけが必要なとき
  • datetime : 日付+時間(最も頻繁に使用)
  • timedelta : 時間の「差」(期間)
  • timezone : 固定オフセットタイムゾーン(例:UTC+9のようにオフセットが変わらない場合)

そして Python 3.9+ では標準ライブラリに zoneinfo が追加され、地域タイムゾーン(例:Asia/Tokyo)を扱いやすくなりました。


2. 「今」を得る方法:naive vs aware から整理

2.1 naive / aware とは?

datetime オブジェクトは大きく二つのカテゴリに分かれます。

  • naive datetime: タイムゾーン情報がない
  • aware datetime: タイムゾーン情報(tzinfo)がある

これらを混在させて演算/比較するとエラーが出ることもありますし、エラーが出なくても結果が意図と異なることがあります。

2.2 推奨されるデフォルト:UTC aware で始める

保存/計算基準を UTC に統一すると、コードがきれいになるケースが多いです。

from datetime import datetime, timezone

utc_now = datetime.now(timezone.utc)  # aware (UTC)
print(utc_now)

ローカル時刻が必要な場合は「表示段階」で変換します。

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

utc_now = datetime.now(timezone.utc)
tokyo_now = utc_now.astimezone(ZoneInfo("Asia/Tokyo"))

print(utc_now)
print(tokyo_now)

3. 日付/時間計算:timedelta が中心

3.1 加算/減算

from datetime import datetime, timedelta, timezone

now = datetime.now(timezone.utc)
print(now + timedelta(days=3))
print(now - timedelta(hours=2))

3.2 二つの時刻の差

from datetime import datetime, timezone

a = datetime(2026, 1, 1, tzinfo=timezone.utc)
b = datetime(2026, 1, 10, tzinfo=timezone.utc)

delta = b - a
print(delta.days)            # 9
print(delta.total_seconds()) # 777600.0

3.3 timedelta で「期間」を表現する感覚

timedelta日/秒/マイクロ秒 単位で機能します。 「1か月後」のように月の長さが変わる表現は timedelta(days=30) で大まかに合わせることはできますが、カレンダー上の「翌月の同じ日」といった表現には別のアプローチが必要です(この部分は標準ライブラリの範囲外で dateutil などがよく使われます)。


4. フォーマットとパース:strftime / strptime

4.1 datetime → 文字列(strftime)

from datetime import datetime, timezone

dt = datetime(2026, 1, 30, 14, 5, 0, tzinfo=timezone.utc)
print(dt.strftime("%Y-%m-%d %H:%M:%S %z"))

よく使うフォーマットパターン:

  • %Y-%m-%d : 2026-01-30
  • %H:%M:%S : 14:05:00
  • %z : +0000 (UTCオフセット)

4.2 文字列 → datetime(strptime)

from datetime import datetime

s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
print(dt)

ここで生成された dtnaive です。タイムゾーンを明確にしたい場合は tzinfo を付ける必要があります。

from datetime import datetime, timezone

s = "2026-01-30 14:05:00"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
print(dt)

replace(tzinfo=...) は「時間値を変換」するのではなく、その時刻にタイムゾーンラベルを付与する 動作をします。 変換が必要なら astimezone() を使用します(次節参照)。


5. タイムゾーン:timezonezoneinfo を分けて使う

5.1 固定オフセットなら timezone

例:UTC、UTC+9 のようにオフセットが固定の場合

from datetime import datetime, timezone, timedelta

kst_fixed = timezone(timedelta(hours=9))
dt = datetime(2026, 1, 30, 12, 0, tzinfo=kst_fixed)
print(dt)

5.2 地域タイムゾーンなら zoneinfo

サマータイムのようにオフセットが変わる地域は zoneinfo が適しています。

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

utc_now = datetime.now(timezone.utc)
ny_now = utc_now.astimezone(ZoneInfo("America/New_York"))
print(ny_now)

5.3 replace(tzinfo=...) vs astimezone(...) の違い

  • replace(tzinfo=...): 時刻値はそのままで、タイムゾーンだけを「ラベリング」します。
  • astimezone(...): 同じ瞬間を別のタイムゾーンの時刻に「変換」します。

この違いを知っておくと、タイムゾーン関連のバグを大幅に減らせます。


6. よく直面する注意点まとめ

6.1 naive/aware 混用

  • 内部計算用時刻は aware(UTC) に統一すると安定します。
  • 外部入力は受け取った瞬間にタイムゾーンを決定し、統一することが推奨されます。

6.2 「ローカル時間」は環境によって変わることがある

datetime.now() は実行環境のローカル設定を参照します。コンテナ/サーバ環境ではローカルが UTC である場合もありますし、そうでない場合もあります。 環境差を減らすには datetime.now(timezone.utc) といった方法を用いるのが望ましいです。

6.3 文字列パースのフォーマット不一致

strptime はフォーマットが 1 文字でも違うと失敗します。入力フォーマットが複数ある場合は、「前処理」やフォーマット候補を段階的に試すコードが必要です(可能であれば入力フォーマットを一つに限定することを推奨します)。


7. よく使うパターン 3 つ

7.1 ISO 8601 形式で保存する

標準的な文字列表現が必要なときは isoformat() が便利です。

from datetime import datetime, timezone

dt = datetime.now(timezone.utc)
print(dt.isoformat())  # 例: 2026-01-30T05:12:34.567890+00:00

7.2 「今日の日付」でファイル名を作る

from datetime import datetime

stamp = datetime.now().strftime("%Y%m%d")
filename = f"report_{stamp}.json"
print(filename)

7.3 特定時刻まで残り時間を計算する

from datetime import datetime, timezone

target = datetime(2026, 2, 1, 0, 0, tzinfo=timezone.utc)
now = datetime.now(timezone.utc)

remaining = target - now
print(remaining)
print(remaining.total_seconds())

8. まとめ

datetime は「日付文字列を作る」だけでなく、時間を 演算可能な値 として扱えるようにする中核的なツールです。 タイムゾーン(timezone, zoneinfo) を含めることで、実行環境が変わっても結果が不安定にならないコードを作りやすくなります。

次回は random を別に取り上げ、乱数生成/サンプリング/シャッフル/シード、そしてセキュリティ用乱数(secrets)までまとめて解説します。


関連記事 :


前回シリーズを見る