Thread: timestamp patch to extend legal range of dates.

timestamp patch to extend legal range of dates.

From
John Cochran
Date:
Greetings,

Here is a patch that modifies how timestamp values are generated and
extends the legal range of timestamps. The overall effect is:

  1. For dates prior to Oct 15, 1582 it uses the Julian calendar.
  2. For dates on and after Oct 15, 1582, it uses the Gregorian calendar.

The implementation of date2j() and j2date() has been carefully done to
prevent integer overflow. As a result of this the legal range of dates
that this code will handle is from
   January 1, 4713 BC  through June 3, 5874898 AD
   (Julian day numbers from 0 to 2147483647)

Reasons for patch:
 1. 02/29/0100, 02/29/0200, 02/29/0300, 02/29/0500,
    02/29/0600, 02/29/0700, 02/29/0900, 02/29/1000,
    02/29/1100, 02/29/1300, 02/29/1400, 02/29/1500,
     are all legal dates on historical records.
 2. I hate overflows if I can at all avoid them.
 3. Legal timestamp range for PostGreSQL can now be advertised as extending
    from 4713 BC to 5874897 AD instead of only 4713 BC to 1465001 AD.

The decision to have the switchover point be Oct 15, 1582 was made to cause
the new routines return the same values as the old routines for the largest
possible range. I did consider having the switchover be Sept 14, 1752 to
be consistent with Unix, but decided against it.


*** postgresql-7.3.1/src/include/utils/datetime.h.old    Sat Feb  1 15:45:08 2003
--- postgresql-7.3.1/src/include/utils/datetime.h    Mon Feb  3 20:17:52 2003
***************
*** 230,249 ****

  extern int    day_tab[2][13];

! #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))

! /* Julian date support for date2j() and j2date()
!  * Set the minimum year to one greater than the year of the first valid day
!  *    to avoid having to check year and day both. - tgl 97/05/08
!  */

! #define JULIAN_MINYEAR (-4713)
! #define JULIAN_MINMONTH (11)
! #define JULIAN_MINDAY (23)

! #define IS_VALID_JULIAN(y,m,d) (((y) > JULIAN_MINYEAR) \
!  || (((y) == JULIAN_MINYEAR) && (((m) > JULIAN_MINMONTH) \
!   || (((m) == JULIAN_MINMONTH) && ((d) >= JULIAN_MINDAY)))))

  #define UTIME_MINYEAR (1901)
  #define UTIME_MINMONTH (12)
--- 230,243 ----

  extern int    day_tab[2][13];

! #define isleap(y) (((y) % 4) == 0 \
!  && (((y) % 100) != 0 || ((y) % 400) == 0 || (y) < 1582))

! /* Julian date support for date2j() and j2date() */

! #define JULIAN_MINYEAR (-4712)

! #define IS_VALID_JULIAN(y,m,d) ((y) >= JULIAN_MINYEAR)

  #define UTIME_MINYEAR (1901)
  #define UTIME_MINMONTH (12)

*** postgresql-7.3.1/src/backend/utils/adt/datetime.c.old    Fri Jan 31 22:14:55 2003
--- postgresql-7.3.1/src/backend/utils/adt/datetime.c    Mon Feb  3 20:19:54 2003
***************
*** 599,643 ****
   * - thomas 97/02/25
   */

  int
! date2j(int y, int m, int d)
  {
!     int            m12 = (m - 14) / 12;

!     return ((1461 * (y + 4800 + m12)) / 4
!             + (367 * (m - 2 - 12 * (m12))) / 12
!             - (3 * ((y + 4900 + m12) / 100)) / 4
!             + d - 32075);
! }    /* date2j() */

  void
! j2date(int jd, int *year, int *month, int *day)
  {
!     int            j,
!                 y,
!                 m,
!                 d;

!     int            i,
!                 l,
!                 n;

!     l = jd + 68569;
!     n = (4 * l) / 146097;
!     l -= (146097 * n + 3) / 4;
!     i = (4000 * (l + 1)) / 1461001;
!     l += 31 - (1461 * i) / 4;
!     j = (80 * l) / 2447;
!     d = l - (2447 * j) / 80;
!     l = j / 11;
!     m = (j + 2) - (12 * l);
!     y = 100 * (n - 49) + i + l;
!
!     *year = y;
!     *month = m;
!     *day = d;
!     return;
! }    /* j2date() */

  int
  j2day(int date)
--- 599,666 ----
   * - thomas 97/02/25
   */

+ /*
+  * Convert a year, month, day into a julian day number.
+  * Dates prior to Oct 15, 1582 are considered to be in the Julian Calendar.
+  * Later dates are considered to be in the Gregorian Calendar.
+  * Legal range of dates range from January 1, 4713 BC to June 3, 5874898 AD
+  * (Julian day numbers from 0 to 2147483647)
+  */
  int
