Go version
go1.20.8
Output of go env
in your module/workspace:
GO111MODULE="on"
GOARCH="arm64"
GOBIN=""
GOCACHE="/Library/Caches/go-build"
GOENV="/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/go/pkg/mod"
GOOS="darwin"
GOPATH="/go"
GOPROXY="https://goproxy.cn,direct"
GOROOT="/go/go1.20.8"
GOSUMDB="off"
GOTMPDIR=""
GOTOOLDIR="/go/go1.20.8/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.20.8"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
What did you do?
There is a bug in the time library when adding or subtracting months. When the time is 15:00:00 on July 31, 2025 and 15:00:00 on August 31, 2025, the output is July and October when t. AddDate (0, -1,0) and t. AddDate (0, 1,0) are executed respectively
What did you see happen?
package main
import ( "fmt" "time" )
func main() { t, _ := time.ParseInLocation("2006-01-02 15:04:05", "2025-07-31 15:00:00", time.Local) fmt.Println(t.AddDate(0, -1, 0).String()) // 2025-07-01 15:00:00 +0800 t, _ = time.ParseInLocation("2006-01-02 15:04:05", "2025-08-31 15:00:00", time.Local) fmt.Println(t.AddDate(0, 1, 0).String()) // 2025-10-01 15:00:00 +0800 }
What did you expect to see?
2025-06-30 15:00:00 +0800 and 2025-09-30 15:00:00 +0800
Comment From: gabyhelp
Related Issues
- golang `time.AddDate` operation incorrect #65397 (closed)
- affected/package: time #63843 (closed)
- time AddDate() Calculate time last month #71069 (closed)
- time: Wrong `time.AddDate` calculation if months' last date is 31 #57139 (closed)
- time.AddDate() can't get Feb #31130 (closed)
- time: time ParseInLocation error #66987 (closed)
- time: time.ParseInLocation can get the seconds wrong #71444 (closed)
- time: AddDate with negative day changed behavior #68718 (closed)
- time: i find some problems when using addDate to do date calculation #66374 (closed)
- time: ParseInLocation error #49284 (closed)
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: adonovan
The problem is that AddDate(+/- 1 month) simply increments/decrements the month column then normalizes, so:
- 31 Jul - 1 month = "31 Jun" = 1 Jul
- 31 Aug + 1 month = "31 Sep" = 1 Oct
Months are not of the same length, so there will always be an edge case trying to map from the last day to "the same day" of the next or previous month. This is a dup of several previous issues, as enumerated by gabyhelp (e.g. https://github.com/golang/go/issues/31145). We cannot change the behavior of AddDate at this point, even if we wanted to (though the previous issues suggest we do not). But this is a recurring source of surprise that should be better documented, and if there is one clear alternative algorithm that many users expect, we should provide it and refer to it.
Comment From: ALTree
It seems to be documented?
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.
Comment From: adonovan
It is documented, yet users are perennially confused enough to report issues. Perhaps it should provide more advice on how to safely work with month strides.
Comment From: ianlancetaylor
The existing documentation seems very clear to me. It explicitly says
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.
which is the exact case that the original poster is misunderstanding. Is there really anything we can do to make this more clear?
Comment From: seankhliao
I think this is more of a case where we provided an API which we shouldn't have, and users don't read the docs before trying to use something.
Comment From: adonovan
I wonder what behavior @Listen-Y expected, and whether there is some other expression (or missing function) that AddDate's doc comment could recommend instead in that case.
Comment From: andig
@adonovan I think the answer to expected behavior is clear: first of month should become first of month for following or previous month. The current API doesn't provide this although AddDate's signature seems to suggest it does. As such, the behavior between adding days and adding months is inconsistent.
Comment From: ALTree
@andig
first of month should become
But that's not the issue in OP's code. The question here is: what does it mean to add "1 month" to a Date on the 31st, if the following month only has 30 days?
You can't have "August 31st" + "1 month" = "Sept 31st" because September 31st does not exist.
Comment From: doggedOwl
The documentation is clear and the behavior is consistent however it is clear that what many need is an AddDateWithClip that does not normalize the date but keeps the month fixed and clips the date to end of that month. Needing a stride of exactly one month (not a fixed number of days) is a common enough problem in scheduling that needs to have it's dedicated method.
Comment From: gopherbot
Timed out in state WaitingForInfo. Closing.
(I am just a bot, though. Please speak up if this is a mistake or you have the requested information.)