unit myGuid;
interface
uses
System.Classes;
const
MaxWord = 65535;
type
PmyGuid = ^TmyGuid;
TmyGuid =
record //: 18 Byte / 144 Bit
C : Word;
//: 2 Byte / 16 Bit
TS1: TDateTime;
//: 8 Byte / 64 Bit
Ts2: TDateTime;
//: 8 Byte / 64 Bit
class function Create(
const aGuid: PmyGuid): TmyGuid;
overload;
static;
class function Create(
const aGuid:
String): TmyGuid;
overload;
static;
class function Create(
const aGuid: TmyGuid): TmyGuid;
overload;
static;
class function CreateEmpty: TmyGuid;
static;
class function CreateNew: TmyGuid;
static;
class function StringIsValidGuid(
const aGuid:
String): Boolean;
static;
class operator Equal(
const Left, Right: TmyGuid): Boolean;
class operator NotEqual(
const Left, Right: TmyGuid): Boolean;
inline;
function GetHashCode: Integer;
function IsEmpty: Boolean;
function ToString:
String;
procedure DoEmpty;
procedure DoNew;
procedure FromString(
const aGuid:
String);
procedure ReadFromStream(
const aStream: TMemoryStream);
procedure WriteToStream(
const aStream: TMemoryStream);
end;
implementation
uses
System.SysUtils, System.DateUtils, System.Hash;
var
customTS1: TDateTime = 0;
customTS2: TDateTime = 0;
customC : Word = 0;
{: *********************************** TmyGuid ********************************** :}
{: ----------------------------------- default ---------------------------------- :}
//: 2023-10-27
class function TmyGuid.Create(
const aGuid:
String): TmyGuid;
begin
Result := TmyGuid.CreateEmpty;
Result.FromString(aGuid);
end;
//: 2023-10-27
class function TmyGuid.Create(
const aGuid: TmyGuid): TmyGuid;
begin
Result := aGuid;
end;
//: 2023-10-27
class function TmyGuid.Create(
const aGuid: PmyGuid): TmyGuid;
begin
Result := aGuid^;
end;
//: 2023-10-27
class function TmyGuid.CreateEmpty: TmyGuid;
begin
Result.TS1 := 0;
Result.TS2 := 0;
Result.C := 0;
end;
//: 2023-10-27
class function TmyGuid.CreateNew: TmyGuid;
begin
customTS2 := Now;
if (customC = MaxWord)
then
customC := 0
else
Inc(customC);
Result.TS1 := customTS1;
Result.TS2 := customTS2;
Result.C := customC;
end;
//: 2023-10-27
class function TmyGuid.StringIsValidGuid(
const aGuid:
String): Boolean;
var
I: Integer;
C: Char;
begin
if (Length(aGuid) = 42)
then
begin
Result := True;
for I := 1
to Length(aGuid)
do
begin
C := aGuid[I];
case I
of
1:
if C <> '
#'
then
begin
Result := False;
Break;
end;
19, 37:
if C <> '
-'
then
begin
Result := False;
Break;
end;
else
if not CharInSet(C, ['
0'..'
9'])
then
begin
Result := False;
Break;
end;
end;
end;
end
else
Result := False;
end;
//: 2023-10-27
class operator TmyGuid.Equal(
const Left, Right: TmyGuid): Boolean;
begin
Result := ((Left.TS1 = Right.TS1)
and (Left.TS2 = Right.TS2)
and (Left.C = Right.C));
end;
//: 2023-10-27
class operator TmyGuid.NotEqual(
const Left, Right: TmyGuid): Boolean;
begin
Result := ((Left.TS1 <> Right.TS1)
or (Left.TS2 <> Right.TS2)
or (Left.C <> Right.C));
end;
//: 2023-10-27
function TmyGuid.GetHashCode: Integer;
begin
Result := 17;
Result := Result * 397 + Integer(C);
Result := Result * 397 + THashBobJenkins.GetHashValue(TS1, sizeOf(TDateTime), 5);
Result := Result * 397 + THashBobJenkins.GetHashValue(TS2, sizeOf(TDateTime), 7);
end;
//: 2023-10-27
function TmyGuid.IsEmpty: Boolean;
begin
Result := ((TS1 = 0)
and (Ts2 = 0)
and (C = 0));
end;
function TmyGuid.ToString:
String;
var
S1, S2, S3:
String;
begin
//: #yyyymmddhhnnsszzz-yyyymmddhhnnsszzz-xxxxx
//: #12345678901234567890123456789012345678901 - > Length = 42
if (TS1 = 0)
then
S1 := '
00000000000000000'
else
S1 := FormatDateTime('
yyyymmddhhnnsszzz', TS1);
if (Ts2 = 0)
then
S2 := '
00000000000000000'
else
S2 := FormatDateTime('
yyyymmddhhnnsszzz', Ts2);
if (C = 0)
then
S3 := '
00000'
else
S3 := Format('
%0.5d', [C]);
Result := '
#' + S1 + '
-' + S2 + '
-' + S3;
end;
//: 2023-10-27
procedure TmyGuid.DoEmpty;
begin
TS1 := 0;
TS2 := 0;
C := 0;
end;
//: 2023-10-27
procedure TmyGuid.DoNew;
var
lGuid: TmyGuid;
begin
lGuid := CreateNew;
TS1 := lGuid.TS1;
TS2 := lGuid.TS2;
C := lGuid.C;
end;
//: 2023-10-27
procedure TmyGuid.FromString(
const aGuid:
String);
function EncodeTS(aGuid:
String): TDateTime;
var
AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word;
begin
AYear := StrToInt(Copy(aGuid, 1, 4));
AMonth := StrToInt(Copy(aGuid, 5, 2));
ADay := StrToInt(Copy(aGuid, 7, 2));
AHour := StrToInt(Copy(aGuid, 9, 2));
AMinute := StrToInt(Copy(aGuid, 11, 2));
ASecond := StrToInt(Copy(aGuid, 13, 2));
AMilliSecond := StrToInt(Copy(aGuid, 15, 3));
Result := EncodeDateTime(AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond);
end;
begin
if (StringIsValidGuid(aGuid))
then
begin
TS1 := EncodeTS(Copy(aGuid, 2, 17));
Ts2 := EncodeTS(Copy(aGuid, 20, 17));
C := StrToInt(Copy(aGuid, 38, 5));
end
else
begin
TS1 := 0;
Ts2 := 0;
C := 0;
end;
end;
//: 2023-10-27
procedure TmyGuid.ReadFromStream(
const aStream: TMemoryStream);
begin
aStream.ReadData(C);
aStream.ReadData(TS1);
aStream.ReadData(Ts2);
end;
//: 2023-10-27
procedure TmyGuid.WriteToStream(
const aStream: TMemoryStream);
begin
aStream.WriteData(C);
aStream.WriteData(TS1);
aStream.WriteData(Ts2);
end;
initialization
customTS1 := Now;
customTS2 := Now;
customC := MaxWord;
end.