Skip to content

Instantly share code, notes, and snippets.

@BeRo1985
Created October 11, 2023 07:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BeRo1985/68417797fb9013a5d69fe3a839b2abb8 to your computer and use it in GitHub Desktop.
Save BeRo1985/68417797fb9013a5d69fe3a839b2abb8 to your computer and use it in GitHub Desktop.
PasFastDateUtils - fast date functions for FreePascal and Delphi
// Fast date functions for FreePascal and Delphi - Pascal implementation by Benjamin Rosseaux - benjamin@rosseaux.com - Public Domain
// Based on: https://www.youtube.com/watch?v=J9KijLyP-yg - Implementing Fast Calendar Algorithms: Speeding Date - Cassio Neri - C++ on Sea 2023
unit PasFastDateUtils;
{$ifdef fpc}
{$mode delphi}
{$endif}
interface
uses SysUtils,Math;
procedure DecodeDate(const aDate:Int32;out aYear,aMonth,aDay:Int32); // Epoch is 0000-Mar-01
function EncodeDate(const aYear,aMonth,aDay:Int32):Int32; // Epoch is 0000-Mar-01
function IsLeapYear(const aYear:UInt32):boolean;
function GetLastDayOfMonth(const aYear,aMonth:Int32):Int32;
implementation
procedure DecodeDate(const aDate:Int32;out aYear,aMonth,aDay:Int32); // Epoch is 0000-Mar-01
var n1,c,nc,n2,z,ny,y,n3,m,d,j:UInt32;
p2:UInt64;
begin
// Faster than the classic method, because no branches and no lookup tables
// Century
n1:=(aDate*4)+3;
c:=n1/146097;
nc:=(n1 mod 146097) div 4;
// Year
n2:=(nc*4)+3;
p2:=n2*UInt64(2939745);
z:=p2 div 4294967296;
ny:=((p2 mod 4294967296) div 2939745) div 4;
j:=((ny-305) shr 31) and 1; // j:=ord(ny>=306) and 1; // j:=IfThen(ny>=306,1,0);
aYear:=((c*100)+z)+j;
// Month and day
n3:=(ny*2141)+197913;
aMonth:=(n3 div 65536)-(j*12);
aDay:=((n3 mod 65536) div 2141)+1;
end;
function EncodeDate(const aYear,aMonth,aDay:Int32):Int32; // Epoch is 0000-Mar-01
var t,j,y,m,d:Int32;
begin
j:=((ny-305) shr 31) and 1; // j:=ord(ny>=306) and 1; // j:=IfThen(ny>=306,1,0);
t:=1-(((m-3) shr 31) and 1); // t:=ord(m<3) and 1; // t:=IfThen(m>2,0,1);
y:=aYear-(t+j);
m:=aMonth-((t-j)*12);
d:=aDay-1;
y:=(((y*1461) div 4)-(y div 100))+(y div 400);
m:=((m*153)-457) div 5;
result:=y+m+d;
end;
function IsLeapYear(const aYear:UInt32):boolean;
var d:UInt32;
begin
// Always does two divisibility checks, 3x faster than the classic method, because lower entropy helps the branch predictor of the CPU
d:=IfThen((aYear mod 400)<>0,4,16); // 4 shl (ord((aYear mod 100)=0) shl 1); // 4 or 16, 16 isn't wrong here, because if a number is multiple of 100 and 400 at the same time, it's also multiple of 16, and power of two is much cheaper to evaluate,
result:=(aYear mod d)=0;
end;
function GetLastDayOfMonth(const aYear,aMonth:Int32):Int32;
begin
// Without lookup tables
case aMonth of
2:begin
result:=28+(ord(IsLeapYear(aYear)) and 1);
end;
else begin
result:=30 or (aMonth xor (aMonth shr 3)); // Alternative: result:=30 or ((9*aMonth) div 8);
end;
end;
end;
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment