Browse Source

Libraries: added TResult and cleanup

Herman Schoenfeld 6 years ago
parent
commit
b62d204332
1 changed files with 288 additions and 167 deletions
  1. 288 167
      src/libraries/sphere10/UCommon.pas

+ 288 - 167
src/libraries/sphere10/UCommon.pas

@@ -36,20 +36,15 @@ const
   MaxSeconds = MaxMilliseconds div 60;
   MaxSeconds = MaxMilliseconds div 60;
   MinSeconds = MinMilliseconds div 60;
   MinSeconds = MinMilliseconds div 60;
 
 
-{ GLOBAL FUNCTIONS }
+{ GLOBAL HELPER FUNCTIONS }
 
 
-{ Converts a string to hexidecimal format }
 function String2Hex(const Buffer: AnsiString): AnsiString;
 function String2Hex(const Buffer: AnsiString): AnsiString;
 function Hex2Bytes(const AHexString: AnsiString): TBytes; overload;
 function Hex2Bytes(const AHexString: AnsiString): TBytes; overload;
 function TryHex2Bytes(const AHexString: AnsiString; out ABytes : TBytes): boolean; overload;
 function TryHex2Bytes(const AHexString: AnsiString; out ABytes : TBytes): boolean; overload;
 function Bytes2Hex(const ABytes: TBytes; AUsePrefix : boolean = false) : AnsiString;
 function Bytes2Hex(const ABytes: TBytes; AUsePrefix : boolean = false) : AnsiString;
-
-{ Binary-safe StrComp replacement. StrComp will return 0 for when str1 and str2 both start with NUL character. }
-function BinStrComp(const Str1, Str2 : String): Integer;
+function BinStrComp(const Str1, Str2 : String): Integer; // Binary-safe StrComp replacement. StrComp will return 0 for when str1 and str2 both start with NUL character.
 function BytesCompare(const ABytes1, ABytes2: TBytes): integer;
 function BytesCompare(const ABytes1, ABytes2: TBytes): integer;
 function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean; inline;
 function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean; inline;
-
-{ Ternary operator equivalent of predicate ? (true-val) : (false-value) }
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64; overload;
@@ -58,21 +53,14 @@ function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Double)
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: string): string; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: string): string; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: TObject): TObject; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: TObject): TObject; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant; overload;
-
+function ClipValue( AValue, MinValue, MaxValue: Integer) : Integer;
+function MinValue(const AArray : array of Cardinal) : Cardinal;
+function MaxValue(const AArray : array of Cardinal) : Cardinal;
 {$IFDEF FPC}
 {$IFDEF FPC}
 function GetSetName(const aSet:PTypeInfo; Value: Integer):string;
 function GetSetName(const aSet:PTypeInfo; Value: Integer):string;
 function GetSetValue(const aSet:PTypeInfo; Name: String): Integer;
 function GetSetValue(const aSet:PTypeInfo; Name: String): Integer;
 {$ENDIF}
 {$ENDIF}
 
 
-{ Clip/Min/Max Value }
-function ClipValue( AValue, MinValue, MaxValue: Integer) : Integer;
-function MinValue(const AArray : array of Cardinal) : Cardinal;
-function MaxValue(const AArray : array of Cardinal) : Cardinal;
-
-{ DateTime functions }
-function TimeStamp : String;
-function UtcTimeStamp : String;
-
 type
 type
 
 
   {$IFNDEF FPC}
   {$IFNDEF FPC}
@@ -167,6 +155,52 @@ type
       destructor Destroy; override;
       destructor Destroy; override;
   end;
   end;
 
 
