1 files changed, 74 insertions(+), 2 deletions(-)

M episode11/README.md
M episode11/README.md +74 -2
@@ 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))
+        }
+    }