UNIX time stamps in Golang
How to use UNIX time stamps in a Golang web service
While building a RESTful JSON web service in Golang (which uses the awesome mgo MongoDB client library) I wanted to use UNIX timestamps all over instead of RFC3339 or ISO dates and found the default behavior of both mgo and encoding/json of marshaling and unmarshaling time.Time objects kind of limiting.
Take the following User struct as an example:
type User struct {
Name string
UpdatedAt time.Time `bson:"updated_at,omitempty" json:"updated_at,omitempty"
}
The first problem here is with the “omitempty” tag, time.Time is never a zero value so it exists always (find zero value time.Time objects with the IsZero() function). The solution is to use a pointer instead of the actual object:
type User struct {
Name string
UpdatedAt *time.Time `bson:"updated_at,omitempty" json:"updated_at,omitempty"
}
In this case, whenever the UpdatedAt field is not set, the pointer is nil and encoding/json knows that it should omit it.
Next thing is that time.Time is marshaled and unmarshaled to/from RFC3339 date, let’s define a new type:
type Timestamp time.Timefunc (t *Timestamp) MarshalJSON() ([]byte, error) {
ts := time.Time(*t).Unix()
stamp := fmt.Sprint(ts) return []byte(stamp), nil
}func (t *Timestamp) UnmarshalJSON(b []byte) error {
ts, err := strconv.Atoi(string(b))
if err != nil {
return err
} *t = Timestamp(time.Unix(int64(ts), 0)) return nil
}
And change our initial struct:
type User struct {
Name string
UpdatedAt *Timestamp `bson:"updated_at,omitempty" json:"updated_at,omitempty"
}
So what’s happening here is that we defined how encoding/json marshals and unmarshals the Timestamp object, which basically return a string of the UNIX time when marshaling and return a new Timestamp object from an int64 timestamp when unmarshaling.
Only problem left is that mgo doesn’t know how to marshal and unmarshal the Timestamp object. We can do allmost the same by implementing the Setter and Getter interfaces.
The result: