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: inconsistent result for Date when date is created during DST #50223

Closed
sbiemont opened this issue Dec 16, 2021 · 5 comments
Closed

time: inconsistent result for Date when date is created during DST #50223

sbiemont opened this issue Dec 16, 2021 · 5 comments
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Milestone

Comments

@sbiemont
Copy link

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

go version go1.17 linux/amd64

Does this issue reproduce with the latest release?

Yes

What did you do?

Inconsistent time creation on DST (a similar problem is reported in issue #41272)

For local time that does not exist, and for different DST, time is not created the same way:

  • In Paris, the 28th march 2021, 02:00 (+0100) becomes 03:00 (+0200)
    • Try to create time at 02:00 => created at 03:00 (ok)
  • In Santiago, the 5th sept 2021, 00:00 (-0400) becomes 01:00 (-0300)
    • Try to create time at 00:00 => created at 23:00 the 4th (should intuitively be 01:00 the 5th)

The documentation is explicit (but not helpful)

In such cases, the choice of time zone, and therefore the time, is not well-defined.
Date returns a time that is correct in one of the two zones involved in the transition, but it does not guarantee which. 

It seems (without any deep investigation) that the creation of a "skipped" time:

  • is pushed forward (eg.: 02:00 => 03:00) for UTC +xxxx
  • is pushed backward (eg.: 02:00 => 01:00) for UTC -xxxx

Example, see go-playground

func print(city, info string, d time.Time) {
	fmt.Printf("%-10s: %s => %s\n", city, info, d)
}

func main() {
	santiago, _ := time.LoadLocation("America/Santiago")
	newyork, _ := time.LoadLocation("America/New_York")
	anchorage, _ := time.LoadLocation("America/Anchorage")
	paris, _ := time.LoadLocation("Europe/Paris")
	sydney, _ := time.LoadLocation("Australia/Sydney")

	print("santiago", "2021-09-05 00:00", time.Date(2021, 9, 5, 0, 0, 0, 0, santiago))
	print("new-york", "2021-03-14 02:00", time.Date(2021, 3, 14, 2, 0, 0, 0, newyork))
	print("anchorage", "2021-03-14 02:00", time.Date(2021, 3, 14, 2, 0, 0, 0, anchorage))
	print("paris", "2021-09-28 02:00", time.Date(2021, 3, 28, 2, 0, 0, 0, paris))
	print("sydney", "2021-10-03 02:00", time.Date(2021, 10, 3, 2, 0, 0, 0, sydney))
}

// output :
// santiago  : 2021-09-05 00:00 => 2021-09-04 23:00:00 -0400 -04
// new-york  : 2021-03-14 02:00 => 2021-03-14 01:00:00 -0500 EST
// anchorage : 2021-03-14 02:00 => 2021-03-14 01:00:00 -0900 AKST
// paris     : 2021-09-28 02:00 => 2021-03-28 03:00:00 +0200 CEST
// sydney    : 2021-10-03 02:00 => 2021-10-03 03:00:00 +1100 AEDT

What did you expect to see?

I expected a similar creation for all times that are created in a DST

Or, at least, a deterministic attribute / value / algo to predict if the conversion will lead before or after DST

What did you see instead?

I see what the documentation explains : an un-guaranteed result which leads to unexpected behaviors when a date is created in a DST

@cherrymui cherrymui changed the title time.Date() explainations required when date is created during DST time: inconsistent result for Date when date is created during DST Dec 16, 2021
@cherrymui
Copy link
Member

As the documentation said, "it does not guarantee which." So this may be working as intended. Thanks.

cc @rsc @ianlancetaylor for if there is anything that could be improved.

@cherrymui cherrymui added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Dec 16, 2021
@cherrymui cherrymui added this to the Backlog milestone Dec 16, 2021
@ianlancetaylor
Copy link
Contributor

Thanks for the bug report, but the function is behaving as documented and intended.

@alchimere
Copy link

alchimere commented Jan 20, 2022

@ianlancetaylor

Thanks for the answer, but could you please be more specific ?

I agree it's documented this way but I don't understand how this behavior could be intended.

Without fixing this bug how could we deal to get the current day (for example) for a given timestamp ?

I think you will agree the following code produces a wrong result, right ?

santiago, _ := time.LoadLocation("America/Santiago")
t := time.Date(2021, 9, 5, 0, 0, 0, 0, santiago)

fmt.Printf("day in santiago: %d (expecting 5)\n", t.Day()) // day in santiago: 4 (expecting 5), this is truely problematic

What could be the reason(s) why this behavior would be intended please ?

@ianlancetaylor
Copy link
Contributor

You are calling time.Date with a time that does not exist. In the America/Santiago timezone when the clock reached the time "2021-09-05 00:00:00" it immediately skipped forward to "2021-09-05 01:00:00". There is no time "2021-09-05 00:00:00" in America/Santiago. So when you call time.Date with that time and location, you can't possible get that time back. When time.Date is asked to produce a time that doesn't exist, it has to do something. For this example, it produces "2021-09-04 23:00:00". You presumably would prefer "2021-09-05 01:00:00". But both choices are equally valid. There is no obvious reason to prefer one or the other.

You presumably do have a reason to prefer one or the other, and now that you are aware of the problem you can check for it in your code and make your code do the right thing for your use case. But that doesn't mean that there is an obviously correct answer for all uses for time.Date.

(I'll note that it's unfortunate that Chile has chosen to do time zone transitions at midnight. Most countries do time zone transitions at 2am to somewhat reduce, though certainly not eliminate, this class of problems.)

@alchimere
Copy link

Thank you for taking time to respond (in whatever timezone you are), it's crystal clear now.

The Chile's choice is indeed very infortunate..

I think we can find a satisfying-ish workaround since the following code works as expected:

time.Date(2021, 9, 4, 23, 0, 0, 0, santiago).Add(time.Hour)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Projects
None yet
Development

No branches or pull requests

5 participants