Computing: Free Pascal Programming

Date and time handling in Free Pascal.


Free Pascal includes everything that you need to read the current date and time, to display dates in a given format, to do date math, as well as to determine the dates associates with a file. Most of these functions are part of the SysUtils unit, that is included by default when creating a Lazarus project. For some special cases, you'll need to include the old DOS unit. Functions to add or subtract dates, to calculate the number of days between two dates, to compare two dates, and lots more are included in the DateUtils unit.

The SysUtils unit provides the data type TDateTime, that may be used to work with dates, times, and combined date/time values. TDateTime values are encoded as double-precision floating point values:

Examples: The dates corresponding to TDateTime values of 0 and 1 are respectively 12/30/1899 and 12/31/1899. The times corresponding to TDateTime values of 0 and 0.5 are respectively 00:00:00 and 12:00:00.

You can read the actual date and/or time using the functions Date, Time, resp. Now. For all three functions, the return value is of type TDateTime. Note, that the DateUtils unit includes the declarations of the functions Yesterday and Tomorrow, that return the TDateTime value of the day before resp. after today.

Example: I started this tutorial in the morning, on March 4, 2025. At 07:12:52, the function Now returned the value 4.5720300610914353E+004.

It is obvious that a floating point representation of dates and times is ok for a computer, but not adequate for us humans. We need to know what the floating point value actually means, i.e. to what year, month, day, hour, minute, second, and millisecond it corresponds. There are 3 Free Pascal procedures to decode a TDateTime value:

To note that the last of these procedures is declared in the DateUtils (and not the SysUtils) unit.

To do the inverse, i.e. to encode a TDateTime value, the following 3 functions are available:

Here again, the last of these functions is declared in the DateUtils (and not the SysUtils) unit.

Invalid arguments of these last three functions result in a runtime error. This may be avoided by using 3 other functions, that before doing the encoding, check if the arguments given are such to constitute a valid date (the third of these functions being declared in DateUtils):

The following sample program is an example of how you can use DecodeDate() and EncodeDate().
      program datetime1;
        {$mode objfpc}{$H+}
      uses
          SysUtils;
      var
          Y, Y0, M, D: Word;
          ADate: TDateTime;
      begin
          ADate := Date;
          DecodeDate(ADate, Y, M, D);
          Writeln('Today''s date is ', Y, '/', M, '/', D);
          ADate += 1;
          DecodeDate(ADate, Y, M, D);
          Writeln('Tomorrow''s date is ', Y, '/', M, '/', D);
          Writeln;
          for Y0 := 2024 to 2025 do begin
              M := 3; D := 1; Y := Y0;
              Write('The date before ', Y, '/', M, '/', D, ' is ');
              ADate := EncodeDate(Y, M, D);
              ADate -= 1;
              DecodeDate(ADate, Y, M, D);
              Writeln(Y, '/', M, '/', D);
              Writeln;
          end;
          Write('Hit ENTER to exit... '); Readln;
      end.

Here is the output of the program.

Date and time handling in Free Pascal: Sample program using DecodeDate() and EncodeDate()

Displaying a date or time can be done as in the program sample, or by using one of the functions available in Free Pascal. Here are 3 of these functions:

The output of these functions is determined by the content of the following variables (declared in SysUtils):

These variables are initialized with the content of the following other predeclared variables, that themselves contain values depending on the operating system's internationalization settings (the Windows Region settings).

The screenshot below shows the date and time format settings on my Windows 10 laptop (default regional settings for Luxembourg):

Date and time handling in Free Pascal: Windows regional settings determining the output of the date/time display format

