Browse Source

* Add TDateTimeHelper by Colin Johnsun. Fixes issue #40097

Michaël Van Canneyt 2 years ago
parent
commit
d5be4efc16
1 changed files with 499 additions and 1 deletions
  1. 499 1
      packages/rtl-objpas/src/inc/dateutil.inc

+ 499 - 1
packages/rtl-objpas/src/inc/dateutil.inc

@@ -1,5 +1,6 @@
 {$mode objfpc}
 {$mode objfpc}
 {$h+}
 {$h+}
+{$modeswitch typehelpers}
 {
 {
     This file is part of the Free Pascal run time library.
     This file is part of the Free Pascal run time library.
     Copyright (c) 1999-2000 by the Free Pascal development team
     Copyright (c) 1999-2000 by the Free Pascal development team
@@ -94,7 +95,8 @@ Function TimeOf(const AValue: TDateTime): TDateTime;
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}
 
 
 Function IsInLeapYear(const AValue: TDateTime): Boolean;
 Function IsInLeapYear(const AValue: TDateTime): Boolean;
-Function IsPM(const AValue: TDateTime): Boolean;
+Function IsPM(const AValue: TDateTime): Boolean; inline;
+function IsAM(const AValue: TDateTime): Boolean; inline;
 Function IsValidDate(const AYear, AMonth, ADay: Word): Boolean;
 Function IsValidDate(const AYear, AMonth, ADay: Word): Boolean;
 Function IsValidTime(const AHour, AMinute, ASecond, AMilliSecond: Word): Boolean;
 Function IsValidTime(const AHour, AMinute, ASecond, AMilliSecond: Word): Boolean;
 Function IsValidDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): Boolean;
 Function IsValidDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): Boolean;
