views:

1462

answers:

8

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 . . . .)

+6  A: 

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

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

initialization
  Expire := EncodeDate(2009, 3, 23);
The_Fox
May as well just declare it as a var instead of a const.
Rob Kennedy
Yes, you are right, but at least my solution uses the const keyword ;)
The_Fox
Jim, my typo originates from your post :P
The_Fox
Ooops, you are right. The first Expire was spelled wrong. I will fix it. I thought it might when I saw it, but then I only looked at the 2nd two instances.
Jim McKeeth
+5  A: 

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.

Rob Kennedy
+3  A: 

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.

Loren Pechtel
+5  A: 

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

François
It only depends on the locale if the language is defined to be sensitive to the locale. Floating-point literals aren't locale-sensitive; they simply don't accept commas as the decimal separator. No reason a date-time literal couldn't have similar restrictions on format.
Rob Kennedy
+1  A: 

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
           ;
JosephStyons
I like it, but as you pointed out it does have some issues.
Jim McKeeth
the fractional part also can cause problems as your date slowly moves from midnight one year, to 6 am, noon, 6pm to return again on leap year to midnight.
skamradt
Yeah, absolutely true. Like I said, this is not really workable unless you spend a lot of time devising a really sophisticated forumla to handle all possibilities. Much better off just getting the value and hard-coding it.
JosephStyons
+2  A: 

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.

skamradt
Why not add another constant: Leap_day = 1. That way there's no spurious "+1" sitting around that may not be intuitive at first glance...
Mason Wheeler
+1  A: 

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.

Ian Boyd
+2  A: 

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, ...).

Wouter van Nifterick