为什么我在 Golang 的日期函数中得到错误的答案?

huangapple go评论61阅读模式
英文:

Why I am getting wrong answers in Date function in golang

问题

在下面的代码中,

  • t1 是从1970年1月1日之后62天的时间(yy/mm/dd)
  • t2 是从1970年1月1日之后63天的时间(yy/mm/dd)
package main

import (
    "fmt"
    "time"
)

func main() {

    t1 := time.Date(0, 0, 62, 0, 0, 0, 0, time.UTC).AddDate(1970, 1, 1)
    t2 := time.Date(0, 0, 63, 0, 0, 0, 0, time.UTC).AddDate(1970, 1, 1)

    fmt.Println("Time1:  ", t1)
    fmt.Println("Time2:  ", t2)
}

如果 t1 是:

Time1: 1970-03-04 00:00:00 +0000 UTC

我期望 t2 是:

Time2: 1970-03-05 00:00:00 +0000 UTC

但实际输出是:

Time2: 1970-03-02 00:00:00 +0000 UTC

这是什么原因呢?

英文:

In the following code,

  • t1 is time on 62 days after the date 1970/1/1 (yy/mm/dd)
  • t2 is time on 63 days after the date 1970/1/1 (yy/mm/dd)

package main

import (
    "fmt"
    "time"
)

func main() {

    t1 := time.Date(0, 0, 62, 0, 0, 0, 0, time.UTC).AddDate(1970, 1, 1)
    t2 := time.Date(0, 0, 63, 0, 0, 0, 0, time.UTC).AddDate(1970, 1, 1)

    fmt.Println("Time1:  ", t1)
    fmt.Println("Time2:  ", t2)
}

If t1 is:

Time1: 1970-03-04 00:00:00 +0000 UTC

I expect t2 to be:

Time2: 1970-03-05 00:00:00 +0000 UTC

But the output is:

Time2: 1970-03-02 00:00:00 +0000 UTC

What is the reason for this?

答案1

得分: 11

t1是在1970年1月1日之后62天的时间(yy/mm/dd),t2是在1970年1月1日之后63天的时间(yy/mm/dd)。

这是不正确的。t1是1970年1月1日之后的时间,无论time.Date(0, 0, 62, 0, 0, 0, 0, time.UTC)表示什么。

fmt.Println(time.Date(0, 0, 62, 0, 0, 0, 0, time.UTC))
fmt.Println(time.Date(0, 0, 63, 0, 0, 0, 0, time.UTC))

给出的结果是:

0000-01-31 00:00:00 +0000 UTC
0000-02-01 00:00:00 +0000 UTC

这是完全错误的。UTC在1972年之前的任何日期都没有定义,公历从1582年开始,也从来没有过0年。尽管忽略了这一切,我不明白一年的第63天怎么会被解释为1月31日,但我们还是继续吧。

让我们给第一个时间戳添加一些内容:加上1970年,我们得到1970-01-31。再加一个月,我们得到1970-02-31。但是1970-02-31不是一个有效的日期。所以它被规范化为3月3日。1970年不是闰年,2月有28天,所以2月29日是3月1日,2月30日是3月2日,2月31日是3月3日。在1970-03-03上再加一天,我们得到1970-03-04。

第二个时间戳已经解析为2月1日。再加一个月,我们得到3月1日,再加一天,我们得到3月2日。

这就是在时间戳上添加月份时会发生的情况。一个月的持续时间并不是非常明确。所以库会试图为你做一些聪明的事情,这就导致了意外的结果。

顺便说一句,由于某种原因:fmt.Println(time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC))被解释为-0001-11-30 00:00:00 +0000 UTC。不知道为什么。这并不重要,因为0年和0月并不存在。但这解释了为什么之前的时间戳最终会在1月31日和2月1日。

没有理由让AddDate按照这个顺序添加内容。据我所见,这并没有在文档中记录。它也可以先添加天数,然后是月份,最后是年份。试着运行这个:

fmt.Println(time.Date(2015, 1, 31, 0, 0, 0, 0, time.UTC).AddDate(1, 0, 0).AddDate(0, 1, 0))
fmt.Println(time.Date(2015, 1, 31, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0).AddDate(1, 0, 0))
英文:

> t1 is time on 62 days after the date 1970/1/1 (yy/mm/dd) t2 is time on 63 days after the date 1970/1/1 (yy/mm/dd)

This is not true. t1 is the time 1970 years, 1 month and 1 day after whatever time.Date(0, 0, 62, 0, 0, 0, 0, time.UTC) means.

fmt.Println(time.Date(0, 0, 62, 0, 0, 0, 0, time.UTC))
fmt.Println(time.Date(0, 0, 63, 0, 0, 0, 0, time.UTC))

gives us:

0000-01-31 00:00:00 +0000 UTC
0000-02-01 00:00:00 +0000 UTC

This is completely wrong. UTC isn't defined for any dates before 1972, the Gregorian calendar doesn't start until 1582 and there was never any year 0. Ignoring all that, I don't see how day 63 of a year could be interpreted as January 31st, but let's go with it anyway.

Let's add things to the first timestamp: add 1970, we get 1970-01-31. Add a month, we get 1970-02-31. But 1970-02-31 isn't a valid date. So it is normalized to March 3rd. 1970 wasn't a leap year, February had 28 days, so Feb 29 is Mar 1, Feb 30 is Mar 2, Feb 31 is Mar 3. Add one day to 1970-03-03 and we get 1970-03-04.

The second timestamp already parses to February 1st. Add a month and we get March 1st, add a day and we get March 2nd.

This is what happens when you add months to timestamps. A month is not a very well defined duration. So the library tries to be clever for you and that gets you unexpected results.

Btw. for some reason: fmt.Println(time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC)) is interpreted as -0001-11-30 00:00:00 +0000 UTC. No idea why. Doesn't really matter since year 0 and month 0 don't exist. But it explains why the earlier timestamps end up at Jan 31st and Feb 1st.

There is no reason for AddDate to add things in this order. It is not documented as far as I can see. It could as well have added the day first, then the month, then the years. Try running this:

fmt.Println(time.Date(2015, 1, 31, 0, 0, 0, 0, time.UTC).AddDate(1, 0, 0).AddDate(0, 1, 0))
fmt.Println(time.Date(2015, 1, 31, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0).AddDate(1, 0, 0)) 

huangapple
  • 本文由 发表于 2016年4月7日 18:25:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/36473385.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定