Home > OS >  Unexpected output when printing time.Time type alias
Unexpected output when printing time.Time type alias

Time:06-28

I am trying to write a unmarshal function for a custom type. Consider the below code (playground)

package main

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

type Time time.Time

func (st *Time) UnmarshalJSON(b []byte) error {
    // "2021-05-21T03:10:20.958450" -> "2021-05-21T03:10:20.958450Z"
    s := strings.Trim(string(b), "\"")
    t, err := time.Parse(time.RFC3339, fmt.Sprintf("%s%s", s, "Z"))
    if err != nil {
        return fmt.Errorf("parse time: %w", err)
    }
    *st = Time(t)
    return nil
}

type User struct {
    Name string
    TS Time
}

const data = `{"id":3, "name":"Name", "ts":"2021-05-21T03:10:20.958450"}`

func main() {
    user := new(User)
    json.Unmarshal([]byte(data), &user)
    fmt.Printf("%v\n", user)
}

I am successfully able to get a valid time.Time value from my time.Parse() but I am not quite grasping why *st = Time(t) gives such a wierd value?

Currently the above prints out:

&{Name {958450000 63757163420 <nil>}}

But I would like to be more similar to:

&{Name 2021-05-21 03:10:20.95845  0000 UTC}

What am I missunderstanding here?

CodePudding user response:

Contrary to time.Time your type does not implement fmt.Stringer, so the fmt.Print* functions have no choice but to use their default formatting logic, which in this case is to print the fields of the underlying time.Time value enclosed in curly braces.

Add a String method which delegates to time.Time.String to your type to get the desired behavior:

func (t Time) String() string {
    return time.Time(t).String()
}

https://go.dev/play/p/5PwOwa49B5X

Alternatively, change your Time type to embed time.Time. This will automatically promote the String method along with all other methods (such as the Marshal* methods):

type Time struct {
    time.Time
}

func (st *Time) UnmarshalJSON(b []byte) error {
    // ...
    st.Time = t // simple assignment without type conversion
    return nil
}

https://go.dev/play/p/0H5qyCO22gu

Also, you should never parse JSON by hand. strings.Trim(string(b), "\"") is not enough to fully decode a JSON string value. Always use json.Unmarshal. And you can simplify by using time.ParseInLocation.

func (st *Time) UnmarshalJSON(b []byte) error {
    var s string
    if err := json.Unmarshal(b, &s); err != nil {
        return err
    }

    t, err := time.ParseInLocation("2006-01-02T15:04:05", s, time.UTC)
    if err != nil {
        return err
    }

    // ...
}
  • Related