5-Minute-Snack: Dealing with dates and time in Delphi. Hints about System.DateUtils.

Thinking back to Delphi 7, I clearly remember that the first thing I did was to import some sort of unit that provided more operations for string and date handling. Looking at Delphi Berlin – things have changed.

Especially when it comes to calculations that deal with time and dates – lots of improvements that might have gone unnoticed. Installing 3rd party units is no longer necessary as the RTL has been extended. Of course, this did not happen recently – still, a lot of Delphi developers are not aware of these extensions.

Thus, let’s look at the unit that offers lots of comfy stuff to calculations with date and time and query calendars: System.DateUtils.pas 

Embarcadero gives this util the following comment as a title:

{       Date/time Utilities Unit                        }

Spending a lot of time in the .NET world myself, I always wondered why Delphi needed that long to provide these utilities as part of the RTL. Granted, .NET was created much later, but the Jedi Code Library was an essential part for these sort of things.

Let’s clear up some things about the terminology used in this unit:

function StartOfTheWeek(const AValue: TDateTime): TDateTime;          
function StartOfAWeek(const AYear, AWeekOfYear: Word;  
  const ADayOfWeek: Word = 1): TDateTime;

Both functions return a date of type TDateTime that points to the start of the week or a week. Subtle difference in speech, big difference for the developer:

  • Whenever the undefined article (“a”) is used in the function or procedure name, you do not have to specify any point in time as a TDateTime  but as another numerical datatype, mostly Word .
  • If the definied article (“the”) is used, you will have to provide the point in time as a TDateTime . 

What does this mean?

Looking at an example: If you want to know the date week 9 starts in 2017 and you do not have any TDateTime  datatype, you may use StartOfAWeek . If you already have a variable of type TDateTime  and you want to determine the start of the week of that date, simply use StartOfTheWeek . Spending time both in the US and Germany during the year, I always become away of a slight difference between both countries with regard to weeks. In the US the week starts on a Sunday, in Germany on a Monday. Does the unit use some sort of calendar? Unlike Apple’s iOS the date operations of this unit cannot be bound to a certain calendar that determines these things. I also heard statements like “It depends on the region settings of the system the code is executed on.” That is actually horribly wrong. If that  were the case we would have to write any date calculation with the region settings in mind. Thus, note the subtle references to ISO 8601 :

{ The following unit is ISO 8601 compliant.  What that means is this unit
  considers Monday the first day of the week (5.2.3).  Additionally ISO 8601
  dictates the following "the first calendar week of the year is the one
  that includes the first Thursday of that year" (3.17).  In other words the
  first week of the year is the first one that has four or more days.  For
  more information about ISO 8601 see: http://www.iso.ch/markete/8601.pdf

  The functions most impacted by ISO 8601 are marked as such in the interface

  The functions marked with "ISO 8601x" are not directly covered by ISO 8601
  but their functionality is a logical extension to the standard.


And in case you did not know which norm was used:

function WeeksInYear(const AValue: TDateTime): Word; inline;          {ISO 8601}
function WeeksInAYear(const AYear: Word): Word;                       {ISO 8601}

What I want to say is this: Embarcadero put these comments there for a reason and they did their best to make sure it would be rather striking. However, even in the documentation they point out what ISO 8601 describes with regard to the function. 

Coming back to our little example: The norm specifies that a week starts on a Monday and ends on a Sunday.

Apart from doing calculations, the unit also offers an easy way to encode and decode date and time values, check for certain things like is the time in the am or pm, etc.

Finally, one more advise before using this unit:

Read the documentation and do not make assumptions about return values!

Why? Simple. I used the following function a couple of days ago:

{ Range query functions }

function DaysBetween(const ANow, AThen: TDateTime): Integer;

So the return value will be negative if ANow is before AThen, as the logic from the parameters is reversed. Wrong! The return value will always be positive! Thus, you cannot use a condition like > 0  to determine if some date is older than another date! Reading the documentation clears this up right away:

DaysBetween  always returns a positive result and therefore the parameter values are interchangeable.

If you upgraded rather recently, spend some time exploring the RTL. You will be surprised what has been added in recent years.

Tags: , , ,