@@ 39,17 39,17 @@ one:
----
In the [previous episode][ep10] we added better floating point number support to
-our encoder. We have implemented all the Go native types, now we’ll implement a
+our encoder. We implemented all the Go native types, now we’ll implement a
custom stype: time.Time, a timestamp type from Go’s standard library. The CBOR
-encoding [supports](https://tools.ietf.org/html/rfc7049#section-2.4.1) 3
-timestamp types natively:
+format [supports](https://tools.ietf.org/html/rfc7049#section-2.4.1) 3 timestamp
+types natively:
- RFC3339 string like "2019-02-01T17:45:23Z"
- floating point epoch based values
- integer value epoch based values
-The [CBOR format][rfc7049] has special values called tags, used to represent
-data with additional semantics like timestamps. Tag’s headers major is type 6 and
+The [CBOR format][rfc7049] has special values called tags to represent data with
+additional semantics like timestamps. Tags’ headers major type is 6 and
represents an integer number used to determine the tag content’s type. Each
tagged type has a unique integer identifier number.
@@ 65,11 65,9 @@ package lets us query and compare value types, so we will check if the value’s
type is time.Time when we have a reflect.Struct kind and write a CBOR timestamp
when that’s the case.
-In the main switch block we add a if condition in the reflect.Struct case to
-handle time.Time if we have a value with that type. There’s a bit of gymnastic
-needed to get time.Time’s type without creating an empty timestamp on the stack,
-we can either do:
-
+In the main switch block we add a conditional statement in the reflect.Struct
+case to handle time.Time if it’s the value’s type. There’s a bit of gymnastic
+needed to get time.Time’s type without allocating extra stuff, we can either do:
reflect.TypeOf(time.Time{})
@@ 77,10 75,11 @@ Or:
reflect.TypeOf((*time.Time)(nil)).Elem()
-In the first case we create an empty time.Time object and get its type, in the
-second case we create an empty interface to time.Time and retreive its type.
-We’ll use the second way because it doesn’t create an empty time.Time object and
-is therefor a tiny bit more efficient:
+In the first case we create an empty time.Time object, pass an interface
+pointing to it to reflect.TypeOf to retreive the reflect.Type we are after.
+In the second case we create an empty interface to time.Time and retreive its
+type directly. We’ll use the second way because it doesn’t create an empty
+time.Time object and is therefor a bit more efficient:
case reflect.Struct:
if x.Type() == reflect.TypeOf((*time.Time)(nil)).Elem() {
@@ 89,11 88,11 @@ is therefor a tiny bit more efficient:
return e.writeStruct(x)
Timestamps have two tagged data item types: 0 for RFC3339 timestamps encoded as
-a unicode strings, or 1 for epoch-based timestamps that include floating point &
-integer values. Let’s add a new function to write CBOR timestamps: writeTime.
+unicode strings, or 1 for epoch-based timestamps —this include floating point &
+integer values—. Let’s add a new function to write the timestamps: writeTime.
First we’ll handle string timestamps, and implement scalar epoch-based timestamp
-types second. We start with [RFC3339][rfc3339] strings, lookup the example from
-the spec, and add our first test case:
+types second. Starting with [RFC3339][rfc3339] strings, we lookup the example
+from the spec, and add our first test case:
func TestTimestamp(t *testing.T) {
var rfc3339Timestamp, _ = time.Parse(time.RFC3339, "2013-03-21T20:04:00Z")
@@ 157,18 156,18 @@ switch statement:
A quick `go test` to confirm writing string timestamps works.
-We are done with string timestamps, epoch-based timestamps are next: they are
-scalar values where 0 corresponds to the Unix epoch (January 1, 1970), they can
-either be integer or floating point values.
+We are done with string timestamps, epoch-based timestamps are next. Epoch-based
+timestamps are scalar values where 0 corresponds to the Unix epoch (January 1,
+1970), that can either be integer or floating point values.
We’ll minimize the size of our output by using the most compact type we can use
without losing precision. The timestamp can either be an integer, a floating
point number, or a RFC3339 string. If the timestamp’s timezone isn’t UTC we’ll
have to use the largest type: RFC3339 strings, because we need to encode the
timezone information and we can’t do it with scalar timestamps. If the
-timestamp’s timezone is UTC or isn’t set we can use a scalar timestamp because
-it’s assumed to be UTC. We’ll use an integer when the timestamp can be
-represented as whole seconds and use floating point number otherwise.
+timestamp’s timezone is UTC or is nil we can use a scalar timestamp because they
+use UTC time. We’ll use an integer when the timestamp can be represented as
+whole seconds and use floating point number otherwise.
First we’ll add a condition to only use RFC3339 strings when the timestamp has a
timezone that’s not UTC:
@@ 184,10 183,10 @@ timezone that’s not UTC:
return ErrNotImplemented
}
-Because we’re changing the behavior of writeTime when the timezone is UTC, we
-have to fix the first test case to use a timestamp with its timezone set. To do
-so we replace the Z —a shortcut for the UTC timezone— at the end of
-rfc3339Timestamp with +07:00:
+Because we are changing the behavior of writeTime when the timezone is UTC, we
+have to fix the first test case to use a timestamp with a non-UTC timezone set,
+otherwise the test will fail with ErrNotImplemented returned. We replace the Z
+—a shortcut for the UTC timezone— at the end of rfc3339Timestamp with +07:00:
func TestTimestamp(t *testing.T) {
var rfc3339Timestamp, _ = time.Parse(time.RFC3339, "2013-03-21T20:04:00+07:00")