The following sample program prints out the content of the variables mentioned above, as well as the actual date with the formats determined by Windows regional settings.
      program datetime2;
      {$mode objfpc}{$H+}
      uses
          SysUtils;
      var
          I: Integer;
      begin
          Writeln('Date separator:  ', DateSeparator);
          Writeln('Time separator:  ', TimeSeparator);
          Writeln;
          Writeln('Long (and short) month names:');
          for I := 1 to 12 do begin
              Write(LongMonthNames[I], '(', ShortMonthNames[I], ')');
              if I <> 12 then
                  Write(', ');
              if (I = 4) or (I = 8) or (I = 12) then
                  Writeln;
          end;
          Writeln;
          Writeln('Long (and short) day names:');
          for I := 1 to 7 do begin
              Write(LongDayNames[I], '(', ShortDayNames[I], ')');
              if I <> 7 then
                  Write(', ');
              if (I = 4) or (I = 7) then
                  Writeln;
          end;
          Writeln;
          Writeln('The short date format is  ', ShortDateFormat);
          Writeln('The long  date format is  ', LongDateFormat);
          Writeln('The short time format is  ', ShortTimeFormat);
          Writeln('The long  time format is  ', LongTimeFormat);
          Writeln;
          Writeln('Today''s date and actual time:');
          Writeln('------------------------------');
          Writeln(DateToStr(Date));
          Writeln(TimeToStr(Time));
          Writeln(DateTimeToStr(Now));
          Writeln;
          Write('Hit ENTER to exit... '); Readln;
      end.

Note: When building this program in Lazarus, you'll get Depreciated symbol warnings for all of these predeclared date/time related variables. This means that in future releases of Lazarus/Free Pascal, they may no longer be supported!

The screenshot below shows the output of the program.

Date and time handling in Free Pascal: Sample program using the predeclared variables and the Windows regional settings date/time display format

By changing the values of the predeclared variables, we can modify the display format of dates and times, as in the following examples:
      ShortDateFormat := 'yyyy.mm.dd';
      Writeln('Today''s date is  ', DateToStr(Date));
      LongTimeFormat := 'hh.nn am/pm';
      Writeln('The actual time is ', TimeToStr(Time));
Here is the output that we get with this code:
    Today's date is 2025.03.05
    The actual time is 11.08 am

The values that may take the date/time predeclared variables are a combination of predefined formatting characters. Here is an overview:

CharacterMeaning
dday of month
ddday of month with leading zero
dddabbreviated name of day of month
ddddfull name of day of month
mmonth
mmmonth with leading zero
mmmabbreviated name of month
mmmmfull name of month
y, or yytwo digits year
yyyyfour digits year
/insert date separator
 
CharacterMeaning
hhour
hhhour with leading zero
nminute
nnminute with leading zero
ssecond
sssecond with leading zero
am/pmuse 12 hour clock and display am and pm accordingly
a/puse 12 hour clock and display a and p accordingly
:insert time separator

We can also insert a text literal, using "<text-literal>". This is, for example useful to overwrite the date separator if this one is a dot and we want it being a slash. In fact if we use the format dd/mm/yy, the slash will be replaced by the actual date separator, i.e. a dot. To get a date with slashes, we'll have to the format dd"/"mm"/"yy instead. The same applies to the colon time separator.

Whereas the 3 functions discussed until now rely on the depreciated predeclared date/time variables, there is a fourth function, that includes an argument to set a display format, the value of this argument being a string made from the formatting characters in the tables above. The function is declared as follows:
    function FormatDateTime(AFormat: string; ADateTime: TDateTime): string;

Note, that with this function, there are some further formatting characters available:

CharacterMeaning
cshortdateformat + space + shorttimeformat
dddddshort date format
ddddddlong date format
tshort time format
ttlong time format

