Declare a TDateTime as a Const in Delphi

12.8k Views Asked by At

As far as I know there is no way to do this, but I am going to ask just in case someone else knows how to do this. How can I declare a date as a const in Delphi?

The only solution I have found is to use the numeric equivalent, which is kind of a pain to maintain because it is not human readable.

const
  Expire : TDateTime = 39895; // Is actually 3/23/2009

What I would like to be able to do is something like this:

const
  Expire : TDateTime = TDateTime ('3/23/2009');

or

const
  Expire : TDateTime = StrToDate('3/23/2009');

So let me know if this is a feature request or if I just missed how to do this (yeah, I know it seems like an odd thing to want . . . .)

11

There are 11 best solutions below

2
On BEST ANSWER

Ok, my reaction is a bit late, but here's a solution for the newer Delphi's.

It uses implicit class overloaders so that records of this type can be used as if they are TDateTime variables.

  TDateRec = record
    year,month,day,hour,minute,second,millisecond:word;
    class operator implicit(aDateRec:TDateRec):TDateTime;
    class operator implicit(aDateTime:TDateTime):TDateRec; // not needed
    class operator implicit(aDateRec:TDateRec):String; // not needed
    class operator implicit(aDateRec:String):TDateRec; // not needed
  end;

Implementation:

uses DateUtils;

class operator TDateRec.Implicit(aDateRec:TDateRec):TDateTime;
begin
  with aDateRec do // Yeah that's right you wankers. I like "with" :)
    Result := encodeDateTime(Year,Month,Day,Hour,Minute,Second,Millisecond);
end;

class operator TDateRec.Implicit(aDateTime:TDateTime):TDateRec;
begin
  with Result do
    DecodeDateTime(aDateTime,Year,Month,Day,Hour,Minute,Second,Millisecond);
end;

class operator TDateRec.Implicit(aDateRec:TDateRec):String;
begin
  Result := DateTimeToStr(aDateRec)
end;

class operator TDateRec.Implicit(aDateRec:String):TDateRec;
begin
  Result := StrToDateTime(aDateRec)
end;

Now you can declare your dates like this:

const
  Date1:TDateRec=(Year:2009;month:05;day:11);
  Date2:TDateRec=(Year:2009;month:05;day:11;hour:05);
  Date3:TDateRec=(Year:2009;month:05;day:11;hour:05;minute:00);

To see if it works, execute the following:

ShowMessage(Date1); // it can act like a string
ShowMessage(DateToStr(Date1)); // it can act like a date

If you really want to replace all your TdateTime variables with this, you probably need to overload some other operators too (Add, subtract, explicit, ...).

1
On

The type "TDateTime" = type "Double".

Algorithm:

  1. Use StrToDateTime('01.01.1900 01:01:01') (or other way) to calculate need double_value. ('01.01.1900 01:01:01' => 2.04237268518519)

2.const DTZiro: TDateTime = 2.04237268518519;

1
On

System.DateUtils has constants for each part of time.

const cDT : TDateTime = (12 * OneHour)   + ( 15 * OneMinute) 
                      + (33 * OneSecond) + (123 * OneMillisecond);
1
On

One solution would be to create a list of constants for years, another for month offsets and then build it on the fly. You would have to take care of leap years yourself by adding 1 to each resulting constant. Just a few below to get you started... :)

Const
  Leap_Day = 1;  // use for clarity for leap year dates beyond feb 29.
  Year_2009 = 39812;  // January 1, 2009
  Year_2010 = Year_2009 + 365; // January 1, 2010
  Year_2011 = Year_2010 + 365; // January 1, 2011
  Year_2012 = Year_2011 + 365; // January 1, 2012 (is leap year)
  Year_2013 = Year_2012 + Leap_Day + 365;  // January 1, 2013

Const
  Month_Jan = -1; // because adding the day will make the offset 0. 
  Month_Feb = Month_Jan + 31; // 31 days more for the first day of Feb.
  Month_Mar = Month_Feb + 28; // 28 days more for the first day of Mar.  
  Month_Apr = Month_Mar + 30; // 30 days more for the first day of Apr.

