« ^ »

1ヶ月毎の期間をPythonで取得する

所要時間: 約 5分

指定した期間の日付の開始日時と終了日時を取得したいとする。例えば、2024年01月01日00時00分00秒から1ヶ月毎の、開始日時と終了日時をペアにして取得したい。ただし、終了時間をミリ秒単位で完全に終了時間を得るのは難しいため、次の期間の開始日時を終了日時とする。つまり次のような情報を取得したい。

(
   (2024年01月01日00時00分00秒, 2024年02月01日00時00分00秒),
   (2024年02月01日00時00分00秒, 2024年03月01日00時00分00秒),
   (2024年03月01日00時00分00秒, 2024年04月01日00時00分00秒),
   (2024年04月01日00時00分00秒, 2024年05月01日00時00分00秒),
   (2024年05月01日00時00分00秒, 2024年06月01日00時00分00秒),
   (2024年06月01日00時00分00秒, 2024年07月01日00時00分00秒),
   (2024年07月01日00時00分00秒, 2024年08月01日00時00分00秒),
   (2024年08月01日00時00分00秒, 2024年09月01日00時00分00秒),
   (2024年09月01日00時00分00秒, 2024年10月01日00時00分00秒),
   (2024年10月01日00時00分00秒, 2024年11月01日00時00分00秒),
   (2024年11月01日00時00分00秒, 2024年12月01日00時00分00秒),
   (2024年12月01日00時00分00秒, 2025年01月01日00時00分00秒),
)

まずは、日時オブジェクトを作成する。日時オブジェクトを作成するには datetime.datetime() を使う。

import datetime

return datetime.datetime(2024, 1, 1)
2024-01-01 00:00:00

指定した日付の00時00分00秒の日時オブジェクトを作成できる。ただし、時刻を特定するにはタイムゾーン情報が必要となる。

時刻情報は、その時刻情報の中にタイムゾーンの情報が含まれているかどうかで分ける事ができる。Pythonではタイムゾーン情報が含まれていない時刻情報を native 、タイムゾーン情報が含まれている時刻情報を aware と表現する。

呼び方タイムゾーン情報完全な時刻の特定
naiveなしできない
awareありできる

Python3.9からはzoneinfoライブラリが標準で梱包されている。それまではタイムゾーンの取り扱いはpytzをインストールして使用していたが、その必要がなくなった。例えばJSTのタイムゾーン情報を取得したければ以下のようになる。

import zoneinfo

return zoneinfo.ZoneInfo("Asia/Tokyo")
Asia/Tokyo
JSTのタイムゾーン情報を生成する例

このZoneInfoを使い、awareな日時オブジェクトを作成する。

import datetime, zoneinfo

return datetime.datetime(2024, 1, 1, tzinfo=zoneinfo.ZoneInfo("Asia/Tokyo"))
2024-01-01 00:00:00+09:00

これらを組み合わせて、以下の状態を取得できれば良い。

import zoneinfo
from datetime import datetime

tz = zoneinfo.ZoneInfo("Asia/Tokyo")

return (
  (datetime(2024, 1, 1, tzinfo=tz), datetime(2024, 2, 1, tzinfo=tz)),
  (datetime(2024, 2, 1, tzinfo=tz), datetime(2024, 3, 1, tzinfo=tz)),
  (datetime(2024, 3, 1, tzinfo=tz), datetime(2024, 4, 1, tzinfo=tz)),
  (datetime(2024, 4, 1, tzinfo=tz), datetime(2024, 5, 1, tzinfo=tz)),
  (datetime(2024, 5, 1, tzinfo=tz), datetime(2024, 6, 1, tzinfo=tz)),
  (datetime(2024, 6, 1, tzinfo=tz), datetime(2024, 7, 1, tzinfo=tz)),
  (datetime(2024, 7, 1, tzinfo=tz), datetime(2024, 8, 1, tzinfo=tz)),
  (datetime(2024, 8, 1, tzinfo=tz), datetime(2024, 9, 1, tzinfo=tz)),
  (datetime(2024, 9, 1, tzinfo=tz), datetime(2024,10, 1, tzinfo=tz)),
  (datetime(2024,10, 1, tzinfo=tz), datetime(2024,11, 1, tzinfo=tz)),
  (datetime(2024,11, 1, tzinfo=tz), datetime(2024,12, 1, tzinfo=tz)),
  (datetime(2024,12, 1, tzinfo=tz), datetime(2025, 1, 1, tzinfo=tz)),
)
((datetime.datetime(2024, 1, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 2, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 2, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 3, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 3, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 4, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 4, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 5, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 5, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 6, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 6, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 7, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 7, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 8, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 8, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 9, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 9, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 10, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 10, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 11, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 11, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 12, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 12, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2025, 1, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))))

