Home > Software design >  How to convert a time to UTC before marshaling as JSON in Go?
How to convert a time to UTC before marshaling as JSON in Go?

Time:12-08

I'm trying to define a Time struct which implements the Marshaler interface such that, when it is marshaled to JSON, it is represented in the format YYYY-mm-ddTHH:MM:SSZ, that is, the time is converted to UTC and rounded to the nearest second. I've tried the following program:

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "time"
)

type Time struct {
    time.Time
}

func (t *Time) MarshalJSON() ([]byte, error) {
    return []byte(t.Time.UTC().Round(time.Second).Format(time.RFC3339)), nil
}

func main() {
    tm := time.Now()
    // tm := time.Now().UTC().Round(time.Second)

    tmJSON, err := json.Marshal(tm)
    if err != nil {
        log.Fatalf("marshal time: %v", err)
    }

    fmt.Println(string(tmJSON))
}

When I run this, however, it prints

> go run main.go
"2022-12-07T16:32:51.494597-08:00"

If, by contrast, I pass in time.Now().UTC().Round(time.Second) as the input to be marshaled (i.e., use the commented-out line in the snippet above), I get the desired output:

> go run main.go
"2022-12-08T00:41:10Z"

My question is: why can't I perform the conversion to UTC and rounding to the nearest second in the MarshalJSON method itself?

CodePudding user response:

To convert a time to UTC before marshaling as JSON in Go, you can use the time.UTC function. This function takes a time.Time value as its argument and returns a time.Time value representing the same time in the UTC time zone.

Here's an example of how you can use this function:

In this example, we first create a time.Time value representing the current time in the local time zone using the time.Now function. We then convert this time to UTC using the time.UTC function, and finally we marshal the time as JSON using the json.Marshal function.

Note that when you marshal a time.Time value as JSON, it will be encoded as a string in the standard ISO 8601 format, which includes the time zone offset (e.g. "2022-12-08T12:34:56 00:00"). So, even if you convert the time to UTC before marshaling, the resulting JSON string will still include the time zone offset indicating that it is in UTC. This is the expected behavior.

package main

import (
    "encoding/json"
    "fmt"
    "time"
)

func main() {
    // Create a time value for the current time in the local time zone.
    t := time.Now()

    // Convert the time to UTC.
    tUTC := t.UTC()

    // Marshal the time as JSON.
    json, err := json.Marshal(tUTC)
    if err != nil {
        fmt.Println(err)
        return
    }

    // Print the JSON.
    fmt.Println(string(json))
}

CodePudding user response:

what are you trying to do?

I tried running your MarshalJSON function and it works as expected

here is what I tried to do:

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "time"
)

type Time struct {
    time.Time
}

func (t *Time) MarshalJSON() ([]byte, error) {
    return []byte(t.Time.UTC().Round(time.Second).Format(time.RFC3339)), nil
}

func main() {
    // tm := time.Now().UTC()
    tm := time.Now().UTC().Round(time.Second)

    tmJSON, err := json.Marshal(tm)
    if err != nil {
        log.Fatalf("marshal time: %v", err)
    }

    fmt.Println(string(tmJSON))

    marshal_time := Time{time.Now().UTC()}
    byt_arr, _ := marshal_time.MarshalJSON()
    fmt.Println(string(byt_arr))
}

and i got the following output:

"2022-12-08T04:41:59Z"
2022-12-08T04:41:59Z

The first line is your previous output and the second output is of your MarshalJSON function.

  • Related