Pythonで日付と時刻を処理する
2026/08/13

Tipsでは済まなかった

  • 最初はTips/チートシートとして、Pythonの日付と時刻処理をまとめようと思いましたが、調べてみると特にタイムゾーンについて配慮が必要なことに気付かされ、結果として普通のレポートになっていました。

目次


日付と時刻処理ではタイムゾーンの扱いが重要

  • Pythonに限りませんが、日付と時刻の処理ではタイムゾーンの扱い方が重要です。大まかにタイムゾーンは地域の経度で決まりますが、歴史的経緯や夏時間があるため、そんなに単純ではありません。

  • 例えば日本国内だけの場合、標準時/協定世界時(UTC)より9時間早い(JST=TZ-9)という「時間シフト」を考えれば良いですが、これを複数の国、例えば日本とアメリカでも使おうとした場合、1国内ですら複数のタイムゾーンが存在するので、タイムゾーンへのケアは必須です。

datetimeオブジェクトのnaiveとaware

  • Pythonで日付時刻情報を扱うにはdatetimeモジュールのdatetimeクラスを使用します。
    • 現在の日付及び時刻の取得は.now()メソッドを使います。
    • datetimeオブジェクトはイミュータブルです。
    from datetime import datetime
    
    dt_now = datetime.now() # 現在の日付と時刻情報
    print(dt_now)
    
    >test.py
    2026-08-11 15:35:55.900025
    
  • 引数無しの.now()メソッドで取得したdatetimeオブジェクトはタイムゾーン情報を持ちません。
    • オブジェクトのtzinfoがNoneです。naive(*1)と呼ばれます。
    • 一方タイムゾーン情報を持つdatetimeオブジェクトはawareと呼ばれます。
    from datetime import datetime
    
    dt_now = datetime.now() # Naiveオブジェクト
    print(dt_now.tzinfo) # datetimeオブジェクトのtzinfo表示
    
    >test.py
    None
    

awareとタイムゾーン設定とIANA形式

  • awareオブジェクトを作成する方法は下記2種類です。
    • datetimeオブジェクト定義時にタイムゾーン情報を与える
    • naiveオブジェクトにタイムゾーンを追加したオブジェクトを作成する

  • zoneinfoパッケージのZoneInfoクラスを使用すれば、'Asia/Tokyo'等のIANA形式でタイムゾーンを指定できます。ですが、Windowsでは下記のようにtzdataのインストールが必須(*2)です。ご注意下さい。
    >pip install tzdata
    
  • それでは実際にタイムゾーン指定でawareオブジェクトを作ってみましょう。
    from datetime import datetime
    from zoneinfo import ZoneInfo
    
    dt_naive = datetime.now() # Naive
    dt_aware = datetime.now(tz=ZoneInfo("Asia/Tokyo")) # Aware
    print('naive:', dt_naive.tzinfo, 'aware:', dt_aware.tzinfo)
    
    dt_replaced = dt_naive.replace(tzinfo=ZoneInfo("Asia/Tokyo")) # Naive->Aware
    print('replaced:', dt_replaced.tzinfo)
    
    >test.py
    naive: None, aware: Asia/Tokyo
    replaced: Asia/Tokyo
    
  • あまり無いと思いますが、awareをnaiveへ変更したい時は、tzinfoにNoneを与えます。
    dt_naive = dt_aware.replace(tzinfo=None)
    

UTCタイムゾーンのdatetime取得

  • UTC(協定世界時)のIANAタイムゾーン名は UTC 又は Etc/UTC です。
    from datetime import datetime
    from zoneinfo import ZoneInfo
    
    dt_utc = datetime.now(tz=ZoneInfo('UTC'))
    print(dt_utc)
    print(dt_utc.tzinfo)
    
    >test.py
    2026-08-12 22:46:27.045797+00:00
    UTC
    

タイムゾーンの変更

  • .astimezone(tz=...)でタイムゾーンを変更したdatetimeオブジェクトを取得できます。
    • ただし.astimezone(tz=...)はawareオブジェクトに適用して下さい。
    from datetime import datetime
    from zoneinfo import ZoneInfo
    
    dt_jst = datetime.now(tz=ZoneInfo('Asia/Tokyo')) # aware であることが重要
    dt_utc = dt_jst.astimezone(tz=ZoneInfo('UTC')) # タイムゾーン変更
    
    print(dt_jst)
    print(dt_utc)
    
    >test.py
    2026-08-13 08:35:09.110073+09:00
    2026-08-12 23:35:09.110073+00:00
    

