The sdk by default marshals time.Time
values as RFC3339 strings. How can you choose to marshal and unmarshal in other ways e.g. millis since epoch?
The SDK mentions the Marshaler and Unmarshaler interfaces but does not explain how to use them.
(As I was about to post my question I figured out the answer by looking into how UnixTime
worked).
To use a custom Marshaler and Unmarshaler you can create a custom type.
type MillisTime time.Time
func (e MillisTime) MarshalDynamoDBAttributeValue(av *dynamodb.AttributeValue) error {
millis := timeAsMillis(time.Time(e))
millisStr := fmt.Sprintf("%d", millis)
av.N = &millisStr
return nil
}
func (e *MillisTime) UnmarshalDynamoDBAttributeValue(av *dynamodb.AttributeValue) error {
millis, err := strconv.ParseInt(*av.N, 10, 0)
if err != nil {
return err
}
*e = MillisTime(millisAsTime(millis))
return nil
}
func timeAsMillis(t time.Time) int64 {
nanosSinceEpoch := t.UnixNano()
return (nanosSinceEpoch / 1_000_000_000) + (nanosSinceEpoch % 1_000_000_000)
}
func millisAsTime(millis int64) time.Time {
seconds := millis / 1_000
nanos := (millis % 1_000) * 1_000_000
return time.Unix(seconds, nanos)
}
NOTE: Example above uses the new number literal syntax introduced in go 1.13.
You can easily marshal and unmarshal structs using MarshalMap
and UnmarshalMap
but the downside is that the fields in your struct type have to use MillisTime instead of time.Time
. Conversion is not easy but is possible.
The SDK defines a UnixTime
type which will handle marshaling and unmarshaling between time.Time
<=> seconds since epoch.