+  { TLogSeverity }
+
+  TLogSeverity = (lsDebug, lsInfo, lsWarning, lsError);
+
+  { TLogMessage }
+
+  TLogMessage = record
+  private
+    FTimestamp : TDateTime;
+    FSeverity : TLogSeverity;
+    FText : String;
+  public
+    property TimeStamp : TDateTime read FTimestamp;
+    property Severity : TLogSeverity read FSeverity;
+    property Text : String read FText;
+    class function From(const AText : String; ASeverity : TLogSeverity; ATimeStamp : TDateTime) : TLogMessage; static; overload;
+    class function From(const AText : String; ASeverity : TLogSeverity = lsError) : TLogMessage; static; overload;
+  end;
+
+  { TResult }
+
+  TResult = record
+  private
+    FMessages : TArray<TLogMessage>;
+    FValue : Variant;
+    FHasError : boolean;
+    function GetIsSuccess : boolean; inline;
+  public
+    property Messages : TArray<TLogMessage> read FMessages;
+    property Value : Variant read FValue write FValue;  // used to carry an optional return value
+    property IsFailure : boolean read FHasError;
+    property IsSuccess : boolean read GetIsSuccess;
+    procedure Add(const ALogMessage : TLogMessage); overload;
+    procedure Add(ASeverity : TLogSeverity; const AString : String); overload;
+    procedure AddDebug(const AString : String);
+    procedure AddInfo(const AString : String);
+    procedure AddWarning(const AString : String);
+    procedure AddError(const AString : String);
+    function ToString(AIncludeTimeStamp : boolean = false) : String; overload;
+    function ToString(AIncludeTimeStamp, AIncludeSeverity : boolean) : String; overload;
+    class function Success : TResult; static;
+    class function Success(const AText : String) : TResult; static; overload;
+    class function Failure : TResult; static; overload;
+    class function Failure(const AText : String) : TResult; static; overload;
+  end;
+
   { TDateTimeHelper }
   { TDateTimeHelper }
 
 
   TDateTimeHelper = record helper for TDateTime
   TDateTimeHelper = record helper for TDateTime
@@ -183,6 +217,7 @@ type
      function GetTime: TDateTime; inline;
      function GetTime: TDateTime; inline;
      function GetYear: Word; inline;
      function GetYear: Word; inline;
      class function GetNow: TDateTime; static; inline;
      class function GetNow: TDateTime; static; inline;
+     class function GetNowUtc: TDateTime; static; inline;
      class function GetToday: TDateTime; static; inline;
      class function GetToday: TDateTime; static; inline;
      class function GetTomorrow: TDateTime; static; inline;
      class function GetTomorrow: TDateTime; static; inline;
      class function GetYesterDay: TDateTime; static; inline;
      class function GetYesterDay: TDateTime; static; inline;
@@ -190,6 +225,7 @@ type
      class function Create(const aYear, aMonth, aDay: Word): TDateTime; overload; static; inline;
      class function Create(const aYear, aMonth, aDay: Word): TDateTime; overload; static; inline;
      class function Create(const aYear, aMonth, aDay, aHour, aMinute, aSecond, aMillisecond: Word): TDateTime; overload; static; inline;
      class function Create(const aYear, aMonth, aDay, aHour, aMinute, aSecond, aMillisecond: Word): TDateTime; overload; static; inline;
 
 
+     class property NowUtc: TDateTime read GetNowUtc;
      class property Now: TDateTime read GetNow;
      class property Now: TDateTime read GetNow;
      class property Today: TDateTime read GetToday;
      class property Today: TDateTime read GetToday;
      class property Yesterday: TDateTime read GetYesterDay;
      class property Yesterday: TDateTime read GetYesterDay;
@@ -209,6 +245,7 @@ type
      property Second: Word read GetSecond;
      property Second: Word read GetSecond;
      property Millisecond: Word read GetMillisecond;
      property Millisecond: Word read GetMillisecond;
 
 
+     function ToIntlString : String; inline;
      function ToString(const aFormatStr: string = ''): string; inline;
      function ToString(const aFormatStr: string = ''): string; inline;
 
 
      function StartOfYear: TDateTime; inline;
      function StartOfYear: TDateTime; inline;
@@ -343,12 +380,15 @@ type
 resourcestring
 resourcestring
   sNotImplemented = 'Not implemented';
   sNotImplemented = 'Not implemented';
   sInvalidParameter_OutOfBounds = 'Invalid Parameter: %s out of bounds';
   sInvalidParameter_OutOfBounds = 'Invalid Parameter: %s out of bounds';
+  sLogDebug = 'DEBUG';
+  sLogInfo = 'INFO';
+  sLogWarn = 'WARNING';
+  sLogError = 'ERROR';
 
 
 implementation
 implementation
 
 
 uses dateutils, {$IFDEF FPC}StrUtils{$ELSE}System.AnsiStrings{$ENDIF};
 uses dateutils, {$IFDEF FPC}StrUtils{$ELSE}System.AnsiStrings{$ENDIF};
 
 
