Created
October 11, 2023 07:40
-
-
Save BeRo1985/68417797fb9013a5d69fe3a839b2abb8 to your computer and use it in GitHub Desktop.
PasFastDateUtils - fast date functions for FreePascal and Delphi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Fast date functions for FreePascal and Delphi - Pascal implementation by Benjamin Rosseaux - [email protected] - 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