diff --git a/uuid.go b/uuid.go index 5320fb5..fbe6645 100644 --- a/uuid.go +++ b/uuid.go @@ -131,6 +131,32 @@ func TimestampFromV6(u UUID) (Timestamp, error) { return Timestamp(uint64(low) + (uint64(mid) << 12) + (uint64(hi) << 28)), nil } +// TimestampFromV7 returns the Timestamp embedded within a V7 UUID. This +// function returns an error if the UUID is any version other than 7. +// +// This is implemented based on revision 03 of the Peabody UUID draft, and may +// be subject to change pending further revisions. Until the final specification +// revision is finished, changes required to implement updates to the spec will +// not be considered a breaking change. They will happen as a minor version +// releases until the spec is final. +func TimestampFromV7(u UUID) (Timestamp, error) { + if u.Version() != 7 { + return 0, fmt.Errorf("uuid: %s is version %d, not version 6", u, u.Version()) + } + + t := 0 | + (int64(u[0]) << 40) | + (int64(u[1]) << 32) | + (int64(u[2]) << 24) | + (int64(u[3]) << 16) | + (int64(u[4]) << 8) | + int64(u[5]) + + // convert to format expected by Timestamp + tsNanos := epochStart + time.UnixMilli(t).UTC().UnixNano()/100 + return Timestamp(tsNanos), nil +} + // Nil is the nil UUID, as specified in RFC-4122, that has all 128 bits set to // zero. var Nil = UUID{} diff --git a/uuid_test.go b/uuid_test.go index 3fa5920..941d8ca 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -256,6 +256,31 @@ func TestTimestampFromV6(t *testing.T) { } } +func TestTimestampFromV7(t *testing.T) { + tests := []struct { + u UUID + want Timestamp + wanterr bool + }{ + {u: Must(NewV1()), wanterr: true}, + // v7 is unix_ts_ms, so zero value time is unix epoch + {u: Must(FromString("00000000-0000-7000-0000-000000000000")), want: 122192928000000000}, + {u: Must(FromString("018a8fec-3ced-7164-995f-93c80cbdc575")), want: 139139245386050000}, + {u: Must(FromString("ffffffff-ffff-7fff-ffff-ffffffffffff")), want: Timestamp(epochStart + time.UnixMilli((1<<48)-1).UTC().UnixNano()/100)}, + } + for _, tt := range tests { + got, err := TimestampFromV7(tt.u) + + switch { + case tt.wanterr && err == nil: + t.Errorf("TimestampFromV7(%v) want error, got %v", tt.u, got) + + case tt.want != got: + t.Errorf("TimestampFromV7(%v) got %v, want %v", tt.u, got, tt.want) + } + } +} + func BenchmarkFormat(b *testing.B) { var tests = []string{ "%s",