20

I have a requirement to store dates and durations arising from multiple different calendars. In particular I need to store dates that:

  1. Span the change to Gregorian calendars in different countries at different times
  2. Cover a historic period of at least 500 years
  3. Deal with multiple types of calendar - lunar, solar, Chinese, Financial, Christian, UTC, Muslim.
  4. Deal with the change, in the UK, of the year end from 31st March to 31st December, and comparable changes in other countries.

I also need to store durations which I have defined as the difference between two timestamps (date and time). This implies the need to be able to store a "zero" date - so I can store durations of, say, three and a half hours; or 10 minutes.

I have details of the computations needed. Firebird's timestamp is based on a date function that starts at January 1st, 100 CE, so is not capable of being used for durations in the way I need to record them. In addition this data type is geared up (like most timestamp functions) to record the number of days since a base date; it is not geared up to record calendar dates.

Could anyone suggest:

  1. A data structure to store dates and durations that meet the above requirements OR
  2. A reference to such a data structure OR
  3. Offer guidelines to approach the structuring of such storage OR
  4. Any points that may help me to a solution.

EDIT:

@Warren P has provided some excellent work in his responses. I obviously have not explained what I am seeking clearly enough, as his work concentrates on the computations and how to go about calculating these. All valuable and useful stuff, but not what I intended my question to convey.

I do have details of all the computations needed to convert between various representations of dates, and I have a fairly good idea of how to implement them (using elements such as Warren suggests). However, my requirement is to STORE dates which meet the various criteria listed above. Example: date to be stored - 'Third June 13 Charles II'. I am trying to determine an appropriate structure within which to store such dates.

EDIT:

I have amended my proposed schema. I have listed the attributes on each table, and defined the tables and attributes by examples, given in the third section of the entity box. I have used the example given in this question and answer in my definition by example, and have amended the example in my question to correspond. Although I have proved my schema by describing somebody else's example, this schema may still be over complicated; over analysed; miss some obvious simplification and may prove very difficult to implement (Indeed, it may be plain wrong). Any comments or suggestions would be most welcome.

enter image description here

Community
  • 1
  • 1
Chris Walton
  • 2,441
  • 3
  • 23
  • 38
  • 3
    +1 for an interesting question! – David Mar 25 '11 at 04:27
  • I really couldn't tell you want a Calendar Element Level IS or what it holds, and so I hope that diagram means something to you, because it doesn't mean much to me. I think coming in from the outside, some definitions would help. I'm guessing that an entry might be (Key,Name)? – Warren P Mar 25 '11 at 13:39
  • 1
    Great. Love the new ER diagram! – Warren P Mar 25 '11 at 18:28

2 Answers2

10

If you are writing your own, as I assume you intend to, I would make a class that contains a TDateTime, and other fields, and I would base it on the functionality in the very nicely written mxDateTime extension for Python, which is very easily readable, open source, C code, that you could use to extract the gregorian calendar logic you are going to need.

Within certain limits, TDateTime is always right. It's epoch value (0) is December 30, 1899 at midnight. From there, you can calculate other julian day numbers. It supports negative values, and thus it will support more than 400 years. I believe you will start having to do corrections, at the time of the last Gregorian calendar reforms. If you go from Friday, 15 October 1582, and figure out its julian day number, and the reforms before and after that, you should be able to do all that you require. Be aware that the time of day runs "backwards" before 1899, but that this is purely a problem in human heads, the computer will be accurate, and will calculate the number of minutes and seconds, up to the limit of double precision floating point math for you. Stick with TDateTime as your base.

I found some really old BorlandPascal/TurboPascal code that handles a really wide range of dates here.

If you need to handle arabic, jewish, and other calendars, again, I refer you to Python as a great source of working examples. Not just the mxdatetime extension, but stuff like this.

For database persistence, you might want to base your date storage around julian day numbers, and your time as C-like seconds since midnight, if the maximum resolution you need is 1 second.

Here's a snippet I would start with, and do code completion on:

TCalendarDisplaySubtype = ( cdsGregorian,cdsHebrew,cdsArabic,cdsAztec,
   cdsValveSoftwareCompany, cdsWhoTheHeckKnows );
TDateInformation = class
      private
         FBaseDateTime:TDateTime;
         FYear,FMonth,FDay:Integer; // if -1 then not calculated yet.
         FCalendarDisplaySubtype:TCalendarDisplaySubtype;
      public 

        function SetByDateInCE(Y,M,D,h,m,s:Integer):Boolean;
        function GetAsDateInCE(var Y,M,D,h,m,s:Integer):Boolean;
        function DisplayStr:String;
        function SetByDateInJewishCalendar( ... );
        property BaseDateTime:TDateTime read FDateTime write FDateTime;
        property JulianDayNumber:Integer read GetJulianDayNumber write SetJulianDayNumber;
        property CalendarDisplaySubType:TCalendarDisplaySubtype;



   end;

I see no reason to STORE both the julian day number, and the TDateTime, just use a constant, subtract/add from the Trunc(FBaseDateTime) value, and return that, in the GetJulianDayNumber,SetJulianDayNumber functions. It might be worth having fields where you calculate the year, month, day, for the given calendar, once, and store them, making the display as string function much simpler and faster.

