Skip to content

Commit

Permalink
add NewMongoTimestamp() and MongoTimestamp.Time(),Counter()
Browse files Browse the repository at this point in the history
code is inspired by go-mgo#202
  • Loading branch information
gedge committed May 22, 2018
1 parent 48a27cc commit c84e674
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 11 deletions.
31 changes: 31 additions & 0 deletions bson/bson.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"errors"
"fmt"
"io"
"math"
"os"
"reflect"
"runtime"
Expand Down Expand Up @@ -426,6 +427,36 @@ func Now() time.Time {
// strange reason has its own datatype defined in BSON.
type MongoTimestamp int64

// Time returns the time part of ts which is stored with second precision.
func (ts MongoTimestamp) Time() time.Time {
return time.Unix(int64(uint64(ts)>>32), 0)
}

// Counter returns the counter part of ts.
func (ts MongoTimestamp) Counter() uint32 {
return uint32(ts)
}

// NewMongoTimestamp creates a timestamp using the given
// date `t` (with second precision) and counter `c` (unique for `t`).
//
// Returns an error if time `t` is not between 1970-01-01T00:00:00Z
// and 2106-02-07T06:28:15Z (inclusive).
//
// Note that two MongoTimestamps should never have the same (time, counter) combination:
// the caller must ensure the counter `c` is increased if creating multiple MongoTimestamp
// values for the same time `t` (ignoring fractions of seconds).
func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) {
u := t.Unix()
if u < 0 || u > math.MaxUint32 {
return -1, errors.New("invalid value for time")
}

i := int64(u<<32 | int64(c))

return MongoTimestamp(i), nil
}

type orderKey int64

// MaxKey is a special value that compares higher than all other possible BSON
Expand Down
70 changes: 59 additions & 11 deletions bson/bson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import (
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"math/rand"
"net/url"
"reflect"
"strings"
Expand Down Expand Up @@ -286,12 +288,12 @@ func (s *S) TestPtrInline(c *C) {
},
{
// nil embed struct
In: &inlinePtrStruct{A: 3},
In: &inlinePtrStruct{A: 3},
Out: bson.M{"a": 3},
},
{
// nil embed struct
In: &inlinePtrPtrStruct{B: 5},
In: &inlinePtrPtrStruct{B: 5},
Out: bson.M{"b": 5},
},
}
Expand Down Expand Up @@ -1205,18 +1207,18 @@ type inlineBadKeyMap struct {
M map[int]int `bson:",inline"`
}
type inlineUnexported struct {
M map[string]interface{} `bson:",inline"`
unexported `bson:",inline"`
M map[string]interface{} `bson:",inline"`
unexported `bson:",inline"`
}
type MStruct struct {
M int `bson:"m,omitempty"`
}
type inlinePtrStruct struct {
A int
A int
*MStruct `bson:",inline"`
}
type inlinePtrPtrStruct struct {
B int
B int
*inlinePtrStruct `bson:",inline"`
}
type unexported struct {
Expand Down Expand Up @@ -1274,11 +1276,11 @@ func (s ifaceSlice) GetBSON() (interface{}, error) {

type (
MyString string
MyBytes []byte
MyBool bool
MyD []bson.DocElem
MyRawD []bson.RawDocElem
MyM map[string]interface{}
MyBytes []byte
MyBool bool
MyD []bson.DocElem
MyRawD []bson.RawDocElem
MyM map[string]interface{}
)

var (
Expand Down Expand Up @@ -1989,3 +1991,49 @@ func (s *S) TestMarshalRespectNil(c *C) {
c.Assert(testStruct2.Map, NotNil)
c.Assert(testStruct2.MapPtr, NotNil)
}

func (s *S) TestMongoTimestampTime(c *C) {
t := time.Now()
ts, err := bson.NewMongoTimestamp(t, 123)
c.Assert(err, IsNil)
c.Assert(ts.Time().Unix(), Equals, t.Unix())
}

func (s *S) TestMongoTimestampCounter(c *C) {
rnd := rand.Uint32()
ts, err := bson.NewMongoTimestamp(time.Now(), rnd)
c.Assert(err, IsNil)
c.Assert(ts.Counter(), Equals, rnd)
}

func (s *S) TestMongoTimestampError(c *C) {
t := time.Date(1969, time.December, 31, 23, 59, 59, 999, time.UTC)
ts, err := bson.NewMongoTimestamp(t, 321)
c.Assert(int64(ts), Equals, int64(-1))
c.Assert(err, ErrorMatches, "invalid value for time")
}

func ExampleNewMongoTimestamp() {

var counter uint32 = 1
var t time.Time

for i := 1; i <= 3; i++ {

if c := time.Now(); t.Unix() == c.Unix() {
counter++
} else {
t = c
counter = 1
}

ts, err := bson.NewMongoTimestamp(t, counter)
if err != nil {
fmt.Printf("NewMongoTimestamp error: %v", err)
} else {
fmt.Printf("NewMongoTimestamp encoded timestamp: %d\n", ts)
}

time.Sleep(500 * time.Millisecond)
}
}

0 comments on commit c84e674

Please sign in to comment.