OSからローカルのタイムゾーンを取得する

  • タイムゾーンをIANA形式でdatetimeオブジェクトに設定する方法はわかりました。すると次は実行システムのタイムゾーン取得ですね。
    • サーバー/オンプレなど異なる実行環境へ対応するには、ローカルタイムゾーン取得は必須です。
    • ここまでの例のように、タイムゾーンをリテラルで書くのは「実はまずい」のです。

  • Windowsの場合、tzlocalパッケージの.get_localzone_name()を使う必要があります。
    • クロスプラットフォームではtzlocal.get_localzone_name()の利用が必須です。
    • tzlocalは非標準パッケージなのでインストールが必要です。
    >pip install tzlocal
    
  • では、ローカルのタイムゾーンをIANA形式で取得してみましょう。そのうえでdatetimeオブジェクトをawareにします。
    from tzlocal import get_localzone_name
    from datetime import datetime
    from zoneinfo import ZoneInfo
    
    tz_iana = get_localzone_name() # ローカルタイムゾーンをIANAで取得
    print('get_localzone_name:', tz_iana)
    
    dt_aware = datetime.now(tz=ZoneInfo(tz_iana))
    print('tzinfo:', dt_aware.tzinfo) # IANA表示に tzinfo.key を参照
    
    >test.py
    get_localzone_name: Asia/Tokyo
    tzinfo: Asia/Tokyo
    

任意日付時刻のdatetimeオブジェクト

  • 任意日付時刻のdatetimeオブジェクトは、定義時に値を設定することで作成できます。
    from datetime import datetime
    from tzlocal import get_localzone_name
    from zoneinfo import ZoneInfo
    
    # naive             2026年, 8月, 12日, 13時, 14分, 15秒, 160マイクロ秒 
    dt_naive = datetime(2026,   8,   12,   13,   14,   15,  160)
    # naive -> aware
    dt_aware = dt_naive.replace(tzinfo=ZoneInfo(get_localzone_name()))
    
    print(dt_aware)
    
    >test.py
    2026-08-12 13:14:15.000160+09:00
    
  • datetimeコンストラクタ実行時にtzinfo引数でタイムゾーン指定することもできます。
    from datetime import datetime
    from tzlocal import get_localzone_name
    from zoneinfo import ZoneInfo
    
    #                   2026年, 8月, 12日, 13時, 14分, 15秒, 160マイクロ秒
    dt_aware = datetime(2026,   8,   12,   13,   14,   15,  160,
                        tzinfo=ZoneInfo(get_localzone_name())) # tzinfo指定
    print(dt_aware)
    
  • datetime.strptime()で文字列からdatetimeオブジェクトを作成できます。
    • 文字列とフォーマット(%Y/%m ...)を引数に指定します。
    • 使用できるフォーマット文字列は、公式ドキュメントを参照下さい。
    from datetime import datetime
    
    dt_naive = datetime.strptime('2026/8/15, 15:20:10', '%Y/%m/%d, %H:%M:%S')
    print(dt_naive)
    
    >test.py
    2026-08-15 15:20:10
    
  • タイムゾーン指定が無い場合はnaiveになるので、.replace()等でawareにして下さい。

datetimeオブジェクトの要素取得

  • datetimeオブジェクトが持つ'年'や'分'等の要素を下記属性として取得できます。
    属性名説明
    datetime.year4桁数値: '年'
    datetime.month1〜12: '月'
    datetime.day1〜31: '日'
    datetime.hour0〜23': 時'
    datetime.minute0〜59: '分'
    datetime.second0〜59: '秒'
    datetime.microsecond0〜999999: 'マイクロ秒'
    datetime.tzinfoNone(naive)又はtzinfo(aware)オブジェクト(*3)
  • 尚tzinfoを含めたtuple化やdict化のメソッドは無いので、自分で作成/定義が必要です。

datetimeオブジェクトの変換

  • datetimeオブジェクトを文字列に変換する場合、strftime()メソッドが使用できます。
    • 引数は文字列としての出力フォーマットです。
    • フォーマット記述方法はstrptime()と同じなので、公式ドキュメントを参照下さい。
    from datetime import datetime
    from tzlocal import get_localzone_name
    from zoneinfo import ZoneInfo
    
    dt = datetime.now(tz=ZoneInfo(get_localzone_name()))
    
    print(dt.strftime("%Y年%m月%d日, %H時%M分%S秒, TZ:%z"))
    
    >test.py
    2026年08月12日, 16時12分33秒, TZ:+0900
    
  • datetimeオブジェクトをエポック秒に変換するにはtimestamp()メソッドを利用します。
    from datetime import datetime
    
    dt = datetime.now()
    print(dt.timestamp())
    print(dt)
    
    >test.py
    1786519318.464638
    2026-08-12 16:21:58.464638
    
  • エポック秒をdatetimeへ変換するには、fromtimestamp()メソッドを利用します。
    • naiveオブジェクトなので、適宜replace()メソッド等でタイムゾーンを追加設定して下さい。
    from datetime import datetime
    
    ts_epoch = 1786519318.464638
    dt = datetime.fromtimestamp(ts_epoch)
    print(dt)
    
    >test.py
    2026-08-12 16:21:58.464638
    

datetime間の日付と時刻差

  • datetimeオブジェクト間で差分を計算することができます。加算はできません。
    • 戻り値はtimedeltaオブジェクトです。
    from datetime import datetime
    from zoneinfo import ZoneInfo
    import time
    
    tz_jst = ZoneInfo('Asia/Tokyo')
    
    dt_start = datetime.now(tz=tz_jst); print(dt_start)
    time.sleep(5) # 5秒待ち
    dt_stop = datetime.now(tz=tz_jst);  print(dt_stop)
    
    td_diff = dt_stop - dt_start # 戻り値は timedeltaオブジェクト
    print(td_diff)
    
    >test.py
    2026-08-13 09:12:20.446030+09:00
    2026-08-13 09:12:25.452396+09:00
    0:00:05.006366
    
  • awareならば、タイムゾーンも考慮されます。
    from datetime import datetime
    from zoneinfo import ZoneInfo
    
    dt_jst = datetime.now(tz=ZoneInfo('Asia/Tokyo')) # aware
    dt_utc = dt_jst.astimezone(tz=ZoneInfo('UTC'))   # タイムゾーン変更
    
    print(dt_jst)
    print(dt_utc)
    
    td_diff = dt_utc - dt_jst
    print(td_diff)
    
    >test.py
    2026-08-13 09:28:13.758421+09:00
    2026-08-13 00:28:13.758421+00:00
    0:00:00
    

datetimeとtimedelta間の演算

  • datetimeとtimedeltaは+と-の両方が演算できます。
    • 戻り値はdatetimeオブジェクトです。
    from datetime import datetime, timedelta
    from zoneinfo import ZoneInfo
    
    dt_now = datetime.now(tz=ZoneInfo('Asia/Tokyo'))
    dt_before = dt_now - timedelta(hours=10)
    dt_after  = dt_now + timedelta(hours=10)
    
    print(dt_before)
    print(dt_now)
    print(dt_after)
    
    >test.py
    2026-08-13 00:10:47.748068+09:00
    2026-08-13 10:10:47.748068+09:00
    2026-08-13 20:10:47.748068+09:00
    

何日? 何時間?

  • datetime同士の差分演算結果はtimedeltaであり、日数, 時:分:秒.マイクロ秒が戻ります。
    • ここでtimedelta同士は*や/演算も可能なため、これを利用して単位計算ができます。
    • 指定できる引数や演算の詳細については公式ドキュメントを参照下さい。
    from datetime import datetime, timedelta
    from zoneinfo import ZoneInfo
    
    dt_pre  = datetime(2026, 8, 13, 10, 10, 0, tzinfo=ZoneInfo('Asia/Tokyo'))
    dt_post = dt_pre + timedelta(weeks=10) # 10週間後
    
    td = dt_post - dt_pre # timedelta
    
    print(dt_pre)
    print(dt_post)
    print(td)
    print(td/timedelta(weeks=1)) # 週単位
    print(td/timedelta(days=1))  # 日数単位
    print(td/timedelta(hours=1)) # 時間単位
    
    >test.py
    2026-08-13 10:10:00+09:00
    2026-10-22 10:10:00+09:00
    70 days, 0:00:00
    10.0
    70.0
    1680.0
    

暦としての差分計算

  • timedeltaは週単位のweeksまでカバーしますが、月数や年数は持っていません。
    • 月数や年数は、月毎の日数、閏年等年毎の日数が変わるためです。
    • そのため、timedeltaでは各月末の計算等は非常に面倒です。
    • このような暦を意識した計算ではdateutil.relativedelta()を使用します。

  • dateutilモジュールは非標準のため、インストールが必要です。
    • ちなみにインストール時のパッケージ名はpython-dateutilです。間違えやすいので注意下さい。
    >pip install python-dateutil
    
  • それではdateutil.relativedelta()を使ってみましょう。各月の月末日を示します。
    from datetime import datetime
    from zoneinfo import ZoneInfo
    from dateutil.relativedelta import relativedelta
    
    dt = datetime(2028, 1, 31, tzinfo=ZoneInfo('Asia/Tokyo'))
    
    for i in range(0, 12):
        dt_i = dt + relativedelta(months=i) # 1〜12ヶ月後まで
        print(dt_i.strftime("%Y/%m/%d"))
    
    >test.py
    2028/01/31
    2028/02/29
    2028/03/31
    2028/04/30
    2028/05/31
    2028/06/30
    2028/07/31
    2028/08/31
    2028/09/30
    2028/10/31
    2028/11/30
    2028/12/31
    

Notes

  1. カタカナ英語のナイーブ。「持っていない」の意味。
  2. Linux, macOSはtzinfoをOSが持っています。Windowsは異なる形式なのでインストールが必要です。
Copyright(C) 2026 Altmo
本HPについて