日付に日数を足すには
日付に日数を足したり引いたりして、月末や年末をまたいだ日付を求める基本形を各言語で示す。
元の値を書き換える言語と新しい値を返す言語の違い、月を足すときの月末の扱い、夏時間で「24 時間後」と「1 日後」がずれる点までを扱う。
公開:
各言語見出しの横のバッジは検証状態を表す。実行確認済みはコードを実際に実行して確認したもの、静的確認は構文と公式 API ドキュメントで確認したものである。
Python 実行確認済み
from datetime import date, datetime, timedelta
d = date(2026, 8, 28)
print(d + timedelta(days=30)) # -> 2026-09-27(月末を超えた分は翌月へ繰り上がる)
print(d - timedelta(days=60)) # -> 2026-06-29
print(d) # -> 2026-08-28(元の値は変わらない)
t = datetime(2026, 8, 28, 21, 30)
print(t + timedelta(days=1, hours=6)) # -> 2026-08-30 03:30:00date や datetime に timedelta を足すと、月末や年末をまたいだ繰り上がりを含めた新しい値が返る。
引くときは timedelta を引けばよく、元の値は変わらない。
timedelta には月や年の単位が無いため、「1 か月後」は標準ライブラリの timedelta だけでは表せない点に注意する。
JavaScript 実行確認済み
const d = new Date(Date.UTC(2026, 7, 28)); // 月は 0 始まり(7 = 8 月)
const later = new Date(d); // setUTCDate は元の Date を書き換えるので複製する
later.setUTCDate(later.getUTCDate() + 30); // 月末を超えた分は翌月へ繰り上がる
console.log(later.toISOString().slice(0, 10)); // -> 2026-09-27
const before = new Date(d);
before.setUTCDate(before.getUTCDate() - 60);
console.log(before.toISOString().slice(0, 10)); // -> 2026-06-29
console.log(d.toISOString().slice(0, 10)); // -> 2026-08-28Date に日数を足す専用のメソッドは無く、setUTCDate(getUTCDate() + n) で日の値を増やす。
範囲を超えた分は翌月へ繰り上がる。
setUTCDate は呼び出した Date 自体を書き換えるので、元の日付を残したいときは new Date(d) で複製してから操作する。
TypeScript 実行確認済み
function addDays(base: Date, days: number): Date {
const result = new Date(base.getTime()); // 引数の Date を書き換えない
result.setUTCDate(result.getUTCDate() + days);
return result;
}
const d = new Date("2026-08-28T00:00:00Z");
console.log(addDays(d, 30).toISOString().slice(0, 10)); // -> 2026-09-27
console.log(addDays(d, -60).toISOString().slice(0, 10)); // -> 2026-06-29
console.log(d.toISOString().slice(0, 10)); // -> 2026-08-28計算は JavaScript と同じだが、引数で受け取った Date を直接書き換えると呼び出し元の値まで変わる。
関数の中で getTime() から新しい Date を作ってから setUTCDate を呼び、戻り値として返す形にしておくと副作用が残らない。
Go 実行確認済み
package main
import (
"fmt"
"time"
)
func main() {
d := time.Date(2026, time.August, 28, 0, 0, 0, 0, time.UTC)
fmt.Println(d.AddDate(0, 0, 30).Format("2006-01-02")) // -> 2026-09-27
fmt.Println(d.AddDate(0, 0, -60).Format("2006-01-02")) // -> 2026-06-29
end := time.Date(2026, time.January, 31, 0, 0, 0, 0, time.UTC)
fmt.Println(end.AddDate(0, 1, 0).Format("2006-01-02")) // -> 2026-03-03(2 月 31 日を正規化した結果)
}time.Time の AddDate(年, 月, 日) に日数を渡すと新しい time.Time が返る。
月末を超える日付は正規化されるため、1 月 31 日に 1 か月を足すと 2 月 31 日ではなく 3 月 3 日になる。
月の加算で月末に揃えたい場合は自分で調整する必要がある。
Rust 実行確認済み
use chrono::{Days, Months, NaiveDate};
fn main() {
let d = NaiveDate::from_ymd_opt(2026, 8, 28).unwrap();
println!("{}", d.checked_add_days(Days::new(30)).unwrap()); // -> 2026-09-27
println!("{}", d.checked_sub_days(Days::new(60)).unwrap()); // -> 2026-06-29
let end = NaiveDate::from_ymd_opt(2026, 1, 31).unwrap();
println!("{}", end.checked_add_months(Months::new(1)).unwrap()); // -> 2026-02-28(月末に丸める)
}標準ライブラリには暦の日付型が無いため chrono を使う。
NaiveDate の checked_add_days と checked_sub_days は結果が範囲外になると None を返すので Option で受け取る。
checked_add_months は存在しない日付を月末に丸めるため、1 月 31 日の 1 か月後は 2 月 28 日になる。
Java 実行確認済み
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
LocalDate d = LocalDate.of(2026, 8, 28);
d.plusDays(30); // 戻り値を捨てると何も起きない(LocalDate は不変)
System.out.println(d); // -> 2026-08-28
System.out.println(d.plusDays(30)); // -> 2026-09-27
System.out.println(d.minusDays(60)); // -> 2026-06-29
LocalDate end = LocalDate.of(2026, 1, 31);
System.out.println(end.plusMonths(1)); // -> 2026-02-28(月末に丸める)
}
}LocalDate は不変オブジェクトで、plusDays や minusDays は新しい LocalDate を返す。
戻り値を受け取らずに d.plusDays(30); と書いても d は変わらないため、結果は必ず変数に代入する。
plusMonths は存在しない日付を月末に丸める。
C# 実行確認済み
var d = new DateTime(2026, 8, 28);
d.AddDays(30); // 戻り値を捨てると d は変わらない
Console.WriteLine(d.ToString("yyyy-MM-dd")); // -> 2026-08-28
Console.WriteLine(d.AddDays(30).ToString("yyyy-MM-dd")); // -> 2026-09-27
Console.WriteLine(d.AddDays(-60).ToString("yyyy-MM-dd")); // -> 2026-06-29
var end = new DateTime(2026, 1, 31);
Console.WriteLine(end.AddMonths(1).ToString("yyyy-MM-dd")); // -> 2026-02-28(月末に丸める)DateTime は値型で、AddDays は新しい DateTime を返す。
戻り値を捨てると元の変数は変わらないので、Java と同じく結果を代入して使う。
負の数を渡せば日数を引ける。
AddMonths は存在しない日付を月末に丸めるため、1 月 31 日の 1 か月後は 2 月 28 日になる。
つまずき
元の値が変わるかどうかは言語で分かれる。
JavaScript の setUTCDate は呼び出した Date 自体を書き換える一方、Java の plusDays と C# の AddDays は新しい値を返すだけで元の変数は変わらない。
後者で戻り値を捨てると、コンパイルエラーも警告も出ないまま日付が進まない。
Python の timedelta の加算、Go の AddDate、Rust の checked_add_days も新しい値を返す。
月を足すと月末の扱いが言語で違う
1 月 31 日に 1 か月を足した結果は言語によって異なる。
Go の AddDate(0, 1, 0) と JavaScript の setUTCMonth は 2 月 31 日を正規化して 3 月 3 日にするのに対し、Java の plusMonths、C# の AddMonths、Rust の checked_add_months は 2 月 28 日に丸める。
Python の timedelta には月の単位が無い。
締め日や請求日のように月単位で進める処理では、どちらの挙動が必要かを先に決めておく。
24 時間後と 1 日後は同じとは限らない
夏時間のある地域のローカル時刻で計算すると、切り替え日をまたぐ「24 時間後」と「1 日後」がずれる。
America/New_York で 2026 年 3 月 7 日 12:00 に 24 時間を足すと翌日の 13:00 になるが、日付として 1 日を足すと 12:00 のままである。
Go の Add(24 * time.Hour) と AddDate(0, 0, 1)、JavaScript のミリ秒の加算と setDate の違いがこれにあたる。
上のコードが UTC で計算しているのは、この差を持ち込まないためである。