0
0

More than 3 years have passed since last update.

Goで月末日の時は増加減した月の月末日を返す

Last updated at Posted at 2020-05-01

GoのtimeパッケージのAddDate()関数ですが、例えば10月31日に1ヶ月を足すと11月31日となりDateの正規化によって12月1日になります。

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Date(2019, 10, 31, 0, 0, 0, 0, time.Local)
    fmt.Println(t)
    fmt.Println(t.AddDate(0, 1, 0), "(Add 1 month)")
}

実行結果

2019-10-31 00:00:00 +0000 UTC
2019-12-01 00:00:00 +0000 UTC (Add 1 month)

そこで、月末日の時は増減した月の月末日、すなわち、10月31日に1ヶ月を足すと11月30日となる関数「AddMonth()」を考えてみました。(注意:横スクロールしてください)

package main

import (
    "fmt"
    "time"
)

// AddMonth returns the time corresponding to adding the
// given number of months to t.
// For example, AddMonth(t, 2) applied to January 1, 2011
// (= t) returns March 1, 2011.
//
// AddMonth does not normalize its result in the same way
// that Date does, so, for example, adding one month to
// October 31 yields November 30.
func AddMonth(t time.Time, months int) time.Time {
    lastMonthDay := func(t time.Time) int {
        return time.Date(t.Year(), t.Month()+1, 1, 0, 0, 0, 0, t.Location()).AddDate(0, 0, -1).Day()
    }

    // Creating 1st Date from t and adding months because AddDate() normalizes t.
    am := time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()).AddDate(0, months, 0)
    ad := lastMonthDay(am)

    if ld := lastMonthDay(t); t.Day() == ld || t.Day() > ad {
        return time.Date(am.Year(), am.Month(), ad, am.Hour(), am.Minute(), am.Second(), am.Nanosecond(), am.Location())
    }

    return t.AddDate(0, months, 0)
}

func main() {
    cnt := 14

    fmt.Println("First Days")
    ft := time.Date(2020, 2, 1, 0, 0, 0, 0, time.Local)
    for i := -cnt; i <= -1; i++ {
        fmt.Println(AddMonth(ft, i), "(Add", i, "month)")
    }
    fmt.Println(ft)
    for i := 1; i <= cnt; i++ {
        fmt.Println(AddMonth(ft, i), "(Add", i, "month)")
    }

    fmt.Println()

    fmt.Println("Last Days")
    lt := time.Date(2020, 2, 29, 0, 0, 0, 0, time.Local)
    for i := -cnt; i <= -1; i++ {
        fmt.Println(AddMonth(lt, i), "(Add", i, "month)")
    }
    fmt.Println(lt)
    for i := 1; i <= cnt; i++ {
        fmt.Println(AddMonth(lt, i), "(Add", i, "month)")
    }
}

実行結果

First Days
2018-12-01 00:00:00 +0000 UTC (Add -14 month)
2019-01-01 00:00:00 +0000 UTC (Add -13 month)
2019-02-01 00:00:00 +0000 UTC (Add -12 month)
2019-03-01 00:00:00 +0000 UTC (Add -11 month)
2019-04-01 00:00:00 +0000 UTC (Add -10 month)
2019-05-01 00:00:00 +0000 UTC (Add -9 month)
2019-06-01 00:00:00 +0000 UTC (Add -8 month)
2019-07-01 00:00:00 +0000 UTC (Add -7 month)
2019-08-01 00:00:00 +0000 UTC (Add -6 month)
2019-09-01 00:00:00 +0000 UTC (Add -5 month)
2019-10-01 00:00:00 +0000 UTC (Add -4 month)
2019-11-01 00:00:00 +0000 UTC (Add -3 month)
2019-12-01 00:00:00 +0000 UTC (Add -2 month)
2020-01-01 00:00:00 +0000 UTC (Add -1 month)
2020-02-01 00:00:00 +0000 UTC
2020-03-01 00:00:00 +0000 UTC (Add 1 month)
2020-04-01 00:00:00 +0000 UTC (Add 2 month)
2020-05-01 00:00:00 +0000 UTC (Add 3 month)
2020-06-01 00:00:00 +0000 UTC (Add 4 month)
2020-07-01 00:00:00 +0000 UTC (Add 5 month)
2020-08-01 00:00:00 +0000 UTC (Add 6 month)
2020-09-01 00:00:00 +0000 UTC (Add 7 month)
2020-10-01 00:00:00 +0000 UTC (Add 8 month)
2020-11-01 00:00:00 +0000 UTC (Add 9 month)
2020-12-01 00:00:00 +0000 UTC (Add 10 month)
2021-01-01 00:00:00 +0000 UTC (Add 11 month)
2021-02-01 00:00:00 +0000 UTC (Add 12 month)
2021-03-01 00:00:00 +0000 UTC (Add 13 month)
2021-04-01 00:00:00 +0000 UTC (Add 14 month)

