The question frequently comes up on how to manipulate dates and time using Delphi.
Delphi 2.0 and later supply the TDateTime format, which is actually a floating point number (stored as a IEEE double) containing the number of days that have passed since 12 December 1899.
(Delphi 1.0 calculated the date from the year 1, so to convert a Delphi 1.0 date to a Delphi 2.0 date, use date_d2 := date_d1 -693594.0;
The date format was changed to be compatible with OLE 2.0 Automation.)
So, to get the current date and time, use one of the following functions:
var
the_date,
the_time,
the_day_and_time: tDateTime;
begin
the_date := date;
the_time := time;
the_day_and_time := now; // same as the_date + the_time
end;
You can add or subtract days from the date.
var
one_week_from_tomorrow: tDateTime;
begin
// (tomorrow is 1 day) plus (one week is 7 days)
one_week_from_tomorrow := date + 8.0;
end;
You can add or subtract months with the IncMonth function:
var two_months_ago: tDateTime;
begin
two_months_ago := IncMonth( now, -2 )
end;
To add or subtract weeks or years, first calculate the correct number of days. That isn't always too easy. See "Getting and Setting the hour..." below for ways to access individual parts of a date, and an example.
TTimeStamp
Sometimes you want just a little more accuracy when dealing with time than a fractional part of a day. For that you will need to use the TTimeStamp type, which is a record defined as:
type
TTimeStamp = record
Time: Integer; { Number of milliseconds since midnight }
Date: Integer; { One plus number of days since 1/1/0001 }
end;
To convert between the two:
var
dt: tDateTime;
ts: tTimeStamp;
begin
ts := DateTimeToTimeStamp( now );
dt := TimeStampToDateTime( ts );
end;
To convert the TTimeStamp.Date field to something compatible with a TDateTime type, use the DateDelta constant.
dt := tDateTime( ts.date ) + DateDelta;
Windows Date and Time
Sometimes you need to send a date and time through the Windows API. However, Windows uses its own date and time type: TSystemTime. Convert between TDateTime and TSystemTime using one of the following methods:
var
delphi_datetime: tDateTime;
windows_datetime: tSystemTime;
begin
// set the system time
delphi_datetime := now;
DateTimeToSystemTime( delphi_datetime, windows_datetime );
SetSystemTime( windows_datetime );
// get the system time
GetSystemTime( windows_datetime );
delphi_datetime := SystemTimeToDateTime( windows_datetime );
end;
DOS and Windows File Dates
The operating system always tracks, at minimum, the last time a file was modified. Modern file systems also provide information about last access, actual creation time, etc.
There are several ways to get to this data. If you are only interested in simplisic DOS file handling, use the DateTimeToFileDate and FileDateToDateTime routines. A common operation is to update a file's time to now, called "touch":
uses SysUtils;
procedure touchFile( hFile: tHandle );
begin
if FileSetDate( hFile, DateTimeToFileDate( now ) ) <> 0
then raise EInOutError.create( 'Could not modify file''s date/time' )
end;
To manipulate the 64-bit Windows FILETIME values, use the Win32 API SystemTimeToFileTime and FileTimeToSystemTime routines. As usual, Delphi abstracts away pointers for you. Unfortunately, when dealing with the Win32 API, you must check for errors yourself --Delphi won't raise exceptions for you.
uses Classes;
procedure touchFile( hFile: tHandle );
var
st: tSystemTime;
ft: tFileTime;
begin
GetSystemTime( st );
if SystemTimeToFileTime( st, ft ) <> 0
then raise EConvertError.create( 'could not convert given system time to a file time' );
SetFileTime( hFile, nil, ft, nil )
end;
Getting and setting the hour, year, month, weekday, etc.
Delphi provides various routines to manipulate specific pieces of information in a TDateTime variable. Let's greet someone and find out if it is a leap year, shall we?
var
year, month, day: word;
hour, min, sec, msec: word;
begin
// Is it morning or afternoon?
DecodeTime( time, hour, min, sec, msec );
if hour < 12
then ShowMessage( 'Good morning!' )
else ShowMessage( 'Good afternoon!' );
// Is it a leap year?
DecodeDate( date, year, month, day );
if IsLeapYear( year )
then ShowMessage( 'This year has an extra day!' )
else ShowMessage( 'Sorry, people with birthdays on Feb 29 are out of luck...' )
end;
You can convert the other way just as easily:
var my_birth: tDateTime;
begin
my_birth := EncodeDate( 1974, 4, 28 )
+ EncodeTime( 6, 08, 0, 0 )
end;
You can get the day of the week with the DayOfWeek function.
begin
if DayOfWeek( date ) in [1, 7]
then ShowMessage( 'Yeah! It''s the weekend!' )
else ShowMessage( 'Alas, it is a workday...' )
end;
As promised, this is also useful for modifying a date/time by something other than a simple number of days. Suppose you want to get a proper date three years from now, accounting for leap years and the like.
var
three_years_from_now: tDateTime;
year, month, day: word;
begin
DecodeDate( date, year, month, day );
inc( year, 3 );
three_years_from_now := EncodeDate( year, month, day )
end;
Reading and Writing Something Humans Like to Read
Delphi provides a lot of routines for converting between a TDateTime and a string. I will not run through them all here. I recommend you peruse your Delphi or FPC documentation. However, what follows should get you started.
I will generally advocate against using the "easiest" routines, since it is rare that you actually need to do things exactly the way Delphi thinks you should. This is particularly true of countries outside the USA, but even inside the USA you may find yourself working with a specific format other than what Delphi expects... (Alas, if everyone would just use the ISO 8601 International Standard...)
Convert To String
To convert to a string is actually pretty straight-forward. If you've ever used the VB Format function with a user-defined Date/Time, you should feel right at home. Beware, however, that there are some differences. Make sure to read the documentation.
{$apptype console}
begin
writeln(
FormatDateTime(
'"Today is" dddd", the" d"th" of mmmm", AD" yyyy.',
date
)
);
writeln(
FormatDateTime(
'"And the time is" h:mm ampm.',
time
)
)
end;
One thing of interest to note is that you can "quote" characters by surrounding them with the double-quote character. (You can also quote with the single-quote character, but that's a lot of visual mayhem with Delphi strings...)
If you prefer a 24-hour clock, most common in many eastern-block countries, you should specify the time as just hh:mm
(without the 'ampm').
Using ampm
causes Delphi to use the postfix most appropriate for your locale. You can guarantee "am" or "pm" by using am/pm
. You can also use "a" and "p" with a/p
.
Convert From A String
Converting from a string is slightly more complicated, only because you have to work for it. Use Delphi's StrToDate, StrToTime, and StrToDateTime routines for simplicity. However, they require that you first play with many of the global variables in the SysUtils unit.
For example, to get a date string you know to be formatted in the silly way they do here in the states, you can say:
{$apptype console}
uses SysUtils;
var
save_ShortDateFormat: string;
save_DateSeparator: char;
users_date: tDateTime;
users_dateString: string;
begin
// Get the user's idea of what the date is
write( 'Please enter the date as d-m-y: ' );
readln( users_dateString );
// Preserve global variables
save_ShortDateFormat := ShortDateFormat;
save_DateSeparator := DateSeparator;
// Try to convert user's input
ShortDateFormat := 'd-m-y';
DateSeparator := '-';
try
users_date := StrToDate( users_dateString )
except on EConvertError do begin
writeln( 'You did not enter a date like I asked you...' );
exit
end end;
// Restore global variables
ShortDateFormat := save_ShortDateFormat;
DateSeparator := save_DateSeparator;
// Check to see if the user got it right
if users_date = date
then writeln( 'Good job! You got it right!' )
else writeln( 'Hmm... I think you need to get a better calendar.' )
end;
The caveat is, of course, that the input must be very strictly formatted. If you need to read dates more robustly, you will have to write your own routine to get all the information, then assemble it with the EncodeDate and EncodeTime functions.
Well, that should be enough to get you started. Questions and comments welcome.