Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

time: TZ/DST AddDate() problem - Two consecutive days with 23h duration #41272

Closed
marko-gacesa opened this issue Sep 8, 2020 · 3 comments
Closed

Comments

@marko-gacesa
Copy link

@marko-gacesa marko-gacesa commented Sep 8, 2020

What version of Go are you using (go version)?

go version go1.15.1 linux/amd64

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/marko/.cache/go-build"
GOENV="/home/marko/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/marko/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/marko/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build275905408=/tmp/go-build -gno-record-gcc-switches"

What did you do?

func main() {
	loc, _ := time.LoadLocation("America/Santiago")
	var t0, t1 time.Time
	t0 = time.Date(2020, 9, 3, 0, 0, 0, 0, loc)
	for i := 0; i < 5; i++ {
		t1 = t0.AddDate(0, 0, 1) // Note that I'm always adding 1 day
		y, m, d := t1.Date()
		fmt.Printf("midnight=[%v]  day duration=%v  date=%02d-%02d-%02d\n", t1, t1.Sub(t0), y, m, d)
		t0 = t1
	}
}

https://play.golang.org/p/F0pGumkcKVb

What did you expect to see?

All days except one are 24h. The day on which DST is introduced should be 23h long.

What did you see instead?

In the loop I'm always adding one day. But Chile started using DST on 6 September (at midnight!). And weirdly there are two days that are 23h long.

midnight=[2020-09-04 00:00:00 -0400 -04]  day duration=24h0m0s  date=2020-09-04
midnight=[2020-09-05 00:00:00 -0400 -04]  day duration=24h0m0s  date=2020-09-05
midnight=[2020-09-05 23:00:00 -0400 -04]  day duration=23h0m0s  date=2020-09-05
midnight=[2020-09-06 23:00:00 -0300 -03]  day duration=23h0m0s  date=2020-09-06
midnight=[2020-09-07 23:00:00 -0300 -03]  day duration=24h0m0s  date=2020-09-07

The root of the problem is probably that time.Date(2020, 9, 6, 0, 0, 0, 0, loc@"America/Santiago") returns 2020-09-05 23:00:00 -04 rather than 2020-09-06 00:00:00 -03.

@marko-gacesa marko-gacesa changed the title TZ/DST problem - Two consecutive days with 23h duration TZ/DST AddDate() problem - Two consecutive days with 23h duration Sep 8, 2020
@marko-gacesa
Copy link
Author

@marko-gacesa marko-gacesa commented Sep 8, 2020

Maybe this illustrates the problem better:

func main() {
	loc, _ := time.LoadLocation("America/Santiago")

	t0 := time.Date(2020, 9, 5, 0, 0, 0, 0, loc)

	t1 := t0.AddDate(0, 0, 1)
	fmt.Printf("midnight=[%v]  day duration=%v\n", t1, t1.Sub(t0))

	t11 := t1.AddDate(0, 0, 1)
	fmt.Printf("midnight=[%v]  day duration=%v\n", t11, t11.Sub(t0))

	t2 := t0.AddDate(0, 0, 2)
	fmt.Printf("midnight=[%v]  day duration=%v\n", t2, t2.Sub(t0))
}

Adding a day twice does not produce the same result as adding two days.

The result:

midnight=[2020-09-05 23:00:00 -0400 -04]  day duration=23h0m0s
midnight=[2020-09-06 23:00:00 -0300 -03]  day duration=46h0m0s
midnight=[2020-09-07 00:00:00 -0300 -03]  day duration=47h0m0s
@andybons andybons changed the title TZ/DST AddDate() problem - Two consecutive days with 23h duration time: TZ/DST AddDate() problem - Two consecutive days with 23h duration Sep 8, 2020
@andybons andybons added this to the Unplanned milestone Sep 8, 2020
@andybons
Copy link
Member

@andybons andybons commented Sep 8, 2020

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Sep 8, 2020

The result is surprising but this is working as documented. time.Time.AddDate will adjust the time, and then, as the documentation says, normalize the result just as is done by time.Date.

In your example we add one day to 2020-09-05 midnight. That gives us 2020-09-06 midnight. But in Chile there is no such time. There is 2020-09-06 23:59:59 -0400. And there is 2020-09-06 01:00:00 -0300. But there is no midnight. So we have to normalize. There are two plausible choices: 2020-09-05 23:00:00 -0400 and 2020-09-06 01:00:00 -0300. As the documentation for time.Date says, in this ambiguous cases we make no guarantee as to which one we select. In this case we happen to choose 2020-09-05 23:00:00, and the timezone for that has to be -0400. And that is the first 23 hour day.

Then the code adds 1 day to that. There result is unambiguous: adding one day to a time at 23:00:00, gives the next day at 23:00:00. In this case the range between those two times includes the skipped hour between midnight and 1am. So that is the second 23 hour day.

So although the results are confusing, there really is no bug. Unfortunately, at a daylight savings time transition, you need to figure out what you want to do. For example, instead of time.AddDate(0, 0, 1), you could write time.Add(24 * time.Hour). Then the difference would always be 24 hours, but your times would start being at 1am instead of midnight. You'll have to decide what is right for your situation.

Closing the issue because the code is working as documented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.