Last Days
2018-12-31 00:00:00 +0000 UTC (Add -14 month)
2019-01-31 00:00:00 +0000 UTC (Add -13 month)
2019-02-28 00:00:00 +0000 UTC (Add -12 month)
2019-03-31 00:00:00 +0000 UTC (Add -11 month)
2019-04-30 00:00:00 +0000 UTC (Add -10 month)
2019-05-31 00:00:00 +0000 UTC (Add -9 month)
2019-06-30 00:00:00 +0000 UTC (Add -8 month)
2019-07-31 00:00:00 +0000 UTC (Add -7 month)
2019-08-31 00:00:00 +0000 UTC (Add -6 month)
2019-09-30 00:00:00 +0000 UTC (Add -5 month)
2019-10-31 00:00:00 +0000 UTC (Add -4 month)
2019-11-30 00:00:00 +0000 UTC (Add -3 month)
2019-12-31 00:00:00 +0000 UTC (Add -2 month)
2020-01-31 00:00:00 +0000 UTC (Add -1 month)
2020-02-29 00:00:00 +0000 UTC
2020-03-31 00:00:00 +0000 UTC (Add 1 month)
2020-04-30 00:00:00 +0000 UTC (Add 2 month)
2020-05-31 00:00:00 +0000 UTC (Add 3 month)
2020-06-30 00:00:00 +0000 UTC (Add 4 month)
2020-07-31 00:00:00 +0000 UTC (Add 5 month)
2020-08-31 00:00:00 +0000 UTC (Add 6 month)
2020-09-30 00:00:00 +0000 UTC (Add 7 month)
2020-10-31 00:00:00 +0000 UTC (Add 8 month)
2020-11-30 00:00:00 +0000 UTC (Add 9 month)
2020-12-31 00:00:00 +0000 UTC (Add 10 month)
2021-01-31 00:00:00 +0000 UTC (Add 11 month)
2021-02-28 00:00:00 +0000 UTC (Add 12 month)
2021-03-31 00:00:00 +0000 UTC (Add 13 month)
2021-04-30 00:00:00 +0000 UTC (Add 14 month)

月末日に関しては、12ヶ月の月末日の並びから

...,31|31,28&29,31,30,31,30,31,31,30,31,30,31|31,...

30日が連続することがないことを考慮して、以下のケースで動作確認しました。(基:基準日の月末日)

-1 基 +1
-- -- --
31,28,31
31,29,31
31,30,31
28,31,30
29,31,30
30,31,30
30,31,31
31,31,28
31,31,29
31,31,30

例)基準日が2019年2月28日で前後2ヶ月の月末日を出力するmain()関数内のコード。

    tl := time.Date(2019, 2, 28, 0, 0, 0, 0, time.Local)
    fmt.Println(AddMonth(tl, -2), "(Add -2 month)")
    fmt.Println(AddMonth(tl, -1), "(Add -1 month)")
    fmt.Println(tl)
    fmt.Println(AddMonth(tl, 1), "(Add 1 month)")
    fmt.Println(AddMonth(tl, 2), "(Add 2 month)")

2020.05.02 11月30日の時に-1ヶ月すると10月31日ではなく10月30日になっていた不具合を修正しました。

2020.05.03 2020年3月31日の時に-1ヶ月すると2月29日ではなく3月1日になっていた不具合を修正しました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0