-{ CONSTANTS }
 const
 const
   IntlDateTimeFormat : TFormatSettings = (
   IntlDateTimeFormat : TFormatSettings = (
     DateSeparator : '-';
     DateSeparator : '-';
@@ -365,16 +405,13 @@ const
   ZeroTimeSpan: TTimeSpan = (FMillis: 0);
   ZeroTimeSpan: TTimeSpan = (FMillis: 0);
   {$ENDIF}
   {$ENDIF}
 
 
-{ VARIABLES }
-
 var
 var
-  {DynamicType: TDynamic = nil;}
   MinTimeStampDateTime : TDateTime = 0;
   MinTimeStampDateTime : TDateTime = 0;
   VarTrue : Variant;
   VarTrue : Variant;
   VarFalse : Variant;
   VarFalse : Variant;
 
 
 
 
-{%region Global functions }
+{ Global helper functions }
 
 
 function String2Hex(const Buffer: AnsiString): AnsiString;
 function String2Hex(const Buffer: AnsiString): AnsiString;
 var
 var
@@ -511,11 +548,148 @@ begin
     Result := CompareMem(@ABytes1[0], @ABytes2[0], ABytes1Len);
     Result := CompareMem(@ABytes1[0], @ABytes2[0], ABytes1Len);
 end;
 end;
 
 
-{%endregion}
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: UInt64): UInt64;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Double): Double;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: string): string;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: TObject): TObject;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant;
+begin
+  if ACondition then
+    Result := ATrueResult
+  else
+    Result := AFalseResult;
+end;
+
+function ClipValue( AValue, MinValue, MaxValue: Integer) : Integer;
+begin
+  if AValue < MinValue then
+    Result := MinValue
+  else if AValue > MaxValue then
+    Result := MaxValue
+  else
+    Result := AValue
+end;
+
+function MinValue(const AArray : array of Cardinal) : Cardinal;
+var i : SizeInt;
+begin
+  if Length(AArray) = 0 then raise EArgumentException.Create('AArray is empty');
+  Result := AArray[Low(AArray)];
+  for i := Low(AArray) to High(AArray) do begin
+    if Result > AArray[i] then
+      Result := AArray[i];
+  end;
+end;
+
+function MaxValue(const AArray : array of Cardinal) : Cardinal;
+var i : SizeInt;
+begin
+  if Length(AArray) = 0 then raise EArgumentException.Create('AArray is empty');
+  Result := AArray[Low(AArray)];
+  for i := Low(AArray) to High(AArray) do begin
+    if Result < AArray[i] then
+      Result := AArray[i];
+  end;
+end;
+
+{$IFDEF FPC}
+
+function GetSetName(const aSet:PTypeInfo; Value: Integer):string;
+var
+  vData1 : PTypeData;
+  vData2 : PTypeData;
+  vCntr  : Integer;
+  v: Integer;
+begin
+  Result := '';
+  if aSet^.Kind = tkSet then begin
+    vData1 := GetTypeData(aSet);
+    vData2 := GetTypeData(vData1^.CompType);
+    for vCntr := vData2^.MinValue to vData2^.MaxValue do
+      if (Value shr vCntr) and 1 <> 0 then
+        Result := Result+ GetEnumName(vData1^.CompType,vCntr)+',';
+    if Result <> '' then Delete(Result, Length(Result), 1);
+  end;
+end;
+
+function GetSetValue(const aSet:PTypeInfo; Name: String): Integer;
+var
+  vData1 : PTypeData;
+  vData2 : PTypeData;
+  vCntr  : Integer;
+  p      : Integer;
+begin
+  Result := 0;
+  if aSet^.Kind = tkSet then begin
+    vData1 := GetTypeData(aSet);
+    vData2 := GetTypeData(vData1^.CompType);
+    for vCntr := vData2^.MinValue to vData2^.MaxValue do begin
+      p := pos(GetEnumName(vData1^.CompType, vCntr), Name);
+      if p = 0 then
+        Continue;
+      if (p = 1) or (Name[p-1] = ',') then
+        Result := Result or (1 shl vCntr);
+    end;
+  end;
+end;
+
+{$ENDIF}
 
 
 {$IFDEF FPC}
 {$IFDEF FPC}
 
 
-{%region TTimeSpan }
+{ TTimeSpan }
 
 
 class constructor TTimeSpan.Create;
 class constructor TTimeSpan.Create;
 begin
 begin
