Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I am getting wrong answers in Date function in golang

Tags:

go

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?

like image 639
Jsmith Avatar asked Apr 07 '16 10:04

Jsmith


People also ask

Is there a date type in Golang?

The specific date Go uses for date and time layouts in string formatting is 01/02 03:04:05PM '06 -0700 .

How can I get current date in go?

Using the “time. Now()” function you can get the date and time in the “yyyy-mm-dd hh:mm:ss. milliseconds timezone” format. This is simplest way for getting the date and time in golang.


1 Answers

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)) 
like image 177
Art Avatar answered Oct 05 '22 10:10

Art