在ruby中,我们如果想 得到某一时刻的 n 个月后,我们通常这样写:

t = Time.parse("2020-03-31 12:00:00")
t + 1.month # => 2020-04-30 12:00:00 +0800
# 或者
1.month.after(t) # => 2020-04-30 12:00:00 +0800

在golang 中使用 time包,想进行同样操作:

loc, _ := time.LoadLocation("Asia/Shanghai")
t := time.Date(2010, 3, 31, 12, 0, 0, 0, loc) # => 2020-03-31 12:00:00 +0800
newTime := t.AddDate(0, 1, 0) # => 2020-05-01 12:00:00 +0800
fmt.Println(newTime)
# 输出 2020-05-01 12:00:00 +0800

最后得出的时间是 2020-05-01 12:00:00 +0800, 和预期的不太一样,
是因为4月没有31号,所以会变成5月1号。

time包关于这个方法的源码:

// AddDate returns the time corresponding to adding the
// given number of years, months, and days to t.
// For example, AddDate(-1, 2, 3) applied to January 1, 2011
// returns March 4, 2010.
//
// AddDate normalizes its result in the same way that Date does,
// so, for example, adding one month to October 31 yields
// December 1, the normalized form for November 31.
func (t Time) AddDate(years int, months int, days int) Time {
    year, month, day := t.Date()
    hour, min, sec := t.Clock()
    return Date(year+years, month+Month(months), day+days, hour, min, sec, int(t.nsec()), t.Location())
}

注释中已经说明了此问题,只是我们使用时,并没有认真去考究,而是想当然的认为。


为了达到和ruby中类似的效果,我自己实现了一个,只能增减 年,月, 如果要天,还是一样会出现问题, 所以不考虑天的增减:

import (
    "github.com/jinzhu/now"
    "time"
)

// AddDate 时间增减
// 类似于ruby中的时间增减,和 time.AddDate 不同
// 如:
// loc, _ := time.LoadLocation("Asia/Shanghai")
// t := time.Date(2010, 3, 31, 12, 0, 0, 0, loc)
// utils.AddDate(t, 0, 1)
// => 2010-04-30 12:00:00 +0800 CST
// 不会因为4月没有31号,而变成5月1号
func AddDate(t time.Time, years int, months int) time.Time {
    year, month, day := t.Date()
    hour, min, sec := t.Clock()

    // firstDayOfMonthAfterAddDate: years 年,months 月后的 那个月份的1号
    firstDayOfMonthAfterAddDate := time.Date(year+years, month+time.Month(months), 1,
        hour, min, sec, t.Nanosecond(), t.Location())
    // firstDayOfMonthAfterAddDate 月份的最后一天
    lastDay := now.New(firstDayOfMonthAfterAddDate).EndOfMonth().Day()

    // 如果 t 的天 > lastDay,则设置为lastDay
    // 如:t 为 2020-03-31 12:00:00 +0800,增加1个月,为4月31号
    // 但是4月没有31号,则设置为4月最后一天lastDay(30号)
    if day > lastDay {
        day = lastDay
    }

    return time.Date(year+years, month+time.Month(months), day,
        hour, min, sec, t.Nanosecond(), t.Location())
}
0条评论 顺序楼层
请先登录再回复