How to get all dates between 2 dates in Pascal

1 Answer

0 votes
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

*)

 



answered 5 days ago by avibootz
...