! date2j(int year, int month, int day)
  {
!    unsigned int julian;

!    if (month > 2) {
!       year += 4800;
!       month += 1;
!    } else {
!       year += 4799;
!       month += 13;
!    }
!    julian  = year;
!    julian *= 365;
!    julian += year/4 + 7834*month/256 + day;
!    julian -= 32205;
!    if (julian >= 2299171) {
!       int centuries;
!       centuries = year / 100;
!       julian -= centuries - centuries/4 - 38;
!    }
!    return julian;
! }

  void
! j2date(int juldat, int *iyyy, int *mm, int *id)
  {
!    unsigned int julian;
!    unsigned int quad;
!    int year;

!    julian = juldat;
!    if (julian >= 2299161) {
!       unsigned int extra;

!       extra = julian - 1867217;
!       quad  = extra / 146097;
!       extra = (extra - quad * 146097) * 4 + 3;
!       julian += 1 + quad * 3 + extra / 146097;
!    }
!    quad    = julian / 1461;
!    julian -= quad * 1461;
!    year    = julian * 4 / 1461;
!    if (year != 0) { /* Non-leap year */
!       julian = (julian + 305) % 365 + 123;
!    } else { /* Leap year */
!       julian = (julian + 306) % 366 + 123;
!    }
!    year  += quad*4;
!    year  -= 4712;
!    *iyyy = year;
!    quad  = julian * 2141/65536;
!    *id   = julian - 7834*quad/256;
!    *mm   = (quad + 10) % 12 + 1;
! }

  int
  j2day(int date)

Re: timestamp patch to extend legal range of dates.

From
Neil Conway
Date:
On Mon, 2003-02-03 at 21:18, John Cochran wrote:
> Here is a patch that modifies how timestamp values are generated and
> extends the legal range of timestamps.

Without knowing anything about the timestamp code, one thing you'll need
to do is update the documentation. If you're keen, updating the
regression tests (and/or adding new tests for this functionality) would
also be appreciated.

Cheers,

Neil
--
Neil Conway <neilc@samurai.com> || PGP Key ID: DB3C29FC




Re: timestamp patch to extend legal range of dates.

From
Tom Lane
Date:
John Cochran <jdc@fiawol.org> writes:
> Here is a patch that modifies how timestamp values are generated and
> extends the legal range of timestamps. The overall effect is:
>   1. For dates prior to Oct 15, 1582 it uses the Julian calendar.
>   2. For dates on and after Oct 15, 1582, it uses the Gregorian calendar.
>
> The decision to have the switchover point be Oct 15, 1582 was made to cause
> the new routines return the same values as the old routines for the largest
> possible range. I did consider having the switchover be Sept 14, 1752 to
> be consistent with Unix, but decided against it.

This seems like rather an odd choice.  Isn't the 1752 date commonly
recognized as the start of Gregorian dating?

I'd be inclined to go with a changeover date that's defensible in the
long run, without regard to what we might have done before.  Backwards
compatibility with mistakes isn't a good design principle IMHO ...

            regards, tom lane

Re: timestamp patch to extend legal range of dates.

From
Oliver Elphick
Date:
On Tue, 2003-02-04 at 04:41, Tom Lane wrote:
> John Cochran <jdc@fiawol.org> writes:
> > Here is a patch that modifies how timestamp values are generated and
> > extends the legal range of timestamps. The overall effect is:
> >   1. For dates prior to Oct 15, 1582 it uses the Julian calendar.
> >   2. For dates on and after Oct 15, 1582, it uses the Gregorian calendar.
> >
> > The decision to have the switchover point be Oct 15, 1582 was made to cause
> > the new routines return the same values as the old routines for the largest
> > possible range. I did consider having the switchover be Sept 14, 1752 to
> > be consistent with Unix, but decided against it.
>
> This seems like rather an odd choice.  Isn't the 1752 date commonly
> recognized as the start of Gregorian dating?

In the 6th Century, Bede proposed a more accurate calendar than the
Julian calendar introduced by Julius Caesar.  In 1582, Pope Gregory
accepted a similar proposal and ordered the change across all the
Catholic world.  Since England had rejected papal authority and the pope
was encouraging Catholic powers to attack England, his order was not
effective here, nor in some other protestant countries.  It actually
took effect that year in France, Spain, Portugal and the Italian states;
parts of Germany, Belgium, the Netherlands, and Switzerland changed
within a couple of years and Hungary in 1587.  The Protestant parts of
Germany changed in 1700.

