program SortDates;
{
Program goal:
-------------
Sort a list of date strings in the format "YYYY-MM-DD".
The approach:
1. Validate and parse each date into a numeric record (year, month, day).
2. Sort using a custom QuickSort with a comparator.
3. Print the sorted results.
Architecture notes:
-------------------
- Parsing is isolated for clarity and testability.
- Sorting uses a classic QuickSort (O(n log n) average).
- Error handling: invalid formats and invalid ranges are detected.
- Edge cases: malformed strings, invalid ranges, leap years.
- Performance: QuickSort is cache-friendly and widely understood.
- Memory: minimal; only an array of records.
- Security: careful bounds checking; no unsafe pointer arithmetic.
Note:
-----
The previous version used TArray.Sort, which is a Delphi feature.
Free Pascal does not provide TArray.Sort in this form, which caused
the compiler error mentioning "CREATE expected but SORT found".
This version replaces that with a custom QuickSort implementation.
}
{$mode objfpc}{$H+}
uses
SysUtils;
type
TDateRec = record
Year: Integer;
Month: Integer;
Day: Integer;
Original: String; { Keep original string for output }
end;
{
Validate that a string is exactly in the form YYYY-MM-DD.
}
function ValidateFormat(const S: String): Boolean;
var
I: Integer;
begin
Result := False;
if Length(S) <> 10 then Exit;
for I := 1 to 10 do
begin
case I of
5, 8:
if S[I] <> '-' then Exit;
else
if not (S[I] in ['0'..'9']) then Exit;
end;
end;
Result := True;
end;
{
Parse a date string into a TDateRec.
Returns True on success, False on failure.
}
function ParseDate(const S: String; out D: TDateRec): Boolean;
begin
if not ValidateFormat(S) then Exit(False);
D.Year := StrToIntDef(Copy(S, 1, 4), -1);
D.Month := StrToIntDef(Copy(S, 6, 2), -1);
D.Day := StrToIntDef(Copy(S, 9, 2), -1);
D.Original := S;
{ Basic range validation }
if (D.Year < 0) or (D.Month < 1) or (D.Month > 12) or (D.Day < 1) or (D.Day > 31) then
Exit(False);
Result := True;
end;
{
Comparator for sorting.
Compares two TDateRec values chronologically.
}
function DateCompare(const A, B: TDateRec): Integer;
begin
if A.Year <> B.Year then Exit(A.Year - B.Year);
if A.Month <> B.Month then Exit(A.Month - B.Month);
Result := A.Day - B.Day;
end;
{
Swap two elements in the array.
}
procedure Swap(var A, B: TDateRec);
var
Temp: TDateRec;
begin
Temp := A;
A := B;
B := Temp;
end;
{
QuickSort implementation for TDateRec arrays.
Average complexity: O(n log n).
}
procedure QuickSortDates(var A: array of TDateRec; L, R: Integer);
var
I, J: Integer;
Pivot: TDateRec;
begin
I := L;
J := R;
Pivot := A[(L + R) div 2];
repeat
while DateCompare(A[I], Pivot) < 0 do Inc(I);
while DateCompare(A[J], Pivot) > 0 do Dec(J);
if I <= J then
begin
Swap(A[I], A[J]);
Inc(I);
Dec(J);
end;
until I > J;
if L < J then QuickSortDates(A, L, J);
if I < R then QuickSortDates(A, I, R);
end;
{
Sort an array of date strings.
Returns True on success, False on failure.
}
function SortDates(const Dates: array of String): Boolean;
var
Parsed: array of TDateRec;
I: Integer;
begin
SetLength(Parsed, Length(Dates));
{ Parse all dates }
for I := 0 to High(Dates) do
begin
if not ParseDate(Dates[I], Parsed[I]) then
begin
WriteLn('Invalid date: ', Dates[I]);
Exit(False);
end;
end;
{ Sort using custom QuickSort }
if Length(Parsed) > 1 then
QuickSortDates(Parsed, 0, High(Parsed));
{ Print results }
WriteLn('Sorted dates:');
for I := 0 to High(Parsed) do
WriteLn(' ', Parsed[I].Original);
Result := True;
end;
{
Helper to run a named test case.
}
procedure RunTest(const Name: String; const Dates: array of String);
begin
WriteLn;
WriteLn('=== ', Name, ' ===');
if not SortDates(Dates) then
WriteLn('Test failed.');
end;
{
Main program with multiple test cases.
}
begin
{ Basic chronological order }
RunTest('Basic chronological order',
['2024-01-05', '2023-12-31', '2024-01-01', '2022-07-15']);
{ Already sorted input }
RunTest('Already sorted input',
['2020-01-01', '2020-01-02', '2020-01-03']);
{ Leap year edge cases (note: only basic range checks here) }
RunTest('Leap year edge cases',
['2020-02-29', '2019-02-28', '2021-03-01']);
{ Invalid date detection }
RunTest('Invalid date detection',
['2024-13-01', '2024-02-30', 'not-a-date']);
end.
{
run:
=== Basic chronological order ===
Sorted dates:
2022-07-15
2023-12-31
2024-01-01
2024-01-05
=== Already sorted input ===
Sorted dates:
2020-01-01
2020-01-02
2020-01-03
=== Leap year edge cases ===
Sorted dates:
2019-02-28
2020-02-29
2021-03-01
=== Invalid date detection ===
Invalid date: 2024-13-01
Test failed.
}