The following sample program shows some usage examples of the FormatDateTime() function.
      program datetime3;
      {$mode objfpc}{$H+}
      uses
          SysUtils;
      const
          EnglishMonthNames: array[1..12] of string = (
          'January', 'February', 'March', 'April', 'May', 'June',
          'July', 'August', 'September', 'October', 'November', 'December'
          );
      var
          Fmt: string;
      begin
          Fmt := 'ddddd';
          Writeln('Date with default display format:                    ', FormatDateTime(Fmt, Date));
          Fmt := 'dd"-"mm"-"yyyy';
          Writeln('Date with hyphen separator and 4 digits year:        ', FormatDateTime(Fmt, Date));
          Fmt := 't';
          Writeln('Time with short display format:                      ', FormatDateTime(Fmt, Time));
          Fmt := 'tt';
          Writeln('Time with long display format:                       ', FormatDateTime(Fmt, Time));
          Fmt := 'hh"."nn am/pm';
          Writeln('12-hours time with dot separator and no seconds:     ', FormatDateTime(Fmt, Time));
          Writeln;
          Fmt := 'mmm"," dd/yyyy';
          Writeln('Date with abbreviated month name and 4 digits year:  ', FormatDateTime(Fmt, Date));
          Fmt := 'dd"." mmmm yy';
          Writeln('Date with full month name and 2 digits year:         ', FormatDateTime(Fmt, Date));
          LongMonthNames := EnglishMonthNames;
          Writeln('The same with forced English month names:            ', FormatDateTime(Fmt, Date));
          Writeln;
          Write('Hit ENTER to exit... '); Readln;
      end.

The screenshot below shows the output of the program.

Date and time handling in Free Pascal: Sample program using the function FormatDateTime()

Free Pascal also includes several functions to convert a date, time, or combined date/time string into a value of type TDateTime.
    function StrToDate(const AString: ShortString): TDateTime;
    function StrToTime(const AString: ShortString): TDateTime;
    function StrToDateTime(const AString: ShortString): TDateTime;
    function TryStrToDate(const AString: ShortString; out ADateTime: TDateTime): Boolean;     function TryStrToTime(const AString: ShortString; out ADateTime: TDateTime): Boolean;     function TryStrToDateTime(const AString: ShortString; out ADateTime: TDateTime): Boolean;

As the first and third of these functions generate a runtime error if the string isn't a valid date, or date/time, the safe way is to use the last 2 functions instead. These return False if the string is not valid.

Usage of these functions is not as simple as it seems. In fact, independently of the value of the variable ShortDateFormat, date strings are only recognized as valid if they are coded using a numerical format, like 'dd/mm/yyyy', or 'm/d/yy'. Important to note, that for the validity check of the date string, the Free Pascal routines consider the content of ShortDateFormat to determine the order of the day, month and year parts of the date, as well as the content of DateDelimiter to determine the date separator. To avoid any problems, extracting the year, month and day values from the string and using the EncodeDate() or EncodeDateTime() functions, is probably the better way to do...

Date and time arithmetic.

Manipulating dates contained in a TDateTime variable is easy: Since the date is encoded as a number of days, date math can be done by simply adding or subtracting a desired number of days. However, not all operations are easily expressible using a number of days: if you want. for example, the same day in the next year, you'll need to know if this year is a leap year. Also, to get the same day in the next month, you'll have to consider the number of days of this month.

The SysUtils unit provides one single date/time math function:
    function IncMonth(const ADateTime: TDateTime; const ANumberOfMonths: Integer = 1): TDateTime;

The DateUtils unit, on the other hand, includes a whole set of date/time arithmetic functions.

Functions to add or subtract a given number of days, weeks, or years:
    function IncDay(ADateTime: TDateTime; ANumberOfDays: Integer = 1): TDateTime;
    function IncWeek(ADateTime: TDateTime; ANumberOfWeeks: Integer = 1): TDateTime;
    function IncYear(ADateTime: TDateTime; ANumberOfYears: Integer = 1): TDateTime;

Functions to add or subtract a given number of hours, minutes, seconds, or milliseconds:
    function IncHour(ADateTime: TDateTime; ANumberOfHours: Int64 = 1): TDateTime;
    function IncMinute(ADateTime: TDateTime; ANumberOfMinutes: Int64 = 1): TDateTime;
    function IncSecond(ADateTime: TDateTime; ANumberOfSeconds: Int64 = 1): TDateTime;
    function IncMilliSecond(ADateTime: TDateTime; ANumberOfMilliSeconds: Int64 = 1): TDateTime;

