The Delphi functions EncodeDate/DecodeDate seem to be able to handle only dates after 1.1.0001. Are there some implementations of EncodeDate/DecodeDate that can handle B.C. tDateTime values?
-
1Hmm, good question. Is there a well defined calendar for dates BC? In fact, is there a well defined calendar for dates with TDateTime<0? – David Heffernan Jul 28 '11 at 09:35
-
That statement seems to contradict the help text shown for `TDateTime` in my Delphi 2006 installation. In fact already the date values for dates before 1899-12-30 have a negative value and `EncodeDate` explicitly allows them. The help text for `TDateTime` makes no statement about the valid/supported range, only `EncodeDate` seems to limit the range to dates after year 1 - this is why I asked for some extended implementation of Delphi's default `EncodeDate/DecodeDate` functions. – blerontin Jul 28 '11 at 09:42
-
I deleted my earlier comment which is of course incorrect. However, I question whether or not TDateTime even works for dates down to 1.1.0001. Anyway, what calendar do you want to use? – David Heffernan Jul 28 '11 at 09:43
-
1I guess the [proleptic Gregorian calendar](https://secure.wikimedia.org/wikipedia/en/wiki/Proleptic_Gregorian_calendar) would be sufficient. But basically both functions should just be symmetrical, calling `Date := MyEncodeDate(-700, 1, 1)` followed sometimes later by `MyDecodeDate(Date, Year, Month, Day)` should return `Year = -700`, `Month = 1` and `Day = 1`. – blerontin Jul 28 '11 at 10:33
-
1David: from what I remember of the Calendar faq, many Eastern Europe countries only changed to Gregorian in the thirties. Even dates after 1899-12-30 are not always correct everywhere. – Marco van de Voort Jul 28 '11 at 11:45
-
Prior to the start of adoption of the Gregorian calendar in 1582, actual dates make no sense since there was no universal calendar in use. So EncodeDate/DecodeDate make no sense before this because they are modelled on the Gregorian calendar. – Misha Jul 28 '11 at 14:14
-
@Misha If you are happy with Proleptic Gregorian calendar then you could just code your own versions of EncodeDate/DecodeDate that used signed integers rather than `Word`. You'd have to deal with the fact that there is no year 0 though! – David Heffernan Jul 28 '11 at 18:24
-
@David, no need - I don't use dates much before 1900. I think the earliest dates I use are dates of birth, so perhaps no dates I use are before 1900. My point is that it makes no logical sense to use dates before a certain time because they do not actually mean anything. What does, say the 1st July, 1001 actually mean? You cannot reasonably calculate day of the week for this date like you can for the 1st July, 2001. I would label the original question as specious. – Misha Jul 29 '11 at 03:49
1 Answers
AFAIK TDateTime
is a Windows base type, common to COM, Variants, DotNet and Delphi. Negative values can be used for dates before 1899.
But that is not so simple - since with negative values comes some trouble, as stated by this page:
The integral part is the date, the fraction is the time. Date.time. That's easy. Things get odd when the value is negative. This is on or before #12/30/1899#.
With modern dates time always runs forwards, as you would suspect. With negative historical dates time actually runs backwards! Midnight #1/1/1800# equals −36522, but noon #1/1/1800# is −36522.5 (less than midnight!) and one second before midnight is −36522.9999884259 (even less). At midnight the clock jumps forward to -36521, which equals #1/2/1800#. The decimal fraction still shows the time and the integral part is the date, but each second decrements the clock while each new day increments it, not just by 1, but by almost 2. Negative times are really counterintuitive.
To make things worse, time values for #12/30/1899# are ambigous in two ways. First, a time value without a date equals that time on #12/30/1899#. This means that 0.5 is either noon or noon on #12/30/1899#, depending on context. Zero is either midnight, #12/30/1899# or midnight #12/30/1899#. The other ambiguity is that all time values come in double for #12/30/1899#. 0.5 is noon #12/30/1899#, but -0.5 is noon #12/30/1899# as well. The integral part is the date, the fraction is the time. Another surprise is here: #12/30/1899 11:59:59 PM# - #12/29/1899 11:59:59 PM# = 2.99997685185185. Not 1, what you normally would expect for a 24-hour period. Be careful when working with historical dates.
To my knowledge, the current implementation of EncodeDate/DecodeDate will work, but you may go into troubles when working with negative or near to zero TDateTime
values...
You should better use your own time format, e.g. ISO-8601 or a simple record as such:
TMyDateTime = packed record
Year: SmallInt;
Month: Byte;
Day: Byte;
end;
And when computing things about duration or displaying date/time, you must be aware that "our time" is not continuous. So calculation using the TDateTime=double
trick won't always work as expected. For instance, I remember that Teresa of Avila died in 1582, on October 4th, just as Catholic nations were making the switch from the Julian to the Gregorian calendar, which required the removal of October 5–14 from the calendar. :)

- 42,305
- 3
- 71
- 159
-
Current implementations of `EncodeDate`/`DecodeDate` only work with positive values. – Ondrej Kelle Jul 28 '11 at 10:08
-
If you go with your own date type/record, then I think `Month: 1..12; Day: 1..31;` would be more "pascalish" :) Perhaps even define special enum types for month and day. – ain Jul 28 '11 at 10:22
-
More information about julian and gregorian date calculations can be found here http://en.wikipedia.org/wiki/Julian_day#Calculation . One must first of all decide the context of a historical date I guess. – LU RD Jul 28 '11 at 10:47
-
1@TOndrej - That is not true. With Delphi you can work with negative TDateTime values up to -693593 (01 Jan 0001 Year). You should understand though that these values corresponds to the proleptic Gregorian calendar which is one of the many alternatives to extend the Gregorian calendar into the past. – kludg Jul 28 '11 at 15:38
-
@Serg I was talking about EncodeDate/DecodeDate. This has nothing to do with the fact that TDateTime itself can be used to store negative values (why shouldn't it, it's an alias for Double, after all). – Ondrej Kelle Jul 28 '11 at 15:43