Skip to content

Commit

Permalink
v7: specify creating uuid from specified time
Browse files Browse the repository at this point in the history
  • Loading branch information
rockwotj committed Jan 29, 2025
1 parent 2d3c2a9 commit eec2cc0
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 5 deletions.
26 changes: 21 additions & 5 deletions version7.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package uuid

import (
"io"
"time"
)

// UUID version 7 features a time-ordered value field derived from the widely
Expand All @@ -21,11 +22,20 @@ import (
// Uses the randomness pool if it was enabled with EnableRandPool.
// On error, NewV7 returns Nil and an error
func NewV7() (UUID, error) {
return NewV7WithTime(nil)
}

// NewV7WithTime returns a time ordered Version 7 UUID.
// It is similar to the NewV7 function, but allows you to specify the time.
// If time is passed as nil, then the current time is used.
//
// If getTime fails to return the current NewV7WithTime returns Nil and an error.
func NewV7WithTime(customTime *time.Time) (UUID, error) {
uuid, err := NewRandom()
if err != nil {
return uuid, err
}
makeV7(uuid[:])
makeV7(customTime, uuid[:])
return uuid, nil
}

Expand All @@ -38,14 +48,14 @@ func NewV7FromReader(r io.Reader) (UUID, error) {
return uuid, err
}

makeV7(uuid[:])
makeV7(nil, uuid[:])
return uuid, nil
}

// makeV7 fill 48 bits time (uuid[0] - uuid[5]), set version b0111 (uuid[6])
// uuid[8] already has the right version number (Variant is 10)
// see function NewV7 and NewV7FromReader
func makeV7(uuid []byte) {
func makeV7(customTime *time.Time, uuid []byte) {
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
Expand All @@ -61,8 +71,14 @@ func makeV7(uuid []byte) {
*/
_ = uuid[15] // bounds check

t, s := getV7Time()

var t, s int64
if customTime == nil {
t, s = getV7Time()
} else {
unix := customTime.UnixNano()
t = unix / nanoPerMilli
s = (unix - t*nanoPerMilli) >> 8
}
uuid[0] = byte(t >> 40)
uuid[1] = byte(t >> 32)
uuid[2] = byte(t >> 24)
Expand Down
65 changes: 65 additions & 0 deletions version7_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package uuid

import (
"testing"
"time"
)

func TestNewV7WithTime(t *testing.T) {
testCases := map[string]string{
"test with current date": time.Now().Format(time.RFC3339), // now
"test with past date": time.Now().Add(-1 * time.Hour * 24 * 365).Format(time.RFC3339), // 1 year ago
"test with future date": time.Now().Add(time.Hour * 24 * 365).Format(time.RFC3339), // 1 year from now
"test with different timezone": "2021-09-01T12:00:00+04:00",
"test with negative timezone": "2021-09-01T12:00:00-12:00",
"test with future date in different timezone": "2124-09-23T12:43:30+09:00",
}

for testName, inputTime := range testCases {
t.Run(testName, func(t *testing.T) {
customTime, err := time.Parse(time.RFC3339, inputTime)
if err != nil {
t.Errorf("time.Parse returned unexpected error %v", err)
}
id, err := NewV7WithTime(&customTime)
if err != nil {
t.Errorf("NewV7WithTime returned unexpected error %v", err)
}
if id.Version() != 7 {
t.Errorf("got %d, want version 7", id.Version())
}
unixTime := time.Unix(id.Time().UnixTime())
// Compare the times in UTC format, since the input time might have different timezone,
// and the result is always in system timezone
if customTime.UTC().Format(time.RFC3339) != unixTime.UTC().Format(time.RFC3339) {
t.Errorf("got %s, want %s", unixTime.Format(time.RFC3339), customTime.Format(time.RFC3339))
}
})
}
}

func TestNewV7FromTimeGeneratesUniqueUUIDs(t *testing.T) {
now := time.Now()
ids := make([]string, 0)
runs := 26000

for i := 0; i < runs; i++ {
id, err := NewV7WithTime(&now)
if err != nil {
t.Errorf("NewV7WithTime returned unexpected error %v", err)
}
if id.Version() != 7 {
t.Errorf("got %d, want version 7", id.Version())
}

// Make sure we add only unique values
if !contains(t, ids, id.String()) {
ids = append(ids, id.String())
}
}

// Check we added all the UIDs
if len(ids) != runs {
t.Errorf("got %d UUIDs, want %d", len(ids), runs)
}
}

0 comments on commit eec2cc0

Please sign in to comment.