skip to Main Content

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?

2

Answers


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

    Login or Signup to reply.
  2. You can use AppendFormat to convert your time string into buffer.

    Also in your question you are not initialising your Time struct for Marshalling.

    Here is a probable solution

    package main
    
    import (
        "encoding/json"
        "errors"
        "fmt"
        "log"
        "time"
    )
    
    type Time struct {
        time.Time
    }
    
    func (t *Time) MarshalJSON() ([]byte, error) {
        if y := t.Year(); y < 0 || y >= 10000 {
            return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
        }
    
        b := make([]byte, 0, len(time.RFC3339)+2)
        b = append(b, '"')
        b = t.UTC().Round(time.Second).AppendFormat(b, time.RFC3339)
        b = append(b, '"')
        return b, nil
    }
    
    func main() {
        now := time.Now()
        mt := &Time{now}
        bytArr, err := json.Marshal(mt)
        if err != nil {
            log.Fatalf("marshal time: %v", err)
        }
    
        fmt.Println(string(bytArr))
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search