Declare a TDateTime as a Const in Delphi - delphi

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

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

The only? possible way, but probably not what you are looking for:
const
{$J+}
Expire: TDateTime = 0;
{$J-}
initialization
Expire := EncodeDate(2009, 3, 23);

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.

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

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

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.

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
;

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.

The type "TDateTime" = type "Double".
Algorithm:
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;

System.DateUtils has constants for each part of time.
const cDT : TDateTime = (12 * OneHour) + ( 15 * OneMinute)
+ (33 * OneSecond) + (123 * OneMillisecond);

Related

Delphi: Advanced string search commands

Kenneth is a string. Let's say it contains 'justabcsome123texthaha'.
I know this already:
To find text:
if(pos('bcsome12',Kenneth) > 0) then
To check length:
if(Length('Kenneth') > 10) then
Question 1:
I want to find 'texthaha', but only if it is at the end of the string.
if(pos('texthaha',Kenneth) > 0) then
Sadly this will find it anywhere, even if it is in the middle. Is there a simple way?
Question 2:
Is there a simple way to do a search, but with a * (any character in between)?
For example, if I want to search for bcsome1*3text and I don't care what character the * is. I think it's called a wildcard, isn't it?
if(pos('bcsome1'*'3text',Kenneth) > 0) then
I know the above doesn't work. but is there a similar way?
Edit: Might be of importance: **Delphi version used is very old, not sure of the version, but it's from year 2006.
There are functions EndsStr() and EndsText() (the last is case-insensitive) in the StrUtils unit
But, you easily could provide the needed functionality with known functions (Pos also has overloaded version with the third parameter in fresh Delphi):
NPos = Length(S) - Length(Sub) + 1;
if PosEx(Sub, S, NPos) = NPos then...
or variant proposed by #Sertac Akyuz:
if Copy(S, NPos, Length(Sub)) = Sub ...
The second problem might be solved with function like MatchesMask()
if MatchesMask(Kenneth, '*bcsome1*3text*')...
To get the last occurrence, try LastDelimiter (see help). For wildcards, see this post.

OLE Automation date in lua

Ok, I really need OLE Automation date in lua.
From here:
public double ToOADate()
Return Value Type: System.Double A double-precision floating-point
number that contains an OLE Automation date equivalent to the value of
this instance.
So in C# this:
Console.Write("DateTime.Now.ToOADate() = " + DateTime.Now.ToOADate());
gives me this:
DateTime.Now.ToOADate() = 42146,4748270602
What is the best way to get simular value in Lua?
Some more details, based on EgorSkriptunoff answer.
So, that Lua code works just fine for me to get OLE Automation date in lua:
-- number of days between December, 30 1899 and January, 1 1970
local magicnumber = 25569
-- don't forget about time zone (UTC+3 for my case)
local utcshift = 3*3600
-- calc and print for test
local oleadate = magicnumber + ((os.time()+utcshift)/(3600*24))
print(oleadate)
Output:
42146.575740741

Bad conversion from EndOfTheMonth(date) to Variant value

I have a TDateTime value (that I get as result from EndOfTheMonth(date)) to a variant type. The result is wrongly rounded. Let's have a look at example:
data := EndOfTheMonth(date);
V := data;
ShowMessage(DateTimeToStr(data) + ' vs ' + VarToStr(V));
// output is
// data = 2012-01-31 23:59:59
// v = 2012-02-01 // why next day?
Is it designed behaviour?
How to work around this?
ShowMessage(DateTimeToStr(data) + ' vs ' + DateTimeToStr(VarToDateTime(V)));
Update: I would guess the problem is that the last millisecond of the month is very close to 0:00:00 the next day, that is, the TDateTime value (which is basically a double) is very close to an integer (e.g. 41029.9999999884 is very close to 41029) and so the VarToStr function assumes the decimals to be numerical fuzz.

matlab indexing into nameless matrix [duplicate]

