浏览代码

* timespan unit based on Delphi documentation declarations and an own implementation based on Microsoft documentation.

marcoonthegit 2 年之前
父节点
当前提交
84ea64e59c
共有 2 个文件被更改,包括 468 次插入0 次删除
  1. 1 0
      packages/rtl-objpas/fpmake.pp
  2. 467 0
      packages/rtl-objpas/src/inc/system.timespan.pp

+ 1 - 0
packages/rtl-objpas/fpmake.pp

@@ -65,6 +65,7 @@ begin
     P.IncludePath.Add('src/common',CommonSrcOSes);
 
     T:=P.Targets.AddUnit('system.uitypes.pp',uitypesOses);
+    T:=P.Targets.AddUnit('system.timespan.pp',uitypesOses);
 
     T:=P.Targets.AddUnit('strutils.pp',StrUtilsOses);
       T.ResourceStrings:=true;

+ 467 - 0
packages/rtl-objpas/src/inc/system.timespan.pp

@@ -0,0 +1,467 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 1999-2022 by Marco van de Voort
+        member of the Free Pascal development team.
+
+    Delphi compatibility unit that emulats the C# timespan record
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit system.timespan;
+
+{$mode Delphi}
+
+interface
+
+uses
+  Classes, SysUtils;
+
+Type
+    TTimeSpan = record
+  private
+    FTicks: Int64;
+  strict private
+    function GetDays: Integer;
+    function GetHours: Integer;
+    function GetMinutes: Integer;
+    function GetSeconds: Integer;
+    function GetMilliseconds: Integer;
+    function GetTotalDays: Double;
+    function GetTotalHours: Double;
+    function GetTotalMinutes: Double;
+    function GetTotalSeconds: Double;
+    function GetTotalMilliseconds: Double;
+    class function GetScaledInterval(Value: Double; Scale: Integer): TTimeSpan; static;
+    class constructor Create;
+  strict private class var
+    FMinValue: TTimeSpan;
+    FMaxValue: TTimeSpan;
+    FZero: TTimeSpan;
+  strict private const
+    MillisecondsPerTick = 0.0001;
+    SecondsPerTick = 1e-07;
+    MinutesPerTick = 1.6666666666666667E-09;
+    HoursPerTick = 2.7777777777777777E-11;
+    DaysPerTick = 1.1574074074074074E-12;
+    MillisPerSecond = 1000;
+    MillisPerMinute = 60 * MillisPerSecond;
+    MillisPerHour = 60 * MillisPerMinute;
+    MillisPerDay = 24 * MillisPerHour;
+    MaxSeconds = 922337203685;
+    MinSeconds = -922337203685;
+    MaxMilliseconds = 922337203685477;
+    MinMilliseconds = -922337203685477;
+  public const
+    TicksPerMillisecond = 10000;
+    TicksPerSecond = 1000 * Int64(TicksPerMillisecond);
+    TicksPerMinute = 60 * Int64(TicksPerSecond);
+    TicksPerHour = 60 * Int64(TicksPerMinute);
+    TicksPerDay = 24 * TIcksPerHour;
+  public
+    constructor Create(ATicks: Int64); overload;
+    constructor Create(Hours, Minutes, Seconds: Integer); overload;
+    constructor Create(Days, Hours, Minutes, Seconds: Integer); overload;
+    constructor Create(Days, Hours, Minutes, Seconds, Milliseconds: Integer); overload;
+    function Add(const TS: TTimeSpan): TTimeSpan; overload;
+    function Duration: TTimeSpan;
+    function Negate: TTimeSpan;
+    function Subtract(const TS: TTimeSpan): TTimeSpan; overload;
+    function ToString: string;
+
+    class function FromDays(Value: Double): TTimeSpan; static;
+    class function FromHours(Value: Double): TTimeSpan; static;
+    class function FromMinutes(Value: Double): TTimeSpan; static;
+    class function FromSeconds(Value: Double): TTimeSpan; static;
+    class function FromMilliseconds(Value: Double): TTimeSpan; static;
+    class function FromTicks(Value: Int64): TTimeSpan; static;
+    class function Subtract(const D1, D2: TDateTime): TTimeSpan; overload; static;
+    class function Parse(const S: string): TTimeSpan; static;
+    class function TryParse(const S: string; out Value: TTimeSpan): Boolean; static;
+    class operator Add(const Left, Right: TTimeSpan): TTimeSpan;
+    class operator Add(const Left: TTimeSpan; Right: TDateTime): TDateTime;
+    class operator Add(const Left: TDateTime; Right: TTimeSpan): TDateTime;
+    class operator Subtract(const Left, Right: TTimeSpan): TTimeSpan;
+    class operator Subtract(const Left: TDateTime; Right: TTimeSpan): TDateTime;
+    class operator Equal(const Left, Right: TTimeSpan): Boolean;
+    class operator NotEqual(const Left, Right: TTimeSpan): Boolean;
+    class operator GreaterThan(const Left, Right: TTimeSpan): Boolean;
+    class operator GreaterThanOrEqual(const Left, Right: TTimeSpan): Boolean;
+    class operator LessThan(const Left, Right: TTimeSpan): Boolean;
+    class operator LessThanOrEqual(const Left, Right: TTimeSpan): Boolean;
+    class operator Negative(const Value: TTimeSpan): TTimeSpan;
+    class operator Positive(const Value: TTimeSpan): TTimeSpan;
+    class operator Implicit(const Value: TTimeSpan): string;
+    class operator Explicit(const Value: TTimeSpan): string;
+    property Ticks: Int64 read FTicks;
+    property Days: Integer read GetDays;
+    property Hours: Integer read GetHours;
+    property Minutes: Integer read GetMinutes;
+    property Seconds: Integer read GetSeconds;
+    property Milliseconds: Integer read GetMilliseconds;
+    property TotalDays: Double read GetTotalDays;
+    property TotalHours: Double read GetTotalHours;
+    property TotalMinutes: Double read GetTotalMinutes;
+    property TotalSeconds: Double read GetTotalSeconds;
+    property TotalMilliseconds: Double read GetTotalMilliseconds;
+    class property MinValue: TTimeSpan read FMinValue;
+    class property MaxValue: TTimeSpan read FMaxValue;
+    class property Zero: TTimeSpan read FZero;
+  end;
+
+implementation
+
+Uses Math;
+
+// Embacadero documentation is poor, I used MS' http://www1.cs.columbia.edu/~lok/csharp/refdocs/System/types/TimeSpan.html
+// mscorlib's docs seems to be not locale dependent, while C# docs mention "culture".
+// for now, I left it non locale dependent.
+
+{ TTimeSpan }
+
+function TTimeSpan.GetDays: Integer;
+begin
+  result:=FTicks div TicksPerDay;
+end;
+
+function TTimeSpan.GetHours: Integer;
+begin
+  result:=(FTicks div TicksPerHour) mod HoursPerDay;
+end;
+
+function TTimeSpan.GetMinutes: Integer;
+begin
+  result:=(FTicks div TicksPerMinute) mod MinsPerHour;
+end;
+
+function TTimeSpan.GetSeconds: Integer;
+begin
+  result:=(FTicks div TicksPerSecond) mod SecsPerMin;
+end;
+
+function TTimeSpan.GetMilliseconds: Integer;
+begin
+  result:=(FTicks div TicksPerMillisecond) mod MillisPerSecond;
+end;
+
+function TTimeSpan.GetTotalDays: Double;
+begin
+  result:=FTicks/TicksPerDay;
+end;
+
+function TTimeSpan.GetTotalHours: Double;
+begin
+  result:=FTicks/TicksPerHour;
+end;
+
+function TTimeSpan.GetTotalMinutes: Double;
+begin
+  result:=FTicks/TicksPerMinute;
+end;
+
+function TTimeSpan.GetTotalSeconds: Double;
+begin
+  result:=FTicks/TicksPerSecond;
+end;
+
+function TTimeSpan.GetTotalMilliseconds: Double;
+begin
+  result:=FTicks/TicksPerMillisecond;
+end;
+
+class function TTimeSpan.GetScaledInterval(Value: Double; Scale: Integer
+  ): TTimeSpan;
+begin //
+  result.FTicks:=round(value*scale);
+end;
+
+class constructor TTimeSpan.Create;
+begin
+  FMinValue.FTicks:= -9223372036854775808;
+  FMaxValue.FTicks:= $7FFFFFFFFFFFFFFF;
+  fzero.fticks:=0;
+end;
+
+constructor TTimeSpan.Create(ATicks: Int64);
+begin
+ FTicks:=ATicks;
+end;
+
+constructor TTimeSpan.Create(Hours, Minutes, Seconds: Integer);
+begin
+ fticks:=seconds*TicksPerSecond+minutes*TicksPerMinute+hours*TicksPerHour;
+end;
+
+constructor TTimeSpan.Create(Days, Hours, Minutes, Seconds: Integer);
+begin
+ fticks:=seconds*TicksPerSecond+minutes*TicksPerMinute+hours*TicksPerHour+days*TicksPerDay;
+end;
+
+constructor TTimeSpan.Create(Days, Hours, Minutes, Seconds,
+  Milliseconds: Integer);
+begin
+ fticks:=milliseconds*TicksPerMillisecond+seconds*TicksPerSecond+minutes*TicksPerMinute+hours*TicksPerHour+days*TicksPerDay;
+
+end;
+
+function TTimeSpan.Add(const TS: TTimeSpan): TTimeSpan;
+begin
+ result.fticks:=fticks+ts.FTicks;
+end;
+
+function TTimeSpan.Duration: TTimeSpan;
+begin
+ result.fticks:=abs(fticks);
+end;
+
+function TTimeSpan.Negate: TTimeSpan;
+begin
+ result.fticks:=-fticks;
+end;
+
+function TTimeSpan.Subtract(const TS: TTimeSpan): TTimeSpan;
+begin
+  result.fticks:=fticks-ts.FTicks;
+end;
+
+//   "[-]d.hh:mm:ss.ff"  according to mscorlib spec.
+function TTimeSpan.ToString: string;
+var  dd,hh,mm,ss,ms : integer;
+     res : int64;
+     moredays : boolean;
+     s : string;
+begin
+  dd:= abs(fticks) div TicksPerDay;
+  res:= abs(fticks) mod TicksPerDay;
+  moredays:=res<>0;
+  hh := res div TicksPerHour;
+  res:= res mod TicksPerHour;
+  mm:=  res div TicksPerMinute;
+  res:= res mod TicksPerMinute;
+  ss:=  res div TicksPerSecond;
+  ms:=  res mod TicksPerSecond;
+  if sign(fticks)=-1 then
+     result:='-'
+  else
+     result:='';
+  if (dd<>0) then
+    begin
+      result:=result+inttostr(dd);
+      if moredays then
+        result:=result+'.';
+    end;
+  // always hhmmss according to comments of parse() docs.
+  result:=result+format('%.2d',[hh])+':';
+  result:=result+format('%.2d',[mm])+':';
+  // .parse docs
+
+  // 7 digit fractional part. Since resolution is .1us, 7 digits is sub ms integer padded with zeroes
+  if ms<>0 then
+    result:=result+format('%.2d',[ss])+'.'+ Format('%.*d',[7, ms])
+    // scan from back to remove trailing zeroes?
+  else
+    result:=result+inttostr(ss);
+  dd:=length(result);
+  while (dd>0) and (result[dd]='0') do
+    dec(dd);
+  setlength(result,dd);
+end;
+
+class function TTimeSpan.FromDays(Value: Double): TTimeSpan;
+begin
+  result.fticks:=round(value*TicksPerDay);
+end;
+
+class function TTimeSpan.FromHours(Value: Double): TTimeSpan;
+begin
+  result.fticks:=round(value*TicksPerHour);
+end;
+
+class function TTimeSpan.FromMinutes(Value: Double): TTimeSpan;
+begin
+  result.fticks:=round(value*TicksPerMinute);
+end;
+
+class function TTimeSpan.FromSeconds(Value: Double): TTimeSpan;
+begin
+  result.fticks:=round(value*TicksPerSecond);
+end;
+
+class function TTimeSpan.FromMilliseconds(Value: Double): TTimeSpan;
+begin
+  result.fticks:=round(value*TicksPerMillisecond);
+end;
+
+class function TTimeSpan.FromTicks(Value: Int64): TTimeSpan;
+begin
+ result.fticks:=value;
+end;
+
+class function TTimeSpan.Subtract(const D1, D2: TDateTime): TTimeSpan;
+begin
+  result.fticks:=round((d1-d2)*TicksPerDay);
+end;
+
+class function TTimeSpan.Parse(const S: string): TTimeSpan;
+begin
+  {if not}   tryparse(s,result);  {then}
+   {some default ? }
+end;
+
+class function TTimeSpan.TryParse(const S: string; out Value: TTimeSpan
+  ): Boolean;
+var i,len,k,v,v2 : integer;
+    sgn : boolean;
+begin
+  value.fticks:=0;
+  i:=1; len:=length(s);
+  while (i<=len) and (s[i]=' ') do // skip spaces.
+   inc(i);
+  sgn:=(i<=len) and (s[i]='-');
+  if sgn then
+    inc(i);
+  k:=pos('.',s,i);
+  v:=pos(':',s,i);
+  if k>v then        // difference dot for days[.]hrs vs dot for sec[.]millisecs
+                     // If : is earlier, there is no days dot.
+    k:=0;
+  if k<>0 then
+    begin
+      if not trystrtoint(copy (s,i,k-i),v) then
+        exit(false);
+       value.fticks:=int64(v)*ticksperday;
+       i:=k+1;
+    end;
+  k:=pos(':',s,i);
+  if k=0 then     // hr part mandatory.
+    exit(false);
+  if not trystrtoint(copy (s,i,k-i),v) then
+    exit(false);
+  value.fticks:=value.fticks+int64(v)*TicksPerHour;
+  i:=k+1;
+  k:=pos(':',s,i);
+  if k=0 then     // min part mandatory.
+    exit(false);
+  if not trystrtoint(copy (s,i,k-i),v) then
+    exit(false);
+   value.fticks:=value.fticks+int64(v)*TicksPerMinute;
+  i:=k+1;
+  k:=pos('.',s,i);
+  if k=0 then     // sec part mandatory, but the dot at the end not.
+    begin
+      k:=pos(' ',s,i); // there could be trailing whitespace.
+      if k=0 then
+        k:=len+1;     // simulate hypothetical point after end of string
+    end;
+  if not trystrtoint(copy (s,i,k-i),v) then
+    exit(false);
+  value.fticks:=value.fticks+int64(v)*TicksPerSecond;
+  if k>len then
+    exit(true);
+  i:=k+1;
+  k:=pos(' ',s,i); // there could be trailing whitespace.
+  if k=0 then
+    k:=len+1;     // simulate hypothetical point after end of string
+  if not trystrtoint(copy (s,i,k-i),v) then
+    exit(false);
+  k:=k-i; // digits.
+  v2:=1;
+  while (k<7) do
+    begin
+      v2:=v2*10;
+      inc(k);
+    end;
+  value.fticks:=value.fticks+int64(v)*v2;
+  if sgn then
+    value.fticks:=-value.fticks;
+  result:=true;
+  // currently doesn't check that after the last space there can be other characters, assumes it is all whitespace
+end;
+
+class operator TTimeSpan.Add(const Left, Right: TTimeSpan): TTimeSpan;
+begin
+ result.fticks:=left.fticks+right.fticks;
+end;
+
+class operator TTimeSpan.Add(const Left: TTimeSpan; Right: TDateTime
+  ): TDateTime;
+begin
+ result:=left.fticks/ticksperday+right;
+end;
+
+class operator TTimeSpan.Add(const Left: TDateTime; Right: TTimeSpan
+  ): TDateTime;
+begin
+  result:=left+right.fticks/TicksPerDay;
+end;
+
+class operator TTimeSpan.Subtract(const Left, Right: TTimeSpan): TTimeSpan;
+begin
+  result.FTicks:=left.FTicks-right.fticks;
+end;
+
+class operator TTimeSpan.Subtract(const Left: TDateTime; Right: TTimeSpan
+  ): TDateTime;
+begin
+  result:=left-right.fticks/TicksPerDay;
+end;
+
+class operator TTimeSpan.Equal(const Left, Right: TTimeSpan): Boolean;
+begin
+  result:=left.fticks=right.fticks;
+end;
+
+class operator TTimeSpan.NotEqual(const Left, Right: TTimeSpan): Boolean;
+begin
+  result:=left.fticks<>right.fticks;
+end;
+
+class operator TTimeSpan.GreaterThan(const Left, Right: TTimeSpan): Boolean;
+begin
+  result:=left.fticks>right.fticks;
+end;
+
+class operator TTimeSpan.GreaterThanOrEqual(const Left, Right: TTimeSpan
+  ): Boolean;
+begin
+  result:=left.fticks>=right.fticks;
+end;
+
+class operator TTimeSpan.LessThan(const Left, Right: TTimeSpan): Boolean;
+begin
+  result:=left.fticks<right.fticks;
+end;
+
+class operator TTimeSpan.LessThanOrEqual(const Left, Right: TTimeSpan): Boolean;
+begin
+  result:=left.fticks<=right.fticks;
+end;
+
+class operator TTimeSpan.Negative(const Value: TTimeSpan): TTimeSpan;
+begin
+  result.fticks:=-value.fticks;
+end;
+
+class operator TTimeSpan.Positive(const Value: TTimeSpan): TTimeSpan;
+begin
+  result.fticks:=value.fticks; // abs ?
+end;
+
+class operator TTimeSpan.Implicit(const Value: TTimeSpan): string;
+begin
+  result:=value.tostring;
+end;
+
+class operator TTimeSpan.Explicit(const Value: TTimeSpan): string;
+begin
+  result:=value.tostring;
+end;
+
+end.
+