119 lines
3.2 KiB
Go
119 lines
3.2 KiB
Go
package mtime
|
|
|
|
// Code based off the timeutil package in github.com/levenlabs/golib
|
|
// Changes performed:
|
|
// - Renamed Timestamp to TS for brevity
|
|
// - Added NewTS function
|
|
// - Moved Float64 method
|
|
// - Moved initialization methods to top
|
|
// - Made MarshalJSON use String method
|
|
// - TSNow -> NowTS, make it use NewTS
|
|
|
|
import (
|
|
"bytes"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
var unixZero = time.Unix(0, 0)
|
|
|
|
func timeToFloat(t time.Time) float64 {
|
|
// If time.Time is the empty value, UnixNano will return the farthest back
|
|
// timestamp a float can represent, which is some large negative value. We
|
|
// compromise and call it zero
|
|
if t.IsZero() {
|
|
return 0
|
|
}
|
|
return float64(t.UnixNano()) / 1e9
|
|
}
|
|
|
|
// TS is a wrapper around time.Time which adds methods to marshal and
|
|
// unmarshal the value as a unix timestamp instead of a formatted string
|
|
type TS struct {
|
|
time.Time
|
|
}
|
|
|
|
// NewTS returns a new TS instance wrapping the given time.Time, which will
|
|
// possibly be truncated a certain amount to account for floating point
|
|
// precision.
|
|
func NewTS(t time.Time) TS {
|
|
return TSFromFloat64(timeToFloat(t))
|
|
}
|
|
|
|
// NowTS is a wrapper around time.Now which returns a TS.
|
|
func NowTS() TS {
|
|
return NewTS(time.Now())
|
|
}
|
|
|
|
// TSFromInt64 returns a TS equal to the given int64, assuming it too is a unix
|
|
// timestamp
|
|
func TSFromInt64(ts int64) TS {
|
|
return TS{time.Unix(ts, 0)}
|
|
}
|
|
|
|
// TSFromFloat64 returns a TS equal to the given float64, assuming it too is a
|
|
// unix timestamp. The float64 is interpreted as number of seconds, with
|
|
// everything after the decimal indicating milliseconds, microseconds, and
|
|
// nanoseconds
|
|
func TSFromFloat64(ts float64) TS {
|
|
secs := int64(ts)
|
|
nsecs := int64((ts - float64(secs)) * 1e9)
|
|
return TS{time.Unix(secs, nsecs)}
|
|
}
|
|
|
|
// TSFromString attempts to parse the string as a float64, and then passes that
|
|
// into TSFromFloat64, returning the result
|
|
func TSFromString(ts string) (TS, error) {
|
|
f, err := strconv.ParseFloat(ts, 64)
|
|
if err != nil {
|
|
return TS{}, err
|
|
}
|
|
return TSFromFloat64(f), nil
|
|
}
|
|
|
|
// String returns the string representation of the TS, in the form of a floating
|
|
// point form of the time as a unix timestamp
|
|
func (t TS) String() string {
|
|
ts := timeToFloat(t.Time)
|
|
return strconv.FormatFloat(ts, 'f', -1, 64)
|
|
}
|
|
|
|
// Float64 returns the float representation of the timestamp in seconds.
|
|
func (t TS) Float64() float64 {
|
|
return timeToFloat(t.Time)
|
|
}
|
|
|
|
var jsonNull = []byte("null")
|
|
|
|
// MarshalJSON returns the JSON representation of the TS as an integer. It
|
|
// never returns an error
|
|
func (t TS) MarshalJSON() ([]byte, error) {
|
|
if t.IsZero() {
|
|
return jsonNull, nil
|
|
}
|
|
|
|
return []byte(t.String()), nil
|
|
}
|
|
|
|
// UnmarshalJSON takes a JSON integer and converts it into a TS, or
|
|
// returns an error if this can't be done
|
|
func (t *TS) UnmarshalJSON(b []byte) error {
|
|
// since 0 is a valid timestamp we can't use that to mean "unset", so we
|
|
// take null to mean unset instead
|
|
if bytes.Equal(b, jsonNull) {
|
|
t.Time = time.Time{}
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
*t, err = TSFromString(string(b))
|
|
return err
|
|
}
|
|
|
|
// IsUnixZero returns true if the timestamp is equal to the unix zero timestamp,
|
|
// representing 1/1/1970. This is different than checking if the timestamp is
|
|
// the empty value (which should be done with IsZero)
|
|
func (t TS) IsUnixZero() bool {
|
|
return t.Equal(unixZero)
|
|
}
|