program CompareDatesFP;
{$mode objfpc}{$H+}
{
Compare two dates in Free Pascal
--------------------------------
This program demonstrates how to compare two dates using only the Free Pascal
standard library. It uses a modular design, clear comments, and a full test suite.
Concepts:
- Parsing dates safely (YYYY-MM-DD)
- Representing dates with a record
- Comparing dates lexicographically (year → month → day)
- Handling invalid formats
- Edge‑case testing
Architecture notes:
- A dedicated function handles parsing.
- Another function performs comparison.
- Main runs multiple predefined test cases.
- No dynamic memory allocation; everything is stack‑based.
- No external dependencies.
Performance notes:
- Comparisons are O(1).
- Parsing is fast and predictable.
- Memory usage is minimal.
Pitfalls:
- Invalid date strings must be handled.
- Comparing raw strings is unsafe; always convert to a structured type.
- No timezone logic here; this is pure date comparison.
}
type
TDate = record
Year: Integer;
Month: Integer;
Day: Integer;
end;
{
Safely parse a date string in the format YYYY-MM-DD.
Error handling:
- Returns False if the date is invalid.
- Writes parsed values into the TDate record when valid.
}
function ParseDate(const S: String; var D: TDate): Boolean;
var
Y, M, Da: Integer;
Dash1, Dash2: Char;
begin
Result := False;
if Length(S) < 10 then Exit;
{ Extract using Val and direct indexing }
if (S[5] <> '-') or (S[8] <> '-') then Exit;
Val(Copy(S, 1, 4), Y);
Val(Copy(S, 6, 2), M);
Val(Copy(S, 9, 2), Da);
if (M < 1) or (M > 12) then Exit;
if (Da < 1) or (Da > 31) then Exit; { Simplified; not checking month lengths }
D.Year := Y;
D.Month := M;
D.Day := Da;
Result := True;
end;
{
Compare two TDate objects.
Returns:
- 'earlier'
- 'later'
- 'equal'
}
function CompareDates(const A, B: TDate): String;
begin
if A.Year < B.Year then Exit('earlier');
if A.Year > B.Year then Exit('later');
if A.Month < B.Month then Exit('earlier');
if A.Month > B.Month then Exit('later');
if A.Day < B.Day then Exit('earlier');
if A.Day > B.Day then Exit('later');
Result := 'equal';
end;
{
Run a single test case:
- Parse both dates
- Handle invalid input
- Compare if valid
}
procedure RunTestCase(const S1, S2: String);
var
D1, D2: TDate;
Ok1, Ok2: Boolean;
begin
Ok1 := ParseDate(S1, D1);
Ok2 := ParseDate(S2, D2);
if (not Ok1) or (not Ok2) then
begin
WriteLn('Compare ''', S1, ''' vs ''', S2, ''' → invalid date format');
Exit;
end;
WriteLn('Compare ''', S1, ''' vs ''', S2, ''' → ', CompareDates(D1, D2));
end;
{
Main test suite:
- Multiple test cases
- Includes edge cases
- Prints results cleanly
}
var
Tests: array[1..11, 1..2] of String;
I: Integer;
begin
WriteLn('Date comparison tests:');
WriteLn;
Tests[1][1] := '2024-01-01'; Tests[1][2] := '2024-01-02';
Tests[2][1] := '2024-01-02'; Tests[2][2] := '2024-01-01';
Tests[3][1] := '2024-01-01'; Tests[3][2] := '2024-01-01';
Tests[4][1] := '1999-12-31'; Tests[4][2] := '2000-01-01';
Tests[5][1] := '2024-02-29'; Tests[5][2] := '2024-03-01';
Tests[6][1] := '2024-02-29'; Tests[6][2] := '2023-02-28';
Tests[7][1] := '2024-13-01'; Tests[7][2] := '2024-01-01';
Tests[8][1] := '2024-00-10'; Tests[8][2] := '2024-01-01';
Tests[9][1] := '2024-01-32'; Tests[9][2] := '2024-01-01';
Tests[10][1] := 'abcd-ef-gh'; Tests[10][2] := '2024-01-01';
Tests[11][1] := '2024-01-01'; Tests[11][2] := 'abcd-ef-gh';
for I := 1 to 11 do
RunTestCase(Tests[I][1], Tests[I][2]);
end.
{
run:
Date comparison tests:
Compare '2024-01-01' vs '2024-01-02' → earlier
Compare '2024-01-02' vs '2024-01-01' → later
Compare '2024-01-01' vs '2024-01-01' → equal
Compare '1999-12-31' vs '2000-01-01' → earlier
Compare '2024-02-29' vs '2024-03-01' → earlier
Compare '2024-02-29' vs '2023-02-28' → later
Compare '2024-13-01' vs '2024-01-01' → invalid date format
Compare '2024-00-10' vs '2024-01-01' → invalid date format
Compare '2024-01-32' vs '2024-01-01' → invalid date format
Compare 'abcd-ef-gh' vs '2024-01-01' → invalid date format
Compare '2024-01-01' vs 'abcd-ef-gh' → invalid date format
}