Update: It looks like you're better at ER Modelling than me, so if you posted that diagram, I'd upvote it, and that would be it. As for me, I'd be storing three fields; A Datetime field that is normalized to modern calendar standards, a text field (free form) containing the original scholarly date in whatever form, and a few other fields, that are subtype lookup table Foreign keys, to help me organize, and search on dates by the date and subtype. That would be IT for me.

Warren P
  • 58,696
  • 38
  • 168
  • 301
  • @Warren - thank you. I appreciate the need for calculations, and rolling my own. The information about the Epoch date, and its support for negative values is useful. However, what I am seeking is an approach to storing such calendars/dates/durations. – Chris Walton Mar 25 '11 at 01:38
  • Most class libraries in C++ and Python handle things based on Julian day numbers, and keep date and time math completely separate. Since it seems you want what TDateTime does (allowing math on time lapses easily, and very quickly), it seems you also need TDateTime in-memory, and perhaps something else, on disk. For a reasonable library of DATA STRUCTURES, see the internals of mxDateTime. – Warren P Mar 25 '11 at 01:41
  • @Warren - your further references are also useful. Storage of the base information for calculations, based on julian day numbers, and the time as seconds since midnight again moves me forward, but does not address the storage of the original date, which calendar it came from, how it was originally recorded, or the subunits the date utilised. – Chris Walton Mar 25 '11 at 01:45
  • @Warren - out comments crossed. I had not appreciated that mxDateTime gave information on data structures. I will pursue that reference. – Chris Walton Mar 25 '11 at 01:47
  • @Warren: +1. Nice effort answering and great research/references. Well done! – Ken White Mar 25 '11 at 02:03
  • The trouble is that not every country switched to Gregorian at the same time. In some Eastern Europe countries this change is even fairly recent. Search for "Calendar Faq" for details. Also Stockton's site (the guy that wrote the referenced TP date routines) and various downloads are interesting. http://www.merlyn.demon.co.uk/pas-time.htm – Marco van de Voort Mar 25 '11 at 10:59
  • 1
    Don't forget to write Unit Tests! :-) – Warren P Mar 25 '11 at 13:34
  • @Warren - I appreciate your update. Your updated proposal has the great merit of simplicity, combined with the required transparency. I am aware I tend to over-complicate things, at least before I reach a final design, and while I am developing ideas. – Chris Walton Mar 25 '11 at 19:55
  • +1 for the resources, code fragments, and implementation guidelines. Accepted for your updated proposal. – Chris Walton Mar 25 '11 at 20:14
3

Only a partial answer but an important piece.

Since you are going to store dates in a very broad range where a lot of things happened to calendars, you need to accommodate for those changes.

The timezone database TZ-database and the Delphi TZDB wrapper around the TZ-database will be of big help.
It has a database with rules how timezones historically behave.

I know they are based on the current calendar schemes, and you need to convert to UTC first.

You need to devise something similar for the other calendar schemes you want to support.

Edit:

The scheme I'd use would be like this:

  • find ways for all your calendars to convert to/from UTC
  • store the calendar type
  • store the dates in their original format, and the source of the date (just in case your source screwed up, and you need to recalculate).
  • use the UTC conversions to go from your original through UTC to the calendar types in your UI

--jeroen

Jeroen Wiert Pluimers
  • 23,000
  • 6
  • 63
  • 142
  • As you say a partial answer, but a very important partial answer. I would not of thought of looking at or for timezone databases and will pursue your references vigorously. Your final comment - that I need to devise something similar for the other calendar schemes I wish to support might well be an abstract for my question. It is precisely this I am having problems getting my head around. Devising a storage mechanism to accommodate those changes is proving extremely difficult. – Chris Walton Mar 25 '11 at 15:11
  • +1 for everything and a virtual +2 for your edit, IMHO, that's the way to go. Store all info in a standardized way: modern UTC dates and convert to human readable representations only when needed. – jachguate Mar 25 '11 at 17:41
  • 1
    Absolutely. I think the thing here is to realize that the human readable representations are a presentation layer concern, at heart. Although storing them in the original state, might be needed too. – Warren P Mar 25 '11 at 18:31
  • 1
    Storing them in the original state is the absolute core of the requirement. Reasoning runs that the dates come from sources that may be some or all of partially illegible; in scripts that are archaic; that are potentially subject to mistranslation; are incorrect; etc. It is seen as essential that the storage of the date be as close as possible to the original, and that any transformation, interpretation, translation or transliteration must be capable of being inspected and potentially challenged or reinterpreted. – Chris Walton Mar 25 '11 at 19:11
  • +1 for the scheme proposal, and the timezone database resource. Your scheme, as modified by @Warren, is the way I am likely to go. – Chris Walton Mar 25 '11 at 19:58
  • @Chris W: Agreed 100%. Maybe I should have said "transformations are a presentation layer concern". Storage of the original form, is essential. – Warren P Mar 28 '11 at 23:48