@@ -474,6 +476,116 @@ Function ISO8601ToDateDef(const DateString: string; aDefault : TDateTime; Return
 Function TryISO8601ToDate(const DateString: string; out ADateTime: TDateTime; ReturnUTC : Boolean = True) : Boolean;
 Function TryISO8601ToDate(const DateString: string; out ADateTime: TDateTime; ReturnUTC : Boolean = True) : Boolean;
 
 
 
 
+type
+  // Original code by Colin Johnsun, used with permission under FPC license.
+  // See https://gitlab.com/freepascal.org/fpc/source/-/issues/40097
+  
+  TDateTimeHelper = type helper for TDateTime
+  private
+    function GetDay: Word; inline;
+    function GetDate: TDateTime; inline;
+    function GetDayOfWeek: Word; inline;
+    function GetDayOfYear: Word; inline;
+    function GetHour: Word; inline;
+    function GetMillisecond: Word; inline;
+    function GetMinute: Word; inline;
+    function GetMonth: Word; inline;
+    function GetSecond: Word; inline;
+    function GetTime: TDateTime; inline;
+    function GetYear: Word; inline;
+    class function GetNow: TDateTime; static; inline;
+    class function GetToday: TDateTime; static; inline;
+    class function GetTomorrow: TDateTime; static; inline;
+    class function GetYesterDay: TDateTime; static; inline;
+    function GetUnixTime: Int64;
+    function GetTotalSecounds: Int64;
+    // Parse a string as TDateTime, using a string format ex.('MM/dd/yyyy hh:mm:ss')
+    class function Parse(Date: string; aFormat: string = ''; aDateSeparator:
+      Char = #0; aTimeSeparator: Char = #0): TDateTime; static; inline;
+
+   // Parse a string as TDateTime, using local string ex. ('en-US')
+    class function ParseLocal(Date: string; local: string = ''): TDateTime;
+      static; inline;
+  public
+    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(Date: string; aFormat: string = ''; aDateSeparator:
+      Char = #0; aTimeSeparator: Char = #0): TDateTime; overload; static; inline;
+    class function CreateLocal(Date: string; local: string = ''): TDateTime;
+      static; inline;
+    class function CreateUnixTime(const Value: Int64): TDateTime; static; inline;
+    class function CreateTotalSeconds(const Value: Int64): TDateTime; static; inline;
+    class property Now: TDateTime read GetNow;
+    class property Today: TDateTime read GetToday;
+    class property Yesterday: TDateTime read GetYesterDay;
+    class property Tomorrow: TDateTime read GetTomorrow;
+    property Date: TDateTime read GetDate;
+    property Time: TDateTime read GetTime;
+    property DayOfWeek: Word read GetDayOfWeek;
+    property DayOfYear: Word read GetDayOfYear;
+    property Year: Word read GetYear;
+    property Month: Word read GetMonth;
+    property Day: Word read GetDay;
+    property Hour: Word read GetHour;
+    property Minute: Word read GetMinute;
+    property Second: Word read GetSecond;
+    property Millisecond: Word read GetMillisecond;
+    property UnixTime: Int64 read GetUnixTime;
+    property TotalSeconds: Int64 read GetTotalSecounds;
+    function ToString(const aFormatStr: string = ''): string; inline;
+    function StartOfYear: TDateTime; inline;
+    function EndOfYear: TDateTime; inline;
+    function StartOfMonth: TDateTime; inline;
+    function EndOfMonth: TDateTime; inline;
+    function StartOfWeek: TDateTime; inline;
+    function EndOfWeek: TDateTime; inline;
+    function StartOfDay: TDateTime; inline;
+    function EndOfDay: TDateTime; inline;
+    function AddYears(const aNumberOfYears: Integer = 1): TDateTime; inline;
+    function AddMonths(const aNumberOfMonths: Integer = 1): TDateTime; inline;
+    function AddDays(const aNumberOfDays: Integer = 1): TDateTime; inline;
+    function AddHours(const aNumberOfHours: Int64 = 1): TDateTime; inline;
+    function AddMinutes(const aNumberOfMinutes: Int64 = 1): TDateTime; inline;
+    function AddSeconds(const aNumberOfSeconds: Int64 = 1): TDateTime; inline;
+    function AddMilliseconds(const aNumberOfMilliseconds: Int64 = 1): TDateTime; inline;
+    function CompareTo(const aDateTime: TDateTime): TValueRelationship; inline;
+    function Equals(const aDateTime: TDateTime): Boolean; inline;
+    function IsSameDay(const aDateTime: TDateTime): Boolean; inline;
+    function InRange(const aStartDateTime, aEndDateTime: TDateTime; const
+      aInclusive: Boolean = True): Boolean; inline;
+    function IsInLeapYear: Boolean; inline;
+    function IsToday: Boolean; inline;
+    function IsAM: Boolean; inline;
+    function IsPM: Boolean; inline;
+    function YearsBetween(const aDateTime: TDateTime): Integer; inline;
+    function MonthsBetween(const aDateTime: TDateTime): Integer; inline;
+    function WeeksBetween(const aDateTime: TDateTime): Integer; inline;
+    function DaysBetween(const aDateTime: TDateTime): Integer; inline;
+    function HoursBetween(const aDateTime: TDateTime): Int64; inline;
+    function MinutesBetween(const aDateTime: TDateTime): Int64; inline;
+    function SecondsBetween(const aDateTime: TDateTime): Int64; inline;
+    function MilliSecondsBetween(const aDateTime: TDateTime): Int64; inline;
+    function WithinYears(const aDateTime: TDateTime; const aYears: Integer):
+      Boolean; inline;
+    function WithinMonths(const aDateTime: TDateTime; const aMonths: Integer):
+      Boolean; inline;
+    function WithinWeeks(const aDateTime: TDateTime; const aWeeks: Integer):
+      Boolean; inline;
+    function WithinDays(const aDateTime: TDateTime; const aDays: Integer):
+      Boolean; inline;
+    function WithinHours(const aDateTime: TDateTime; const aHours: Int64):
+      Boolean; inline;
+    function WithinMinutes(const aDateTime: TDateTime; const aMinutes: Int64):
+      Boolean; inline;
+    function WithinSeconds(const aDateTime: TDateTime; const aSeconds: Int64):
+      Boolean; inline;
+    function WithinMilliseconds(const aDateTime: TDateTime; const AMilliseconds:
+      Int64): Boolean; inline;
+  end;
+
+
 implementation
 implementation
 
 
 uses sysconst;
 uses sysconst;
@@ -525,6 +637,11 @@ begin
   Result:=(HourOf(AValue)>=12);
   Result:=(HourOf(AValue)>=12);
 end;
 end;
 
 
+Function IsAM(const AValue: TDateTime): Boolean; inline;
+begin
+  Result:=(HourOf(AValue)<12);
+end;
+
 
 
 Function IsValidDate(const AYear, AMonth, ADay: Word): Boolean;
 Function IsValidDate(const AYear, AMonth, ADay: Word): Boolean;
 begin
 begin
@@ -3058,6 +3175,387 @@ begin
     Result:=aDefault;
     Result:=aDefault;
 end;
 end;
 
 
+{ TDateTimeHelper }
+
+function TDateTimeHelper.AddDays(const aNumberOfDays: Integer): TDateTime;
+begin
+  Result := IncDay(Self, aNumberOfDays);
+end;
+
+function TDateTimeHelper.AddHours(const aNumberOfHours: Int64): TDateTime;
+begin
+  Result := IncHour(Self, aNumberOfHours);
+end;
+
+function TDateTimeHelper.AddMilliseconds(const aNumberOfMilliseconds: Int64): TDateTime;
+begin
+  Result := IncMilliSecond(Self, aNumberOfMilliseconds);
+end;
+
+function TDateTimeHelper.AddMinutes(const aNumberOfMinutes: Int64): TDateTime;
+begin
+  Result := IncMinute(Self, aNumberOfMinutes);
+end;
+
+function TDateTimeHelper.AddMonths(const aNumberOfMonths: Integer): TDateTime;
+begin
+  Result := IncMonth(Self, aNumberOfMonths);
+end;
+
+function TDateTimeHelper.AddSeconds(const aNumberOfSeconds: Int64): TDateTime;
+begin
+  Result := IncSecond(Self, aNumberOfSeconds);
+end;
+
+function TDateTimeHelper.AddYears(const aNumberOfYears: Integer): TDateTime;
+begin
+  Result := IncYear(Self, aNumberOfYears);
+end;
+
+function TDateTimeHelper.CompareTo(const aDateTime: TDateTime): TValueRelationship;
+begin
+  Result := CompareDateTime(Self, aDateTime);
+end;
+
+class function TDateTimeHelper.Create(const aYear, aMonth, aDay: Word): TDateTime;
+begin
+  Result := EncodeDate(aYear, aMonth, aDay);
+end;
+
+class function TDateTimeHelper.Create(const aYear, aMonth, aDay, aHour, aMinute,
+  aSecond, aMillisecond: Word): TDateTime;
+begin
+  Result := EncodeDateTime(aYear, aMonth, aDay, aHour, aMinute, aSecond, aMillisecond);
+end;
+
+class function TDateTimeHelper.CreateTotalSeconds(const Value: Int64): TDateTime;
+begin
+  Result := (Value / 86400);
+end;
+
+class function TDateTimeHelper.CreateUnixTime(const Value: Int64): TDateTime;
+begin
+  Result := (Value / 86400) + 25569;
+end;
+
+function TDateTimeHelper.DaysBetween(const aDateTime: TDateTime): Integer;
+begin
+  Result :=DateUtils.DaysBetween(Self, aDateTime);
+end;
+
+function TDateTimeHelper.EndOfDay: TDateTime;
+begin
+  Result := EndOfTheDay(Self);
+end;
+
+function TDateTimeHelper.EndOfMonth: TDateTime;
+begin
+  Result := EndOfTheMonth(Self);
+end;
+
+function TDateTimeHelper.EndOfWeek: TDateTime;
+begin
+  Result := EndOfTheWeek(Self);
+end;
+
+function TDateTimeHelper.EndOfYear: TDateTime;
+begin
+  Result := EndOfTheYear(Self);
+end;
+
+function TDateTimeHelper.Equals(const aDateTime: TDateTime): Boolean;
+begin
+  Result := SameDateTime(Self, aDateTime);
+end;
+
+function TDateTimeHelper.GetDate: TDateTime;
+begin
+  Result := DateOf(Self);
+end;
+
+function TDateTimeHelper.GetDay: Word;
+begin
+  Result := DayOf(Self);
+end;
+
+function TDateTimeHelper.GetDayOfWeek: Word;
+begin
+  Result := DayOfTheWeek(Self);
+end;
+
+function TDateTimeHelper.GetDayOfYear: Word;
+begin
+  Result := DayOfTheYear(Self);
+end;
+
+function TDateTimeHelper.GetHour: Word;
+begin
+  Result := HourOf(Self);
+end;
+
+function TDateTimeHelper.GetMillisecond: Word;
+begin
+  Result := MilliSecondOf(Self);
+end;
+
+function TDateTimeHelper.GetMinute: Word;
+begin
+  Result := MinuteOf(Self);
+end;
+
+function TDateTimeHelper.GetMonth: Word;
+begin
+  Result := MonthOf(Self);
+end;
+
+class function TDateTimeHelper.GetNow: TDateTime;
+begin
+  Result := SysUtils.Now;
+end;
+
+function TDateTimeHelper.GetSecond: Word;
+begin
+  Result := SecondOf(Self);
+end;
+
+function TDateTimeHelper.GetTime: TDateTime;
+begin
+  Result := TimeOf(Self);
+end;
+
+class function TDateTimeHelper.GetToday: TDateTime;
+begin
+  Result := SysUtils.Date;
+end;
+
+class function TDateTimeHelper.GetTomorrow: TDateTime;
+begin
+  Result := SysUtils.Date + 1;
+end;
+
+function TDateTimeHelper.GetTotalSecounds: Int64;
+begin
+  Result := Trunc(86400 * self);
+end;
+
+function TDateTimeHelper.GetUnixTime: Int64;
+begin
+  Result := Trunc((self - 25569) * 86400);
+end;
+
+function TDateTimeHelper.GetYear: Word;
+begin
+  Result := YearOf(Self);
+end;
+
+class function TDateTimeHelper.GetYesterDay: TDateTime;
+begin
+  Result := SysUtils.Date - 1;
+end;
+
+function TDateTimeHelper.HoursBetween(const aDateTime: TDateTime): Int64;
+begin
+  Result := DateUtils.HoursBetween(Self, aDateTime);
+end;
+
+function TDateTimeHelper.InRange(const aStartDateTime, aEndDateTime: TDateTime;
+  const aInclusive: Boolean): Boolean;
+begin
+  Result := DateTimeInRange(Self, aStartDateTime, aEndDateTime, aInclusive);
+end;
+
+function TDateTimeHelper.IsAM: Boolean;
+begin
+  Result := DateUtils.IsAM(Self);
+end;
+
+function TDateTimeHelper.IsInLeapYear: Boolean;
+begin
+  Result := DateUtils.IsInLeapYear(Self);
+end;
+
+function TDateTimeHelper.IsPM: Boolean;
+begin
+  Result := DateUtils.IsPM(Self);
+end;
+
+function TDateTimeHelper.IsSameDay(const aDateTime: TDateTime): Boolean;
+begin
+  Result := DateUtils.IsSameDay(Self, aDateTime);
+end;
+
+function TDateTimeHelper.IsToday: Boolean;
+begin
+  Result := DateUtils.IsToday(Self);
+end;
+
+function TDateTimeHelper.MilliSecondsBetween(const aDateTime: TDateTime): Int64;
+begin
+  Result := DateUtils.MilliSecondsBetween(Self, aDateTime);
+end;
+
+function TDateTimeHelper.MinutesBetween(const aDateTime: TDateTime): Int64;
+begin
+  Result := DateUtils.MinutesBetween(Self, aDateTime);
+end;
+
+function TDateTimeHelper.MonthsBetween(const aDateTime: TDateTime): Integer;
+begin
+  Result := DateUtils.MonthsBetween(Self, aDateTime);
+end;
+
+function TDateTimeHelper.SecondsBetween(const aDateTime: TDateTime): Int64;
+begin
+  Result := DateUtils.SecondsBetween(Self, aDateTime);
+end;
+
+function TDateTimeHelper.StartOfDay: TDateTime;
+begin
+  Result := StartOfTheDay(Self);
+end;
+
+function TDateTimeHelper.StartOfMonth: TDateTime;
+begin
+  Result := StartOfTheMonth(Self);
+end;
+
+function TDateTimeHelper.StartOfWeek: TDateTime;
+begin
+  Result := StartOfTheWeek(Self);
+end;
+
+function TDateTimeHelper.StartOfYear: TDateTime;
+begin
+  Result := StartOfTheYear(Self);
+end;
+
+function TDateTimeHelper.ToString(const aFormatStr: string): string;
+begin
+  if aFormatStr = '' then
+    Result := DateToStr(Self)
+  else
+    Result := FormatDateTime(aFormatStr, Self);
+end;
+
+function TDateTimeHelper.WeeksBetween(const aDateTime: TDateTime): Integer;
+begin
+  Result := DateUtils.WeeksBetween(Self, aDateTime);
+end;
+
+function TDateTimeHelper.WithinDays(const aDateTime: TDateTime; const aDays:
+  Integer): Boolean;
+begin
+  Result := DateUtils.WithinPastDays(Self, aDateTime, aDays);
+end;
+
+function TDateTimeHelper.WithinHours(const aDateTime: TDateTime; const aHours:
+  Int64): Boolean;
+begin
+  Result := DateUtils.WithinPastHours(Self, aDateTime, aHours);
+end;
+
+function TDateTimeHelper.WithinMilliseconds(const aDateTime: TDateTime; const
+  AMilliseconds: Int64): Boolean;
+begin
+  Result := DateUtils.WithinPastMilliSeconds(Self, aDateTime, AMilliseconds);
+end;
+
+function TDateTimeHelper.WithinMinutes(const aDateTime: TDateTime; const
+  aMinutes: Int64): Boolean;
+begin
+  Result := DateUtils.WithinPastMinutes(Self, aDateTime, aMinutes);
+end;
+
+function TDateTimeHelper.WithinMonths(const aDateTime: TDateTime; const aMonths:
+  Integer): Boolean;
+begin
+  Result := DateUtils.WithinPastMonths(Self, aDateTime, aMonths);
+end;
+
+function TDateTimeHelper.WithinSeconds(const aDateTime: TDateTime; const
+  aSeconds: Int64): Boolean;
+begin
+  Result := DateUtils.WithinPastSeconds(Self, aDateTime, aSeconds);
+end;
+
+function TDateTimeHelper.WithinWeeks(const aDateTime: TDateTime; const aWeeks:
+  Integer): Boolean;
+begin
+  Result := DateUtils.WithinPastWeeks(Self, aDateTime, aWeeks);
+end;
+
+function TDateTimeHelper.WithinYears(const aDateTime: TDateTime; const aYears:
+  Integer): Boolean;
+begin
+  Result := DateUtils.WithinPastYears(Self, aDateTime, aYears);
+end;
+
+function TDateTimeHelper.YearsBetween(const aDateTime: TDateTime): Integer;
+begin
+  Result := DateUtils.YearsBetween(Self, aDateTime);
+end;
+
+class function TDateTimeHelper.Parse(Date: string; aFormat: string = '';
+  aDateSeparator: Char = #0; aTimeSeparator: Char = #0): TDateTime;
+var
+  fs: TFormatSettings;
+  aFormats: TStringArray;
+  aLength: Integer;
+begin
+  aFormats := aFormat.split([' ']);
+
+  aLength := Length(aFormats);
+
+  if aLength = 0 then
+    exit(StrToDateTime(Date));
+
+  fs:=FormatSettings;
+  with fs do
+  begin
+
+    if aDateSeparator <> #0 then
+      DateSeparator := aDateSeparator;
+
+    if not aFormats[0].Trim.IsEmpty then
+      ShortDateFormat := aFormats[0];
+
+    if aLength > 1 then
+    begin
+      if aTimeSeparator <> #0 then
+        TimeSeparator := aTimeSeparator;
+
+      if not aFormats[1].Trim.IsEmpty then
+        ShortTimeFormat := aFormats[1];
+    end;
+  end;
+  Result := StrToDateTime(Date, fs);
+end;
+
+class function TDateTimeHelper.ParseLocal(Date: string; local: string = ''): TDateTime;
+var
+  fs: TFormatSettings;
+begin
+  if local.Trim.IsEmpty then
+    fs := FormatSettings
+  else
+    fs := FormatSettings;
+    // Not yet supported...
+//    fs := TFormatSettings.Create(local);
+
+  Result := StrToDateTime(Date, fs);
+end;
+
+class function TDateTimeHelper.Create(Date, aFormat: string; aDateSeparator,
+  aTimeSeparator: Char): TDateTime;
+begin
+  Result := Parse(Date, aFormat, aDateSeparator, aTimeSeparator);
+end;
+
+class function TDateTimeHelper.CreateLocal(Date, local: string): TDateTime;
+begin
+  Result:= ParseLocal(Date,local);
+end;
+
 
 
 {$else}
 {$else}
 implementation
 implementation