Commit 9112f21e authored by Kirill Smelkov's avatar Kirill Smelkov

go/zodb: Don't truncate Tid time precision to 1µs

The format of tid assumes ~ ns precision, and it is only formatted to µs
precision by default. So don't truncate TimeStamp value when computing
it from Tid, and perform the µs-rounding only on formatting.

The float numbers are not always exactly as in python. For example the
following program

	tidv = [
	    0x0000000000000000,
	    0x0285cbac258bf266,
	    0x0285cbad27ae14e6,
	    0x037969f722a53488,
	    0x03b84285d71c57dd,
	    0x03caa84275fc1166,
	]

	for tid in tidv:
	    t = TimeStamp.TimeStamp(p64(tid))
	    print '0x%016x %s %.9f\t%.9f' % (tid, t, t.timeTime(), t.second())

prints:

	0x0000000000000000 1900-01-01 00:00:00.000000 -2208988800.000000000     0.000000000
	0x0285cbac258bf266 1979-01-03 21:00:08.800000 284245208.800000191       8.800000185
	0x0285cbad27ae14e6 1979-01-03 21:01:09.300001 284245269.300001621       9.300001496	<-- ex here
	0x037969f722a53488 2008-10-24 05:11:08.120000 1224825068.119999886      8.119999878
	0x03b84285d71c57dd 2016-07-01 09:41:50.416574 1467366110.416574001      50.416573989
	0x03caa84275fc1166 2018-10-01 16:34:27.652650 1538411667.652649879      27.652650112

the difference is due to floating point operation ordering, because
TimeStamp.timeTime() looses precision - e.g. for marked case:

	In [8]: '%.10f' % (281566860.000000000 + 9.300001496)
	Out[8]: '281566869.3000015020'

We don't try to mimic float64 behaviour to Python exactly - because it is even
different for PURE_PYTHON=y or C TimeStamp implementations. However we don't
limit due to that our timestamp precision to only 1µs.

In other words we keep on maintaining exact compatibility with Python on
printing, but timestamp values itself are now ~ ns precision.
parent c72aaa0d
...@@ -36,8 +36,17 @@ func (t TimeStamp) String() string { ...@@ -36,8 +36,17 @@ func (t TimeStamp) String() string {
} }
func (t TimeStamp) XFmtString(b []byte) []byte { func (t TimeStamp) XFmtString(b []byte) []byte {
// NOTE UTC() in case we get TimeStamp with modified from-outside location // round to microsecond on formatting: zodb/py does this, and without rounding it is sometimes
return t.UTC().AppendFormat(b, "2006-01-02 15:04:05.000000") // not exactly bit-to-bit the same in text output compared to zodb/py. Example:
// 037969f722a53488: timeStr = "2008-10-24 05:11:08.119999" ; want "2008-10-24 05:11:08.120000"
//
// This happens because Go's time.Format() does not perform rounding,
// and so even if t = 0.999, formatting it with .00 won't give 0.10.
//
// NOTE UTC() in case we get TimeStamp with modified from-outside location.
tµs := t.UTC().Round(time.Microsecond)
return tµs.AppendFormat(b, "2006-01-02 15:04:05.000000")
} }
...@@ -65,11 +74,6 @@ func (tid Tid) Time() TimeStamp { ...@@ -65,11 +74,6 @@ func (tid Tid) Time() TimeStamp {
int(nsec), int(nsec),
time.UTC) time.UTC)
// round to microsecond: zodb/py does this, and without rounding it is sometimes
// not exactly bit-to-bit the same in text output compared to zodb/py. Example:
// 037969f722a53488: timeStr = "2008-10-24 05:11:08.119999" ; want "2008-10-24 05:11:08.120000"
t = t.Round(time.Microsecond)
return TimeStamp{t} return TimeStamp{t}
} }
......
...@@ -22,18 +22,25 @@ package zodb ...@@ -22,18 +22,25 @@ package zodb
import "testing" import "testing"
func TestTidTime(t *testing.T) { func TestTidTime(t *testing.T) {
var testv = []struct {tid Tid; timeStr string} { var testv = []struct {tid Tid; timeStr string; timeFloat float64} {
{0x0000000000000000, "1900-01-01 00:00:00.000000"}, {0x0000000000000000, "1900-01-01 00:00:00.000000", -2208988800.000000000},
{0x0285cbac258bf266, "1979-01-03 21:00:08.800000"}, {0x0285cbac258bf266, "1979-01-03 21:00:08.800000", 284245208.800000191},
{0x0285cbad27ae14e6, "1979-01-03 21:01:09.300001"}, {0x0285cbad27ae14e6, "1979-01-03 21:01:09.300001", 284245269.300001502},
{0x037969f722a53488, "2008-10-24 05:11:08.120000"}, {0x037969f722a53488, "2008-10-24 05:11:08.120000", 1224825068.120000124},
{0x03b84285d71c57dd, "2016-07-01 09:41:50.416574"}, {0x03b84285d71c57dd, "2016-07-01 09:41:50.416574", 1467366110.416574001},
{0x03caa84275fc1166, "2018-10-01 16:34:27.652650", 1538411667.652650118},
} }
for _, tt := range testv { for _, tt := range testv {
timeStr := tt.tid.Time().String() tidtime := tt.tid.Time()
timeStr := tidtime.String()
if timeStr != tt.timeStr { if timeStr != tt.timeStr {
t.Errorf("%v: timeStr = %q ; want %q", tt.tid, timeStr, tt.timeStr) t.Errorf("%v: timeStr = %q ; want %q", tt.tid, timeStr, tt.timeStr)
} }
timeFloat := float64(tidtime.UnixNano()) * 1E-9
if timeFloat != tt.timeFloat {
t.Errorf("%v: timeFloat = %.9f ; want %.9f", tt.tid, timeFloat, tt.timeFloat)
}
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment