Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closes #158 by providing .Time() and .Count() #202

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion bson/bson.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"errors"
"fmt"
"io"
"math"
"os"
"reflect"
"runtime"
Expand Down Expand Up @@ -201,7 +202,6 @@ func readRandomUint32() uint32 {
return uint32((uint32(b[0]) << 0) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24))
}


// machineId stores machine id generated once and used in subsequent calls
// to NewObjectId function.
var machineId = readMachineId()
Expand Down Expand Up @@ -352,6 +352,46 @@ 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.
//
// Returns -1 and en error if time t is earlier than 1970-01-01T00:00:00Z
// or later than 2106-02-07T06:28:15Z.
//
// Note that two timestamps are not allowed to have the same combination of time and counter,
// so you have to make sure to increase the counter when creating multiple MongoTimestamps
// within one second.
func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) {
var tv uint32

u := t.Unix()

if u < 0 || u > math.MaxUint32 {
return -1, errors.New("invalid value for time")
}

tv = uint32(u)

buf := bytes.Buffer{}

binary.Write(&buf, binary.BigEndian, tv)
binary.Write(&buf, binary.BigEndian, c)

i := int64(binary.BigEndian.Uint64(buf.Bytes()))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@niemeyer Also, should this style be converted to the terse bitshifting syntax?


return MongoTimestamp(i), nil
}

type orderKey int64

// MaxKey is a special value that compares higher than all other possible BSON
Expand Down
49 changes: 49 additions & 0 deletions bson/bson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/rand"
"net/url"
"reflect"
"strings"
Expand Down Expand Up @@ -1691,3 +1693,50 @@ func (s *S) BenchmarkUnmarshalRaw(c *C) {
panic(err)
}
}

func (s *S) TestMongoTimestampTime(c *C) {
t := time.Now()
ts, err := bson.NewMongoTimestamp(t, 1)
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, 1)
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(time.Now(), counter)

if err != nil {
fmt.Printf("Error occured: %v", err)
} else {
fmt.Printf("Encoded timestamp: %d\n", ts)
}

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