In 1752, England adopted the Gregorian calendar at last, causing popular
unrest ("Give us back our 11 days!").  The American colonies adopted it
at the same time.  Sweden changed in 1753 and Russia in 1918.

In 1751/2 England also changed from a new year date of 25th March to 1st
January as part of the same calendar change.  (The Inland Revenue didn't
change, which is why our tax year starts on April 6th.)  So in England
and America, 1751 ran from 25th March to 31st December; 1752 ran from
1st January to 2nd September and then 14th September to 31st December.
24th March 1750 was one day before 25th March 1751.

All of which adds up to: which you choose depends on what you are using
the date for.  Historical records of Catholic coutries need 1582; those
of England and English colonies need 1752.  Does anyone know how
historians cope when tracking both together?

--
Oliver Elphick                                Oliver.Elphick@lfix.co.uk
Isle of Wight, UK                             http://www.lfix.co.uk/oliver
GPG: 1024D/3E1D0C1C: CA12 09E0 E8D5 8870 5839  932A 614D 4C34 3E1D 0C1C
                 ========================================
     "That at the name of Jesus every knee should bow, of
      things in heaven, and things in earth, and things
      under the earth; And that every tongue should confess
      that Jesus Christ is Lord, to the glory of God the
      Father."              Philippians 2:10,11


Re: timestamp patch to extend legal range of dates.

From
Tom Lane
Date:
Oliver Elphick <olly@lfix.co.uk> writes:
> On Tue, 2003-02-04 at 04:41, Tom Lane wrote:
>> This seems like rather an odd choice.  Isn't the 1752 date commonly
>> recognized as the start of Gregorian dating?

> [ historical calendars are a mess ]

True.  So if I've got this straight:

    Oct 15, 1582 = Gregorian changeover in Catholic countries
    Sep 14, 1752 = changeover in Britain and her colonies
    various other dates in other places

However, that doesn't mean we should just toss a coin to decide which
behavior to follow.  John says that there is a precedent for using
1752 (but which Unix are you speaking of here, John?  Most of 'em don't
keep time before ~1900, period).  I'd be inclined to follow that
precedent not strike out on our own.

Also, given that the majority of Postgres users are (so far as I can
tell) in English-speaking countries, the 1752 date seems most useful
to the majority.

I suppose we could contemplate making the switch occur on a date
determined by LC_TIME ;-) ... but I don't think I wanna go there ...

            regards, tom lane

Re: timestamp patch to extend legal range of dates.

From
Rod Taylor
Date:
> 1752 (but which Unix are you speaking of here, John?  Most of 'em don't
> keep time before ~1900, period).  I'd be inclined to follow that
> precedent not strike out on our own.

bash-2.05b$ cal 09 1752
   September 1752
Su Mo Tu We Th Fr Sa
       1  2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

bash-2.05b$ uname -a
FreeBSD jester 4.7-RELEASE FreeBSD 4.7-RELEASE #10: Sat Nov 16 16:00:07
EST 2002     root@jester:/usr/obj/usr/src/sys/JESTER  i386


--
Rod Taylor <rbt@rbt.ca>

PGP Key: http://www.rbt.ca/rbtpub.asc

Attachment

Re: timestamp patch to extend legal range of dates.

From
Oliver Elphick
Date:
On Tue, 2003-02-04 at 16:09, Rod Taylor wrote:
> > 1752 (but which Unix are you speaking of here, John?  Most of 'em don't
> > keep time before ~1900, period).  I'd be inclined to follow that
> > precedent not strike out on our own.
>
> bash-2.05b$ cal 09 1752
>    September 1752
> Su Mo Tu We Th Fr Sa
>        1  2 14 15 16
> 17 18 19 20 21 22 23
> 24 25 26 27 28 29 30
>
> bash-2.05b$ uname -a
> FreeBSD jester 4.7-RELEASE FreeBSD 4.7-RELEASE #10: Sat Nov 16 16:00:07
> EST 2002     root@jester:/usr/obj/usr/src/sys/JESTER  i386

So cal is using English dates, but it isn't consistent because it
doesn't know about the change in the new year date in England in 1751/2.

I got my data from
http://serendipity.magnet.ch/hermetic/cal_stud/cal_art.htm
which also talks about proleptic calendars; that is, the current
calendar is projected backwards (or the Julian calendar is projected
forwards), so that there are no breaks in the sequence of dates.

Does the SQL standard define what calendar it is using?  My copy of Date
& Cannan: 'SQL -- The Standard Handbook' says:

"YEAR - Valid values: 0001 to 9999.  This permits the specification of
dates prior to the invention of the Gregorian calendar and assumes that
the rules of the Gregorian calendar can be applied retrospectively."

So I suggest we should use the Gregorian proleptic calendar as the
default.

