Search code examples
jsongoencoding-json-go

How to convert a time to UTC before marshaling as JSON in Go?


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?


Solution

  • 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.