To calculate the number of days, weeks, months, or years elapsed between two dates (or combined date/time values), you can use the following functions:
    function DaySpan(const ADateTimeNow, ADateTimeThen: TDateTime): Double;
    function WeekSpan(const ADateTimeNow, ADateTimeThen: TDateTime): Double;
    function MonthSpan(const ADateTimeNow, ADateTimeThen: TDateTime): Double;
    function YearSpan(const ADateTimeNow, ADateTimeThen: TDateTime): Double;
or the following:
    function DaysBetween(const ADateTimeNow, ADateTimeThen: TDateTime): Integer;
    function WeeksBetween(const ADateTimeNow, ADateTimeThen: TDateTime): Integer;
    function MonthsBetween(const ADateTimeNow, ADateTimeThen: TDateTime): Integer;
    function YearsBetween(const ADateTimeNow, ADateTimeThen: TDateTime): Integer;

The first set of functions return a value with a fractional part, i.e. also partial day, week, month, or year periods are counted. The second set of functions return an integer value, i.e. the effective span of the day, week, month, or year period is rounded, the result being the period that has completely elapsed.

Another important point to consider is that the values returned by MonthSpan(), YearSpan(), MonthsBetween(), and YearsBetween() are approximations, based on the average number of days in a month, resp. in a year.

The number of hours, minutes, seconds, and milliseconds between to time, or combined date/time values can be determined using the following functions:
    function HourSpan(const ADateTimeNow, ADateTimeThen: TDateTime): Double;
    function MinuteSpan(const ADateTimeNow, ADateTimeThen: TDateTime): Double;
    function SecondSpan(const ADateTimeNow, ADateTimeThen: TDateTime): Double;
    function MilliSecondSpan(const ADateTimeNow, ADateTimeThen: TDateTime): Double;

Important to remember that the time part of a TDateTime value is stored as a fraction, thus arithmetical operations are subjected to rounding errors. In particular, comparisons risk to never result in a True value, as in this example code
      if HourSpan(EncodeTime(H1, 0, 0, 0), EncodeTime(H2, 0, 0, 0)) = 1 then
          Writeln('One hour elapsed');
For H1 = 10 and H2 = 11, for example, the result returned by the HourSpan() function will be something like 0.9999999, that is less than 1, and the message will (probably) never be printed.

Possible work-arounds are to check if the span period is greater than some fractional number like 0.9999
      if HourSpan(EncodeTime(H1, 0, 0, 0), EncodeTime(H2, 0, 0, 0)) > 0.9999 then
          Writeln('One hour elapsed');
or to check if the span period rounded to for example 4 decimal digits equals 1.
      if Round(10000 * HourSpan(EncodeTime(H1, 0, 0, 0), EncodeTime(H2, 0, 0, 0))) / 10000 = 1 then
          Writeln('One hour elapsed');

Other SysUtils date/time functions.

The function
    function DayOfWeek(ADateTime: TDateTime): Integer;
returns the day of the week as a value between 1 and 7. Sunday is counted as day 1, Saturday as day 7. To note that the result of DayOfWeek can be used as an index to the ShortDayNames and LongDayNames arrays, to retrieve the abbreviated resp. full name of the day (in the language determined by the Windows regional settings).

The function
    function IsLeapYear(AYear: Word): Boolean;
returns True if the year is a leap year, False otherwise.

The SysUtils unit also contains several functions to convert timestamps, system time and file dates.

Other DateUtils date/time functions.

The DateUtils unit declares a whole bunch of further date/time procedures and functions. I'll mention just some of them here; if you are interested in more, please, have a look at Reference for unit 'DateUtils': Procedures and functions at the Free Pascal website.

The function
    function DayOfTheWeek(const ADateTime: TDateTime): Word;
