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:
- The integer part contains the number of elapsed days since 30 Dec 1899.
- The fractional part contains the number of elapsed milliseconds since the start of the day, divided by the total number of milliseconds in a day.
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:
- Decoding of a date value:
procedure DecodeDate(ADate: TDateTime; out AYear, AMonth, ADay: Word); - Decoding of a time value:
procedure DecodeTime(ATime: TDateTime; out AHour, AMinute, ASecond, AMilliSecond: Word); - Decoding of a combined date/time value:
procedure DecodeDateTime(const ADateTime: TDateTime; out AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word);
To do the inverse, i.e. to encode a TDateTime value, the following 3 functions are available:
- Encoding of a date value:
function EncodeDate(AYear, AMonth, ADay: Word): TDateTime; - Encoding of a time value:
function EncodeTime(AHour, AMinute, ASecond, AMilliSecond: Word): TDateTime; - Encoding of a combined date/time value:
function EncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word): TDateTime;
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):
- Encoding of a date value:
function TryEncodeDate(AYear, AMonth, ADay: Word; out ADate: TDateTime): Boolean; - Encoding of a time value:
function TryEncodeTime(AHour, AMinute, ASecond, AMilliSecond: Word; out ATime: TDateTime): Boolean; - Encoding of a combined date/time value:
function TryEncodeDateTime(const AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliSecond: Word; out ADateTime: TDateTime): Boolean;
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.
![]() |
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:
- Converting a TDateTime date to a string:
function DateToStr(ADate: TDateTime): string; - Converting a TDateTime time to a string:
function TimeToStr(ATime: TDateTime): string; - Converting a TDateTime combined date/time to a string:
function DateTimeToStr(ADateTime: TDateTime): string;;
The output of these functions is determined by the content of the following variables (declared in SysUtils):
- The format for short date notation (ShortDateFormat) is used in DateToStr and DateTimeToStr.
- The format for long time notation (LongTimeFormat) is used in TimeToStr and DateTimeToStr.
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).
- DateSeparator: A character used as the separator character for dates.
- TimeSeparator: A character used as the separator character for times.
- LongMonthNames: An array with the full names of the months.
- ShortMonthNames: An array with the abbreviated names of the months.
- LongDayNames: An array with the full names of the days.
- ShortDayNames: An array with the abbreviated names of the days.
The screenshot below shows the date and time format settings on my Windows 10 laptop (default regional settings for Luxembourg):
![]() |
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.
![]() |
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:
|
|
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:
Character | Meaning |
---|---|
c | shortdateformat + space + shorttimeformat |
ddddd | short date format |
dddddd | long date format |
t | short time format |
tt | long 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.
![]() |
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.