DESIGN / CODE / INFRA / AI

解説

1か月後と30日後は同じ?AIへ日程を頼む前に月末のルールを決めよう

1月31日の1か月後は何日?存在しない日を月末へ収める架空ルールをPythonで検算し、日数の加算・繰り返し・月末固定を分ける。

1か月後と30日後は同じ?AIへ日程を頼む前に月末のルールを決めよう
保存リストを見る →
この記事の道しるべ 4項目

AIに「1月31日の1か月後へ予定を移して」と頼むとき、何月何日になる想定かな。毎月の同じ日を目指すことと、30日を足すことは別だよ。さらに、翌月に31日がない場合の決め方も必要になる。

この記事では、日付の一覧を作る前に月単位のルールを固定する。題材は架空の日程。契約の満了日や請求日を判定する仕組みではなく、Python 3の標準ライブラリで小さく検算する練習だよ。

日数と、暦の月を分ける

2026年1月31日へ30日を足すと3月2日。一方、「翌月の同じ日を目指し、その日がなければ翌月の末日」と決めれば2月28日になる。どちらか一方だけが、すべての場面で正しいわけではないんだ。

Pythonのtimedeltaは日・秒などの時間差を表し、月という引数はない。月の日数は一定ではないため、timedelta(days=30)を「1か月」と読み替えないようにしよう。

元の日 今回決めた1か月後 30日後
2026-01-31 2026-02-28 2026-03-02
2028-01-31 2028-02-29 2028-03-01

うるう年の2028年は2月が29日まで。日付の文字を置き換えるだけでは、存在しない2月31日を作ってしまうね。

今回のルールを関数にする

ここでは「移動先の年月を決め、元の日と移動先の末日の小さい方を使う」と定める。これは今回の検算用ルールで、Pythonやすべてのカレンダーサービスが自動で同じ処理をするという意味ではないよ。

from datetime import date, timedelta
from calendar import monthrange

def add_months_clamped(day, months):
    year, zero_based_month = divmod(
        day.year * 12 + day.month - 1 + months, 12
    )
    month = zero_based_month + 1
    last_day = monthrange(year, month)[1]
    return date(year, month, min(day.day, last_day))

start = date(2026, 1, 31)
print(add_months_clamped(start, 1))
print(start + timedelta(days=30))

表示は順に2026-02-28、2026-03-02。monthrangeの二つ目の返り値は、その年月の日数だよ。時刻やタイムゾーンのないdateを使っているので、「現地時間で何時に実行するか」はこの計算の対象外になる。

一度に2か月と、1か月を2回は違う

月末へ収めると、元が31日だった情報は結果の日付から失われる。上のコードに続けて、次も実行してみよう。

print(add_months_clamped(start, 2))
print(add_months_clamped(add_months_clamped(start, 1), 1))

最初は2026-03-31、次は2026-03-28。後者は一度2月28日へ移ってから、その28日を基準にしているんだ。繰り返し予定を31日へ戻したいなら、毎回の結果へ足すのではなく、最初の日付と何か月後かを保持する方法を考えよう。

「常に月末」も別ルール。4月30日の翌月をこの関数で計算すると5月30日であり、5月31日にはならない。毎月最終日という指定なら、移動先の末日を直接選ぶ必要があるね。逆方向へ戻す操作でも、月末へ収めた後に元の日を必ず復元できるわけではないよ。

AIへの依頼に、判断条件を残す

架空の日付一覧を整理してください。
「nか月後」は元の年月からnか月先を求めます。
同じ日が存在しなければ、その月の末日を使います。
各回は最初の日付を基準にし、直前の結果へ加算しません。
元の日付・n・結果・適用したルールを表に残してください。
不明な契約上の期限や休日調整は推測しないでください。

これは依頼文の例であり、AIを実行して得た結果ではないよ。結果は1月31日、うるう年、年またぎ、1か月後と2か月後を検算しよう。今回のコードはdatetime.dateが扱う年の範囲内で使い、入力の形式検査や契約条件の判定は別に行う。

編集部では2026年11月1日に公式資料を確認し、掲載コードと月末・うるう年・年またぎの例をPython 3.12.14で検算した。実アカウントの予定や契約は変更していない。営業日の起算日と休日、日付形式のあいまいさも別々に確認してみよう。

参考・出典

公開と変更の記録
  • :初回公開

日時は日本時間です。記載のある変更は、確認できた編集記録に基づきます。更新日の違いだけから変更内容を推測して載せることはありません。

更新・訂正の方針 →

みんなのコメント 0

感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。

最初のコメントをどうぞ。