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.

Duoas, congratulations for this excelent tutorial.

First, let me add that DateUtils unit (delphi 7) provide a lot of other functions to manipulate date and time (if someone look for it on Delphi help).

And, at last, I would like to share one function that could be util if someone need to display total time greater 23:59 - not provide by Delphi:

function FullTimeToStr(SUMTime: TDateTime): string;
var
  StrHor,
  StrMin :string;
  TotHor :double;
begin
  TotHor := SUMTime *24;
  if (TotHor -Trunc(TotHor)) > 0.9999 then
    TotHor := Round(TotHor);
  StrHor := FormatFloat('##0:', Int(TotHor));
  StrMin := FormatDateTime('nn:ss', Frac(TotHor)/24);
  Result := StrHor +StrMin;
end;

The if test is necessary becose there is some bug with float numbers on Delphi. For example, 25.0000000008 it will turn to 24.99999 and we will get 24 when we call Int function in the next instruction (that's wrong).

Bye

Thanks!

As always, readers should do as Micheus did and immediately refer themselves to the Delphi documentation. Even if you, like he, already know quite a lot about handling dates in Delphi. There is a lot of useful stuff in there.

Also, the unit(s) you need to put in your uses clause vary between Delphi versions. The DateUtils unit was introduced in Delphi 6. FPC uses DateUtils also. For Delphi 5 and earlier all the date stuff is in the System unit.


BTW, Micheus, thanks for that nifty function! That floating point error there is not Delphi's fault though... that is a problem common to all digital computers. The way you handled it is brilliant and instructive!


Finally, I would like to add some thoughts to reading time/date information from a string. If you know your input will take one of several distinct forms, you can use try..except blocks until you get a correct date. For example:

function read_date_format_1( s: string ): tDateTime;
  begin
  // this function tries to read dates formatted as '2007-12-31'
  end;

function read_date_format_2( s: string ): tDateTime;
  begin
  // this function tries to read dates formatted as '2007 12 31'
  end;

function read_date_format_3( s: string ): tDateTime;
  begin
  // this function tries to read dates formatted as '31/12/2007'
  end;

function read_date( users_input: string ): tDateTime;
  begin
    try
      result := read_date_format_1( users_input )
    except on EConvertError do
      try
        result := read_date_format_2( users_input )
      except on EConvertError do
        result := read_date_format_3( users_input )
      end
    end
  end;

This code is for clarity and illustration only. (There are much better ways of writing it.)

BTW, Micheus, thanks for getting my name right. :)

Hi

First of all thanks for the tutorial and other explanations.

I have tried with the next program:

program Project23;

{$APPTYPE CONSOLE}

{$apptype console}
uses
  SysUtils,dateutils;

var
  save_ShortDateFormat: string;
  save_DateSeparator:   char;
  users_date:           tDateTime;
  users_dateString:     string;

begin
  repeat
  // 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  DaysBetween(users_date,date)>90 then
     writeln( 'Too many days!' )
    until (DaysBetween(users_date,date)<90);
    readln;
    end.

The problem is that if I don't introduce the date as the program requires, it doesn't write the message 'You did not enter a date like I asked you...'. It gives Error. Why?How can I solve it?

BTW, Micheus, thanks for getting my name right. :)

Ops...
I was looking for my mistake and found it here. I'm very sorry and will try don't make it again. :$

Here is common some people to write my name by this way: michel, michels, michells. I understand You. :)

Bye

Altzaportu
Your program works fine for me. Here's some test inputs I used:

D:\prog\d\foo\dt\altzaportu>Project23.exe
Please enter the date as d-m-y: foo You did not enter a date like I asked you...

D:\prog\d\foo\dt\altzaportu>Project23.exe
Please enter the date as d-m-y: 1 jan 2008 You did not enter a date like I asked you...

D:\prog\d\foo\dt\altzaportu>Project23.exe
Please enter the date as d-m-y: 1-jan-2008 You did not enter a date like I asked you...

D:\prog\d\foo\dt\altzaportu>Project23.exe
Please enter the date as d-m-y: 1-1-2008 D:\prog\d\foo\dt\altzaportu>Project23.exe
Please enter the date as d-m-y: 1-1-2007 Too many days!
Please enter the date as d-m-y: 1-1-2008

What do you mean by "It gives Error"? Please post some output.

BTW, your program does have a bug: What happens if you enter a date that is exactly 90 days different than today's date?


Micheus
Yoinks. I didn't expect you to start looking around for some instance where you got my name wrong (I didn't think you had...). It's just that it has so many vowels in it people wind-up typing "Duos" and the like.

The name is actually Dúthomhas, but no one (except Irish folk) could hope to pronounce that right...

So actually I was complimenting you on your keen observations... :$

A window opens with the next message:

Project Project2.exe raised exception class EConvert Error with message “247’ is not a valid date’. Process Stopped . Use step or Run to Continue.

I suspect the problem is the same as I noted in your other thread.

If it is not, please also post the code where the exception is occurring, as I cannot debug code I've never seen (my mental powers are not yet that developed :twisted: )

This article has been dead for over six months. Start a new discussion instead.