Vincent Parrett 750 Posted August 21 Hi All I created a Delphi implementation of UUIDv7 - RFC 9562 UUIDv7 values are time-sortable, which means you can sort them in increasing order based on when they were generated. https://github.com/VSoftTechnologies/VSoft.UUIDv7 Should work with XE2=12.x Win32/Win64 and all platforms on 11.3 or later. Usage : var guid : TGuid; begin guid := TUUIDv7Helper.CreateV7; writeln(guid.ToString); end; 2 6 Share this post Link to post
Edwin Yip 154 Posted August 22 (edited) Well done! Is it possible to extract datetime from a uuid? Edited August 22 by Edwin Yip Share this post Link to post
Vincent Parrett 750 Posted August 22 30 minutes ago, Edwin Yip said: Is it possible to extract datetime from a uuid? for v7 yes - in theory just reverse the process that inserts the unix epoc timestamp and convert to datetime. I would have created a record helper for TGuid - except the rtl already has one (where all the useful methods are) - and only 1 helper per type can be in scope at a time. Share this post Link to post
Vincent Parrett 750 Posted August 22 Updated to add some new methods type TUUIDV7Helper = record class function CreateV7 : TGuid;overload;static; class function CreateV7(const dt : TDateTime) : TGuid;overload;static; class function CreatedUTC(const guid : TGUID) : TDateTime;static; class function IsV7(const guid : TGuid) : boolean;static;inline; class function Version(const guid : TGuid) : integer;static;inline; end; 1 Share this post Link to post
João Antônio Duarte 6 Posted September 5 Interesting, I also made my own implementation of GUID V7 a while back. Your implementation seems to be more optimized than mine. function NewGuidV7: TGUID; var LBytes: array[0..15] of Byte; LUnixMSec: Int64; I: Integer; begin for I := 0 to 15 do LBytes[I] := Random($FF); LUnixMSec := DateTimeToMilliseconds(TTimeZone.Local.ToUniversalTime(Now)) - Int64(UnixDateDelta + DateDelta) * MSecsPerDay; LBytes[0] := (LUnixMSec shr 40) and $FF; LBytes[1] := (LUnixMSec shr 32) and $FF; LBytes[2] := (LUnixMSec shr 24) and $FF; LBytes[3] := (LUnixMSec shr 16) and $FF; LBytes[4] := (LUnixMSec shr 8) and $FF; LBytes[5] := LUnixMSec and $FF; LBytes[6] := (LBytes[6] and $0F) or $70; LBytes[8] := (LBytes[8] and $3F) or $80; Result := TGuid.Create(LBytes, 0, True); end; 1 Share this post Link to post
Kas Ob. 121 Posted September 5 1 hour ago, João Antônio Duarte said: function NewGuidV7: TGUID; var LBytes: array[0..15] of Byte; LUnixMSec: Int64; I: Integer; begin for I := 0 to 15 do LBytes[I] := Random($FF); LUnixMSec := DateTimeToMilliseconds(TTimeZone.Local.ToUniversalTime(Now)) - Int64(UnixDateDelta + DateDelta) * MSecsPerDay; LBytes[0] := (LUnixMSec shr 40) and $FF; LBytes[1] := (LUnixMSec shr 32) and $FF; LBytes[2] := (LUnixMSec shr 24) and $FF; LBytes[3] := (LUnixMSec shr 16) and $FF; LBytes[4] := (LUnixMSec shr 8) and $FF; LBytes[5] := LUnixMSec and $FF; LBytes[6] := (LBytes[6] and $0F) or $70; LBytes[8] := (LBytes[8] and $3F) or $80; Result := TGuid.Create(LBytes, 0, True); end; Where is Byte[7] ?!! Anyway, try this for fair comparison function NewGuidV7: TGUID; var LUnixMSec: Int64; LUnixMSecBytes: array[0..7] of Byte absolute LUnixMSec; LBytes: array[0..15] of Byte; I: Integer; begin for I := 0 to 15 do LBytes[I] := Random($FF); LUnixMSec := DateTimeToMilliseconds(TTimeZone.Local.ToUniversalTime(Now)) - Int64(UnixDateDelta + DateDelta) * MSecsPerDay; LBytes[0] := LUnixMSecBytes[5]; LBytes[1] := LUnixMSecBytes[4]; LBytes[2] := LUnixMSecBytes[3]; LBytes[3] := LUnixMSecBytes[2]; LBytes[4] := LUnixMSecBytes[1]; LBytes[5] := LUnixMSecBytes[0]; {LBytes[0] := (LUnixMSec shr 40) and $FF; LBytes[1] := (LUnixMSec shr 32) and $FF; LBytes[2] := (LUnixMSec shr 24) and $FF; LBytes[3] := (LUnixMSec shr 16) and $FF; LBytes[4] := (LUnixMSec shr 8) and $FF; LBytes[5] := LUnixMSec and $FF;} LBytes[6] := (LBytes[6] and $0F) or $70; LBytes[8] := (LBytes[8] and $3F) or $80; Result := TGuid.Create(LBytes, 0, True); end; Fix LByte[7] as should, because in my sample it is missing too ! In case you did a benchmark then please share the result with us, (i didn't) Share this post Link to post
João Antônio Duarte 6 Posted September 5 6 hours ago, Kas Ob. said: Where is Byte[7] ?!! Byte[7] is filled at array initialization with random values. 1 Share this post Link to post
Vincent Parrett 750 Posted September 5 I rely on TGUID.NewGuid to generate the random parts - under the hood it uses CoCreateGuid which uses windows cryptographic apis - which are far more random than Delphi's Random function (and faster). 3 Share this post Link to post
João Antônio Duarte 6 Posted September 6 14 hours ago, Vincent Parrett said: I rely on TGUID.NewGuid to generate the random parts - under the hood it uses CoCreateGuid which uses windows cryptographic apis - which are far more random than Delphi's Random function (and faster). I ran a benchmark test and found that your implementation is much more performant than mine. Congratulations on the great work! 1 1 Share this post Link to post