概要
time.AddDate()で月末の月加算がずれることに気づいた際のメモ
下記の記事がとても参考になりました
https://tech.mirrativ.stream/entry/2023/10/31/120000
問題点
- time.AddDate()で月末の月加算がずれる
- 12月末 + 2ヶ月 = 2月末としたいが、3/2や3/3となる
func main() {
start := time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC)
twoMonthLater := start.AddDate(0, 2, 0)
fmt.Println(twoMonthLater)
}
// 出力: 2024-03-02 00:00:00 +0000 UTC
解決方法
- 独自に月を加算する関数を作成
// AddMonthsPreservingEndOfMonth は、指定された月数を日付 t に加算します。元の日付が月の末日付近であった場合、は日付の調整を行います。
// 標準の time.AddDate 関数では、月末のない月の処理が行えないためこの関数を作成しました。
// 例えば、1月31日に1ヶ月をtime.AddDateで加算すると、3月3日となりますが、
// この関数では加算後の月の末日(2月の最終日28日または29日)に調整します
//
// 引数:
// - t: 基準の日付
// - months: 加算する月数
//
// 返り値:
// - time.Time: 月を加算した後の新しい日付
func AddMonthsPreservingEndOfMonth(t time.Time, months int) time.Time {
// 基準日の日付部分を取得
day := t.Day()
// 加算後の日付の最終日を取得
year, month, dayLimit := t.AddDate(0, months+1, -day).Date()
// 基準日が月末付近だった際の日付の調整
// 左辺は基準日の日付が30日や31日で加算後の月に30日や31日が存在しない場合に対応するため
// 右辺は基準日の月の月末が30日で加算後の月の月末が31日の場合に対応するため
if day > dayLimit || t.Month() != t.AddDate(0, 0, 1).Month() {
day = dayLimit
}
h, m, s := t.Clock()
return time.Date(year, month, day, h, m, s, t.Nanosecond(), t.Location())
}
func main() {
start := time.Date(2023, 12, 31, 0, 0, 0, 0, time.UTC)
twoMonthLater := AddMonthsPreservingEndOfMonth(start, 2)
fmt.Println(twoMonthLater)
}
// 出力: 2024-02-29 00:00:00 +0000 UTC