从 C++ 转 Go 后,当操作工夫变量的时候,Go 原生的 time 包用起来几乎不要太难受,再也不必本人写轮子了。我之前就写过一篇文章介绍了 time 的罕用用法。
不过在开发过程中其实也遇到 time 在 AddDate 的一个坑,因而撰此薄文分享一下。
问题发现
AddDate
有三个参数,别离是年、月、日。在官网文档中,对 time.AddDate
办法的阐明如下:
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.
简略翻译一下:
AddDate
依据指定的年、月、日数字,加到原来的time
类型值上并返回。比方对于 2011-1-1 这个日期,执行AddDate(-1, 2, 3)
会返回 2010-3-4AddDate
将它的后果按理论日期进行标准化,所以,比方在 10 月 31 日加上一个月,会返回 12 月 1 日,而不是 11 月 31 日。
上文解释的第二段就是坑所在:AddDate
函数中,year
参数等于 365 天,month
参数等于 30 天。实际上,在日常生活中,如果真有一个人在 10 月 31 日说:“下个月”(AddDate(0, 1, 0)
),大部分人会了解为 11 月 30 日,而不是官网例子给出的 12 月 1 日!
问题解决
其实问题的解决也不难,首先确立以下逻辑:
- 优先依照年、月、日的程序来调整日期
- 增减年份时,间接调整年份字段,不影响月和日
- 增减月份时,首先调整月份字段,如果日字段在调整后仍然非法,则不调整
- 如果增减之后的日期不非法(当月不存在本日),则间接将日改为当月的最初一天
- 残余的日数,则间接应用原生的 AddDate 逻辑计算即可。
好了,不必本人写轮子了,我曾经造好了这样的一个轮子,timeconv,实现了 AddDate
函数如下:
package main
import (
"fmt"
"time"
"github.com/Andrew-M-C/go.timeconv"
)
func main() {t := time.Date(2019, 1, 31, 0, 0, 0, 0, time.UTC) // 2019-01-31
nt := t.AddDate(0, 1, 0) // 后推一个月
fmt.Printf("%v\n", nt) // 2019-03-03 00:00:00 +0000 UTC, 不是咱们想要的
nt = timeconv.AddDate(t, 0, 1, 0)
fmt.Printf("%v\n", nt) // 2019-02-28 00:00:00 +0000 UTC,这就对了
}
本文章采纳 常识共享署名 - 非商业性应用 - 雷同形式共享 4.0 国内许可协定 进行许可。
原作者:amc,欢送转载,但请注明出处。
原文题目:《Go time 包中的 AddDate 的逻辑避坑指南》
公布日期:2021-03-24
本文链接:https://segmentfault.com/a/1190000039701631
原文链接:https://cloud.tencent.com/developer/article/1803695,也是自己的博客