For example, if I want to read the middle value from magic(5), I can do so like this:
M = magic(5);
value = M(3,3);
to get value == 13. I'd like to be able to do something like one of these:
value = magic(5)(3,3);
value = (magic(5))(3,3);
to dispense with the intermediate variable. However, MATLAB complains about Unbalanced or unexpected parenthesis or bracket on the first parenthesis before the 3.
Is it possible to read values from an array/matrix without first assigning it to a variable?
It actually is possible to do what you want, but you have to use the functional form of the indexing operator. When you perform an indexing operation using (), you are actually making a call to the subsref function. So, even though you can't do this:
value = magic(5)(3, 3);
You can do this:
value = subsref(magic(5), struct('type', '()', 'subs', {{3, 3}}));
Ugly, but possible. ;)
In general, you just have to change the indexing step to a function call so you don't have two sets of parentheses immediately following one another. Another way to do this would be to define your own anonymous function to do the subscripted indexing. For example:
subindex = #(A, r, c) A(r, c); % An anonymous function for 2-D indexing
value = subindex(magic(5), 3, 3); % Use the function to index the matrix
However, when all is said and done the temporary local variable solution is much more readable, and definitely what I would suggest.
There was just good blog post on Loren on the Art of Matlab a couple days ago with a couple gems that might help. In particular, using helper functions like:
paren = #(x, varargin) x(varargin{:});
curly = #(x, varargin) x{varargin{:}};
where paren() can be used like
paren(magic(5), 3, 3);
would return
ans = 16
I would also surmise that this will be faster than gnovice's answer, but I haven't checked (Use the profiler!!!). That being said, you also have to include these function definitions somewhere. I personally have made them independent functions in my path, because they are super useful.
These functions and others are now available in the Functional Programming Constructs add-on which is available through the MATLAB Add-On Explorer or on the File Exchange.
How do you feel about using undocumented features:
>> builtin('_paren', magic(5), 3, 3) %# M(3,3)
ans =
13
or for cell arrays:
>> builtin('_brace', num2cell(magic(5)), 3, 3) %# C{3,3}
ans =
13
Just like magic :)
UPDATE:
Bad news, the above hack doesn't work anymore in R2015b! That's fine, it was undocumented functionality and we cannot rely on it as a supported feature :)
For those wondering where to find this type of thing, look in the folder fullfile(matlabroot,'bin','registry'). There's a bunch of XML files there that list all kinds of goodies. Be warned that calling some of these functions directly can easily crash your MATLAB session.
At least in MATLAB 2013a you can use getfield like:
a=rand(5);
getfield(a,{1,2}) % etc
to get the element at (1,2)
unfortunately syntax like magic(5)(3,3) is not supported by matlab. you need to use temporary intermediate variables. you can free up the memory after use, e.g.
tmp = magic(3);
myVar = tmp(3,3);
clear tmp
Note that if you compare running times with the standard way (asign the result and then access entries), they are exactly the same.
subs=#(M,i,j) M(i,j);
>> for nit=1:10;tic;subs(magic(100),1:10,1:10);tlap(nit)=toc;end;mean(tlap)
ans =
0.0103
>> for nit=1:10,tic;M=magic(100); M(1:10,1:10);tlap(nit)=toc;end;mean(tlap)
ans =
0.0101
To my opinion, the bottom line is : MATLAB does not have pointers, you have to live with it.
It could be more simple if you make a new function:
function [ element ] = getElem( matrix, index1, index2 )
element = matrix(index1, index2);
end
and then use it:
value = getElem(magic(5), 3, 3);
Your initial notation is the most concise way to do this:
M = magic(5); %create
value = M(3,3); % extract useful data
clear M; %free memory
If you are doing this in a loop you can just reassign M every time and ignore the clear statement as well.
To complement Amro's answer, you can use feval instead of builtin. There is no difference, really, unless you try to overload the operator function:
BUILTIN(...) is the same as FEVAL(...) except that it will call the
original built-in version of the function even if an overloaded one
exists (for this to work, you must never overload
BUILTIN).
>> feval('_paren', magic(5), 3, 3) % M(3,3)
ans =
13
>> feval('_brace', num2cell(magic(5)), 3, 3) % C{3,3}
ans =
13
What's interesting is that feval seems to be just a tiny bit quicker than builtin (by ~3.5%), at least in Matlab 2013b, which is weird given that feval needs to check if the function is overloaded, unlike builtin:
>> tic; for i=1:1e6, feval('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 49.904117 seconds.
>> tic; for i=1:1e6, builtin('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 51.485339 seconds.

Confusing of TTimeSpan usage in Delphi 2010

I tried the new Record type TTimeSpan in Delphi 2010. But I encourage a very strange problem.
assert(TTimeSpan.FromMilliseconds(5000).Milliseconds = 5000);
This assertion does not pass. The value of 'TTimeSpan.FromMilliseconds(5000).Milliseconds' is expected to be 5000, but it was 0.
I dig deeper:
function TTimeSpan.GetMilliseconds: Integer;
begin
Result := Integer((FTicks div TicksPerMillisecond) mod 1000);
end;
FTicks = 50000000
TicksPerMillisecond = 10000
FTick div TicksPerMillisecond = 50000000 div 10000 = 5000
(FTick div TicksPerMillisecond) mod 1000 = 5000 mod 1000 = 0 // I do not understand, why mod 1000
Integer((FTick div TicksPerMillisecond) mod 1000) = Integer(0) = 0
My code interpretation is correct, isn't it?
UPDATE: The method GetTotalMilliseconds (double precision) is implemented correctly.
You are confusing the properties giving the total amount expressed in a given unit with the properties giving the portion of a value when you break it up into its components (days, hours, minutes, seconds, milliseconds, ticks).
With those, you get the integer remainder for each category. So, Milliseconds will always be between 0 and 999 (Number Of Milliseconds Per Second - 1).
Or, another example, if you have 72 minutes, TotalMinutes is 72, but Minutes is 12.
It is very much similar to the DecodeDateTime function to break up a TDateTime.
And for what you want to achieve, you definitely need to use the TotalMilliseconds property, as TridenT pointed out, but the code for GetMilliseconds is indeed correct in TimeSpan.
You must use TotalMilliseconds instead of Milliseconds property.
It works better !
assert(TTimeSpan.FromMilliseconds(5000).TotalMilliseconds = 5000);
From documentation:
TotalMilliseconds Double
Timespan expressed as milliseconds and
part milliseconds

Resources