Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does golang time.AddDate result in inconsistent timestamps during DST transition in America/Santiago?

Tags:

dst

go

I'm trying to understand why time.AddDate in Go produces unexpected results during the Daylight Saving Time (DST) transition for the America/Santiago time zone in 2024.

Santiago enter DST at 2024-09-08 00:00:00

here is my code

func TestTimezone1(t *testing.T) {
    loc, err := time.LoadLocation("America/Santiago")
    if err != nil {
        panic(err)
    }
    t1, err := time.ParseInLocation(time.DateTime, "2024-09-07 00:00:00", loc)
    if err != nil {
        panic(err)
    }

    fmt.Println(t1.Unix(), t1.Format(time.DateTime))

    t2 := t1.AddDate(0, 0, 1)
    fmt.Println(t2.Unix(), t2.Format(time.DateTime), t2.Unix()-t1.Unix())

    t3 := t2.AddDate(0, 0, 1)
    fmt.Println(t3.Unix(), t3.Format(time.DateTime), t3.Unix()-t2.Unix())

    t4 := t1.AddDate(0, 0, 2)
    fmt.Println(t4.Unix(), t4.Format(time.DateTime), t4.Unix()-t3.Unix())
}

I Got result

=== RUN   TestTimezone1

1725681600 2024-09-07 00:00:00

1725764400 2024-09-07 23:00:00 82800

1725847200 2024-09-08 23:00:00 82800

1725850800 2024-09-09 00:00:00 3600

--- PASS: TestTimezone1 (0.00s)

but I expect

1725681600 2024-09-07 00:00:00

1725768000 2024-09-08 01:00:00 86400

1725850800 2024-09-09 00:00:00 82800

1725850800 2024-09-09 00:00:00 0

and why t3 != t4 ?

like image 807
Chi Feng Avatar asked Nov 02 '25 13:11

Chi Feng


1 Answers

TL;DR : you hit this weirdness because running .AddDate(0,0,1) on the first time.Time object asks for an invalid date-time in this timezone (2024-09-08 00:00:00 does not exist), and because of the way the standard time package in go deals with these non existing date-times.


fact: the date-time2024-09-08 00:00:00 does not exist in the "America/Santiago" timezone -- actually, the whole hour is skipped when switching to summer time, any 2024-09-08 00:mm:ss is an invalid date-time in that timezone.

t.AddDate(...) internally calls time.Date(...) (link to code), and it so happens that the implementation of this function has its own heuristic to dance around non existing date time inputs. Illustration:

a := time.Date(2024, 9, 7, 23, 59, 59, 0, loc_santiago)
b := time.Date(2024, 9, 8, 0, 0, 0, 0, loc_santiago)
c := time.Date(2024, 9, 8, 0, 59, 59, 0, loc_santiago)
d := time.Date(2024, 9, 8, 1, 0, 0, 0, loc_santiago)

// 2024-09-07 23:59:59 -04
// 2024-09-07 23:00:00 -04   # clock is rolled back one hour ... :/
// 2024-09-07 23:59:59 -04
// 2024-09-08 01:00:00 -03

https://go.dev/play/p/AMLkuk5d79W

So: when you start at 2024-09-07 00:00:00, and then add "1 day", you reach the time.Time described by b in the example above.


I must say I am also surprised by this implementation, I would probably expect to have a heuristic which would generate increasing unix timestamps when parsing increasing date-time strings.

The documentation of both Date() and Time.AddDate() describe the behavior when dealing with ambiguous date-time values, but not really what happens on invalid date-time values.

You could probably open an issue or ask on the golang slack channel for more details.

like image 179
LeGEC Avatar answered Nov 04 '25 14:11

LeGEC