@@ -748,189 +922,138 @@ begin
   AString := Value.ToString;
   AString := Value.ToString;
 end;
 end;
 
 
-{%endregion}
-
 {$ENDIF}
 {$ENDIF}
 
 
-{%region Language-level tools }
+{ TBox }
 
 
-function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal;
+class constructor TBox<T>.Create;
 begin
 begin
-  if ACondition then
-    Result := ATrueResult
-  else
-    Result := AFalseResult;
+  Instances := 0;
 end;
 end;
 
 
-function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer;
+constructor TBox<T>.Create(const AValue: T);
 begin
 begin
-  if ACondition then
-    Result := ATrueResult
-  else
-    Result := AFalseResult;
+  Inherited Create;
+  Inc(Instances);
+  FValue := AValue;
 end;
 end;
 
 
-function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64;
+destructor TBox<T>.Destroy;
 begin
 begin
-  if ACondition then
-    Result := ATrueResult
-  else
-    Result := AFalseResult;
+  Inherited;
+  Dec(Instances);
 end;
 end;
 
 
-function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: UInt64): UInt64;
+{ TLogMessage }
+
+class function TLogMessage.From(const AText : String; ASeverity : TLogSeverity; ATimeStamp : TDateTime) : TLogMessage;
 begin
 begin
-  if ACondition then
-    Result := ATrueResult
-  else
-    Result := AFalseResult;
+  Result := From(AText, lsError);
 end;
 end;
 
 
-function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Double): Double;
+class function TLogMessage.From(const AText : String; ASeverity : TLogSeverity = lsError) : TLogMessage;
 begin
 begin
-  if ACondition then
-    Result := ATrueResult
-  else
-    Result := AFalseResult;
+  Result.FTimeStamp := TDateTime.NowUtc;
+  Result.FSeverity := ASeverity;
+  Result.FText := AText;
 end;
 end;
 
 
-function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: string): string;
+{ TResult }
+
+function TResult.GetIsSuccess : boolean;
 begin
 begin
-  if ACondition then
-    Result := ATrueResult
-  else
-    Result := AFalseResult;
+  Result := NOT FHasError;
 end;
 end;
 
 
-function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: TObject): TObject;
+procedure TResult.Add(const ALogMessage : TLogMessage); overload;
 begin
 begin
-  if ACondition then
-    Result := ATrueResult
-  else
-    Result := AFalseResult;
+  TArrayTool<TLogMessage>.Add(FMessages, ALogMessage);
+  if ALogMessage.Severity = lsError then
+    Self.FHasError := true;
 end;
 end;
 
 
-function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: variant): variant;
+procedure TResult.Add(ASeverity : TLogSeverity; const AString : String); overload;
 begin
 begin
-  if ACondition then
-    Result := ATrueResult
-  else
-    Result := AFalseResult;
+  Add(TLogMessage.From(AString, ASeverity));
 end;
 end;
 
 
-{$IFDEF FPC}
-
-{ Enums }
-
-function GetSetName(const aSet:PTypeInfo; Value: Integer):string;
-var
-  vData1 : PTypeData;
-  vData2 : PTypeData;
-  vCntr  : Integer;
-  v: Integer;
+procedure TResult.AddDebug(const AString : String);
 begin
 begin
-  Result := '';
-  if aSet^.Kind = tkSet then begin
-    vData1 := GetTypeData(aSet);
-    vData2 := GetTypeData(vData1^.CompType);
-    for vCntr := vData2^.MinValue to vData2^.MaxValue do
-      if (Value shr vCntr) and 1 <> 0 then
-        Result := Result+ GetEnumName(vData1^.CompType,vCntr)+',';
-    if Result <> '' then Delete(Result, Length(Result), 1);
-  end;
+  Add(lsDebug, AString);
 end;
 end;
 
 
-function GetSetValue(const aSet:PTypeInfo; Name: String): Integer;
-var
-  vData1 : PTypeData;
-  vData2 : PTypeData;
-  vCntr  : Integer;
-  p      : Integer;
+procedure TResult.AddInfo(const AString : String);
 begin
 begin
-  Result := 0;
-  if aSet^.Kind = tkSet then begin
-    vData1 := GetTypeData(aSet);
-    vData2 := GetTypeData(vData1^.CompType);
-    for vCntr := vData2^.MinValue to vData2^.MaxValue do begin
-      p := pos(GetEnumName(vData1^.CompType, vCntr), Name);
-      if p = 0 then
-        Continue;
-      if (p = 1) or (Name[p-1] = ',') then
-        Result := Result or (1 shl vCntr);
-    end;
-  end;
+  Add(lsInfo, AString);
 end;
 end;
 
 
