Home > Mobile >  How to set a timezone to an existing timestamp without reinterpreting it?
How to set a timezone to an existing timestamp without reinterpreting it?

Time:11-13

I'm parsing timestamps sent by users. The timestamps are local to a location but the source string doesn't specify it. Server-side I'm looking up the timezone of the location and need to shift the time into that timezone, without changing its display value.

I know I can do this to give me the equivalent time at a different location:

package main

import (
    "fmt"
    "time"
)

func main() {
    myTime := time.Now()
    fmt.Println(myTime.Format(time.RFC3339))
    
    loc, err := time.LoadLocation("America/New_York")
    if err != nil {
        panic(err)
    }
    
    fmt.Println(myTime.In(loc).Format(time.RFC3339))
}

This just prints:

2009-11-10T23:00:00Z
2009-11-10T18:00:00-05:00

Which isn't what I want.

I'm trying to find a way of setting the timezone to e.g. America/New_York, so I should get e.g. 2009-11-10T23:00:00-05:00, which is the original local time, but with the New York offset applied.

How can I do this in Go?

CodePudding user response:

The confusion comes from the fact that the API that intuitively comes to mind In simply interprets the same point in time as if it were in a different time zone. So when you print it, the display isn't what you want.

To set the time zone to a timestamp while keeping the same display value you can simply construct the new timestamp with time.Date with the same values as the original timestamp and the new location:

t := time.Date(myTime.Year(), myTime.Month(), myTime.Day(), myTime.Hour(), myTime.Minute(), myTime.Second(), myTime.Nanosecond(), loc)
// 2009-11-10T23:00:00-05:00 in the playground

Another option is to set the time instance to the new time zone, then use Zone() to get the offset, and then subtract its value in seconds from the localized time.

package main

import (
    "fmt"
    "time"
)

func main() {
    myTime := time.Now()
    fmt.Println(myTime.Format(time.RFC3339))
    
    loc, err := time.LoadLocation("America/New_York")
    if err != nil {
        panic(err)
    }

    locTime := myTime.In(loc)
    _, zoneOffset := locTime.Zone()
    
    inZoneTime := locTime.Add(-time.Duration(zoneOffset) * time.Second)

    // handle DST transitions
    if inZoneTime.IsDST() {
        inZoneTime = inZoneTime.Add(1*time.Hour)
    }
    
    fmt.Println(inZoneTime.Format(time.RFC3339))
    // 2009-11-10T23:00:00-05:00
}

To test the DST transition in your local machine today (assuming you are in a non-DST country, as I am) you can change the location to a place where DST is active, e.g. Australia/Canberra.

With an input of time.Now() without DST into Australia/Canberra, the above program prints the following:

2021-11-12T13:27:33 01:00
is DST: true
2021-11-12T13:27:33 11:00

Playground: https://play.golang.org/p/5qy2tOcIMwn

  • Related