從 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,這就對了
}