@@ 212,12 212,35 @@ the Z at the end of rfc3339Timestamp wit
}
Now let’s implement floating point numbers when there’s no timezone information
-to encode. Because time.Time store its internal time as an integer counting the
+to encode. As usual we start by adding a test case for this from the spec:
+
+ func TestTimestamp(t *testing.T) {
+ ...
+ var cases = []struct {
+ Value time.Time
+ Expected []byte
+ }{
+ ...
+ {
+ Value: time.Unix(1363896240, 0.5*1e9).UTC(),
+ Expected: []byte{0xc1, 0xfb, 0x41, 0xd4, 0x52, 0xd9, 0xec, 0x20, 0x00, 0x00},
+ },
+ }
+
+ ...
+ }
+
+Because time.Time store its internal time as an integer counting the
number of nanoseconds since the Epoch, we’ll have to convert it into a floating
-point number in seconds.
+point number in seconds. To do this we use the units from the time module to
+define a constant to convert from nanoseconds to seconds:
const nanoSecondsInSecond = time.Second / time.Nanosecond
+Then we add the new code after the first if block. We write the header with the
+minorTimeEpoch as the sub-type to indicate we have a scalar timestamp, and write
+the converted value as a floating point number:
+
func (e *Encoder) writeTime(v reflect.Value) error {
var t = v.Interface().(time.Time)
if t.Location() != time.UTC && t.Location() != nil {
@@ 236,3 259,52 @@ point number in seconds.
float64(unixTimeNano) / float64(nanoSecondsInSecond))
}
+If the timestamp in seconds doesn’t have a fractional part this means we can
+write it as integer without losing data. Integers are usually more compact than
+floating point numbers, so let’s use them when possible. We add another test
+from the spec:
+
+ func TestTimestamp(t *testing.T) {
+ ...
+ var cases = []struct {
+ Value time.Time
+ Expected []byte
+ }{
+ ...
+ {
+ Value: time.Unix(1363896240, 0).UTC(),
+ Expected: []byte{0xc1, 0x1a, 0x51, 0x4b, 0x67, 0xb0},
+ },
+ }
+
+ ...
+ }
+
+To determine if we can write an integer timestamp we check if the fractional
+part of the timestamp in seconds would be zero. Then we convert unixTimeNano
+into seconds, set the minor type depending on the timestamp’s sign, and use
+writeInteger to write the timestamp:
+
+ const nanoSecondsInSecond = time.Second / time.Nanosecond
+
+ func (e *Encoder) writeTime(v reflect.Value) error {
+ ...
+
+ // write an epoch timestamp to preserve space
+ if err := e.writeHeader(majorTag, minorTimeEpoch); err != nil {
+ return err
+ }
+ var unixTimeNano = t.UnixNano()
+ if unixTimeNano%int64(nanoSecondsInSecond) == 0 {
+ var unixTime = unixTimeNano / int64(nanoSecondsInSecond)
+ var sign byte = majorPositiveInteger
+ if unixTime < 0 {
+ sign = majorNegativeInteger
+ unixTime = -unixTime
+ }
+ return e.writeInteger(sign, uint64(unixTime))
+ } else {
+ return e.writeFloat(
+ float64(unixTimeNano) / float64(nanoSecondsInSecond))
+ }
+ }