Example:    England       Italy         Proleptic
                                        Gregorian

          15 Sep 1752   15 Sep 1752    15 Sep 1752
           1 Sep 1752   12 Sep 1752    12 Sep 1752
          25 Mar 1751    5 Apr 1751     5 Apr 1751
          24 Mar 1750    4 Apr 1751     4 Apr 1751
           6 Oct 1582   15 Oct 1582    15 Oct 1582
           5 Oct 1582    5 Oct 1582    14 Oct 1582
          24 Mar 1581   24 Mar 1582     3 Apr 1582

Admittedly, nowhere in the world would have used the proleptic Gregorian
date before 15 Oct 1582 (except for all of the third century AD in the
Roman empire), but it has the virtue of consistency, and any system that
changes the calendar basis at a particular date will also be wrong for a
large number of users.  Even if we say that most of our users are from
England and its colonies, that is not to say that their uses for
historical dates are limited to events in those places or seen from the
point of view of their then inhabitants.  It is really only historians,
genealogists and astronomers who are likely to want to use such ancient
dates, so we ought to consider their needs before fixing the type.
Consider how you would record the marriage date of an Italian marrying a
Briton in the seventeenth century, if you wanted to relate it to events
in both countries.

If the date type were able to have subtypes, the typmod field could be
used to determine what system a date belonged to and therefore how it
should be represented, or else these could be predefined function
parameters.  E.g.:

           0  Proleptic Gregorian - default
           1  Astronomical
           2  England and colonies
           4  Italy, Spain, France, Portugal
           5  Catholic Germany
           6  Protestant Germany
           7  Sweden
           8  Russia
           9  Greece
          10  Turkey
          11  Eastern Orthodox Church
          12  ab urbe condita
          13  Jewish
          ... etc ...

While the conversions for these could be written to be based on John's
current proposal, it would be purer and simpler if they did not have to
cope with any gaps at all.  We don't have to define all of them, but
make the structure so that they can be filled in later.

The internal effect would be that the earliest possible date would be
about 29th November 4714BC (Gregorian proleptic calendar); since that
exceeds the SQL limit, it should not be a problem.

--
Oliver Elphick                                Oliver.Elphick@lfix.co.uk
Isle of Wight, UK                             http://www.lfix.co.uk/oliver
GPG: 1024D/3E1D0C1C: CA12 09E0 E8D5 8870 5839  932A 614D 4C34 3E1D 0C1C
                 ========================================
     "That at the name of Jesus every knee should bow, of
      things in heaven, and things in earth, and things
      under the earth; And that every tongue should confess
      that Jesus Christ is Lord, to the glory of God the
      Father."              Philippians 2:10,11


Re: timestamp patch to extend legal range of dates.

From
John Cochran
Date:
 [snip...]

> I got my data from
> http://serendipity.magnet.ch/hermetic/cal_stud/cal_art.htm
> which also talks about proleptic calendars; that is, the current
> calendar is projected backwards (or the Julian calendar is projected
> forwards), so that there are no breaks in the sequence of dates.
>
> Does the SQL standard define what calendar it is using?  My copy of Date
> & Cannan: 'SQL -- The Standard Handbook' says:
>
> "YEAR - Valid values: 0001 to 9999.  This permits the specification of
> dates prior to the invention of the Gregorian calendar and assumes that
> the rules of the Gregorian calendar can be applied retrospectively."
>
> So I suggest we should use the Gregorian proleptic calendar as the
> default.
>
> Example:    England       Italy         Proleptic
>                                         Gregorian
>
>           15 Sep 1752   15 Sep 1752    15 Sep 1752
>            1 Sep 1752   12 Sep 1752    12 Sep 1752
>           25 Mar 1751    5 Apr 1751     5 Apr 1751
>           24 Mar 1750    4 Apr 1751     4 Apr 1751
>            6 Oct 1582   15 Oct 1582    15 Oct 1582
>            5 Oct 1582    5 Oct 1582    14 Oct 1582
>           24 Mar 1581   24 Mar 1582     3 Apr 1582
>
 [snip...]

Point taken. I withdraw my patch. However, I'm going to examine date2j()
and j2date() functions a bit closer and see if I can restructure them to
eliminate the overflow problems they have. I would still like for those
functions to be capable of dealing with the full range of available numbers.


Re: timestamp patch to extend legal range of dates.

From
Tom Lane
Date:
John Cochran <jdc@fiawol.org> writes:
> Point taken. I withdraw my patch. However, I'm going to examine date2j()
> and j2date() functions a bit closer and see if I can restructure them to
> eliminate the overflow problems they have. I would still like for those
> functions to be capable of dealing with the full range of available numbers.

Sure, that's worth doing in any case.

            regards, tom lane