returns the day of the week as a value between 1 and 7. As a difference with the SysUtils function DayOfWeek(), this function (as all functions of the DateUtils unit) is ISO 8601 compliant. It returns 1 for Monday, and 7 for Sunday. Obvious, that this return values may not be used as such as index to the ShortDayNames and LongDayNames arrays!

Similar functions, declared in the DatesUtil unit are:
    function DayOfTheMonth(const ADateTime: TDateTime): Word;
    function DayOfTheYear(const ADateTime: TDateTime): Word;
    function WeekOf(const ADateTime: TDateTime): Word;
    function MonthOf(const ADateTime: TDateTime): Word;
The functions return the number of days passed since the beginning of the month, or year, resp. the number of weeks, or month passed since the beginning of the year.

There are similar functions for times, in particular
    function HourOfTheDay(const ADateTime: TDateTime): Word;
that returns the number of hours passed since the start of the day till the moment indicated by ADateTime. This is a zero-based number; for example, 00:59:59 will return 0.

Date and time compare functions include:
    function CompareDate(const ADateTime1: TDateTime; const ADateTime2: TDateTime): TValueRelationship;
    function CompareTime(const ADateTime1: TDateTime; const ADateTime2: TDateTime): TValueRelationship;
    function CompareDateTime(const ADateTime1: TDateTime; const ADateTime2: TDateTime): TValueRelationship;
The functions' result is a number, that is zero if the comparison operands (date-part, time-part, or entire value of ADateTime1 and ADateTime2) are equal, less than zero if the compared part of ADateTime1 is earlier than the one of ADateTime2, greater than zero if the compared part of ADateTime1 is later than the one of ADateTime2.

Here are two further comparison functions that may be useful:
    function IsSameDay(const ADateTime1: TDateTime; const ADateTime2: TDateTime): Boolean;
    function IsSameMonth(const ADateTime1: TDateTime; const ADateTime2: TDateTime): Boolean;
The first of these functions returns True if ADateTime1 and ADateTime2 have the same date part, the second one returns True if ADateTime1 and ADateTime2 occur in the same year and month.

There are also functions to get the local timezone offset, i.e. the difference between UTC time and local time, as well as to convert local time to UTC time, and vice-versa. To avoid errors, please, read the documentation before using them.

As I said, there are lots more. View the doc...

Timestamps, system time and file dates.

That's a rather complex topic, and I will discuss it in a separate article (that I will write some day...).

Programs on my site, that deal with dates and/or times:

World clock is a Free Pascal GUI application with display of UTC time, local time and the time in 8 world cities. Click the following link to visit the World clock documentation page.

Music alarm clock is a Free Pascal GUI application with individual time/music settings for each week day resp. 3 freely selectable dates. Click the following link to visit the Music alarm clock documentation page.

Biorhythm cycles is a Free Pascal GUI application that draws the biological cycles of a person born at a given date for a given month of a given year. Click the following link to visit the Biorhythm cycles documentation page. There is also a biorhythm program available for DOS. Written in True BASIC, you find the download link of the source code in the DOS programming part of the DOS, OS/2 & Windows Programming section of my site.

Catholic holidays is a Free Pascal GUI application (in Luxembourgish) that, for a given year, determines the date of Easter and other catholic holidays. Click the following link to visit the Catholic holidays documentation page.

Simple time calculator is a Free Pascal GUI application to add times (instead of simple numbers). Click the following link to visit the Simple time calculator documentation page.

3-month calendar is a multi-lingual Free Pascal console application that displays a calendar for 3 months of a given year. Click the following link to visit the 3-months calendar documentation page. This program is also available for DOS; you find the download link of the source code in the DOS programming part of the DOS, OS/2 & Windows Programming section of my site.

In my tutorial An introduction to FORTRAN 77 (G77), you find FORTRAN code samples in relationship with the determination of the date of Easter.


If you find this text helpful, please, support me and this website by signing my guestbook.