ugorji / go

idiomatic codec and rpc lib for msgpack, cbor, json, etc. msgpack.org[Go]
MIT License
1.86k stars 295 forks source link

With CBOR, time values not equal after encoding & decoding. #239

Closed ghts closed 6 years ago

ghts commented 6 years ago

TestCborTimeIssue.go.txt

This simple test case check the correctness of 'encoding & decoding' of time value in CBOR.

(For the source code, please refer to the attachment.)

A random time value is 'encoded & decoded' and compared with original value.

Expected 2 time values are equal, but, test result shows that it is not the case.

Test Environment:

I ran "go test github.com/ugorji/go/codec -run ^TestCborTimeIssue$"

package codec

import (
    "bytes"
    "fmt"
    "math/rand"
    "testing"
    "time"
)

func TestCborTimeIssue(t *testing.T) {
    cborHandle := new(CborHandle)
    buffer := new(bytes.Buffer)

    var tt, tt2 time.Time

        // randome seed
    r := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63()))

    // random time value
    tt = time.Date(r.Intn(200)+1970, time.Month(r.Intn(12)), r.Intn(31),
        r.Intn(24), r.Intn(60), r.Intn(60), r.Intn(1000000000), time.Now().Location())

    // encoding
    if err := NewEncoder(buffer, cborHandle).Encode(tt); err != nil {
        println(err.Error())
        t.Fail()
    }

    // decoding
    if err := NewDecoderBytes(buffer.Bytes(), cborHandle).Decode(&tt2); err != nil {
        println(err.Error())
        t.Fail()
    }

    // compare with original value
    if !tt.Equal(tt2) {
        fmt.Printf("time : values not equal\n1: %v\n2: %v\ndifference: %v\n", tt2, tt, tt2.Sub(tt))
        t.Fail()
    }
}

and received output below

time : values not equal
1: 2012-11-16 15:25:32.417818 +0000 UTC
2: 2012-11-17 00:25:32.417817656 +0900 KST
difference: 344ns
FAIL    github.com/ugorji/go/codec  0.653s

but expected output below.

ok      github.com/ugorji/go/codec  1.231s
ugorji commented 6 years ago

cbor spec defines 2 modes of encoding time:

By default, seconds since epoch is used. CborHandle allows you switch to RFC3339 format using the flag: TimeRFC3339=true (see https://godoc.org/github.com/ugorji/go/codec#CborHandle).

Because you inadvertently used the former model (seconds since epoch), all milliseconds etc are ignored, or a float is used which is guaranteed to not be exact. Thus the discrepancy in equals.

This is a limitation of cbor format leveraging floats, and the inability to exactly represent large fractional floats in computing.

To have a better calculation, use exact milliseconds (not nanoseconds) and compare using UTC on both sides, or use RFC 3339 format.

Closing this.

ugorji commented 6 years ago

P.S. Please update if you use exact milliseconds (or 0 milliseconds) and UTC on both sides and still get a difference. I have tests that exact this code, and worked hard to get it to some semblance of correctness. So would want to ensure that this works as designed.

Please explicitly tag me (ie use @ugorji in your response) if you still see an issue, so I can re-check.

ghts commented 6 years ago

@ugorji

This is a limitation of cbor format.

When I looked the cbor code, I could not understand why using Float type for time.

Now, I am clear that it is limitation of CBOR format.

Based on your detailed explanation,

I chose to use (updated) MessagePack format, (rather than CBOR format).

Thank you for generous & detailed reply.

Cheers.~