Skip to content

Commit

Permalink
Merge pull request #31 from rkuris/master
Browse files Browse the repository at this point in the history
Add Time() and Nanos() methods

Signed-off-by: Tim Heckman <[email protected]>
  • Loading branch information
theckman committed Aug 22, 2018
2 parents c52a240 + bfaa575 commit d41eeda
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
30 changes: 30 additions & 0 deletions uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
package uuid

import (
"encoding/binary"
"encoding/hex"
"fmt"
"time"
)

// Size of a UUID in bytes.
Expand Down Expand Up @@ -64,6 +67,33 @@ const (
DomainOrg
)

// Timestamp is the count of 100-nanosecond intervals since 00:00:00.00,
// 15 October 1582 within a V1 UUID. This type has no meaning for V2-V5
// UUIDs since they don't have an embedded timestamp.
type Timestamp uint64

const _100nsPerSecond = 10000000

// Time returns the UTC time.Time representation of a Timestamp
func (t Timestamp) Time() (time.Time, error) {
secs := uint64(t) / _100nsPerSecond
nsecs := 100 * (uint64(t) % _100nsPerSecond)
return time.Unix(int64(secs)-(epochStart/_100nsPerSecond), int64(nsecs)), nil
}

// TimestampFromV1 returns the Timestamp embedded within a V1 UUID.
// Returns an error if the UUID is any version other than 1.
func TimestampFromV1(u UUID) (Timestamp, error) {
if u.Version() != 1 {
err := fmt.Errorf("uuid: %s is version %d, not version 1", u, u.Version())
return 0, err
}
low := binary.BigEndian.Uint32(u[0:4])
mid := binary.BigEndian.Uint16(u[4:6])
hi := binary.BigEndian.Uint16(u[6:8]) & 0xfff
return Timestamp(uint64(low) + (uint64(mid) << 32) + (uint64(hi) << 48)), nil
}

// String parse helpers.
var (
urnPrefix = []byte("urn:uuid:")
Expand Down
48 changes: 48 additions & 0 deletions uuid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"bytes"
"fmt"
"testing"
"time"
)

func TestUUID(t *testing.T) {
Expand Down Expand Up @@ -133,3 +134,50 @@ func TestMust(t *testing.T) {
}
Must(fn())
}

func TestTimeFromTimestamp(t *testing.T) {
tests := []struct {
t Timestamp
want time.Time
}{
// a zero timestamp represents October 15, 1582 at midnight UTC
{t: Timestamp(0), want: time.Date(1582, 10, 15, 0, 0, 0, 0, time.UTC)},
// a one value is 100ns later
{t: Timestamp(1), want: time.Date(1582, 10, 15, 0, 0, 0, 100, time.UTC)},
// 10 million 100ns intervals later is one second
{t: Timestamp(10000000), want: time.Date(1582, 10, 15, 0, 0, 1, 0, time.UTC)},
{t: Timestamp(60 * 10000000), want: time.Date(1582, 10, 15, 0, 1, 0, 0, time.UTC)},
{t: Timestamp(60 * 60 * 10000000), want: time.Date(1582, 10, 15, 1, 0, 0, 0, time.UTC)},
{t: Timestamp(24 * 60 * 60 * 10000000), want: time.Date(1582, 10, 16, 0, 0, 0, 0, time.UTC)},
{t: Timestamp(365 * 24 * 60 * 60 * 10000000), want: time.Date(1583, 10, 15, 0, 0, 0, 0, time.UTC)},
// maximum timestamp value in a UUID is the year 5236
{t: Timestamp(uint64(1<<60 - 1)), want: time.Date(5236, 03, 31, 21, 21, 0, 684697500, time.UTC)},
}
for _, tt := range tests {
got, _ := tt.t.Time()
if !got.Equal(tt.want) {
t.Errorf("%v.Time() == %v, want %v", tt.t, got, tt.want)
}
}
}

func TestTimestampFromV1(t *testing.T) {
tests := []struct {
u UUID
want Timestamp
wanterr bool
}{
{u: Must(NewV4()), wanterr: true},
{u: Must(FromString("00000000-0000-1000-0000-000000000000")), want: 0},
{u: Must(FromString("424f137e-a2aa-11e8-98d0-529269fb1459")), want: 137538640775418750},
{u: Must(FromString("ffffffff-ffff-1fff-ffff-ffffffffffff")), want: Timestamp(1<<60 - 1)},
}
for _, tt := range tests {
got, goterr := TimestampFromV1(tt.u)
if tt.wanterr && goterr == nil {
t.Errorf("TimestampFromV1(%v) want error, got %v", tt.u, got)
} else if tt.want != got {
t.Errorf("TimestampFromV1(%v) got %v, want %v", tt.u, got, tt.want)
}
}
}

0 comments on commit d41eeda

Please sign in to comment.