Skip to content

Commit 0d9d58e

Browse files
gedgedomodwyer
authored andcommitted
add NewMongoTimestamp() and MongoTimestamp.Time(),Counter() (#171)
code is inspired by go-mgo#202
1 parent 48a27cc commit 0d9d58e

File tree

2 files changed

+90
-11
lines changed

2 files changed

+90
-11
lines changed

bson/bson.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"errors"
4343
"fmt"
4444
"io"
45+
"math"
4546
"os"
4647
"reflect"
4748
"runtime"
@@ -426,6 +427,36 @@ func Now() time.Time {
426427
// strange reason has its own datatype defined in BSON.
427428
type MongoTimestamp int64
428429

430+
// Time returns the time part of ts which is stored with second precision.
431+
func (ts MongoTimestamp) Time() time.Time {
432+
return time.Unix(int64(uint64(ts)>>32), 0)
433+
}
434+
435+
// Counter returns the counter part of ts.
436+
func (ts MongoTimestamp) Counter() uint32 {
437+
return uint32(ts)
438+
}
439+
440+
// NewMongoTimestamp creates a timestamp using the given
441+
// date `t` (with second precision) and counter `c` (unique for `t`).
442+
//
443+
// Returns an error if time `t` is not between 1970-01-01T00:00:00Z
444+
// and 2106-02-07T06:28:15Z (inclusive).
445+
//
446+
// Note that two MongoTimestamps should never have the same (time, counter) combination:
447+
// the caller must ensure the counter `c` is increased if creating multiple MongoTimestamp
448+
// values for the same time `t` (ignoring fractions of seconds).
449+
func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) {
450+
u := t.Unix()
451+
if u < 0 || u > math.MaxUint32 {
452+
return -1, errors.New("invalid value for time")
453+
}
454+
455+
i := int64(u<<32 | int64(c))
456+
457+
return MongoTimestamp(i), nil
458+
}
459+
429460
type orderKey int64
430461

431462
// MaxKey is a special value that compares higher than all other possible BSON

bson/bson_test.go

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import (
3232
"encoding/json"
3333
"encoding/xml"
3434
"errors"
35+
"fmt"
36+
"math/rand"
3537
"net/url"
3638
"reflect"
3739
"strings"
@@ -286,12 +288,12 @@ func (s *S) TestPtrInline(c *C) {
286288
},
287289
{
288290
// nil embed struct
289-
In: &inlinePtrStruct{A: 3},
291+
In: &inlinePtrStruct{A: 3},
290292
Out: bson.M{"a": 3},
291293
},
292294
{
293295
// nil embed struct
294-
In: &inlinePtrPtrStruct{B: 5},
296+
In: &inlinePtrPtrStruct{B: 5},
295297
Out: bson.M{"b": 5},
296298
},
297299
}
@@ -1205,18 +1207,18 @@ type inlineBadKeyMap struct {
12051207
M map[int]int `bson:",inline"`
12061208
}
12071209
type inlineUnexported struct {
1208-
M map[string]interface{} `bson:",inline"`
1209-
unexported `bson:",inline"`
1210+
M map[string]interface{} `bson:",inline"`
1211+
unexported `bson:",inline"`
12101212
}
12111213
type MStruct struct {
12121214
M int `bson:"m,omitempty"`
12131215
}
12141216
type inlinePtrStruct struct {
1215-
A int
1217+
A int
12161218
*MStruct `bson:",inline"`
12171219
}
12181220
type inlinePtrPtrStruct struct {
1219-
B int
1221+
B int
12201222
*inlinePtrStruct `bson:",inline"`
12211223
}
12221224
type unexported struct {
@@ -1274,11 +1276,11 @@ func (s ifaceSlice) GetBSON() (interface{}, error) {
12741276

12751277
type (
12761278
MyString string
1277-
MyBytes []byte
1278-
MyBool bool
1279-
MyD []bson.DocElem
1280-
MyRawD []bson.RawDocElem
1281-
MyM map[string]interface{}
1279+
MyBytes []byte
1280+
MyBool bool
1281+
MyD []bson.DocElem
1282+
MyRawD []bson.RawDocElem
1283+
MyM map[string]interface{}
12821284
)
12831285

12841286
var (
@@ -1989,3 +1991,49 @@ func (s *S) TestMarshalRespectNil(c *C) {
19891991
c.Assert(testStruct2.Map, NotNil)
19901992
c.Assert(testStruct2.MapPtr, NotNil)
19911993
}
1994+
1995+
func (s *S) TestMongoTimestampTime(c *C) {
1996+
t := time.Now()
1997+
ts, err := bson.NewMongoTimestamp(t, 123)
1998+
c.Assert(err, IsNil)
1999+
c.Assert(ts.Time().Unix(), Equals, t.Unix())
2000+
}
2001+
2002+
func (s *S) TestMongoTimestampCounter(c *C) {
2003+
rnd := rand.Uint32()
2004+
ts, err := bson.NewMongoTimestamp(time.Now(), rnd)
2005+
c.Assert(err, IsNil)
2006+
c.Assert(ts.Counter(), Equals, rnd)
2007+
}
2008+
2009+
func (s *S) TestMongoTimestampError(c *C) {
2010+
t := time.Date(1969, time.December, 31, 23, 59, 59, 999, time.UTC)
2011+
ts, err := bson.NewMongoTimestamp(t, 321)
2012+
c.Assert(int64(ts), Equals, int64(-1))
2013+
c.Assert(err, ErrorMatches, "invalid value for time")
2014+
}
2015+
2016+
func ExampleNewMongoTimestamp() {
2017+
2018+
var counter uint32 = 1
2019+
var t time.Time
2020+
2021+
for i := 1; i <= 3; i++ {
2022+
2023+
if c := time.Now(); t.Unix() == c.Unix() {
2024+
counter++
2025+
} else {
2026+
t = c
2027+
counter = 1
2028+
}
2029+
2030+
ts, err := bson.NewMongoTimestamp(t, counter)
2031+
if err != nil {
2032+
fmt.Printf("NewMongoTimestamp error: %v", err)
2033+
} else {
2034+
fmt.Printf("NewMongoTimestamp encoded timestamp: %d\n", ts)
2035+
}
2036+
2037+
time.Sleep(500 * time.Millisecond)
2038+
}
2039+
}

0 commit comments

Comments
 (0)