@@ -262,11 +262,14 @@ func TestTimestampFromV7(t *testing.T) {
262262 want Timestamp
263263 wanterr bool
264264 }{
265+ // These non-V7 versions should not be able to be provided to TimestampFromV7
265266 {u : Must (NewV1 ()), wanterr : true },
267+ {u : NewV3 (NamespaceDNS , "a.example.com" ), wanterr : true },
266268 // v7 is unix_ts_ms, so zero value time is unix epoch
267269 {u : Must (FromString ("00000000-0000-7000-0000-000000000000" )), want : 122192928000000000 },
268270 {u : Must (FromString ("018a8fec-3ced-7164-995f-93c80cbdc575" )), want : 139139245386050000 },
269- {u : Must (FromString ("ffffffff-ffff-7fff-ffff-ffffffffffff" )), want : Timestamp (epochStart + time .UnixMilli ((1 << 48 )- 1 ).UTC ().UnixNano ()/ 100 )},
271+ // Calculated as `(1<<48)-1` milliseconds, times 100 ns per ms, plus epoch offset from 1970 to 1582.
272+ {u : Must (FromString ("ffffffff-ffff-7fff-bfff-ffffffffffff" )), want : 2936942695106550000 },
270273 }
271274 for _ , tt := range tests {
272275 got , err := TimestampFromV7 (tt .u )
@@ -281,6 +284,56 @@ func TestTimestampFromV7(t *testing.T) {
281284 }
282285}
283286
287+ func TestMinMaxTimestamps (t * testing.T ) {
288+ tests := []struct {
289+ u UUID
290+ want time.Time
291+ }{
292+
293+ // v1 min and max
294+ {u : Must (FromString ("00000000-0000-1000-8000-000000000000" )), want : time .Date (1582 , 10 , 15 , 0 , 0 , 0 , 0 , time .UTC )}, //1582-10-15 0:00:00 (UTC)
295+ {u : Must (FromString ("ffffffff-ffff-1fff-bfff-ffffffffffff" )), want : time .Date (5236 , 3 , 31 , 21 , 21 , 00 , 684697500 , time .UTC )}, //5236-03-31 21:21:00 (UTC)
296+
297+ // v6 min and max
298+ {u : Must (FromString ("00000000-0000-6000-8000-000000000000" )), want : time .Date (1582 , 10 , 15 , 0 , 0 , 0 , 0 , time .UTC )}, //1582-10-15 0:00:00 (UTC)
299+ {u : Must (FromString ("ffffffff-ffff-6fff-bfff-ffffffffffff" )), want : time .Date (5236 , 3 , 31 , 21 , 21 , 00 , 684697500 , time .UTC )}, //5236-03-31 21:21:00 (UTC)
300+
301+ // v7 min and max
302+ {u : Must (FromString ("00000000-0000-7000-8000-000000000000" )), want : time .Date (1970 , 1 , 1 , 0 , 0 , 0 , 0 , time .UTC )}, //1970-01-01 0:00:00 (UTC)
303+ {u : Must (FromString ("ffffffff-ffff-7fff-bfff-ffffffffffff" )), want : time .Date (10889 , 8 , 2 , 5 , 31 , 50 , 655000000 , time .UTC )}, //10889-08-02 5:31:50.655 (UTC)
304+ }
305+ for _ , tt := range tests {
306+ var got Timestamp
307+ var err error
308+ var functionName string
309+
310+ switch tt .u .Version () {
311+ case V1 :
312+ functionName = "TimestampFromV1"
313+ got , err = TimestampFromV1 (tt .u )
314+ case V6 :
315+ functionName = "TimestampFromV6"
316+ got , err = TimestampFromV6 (tt .u )
317+ case V7 :
318+ functionName = "TimestampFromV7"
319+ got , err = TimestampFromV7 (tt .u )
320+ }
321+
322+ if err != nil {
323+ t .Errorf (functionName + "(%v) got error %v, want %v" , tt .u , err , tt .want )
324+ }
325+
326+ tm , err := got .Time ()
327+ if err != nil {
328+ t .Errorf (functionName + "(%v) got error %v, want %v" , tt .u , err , tt .want )
329+ }
330+
331+ if ! tt .want .Equal (tm ) {
332+ t .Errorf (functionName + "(%v) got %v, want %v" , tt .u , tm .UTC (), tt .want )
333+ }
334+ }
335+ }
336+
284337func BenchmarkFormat (b * testing.B ) {
285338 var tests = []string {
286339 "%s" ,
@@ -300,3 +353,90 @@ func BenchmarkFormat(b *testing.B) {
300353 })
301354 }
302355}
356+
357+ var uuidBenchmarkSink UUID
358+ var timestampBenchmarkSink Timestamp
359+ var timeBenchmarkSink time.Time
360+
361+ func BenchmarkTimestampFrom (b * testing.B ) {
362+ var err error
363+ numbUUIDs := 1000
364+ if testing .Short () {
365+ numbUUIDs = 10
366+ }
367+
368+ funcs := []struct {
369+ name string
370+ create func () (UUID , error )
371+ timestamp func (UUID ) (Timestamp , error )
372+ }{
373+ {"v1" , NewV1 , TimestampFromV1 },
374+ {"v6" , NewV6 , TimestampFromV6 },
375+ {"v7" , NewV7 , TimestampFromV7 },
376+ }
377+
378+ for _ , fns := range funcs {
379+ b .Run (fns .name , func (b * testing.B ) {
380+ // Make sure we don't just encode the same string over and over again as that will hit memory caches unrealistically
381+ uuids := make ([]UUID , numbUUIDs )
382+ for i := 0 ; i < numbUUIDs ; i ++ {
383+ uuids [i ] = Must (fns .create ())
384+ if ! testing .Short () {
385+ time .Sleep (1 * time .Millisecond )
386+ }
387+ }
388+ b .ResetTimer ()
389+ for i := 0 ; i < b .N ; i ++ {
390+ timestampBenchmarkSink , err = fns .timestamp (uuids [i % numbUUIDs ])
391+
392+ if err != nil {
393+ b .Fatal (err )
394+ }
395+ }
396+ })
397+ }
398+ }
399+
400+ func BenchmarkTimestampTime (b * testing.B ) {
401+ var err error
402+ numbUUIDs := 1000
403+ if testing .Short () {
404+ numbUUIDs = 10
405+ }
406+
407+ funcs := []struct {
408+ name string
409+ create func () (UUID , error )
410+ timestamp func (UUID ) (Timestamp , error )
411+ }{
412+ {"v1" , NewV1 , TimestampFromV1 },
413+ {"v6" , NewV6 , TimestampFromV6 },
414+ {"v7" , NewV7 , TimestampFromV7 },
415+ }
416+
417+ for _ , fns := range funcs {
418+ b .Run (fns .name , func (b * testing.B ) {
419+ // Make sure we don't just encode the same string over and over again as that will hit memory caches unrealistically
420+ uuids := make ([]UUID , numbUUIDs )
421+ timestamps := make ([]Timestamp , numbUUIDs )
422+ for i := 0 ; i < numbUUIDs ; i ++ {
423+ uuids [i ] = Must (fns .create ())
424+ timestamps [i ], err = fns .timestamp (uuids [i ])
425+ if err != nil {
426+ b .Fatal (err )
427+ }
428+ if ! testing .Short () {
429+ time .Sleep (1 * time .Millisecond )
430+ }
431+ }
432+ b .ResetTimer ()
433+ for i := 0 ; i < b .N ; i ++ {
434+ timeBenchmarkSink , err = timestamps [i % numbUUIDs ].Time ()
435+ if err != nil {
436+ b .Fatal (err )
437+ }
438+ }
439+ })
440+ }
441+
442+ }
0 commit comments