program DateRangeGenerator;
{$mode objfpc}{$H+}
uses
SysUtils, Classes, DateUtils;
{**
* Helper function to safely parse a "YYYY-MM-DD" string into a TDateTime
* independently of system locale settings.
*}
function ParseISOStringDate(const ADateStr: string; out ADate: TDateTime): Boolean;
var
Year, Month, Day: Word;
Code: Integer;
begin
Result := False;
// Validate basic length and dash positions for "YYYY-MM-DD"
if (Length(ADateStr) <> 10) or (ADateStr[5] <> '-') or (ADateStr[8] <> '-') then
Exit;
// Extract and convert year, month, and day components safely
Val(Copy(ADateStr, 1, 4), Year, Code);
if Code <> 0 then Exit;
Val(Copy(ADateStr, 6, 2), Month, Code);
if Code <> 0 then Exit;
Val(Copy(ADateStr, 9, 2), Day, Code);
if Code <> 0 then Exit;
// TryEncodeDate validates if it's a real calendar date (handles leap years, etc.)
Result := TryEncodeDate(Year, Month, Day, ADate);
end;
{**
* Generates all dates between AStartDateStr and AEndDateStr inclusive.
*
* @param AStartDateStr Start date in "YYYY-MM-DD" format.
* @param AEndDateStr End date in "YYYY-MM-DD" format.
* @return TStringList List of dates. Caller is responsible for freeing the list.
*}
function GetDatesBetween(const AStartDateStr, AEndDateStr: string): TStringList;
var
StartDate, EndDate, CurrentDate: TDateTime;
begin
Result := TStringList.Create;
try
// 1. Parse and validate start date
if not ParseISOStringDate(AStartDateStr, StartDate) then
begin
FreeAndNil(Result);
raise Exception.CreateFmt('Invalid start date: "%s". Use YYYY-MM-DD.', [AStartDateStr]);
end;
// 2. Parse and validate end date
if not ParseISOStringDate(AEndDateStr, EndDate) then
begin
FreeAndNil(Result);
raise Exception.CreateFmt('Invalid end date: "%s". Use YYYY-MM-DD.', [AEndDateStr]);
end;
// 3. Ensure logical date range order
if StartDate > EndDate then
begin
FreeAndNil(Result);
raise Exception.Create('Start date cannot be after the end date.');
end;
// 4. Iterate day-by-day safely
CurrentDate := StartDate;
while CurrentDate <= EndDate do
begin
// Add formatted date string to the result list
Result.Add(FormatDateTime('yyyy-mm-dd', CurrentDate));
// Increment safely using DateUtils.IncDay (handles month/year rollovers and leap years)
CurrentDate := IncDay(CurrentDate, 1);
end;
except
Result.Free;
raise;
end;
end;
var
DateList: TStringList;
I: Integer;
StartDateStr, EndDateStr: string;
begin
StartDateStr := '2026-02-26';
EndDateStr := '2026-03-02';
try
DateList := GetDatesBetween(StartDateStr, EndDateStr);
try
Writeln('Dates between ', StartDateStr, ' and ', EndDateStr, ':');
for I := 0 to DateList.Count - 1 do
begin
Writeln(DateList[I]);
end;
finally
DateList.Free; // Always free allocated objects in Pascal
end;
except
on E: Exception do
Writeln('Error: ', E.Message);
end;
end.
(*
run:
Dates between 2026-02-26 and 2026-03-02:
2026-02-26
2026-02-27
2026-02-28
2026-03-01
2026-03-02
*)