Const
  Expire_Jan1 : tDateTime = Year_2009 + Month_Jan + 1;
  Expire : tDateTime = Year_2009 + Month_Mar + 23;

If you have a leap year then you have to add 1 to anything beyond february of that year.

Const
  Expire : tDateTime = Year_2008 + Month_Mar + 23 + Leap_Day;

EDIT

Added a few more years for clarity, and added a Leap_Day constant.

3
On

No, Delphi doesn't support that.

Your first idea would be a request for date-time literals distinct from ordinary floating-point literals. I found QC 72000, which is about displayingTDateTime values as dates in the debugger, but nothing about your particular request. It's not like nobody's ever mentioned it before, though. It's a perennial topic on the newsgroups; I just can't find anything in QC about it.

Your second idea would require StrToDate to be evaluable at compile time. I don't see any entries in QC about it either, but for what it's worth, C++ is getting such a feature for functions that are shown to have the necessary qualities. StrToDate wouldn't meet those requirements, though, because it's sensitive to the current locale's date settings.

0
On

Rob Kennedy's answer shows that the StrToDate solution is inherently out of the question as you don't want your code to break if it's compiled in Europe!

I do agree there should be some way to do EncodeDate but there isn't.

As far as I'm concerned the compiler should simply compile and run any code it finds in a constant assignment and store the result into the constant. I'd leave it up to the programmer to ensure the code isn't sensitive to it's environment.

2
On

There is no way to do this because interpreting a date litteral in itself is not deterministic, it depends on the convention/locale you follow.
'1/4/2009' is not in January for any French person for instance, and having the compiler translating as January 4th would make it a fool's compiler ;-)
Unless the compiler implements some (well documented) "magic" bijective function for pairing a date value and a display representation... And anyway, half of the planet would not like it.
The only non ambiguous way I see now is to provide the value even if it looks like a pain... ... my $0.02

3
On

A Delphi date is the # of days since Dec 30, 1899. So you could probably come up with an elaborate mathematical formula to express a date as a const. Then you could format it very oddly, to emphasize the human-readable parts. My best attempt is below, but it is very much incomplete; for one thing, it assumes that all months have 30 days.

My example is mostly for fun though. In practice, this is pretty ridiculous.

const
  MyDate = ((
           2009  //YEAR
                                          - 1900) * 365.25) + ((
           3     //MONTH
                                          - 1) * 30) +
           24    //DAY
           ;
5
On

The only? possible way, but probably not what you are looking for:

const
{$J+}
  Expire: TDateTime = 0;
{$J-}

initialization
  Expire := EncodeDate(2009, 3, 23);
0
On

I tend to simulate const dates with a function. Technically they're a little more constant than the "pseudo-constant" assignable typed const's.

function Expire: TDateTime;
begin
  Result := EncodeDate(2009, 3, 23);
end;

NOTE the use of EncodeDate rather than StrToDate. StrToDate is affected by regional settings meaning there's no guarantee a string will be interpreted as would be expected.

For example, did you know that there's a strange a group of people who think it makes sense to "shuffle" date parts into an inconsistent order of significance? They use middle, then least, then most significant part (e.g. '3/23/2009') <cheeky grin>. The only time that logic makes sense is when you turn 102 years old - then you can claim your age is 021.

For the premature optimisers out there, if the function is called so frequently that the nano seconds required to encode a date becomes an issue - you have a far bigger problem than this minor inefficiency in the name of readable, maintainable code.

0
On

i think the best solution available to you is to declare:

ArmisticeDay: TDateTime = 6888.0 + (11.0/24.0); //Nov 11, 1918 11 AM

and just accept it.


My attempt Nº1

Expire = EncodeDate(2009, 3, 23);

[Error] Constant expression expected

My attempt Nº2

Expire: TDateTime = EncodeDate(2009, 3, 23);

[Error] Constant expression expected

So even though they're constant, and deterministic (i.e. do not depend on any locale information), it still doesn't work.