-{$ENDIF}
-
-{ Clip/Min/Max Value }
-
-function ClipValue( AValue, MinValue, MaxValue: Integer) : Integer;
+procedure TResult.AddWarning(const AString : String);
 begin
 begin
-  if AValue < MinValue then
-    Result := MinValue
-  else if AValue > MaxValue then
-    Result := MaxValue
-  else
-    Result := AValue
+  Add(lsWarning, AString);
 end;
 end;
 
 
-function MinValue(const AArray : array of Cardinal) : Cardinal;
-var i : SizeInt;
+procedure TResult.AddError(const AString : String);
 begin
 begin
-  if Length(AArray) = 0 then raise EArgumentException.Create('AArray is empty');
-  Result := AArray[Low(AArray)];
-  for i := Low(AArray) to High(AArray) do begin
-    if Result > AArray[i] then
-      Result := AArray[i];
-  end;
+  Add(lsError, AString);
 end;
 end;
 
 
-function MaxValue(const AArray : array of Cardinal) : Cardinal;
-var i : SizeInt;
+function TResult.ToString(AIncludeTimeStamp : boolean = false) : String;
 begin
 begin
-  if Length(AArray) = 0 then raise EArgumentException.Create('AArray is empty');
-  Result := AArray[Low(AArray)];
-  for i := Low(AArray) to High(AArray) do begin
-    if Result < AArray[i] then
-      Result := AArray[i];
-  end;
+  Result := ToString(AIncludeTimeStamp, false);
 end;
 end;
 
 
-{ TBox }
-
-class constructor TBox<T>.Create;
+function TResult.ToString(AIncludeTimeStamp, AIncludeSeverity : boolean) : String;
+var
+  i : integer;
+  LLine : String;
 begin
 begin
-  Instances := 0;
+  Result := '';
+  for i := Low(Messages) to High(Messages) do begin
+    LLine := '';
+    if AIncludeTimeStamp then
+      LLine := LLine + '[' + Messages[i].TimeStamp.ToIntlString + '] ';
+    if AIncludeSeverity then
+      case Messages[i].Severity of
+        lsDebug: LLine := LLine + ' ' + sLogDebug + ': ';
+        lsInfo: LLine := LLine + ' ' + sLogInfo + ': ';
+        lsWarning: LLine := LLine + ' ' + sLogWarn + ': ';
+        lsError: LLine := LLine + ' ' + sLogError + ': ';
+      end;
+    LLine := LLine + Messages[i].Text;
+    if i < High(Messages) then
+      LLine := LLine + sLineBreak;
+    Result := Result + LLine;
+  end;
 end;
 end;
 
 
-constructor TBox<T>.Create(const AValue: T);
+class function TResult.Success : TResult; static;
 begin
 begin
-  Inherited Create;
-  Inc(Instances);
-  FValue := AValue;
+  SetLength(Result.FMessages, 0);
+  Result.FValue := Variants.Null;
+  Result.FHasError := false;
 end;
 end;
 
 
-destructor TBox<T>.Destroy;
+class function TResult.Success(const AText : String) : TResult; static; overload;
 begin
 begin
-  Inherited;
-  Dec(Instances);
+  Result := Success;
+  Result.AddInfo(AText);
 end;
 end;
 
 
-{%endregion}
-
-{%region Date/Time Support }
-
-function TimeStamp : String;
+class function TResult.Failure : TResult; static; overload;
 begin
 begin
-  Result := FormatDateTime('yyy-mm-dd hh:nn:ss', Now);
+  SetLength(Result.FMessages, 0);
+  Result.FValue := Variants.Null;
+  Result.FHasError := true;
 end;
 end;
 
 
-function UtcTimeStamp : String;
+class function TResult.Failure(const AText : String) : TResult; static; overload;
 begin
 begin
-  raise Exception.Create(sNotImplemented);
+  Result := Failure;
+  Result.AddError(AText);
 end;
 end;
 
 