日時情報は全て1ヶ月ずつずれていく。基準の日時オブジェクトを元に月数を加算して日時をずらすように変更する。Pythonの標準ライブラリでそれを実装するには工夫が必要になるけれど、サードパーティライブラリの python-dateutil を使うと非常に簡単に実装できる。

pip install python-dateutil

python-dateutil を使い1ヶ月ずつずらす。

import zoneinfo
from datetime import datetime
from dateutil.relativedelta import relativedelta

tz = zoneinfo.ZoneInfo("Asia/Tokyo")
base_dt = datetime(2024, 1, 1, tzinfo=tz)

return (
  (base_dt + relativedelta(months= 0), base_dt + relativedelta(months= 1)),
  (base_dt + relativedelta(months= 1), base_dt + relativedelta(months= 2)),
  (base_dt + relativedelta(months= 2), base_dt + relativedelta(months= 3)),
  (base_dt + relativedelta(months= 3), base_dt + relativedelta(months= 4)),
  (base_dt + relativedelta(months= 4), base_dt + relativedelta(months= 5)),
  (base_dt + relativedelta(months= 5), base_dt + relativedelta(months= 6)),
  (base_dt + relativedelta(months= 6), base_dt + relativedelta(months= 7)),
  (base_dt + relativedelta(months= 7), base_dt + relativedelta(months= 8)),
  (base_dt + relativedelta(months= 8), base_dt + relativedelta(months= 9)),
  (base_dt + relativedelta(months= 9), base_dt + relativedelta(months=10)),
  (base_dt + relativedelta(months=10), base_dt + relativedelta(months=11)),
  (base_dt + relativedelta(months=11), base_dt + relativedelta(months=12)),
)
((datetime.datetime(2024, 1, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 2, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 2, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 3, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 3, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 4, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 4, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 5, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 5, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 6, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 6, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 7, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 7, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 8, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 8, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 9, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 9, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 10, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 10, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 11, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 11, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 12, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 12, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2025, 1, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))))

itertools.count()で数列を返すジェネレータを作成し、その数字を月に当てはめて、期間の日付情報を取得する。

import itertools
import zoneinfo
from datetime import datetime
from dateutil.relativedelta import relativedelta

tz = zoneinfo.ZoneInfo("Asia/Tokyo")
base_dt = datetime(2024, 1, 1, tzinfo=tz)
counter = itertools.count()

def get_term():
    num = next(counter)

    start_delta = relativedelta(months=num)
    end_delta = relativedelta(months=num+1)

    return (base_dt+start_delta, base_dt+end_delta)

return (
    get_term(),
    get_term(),
    get_term(),
    get_term(),
    get_term(),
    get_term(),
    get_term(),
    get_term(),
    get_term(),
    get_term(),
    get_term(),
    get_term(),
)
((datetime.datetime(2024, 1, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 2, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 2, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 3, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 3, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 4, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 4, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 5, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 5, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 6, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 6, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 7, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 7, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 8, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 8, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 9, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 9, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 10, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 10, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 11, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 11, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2024, 12, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))), (datetime.datetime(2024, 12, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo')), datetime.datetime(2025, 1, 1, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='Asia/Tokyo'))))

良さそうだ。