+{ TDateTimeHelper }
+
 function TDateTimeHelper.AddDays(const aNumberOfDays: Integer): TDateTime;
 function TDateTimeHelper.AddDays(const aNumberOfDays: Integer): TDateTime;
 begin
 begin
   Result := IncDay(Self, aNumberOfDays);
   Result := IncDay(Self, aNumberOfDays);
@@ -1058,6 +1181,11 @@ begin
   Result := SysUtils.Now;
   Result := SysUtils.Now;
 end;
 end;
 
 
+class function TDateTimeHelper.GetNowUtc: TDateTime;
+begin
+  Result := LocalTimeToUniversal(SysUtils.Now);
+end;
+
 function TDateTimeHelper.GetSecond: Word;
 function TDateTimeHelper.GetSecond: Word;
 begin
 begin
   Result := SecondOf(Self);
   Result := SecondOf(Self);
@@ -1166,6 +1294,11 @@ begin
   Result := StartOfTheYear(Self);
   Result := StartOfTheYear(Self);
 end;
 end;
 
 
+function TDateTimeHelper.ToIntlString : String;
+begin
+  Result := ToString('yyy-mm-dd hh:nn:ss');
+end;
+
 function TDateTimeHelper.ToString(const aFormatStr: string): string;
 function TDateTimeHelper.ToString(const aFormatStr: string): string;
 begin
 begin
   if aFormatStr = '' then
   if aFormatStr = '' then
@@ -1232,9 +1365,7 @@ begin
   Result := DateUtils.YearsBetween(Self, aDateTime);
   Result := DateUtils.YearsBetween(Self, aDateTime);
 end;
 end;
 
 
-{%endregion}
-
-{%region TNotifyManyEventHelper}
+{ TNotifyManyEventHelper }
 
 
 procedure TNotifyManyEventHelper.Add(listener : TNotifyEvent);
 procedure TNotifyManyEventHelper.Add(listener : TNotifyEvent);
 begin
 begin
@@ -1255,9 +1386,7 @@ begin
     self[i](sender);
     self[i](sender);
 end;
 end;
 
 
-{%endregion}
-
-{%region TNotifyManyEventHelperEx}
+{ TNotifyManyEventHelperEx }
 
 
 procedure TNotifyManyEventExHelper.Add(listener : TNotifyEventEx);
 procedure TNotifyManyEventExHelper.Add(listener : TNotifyEventEx);
 begin
 begin
@@ -1278,9 +1407,7 @@ begin
     self[i](sender, args);
     self[i](sender, args);
 end;
 end;
 
 
-{%endregion}
-
-{%region TArrayTool}
+{ TArrayTool }
 
 
 class function TArrayTool<T>.Empty : TArray<T>;
 class function TArrayTool<T>.Empty : TArray<T>;
 begin
 begin
@@ -1514,9 +1641,7 @@ begin
    Result := Length(Values);
    Result := Length(Values);
 end;
 end;
 
 
-{%endregion}
-
-{%region TVariantTool}
+{ TVariantTool }
 
 
 class function TVariantTool.IsBool(const AValue : Variant) : boolean;
 class function TVariantTool.IsBool(const AValue : Variant) : boolean;
 begin
 begin
@@ -1679,8 +1804,6 @@ begin
   Result := (lowercmp = 1) AND (uppercmp = -1);
   Result := (lowercmp = 1) AND (uppercmp = -1);
 end;
 end;
 
 
-{%endregion}
-
 { TFileStreamHelper }
 { TFileStreamHelper }
 {$IFNDEF FPC}
 {$IFNDEF FPC}
 procedure TFileStreamHelper.WriteAnsiString(const AString : String);
 procedure TFileStreamHelper.WriteAnsiString(const AString : String);
@@ -1689,7 +1812,7 @@ begin
 end;
 end;
 {$ENDIF}
 {$ENDIF}
 
 
-{%region TFileTool }
+{ TFileTool }
 
 
 class procedure TFileTool.AppendText(const AFileName: string; const AText: string);
 class procedure TFileTool.AppendText(const AFileName: string; const AText: string);
 var
 var
@@ -1709,8 +1832,6 @@ begin
   end;
   end;
 end;
 end;
 
 
-{%endregion}
-
 initialization
 initialization
   MinTimeStampDateTime:= StrToDateTime('1980-01-01 00:00:000', IntlDateTimeFormat);
   MinTimeStampDateTime:= StrToDateTime('1980-01-01 00:00:000', IntlDateTimeFormat);
   VarTrue := True;
   VarTrue := True;