aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2003-02-19 03:48:11 +0000
committerBruce Momjian <bruce@momjian.us>2003-02-19 03:48:11 +0000
commita286f7321083f06e395959dffeb9b87f028defa0 (patch)
treed5ab53649d5abe11dd393c26ef7ff6b3b5cce96c /src
parent4efbbd731899df673cab4415b9111744d9537a62 (diff)
downloadpostgresql-a286f7321083f06e395959dffeb9b87f028defa0.tar.gz
postgresql-a286f7321083f06e395959dffeb9b87f028defa0.zip
The following patches eliminate the overflows in the j2date() and date2j()
functions which limited the maximum date for a timestamp to AD 1465001. The new limit is AD 5874897. The files affected are: doc/src/sgml/datatype.sgml: Documentation change due to patch. Included is a notice about the reduced range when using an eight-byte integer for timestamps. src/backend/utils/adt/datetime.c: Replacement functions for j2date() and date2j() functions. src/include/utils/datetime.h: Corrected a bug with the limit on the earliest possible date, Nov 23,-4713 has a Julian day count of -1. The earliest possible date should be Nov 24, -4713 with a day count of 0. src/test/regress/expected/horology-no-DST-before-1970.out: src/test/regress/expected/horology-solaris-1947.out: src/test/regress/expected/horology.out: Copies of expected output for regression testing. Note: Only horology.out has been physically tested. I do not have access to a Solaris box and I don't know how to provoke the "pre-1970" test. src/test/regress/sql/horology.sql: Added some test cases to check extended range. John Cochran
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/datetime.c87
-rw-r--r--src/include/utils/datetime.h4
-rw-r--r--src/test/regress/expected/horology-no-DST-before-1970.out24
-rw-r--r--src/test/regress/expected/horology-solaris-1947.out24
-rw-r--r--src/test/regress/expected/horology.out24
-rw-r--r--src/test/regress/sql/horology.sql4
6 files changed, 127 insertions, 40 deletions
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index b73554bf2e6..994ab9fae08 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.99 2003/01/29 01:08:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.100 2003/02/19 03:48:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -587,66 +587,77 @@ static datetkn *deltacache[MAXDATEFIELDS] = {NULL};
* since it is numerically accurate and computationally simple.
* The algorithms here will accurately convert between Julian day
* and calendar date for all non-negative Julian days
- * (i.e. from Nov 23, -4713 on).
- *
- * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
- * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
- *
- * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
- * now at Aerospace Corp. (hi, Henry!)
+ * (i.e. from Nov 24, -4713 on).
*
* These routines will be used by other date/time packages
* - thomas 97/02/25
+ *
+ * Rewritten to eliminate overflow problems. This now allows the
+ * routines to work correctly for all Julian day counts from
+ * 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming
+ * a 32-bit integer. Longer types should also work to the limits
+ * of their precision.
*/
int
date2j(int y, int m, int d)
{
- int m12 = (m - 14) / 12;
+ int julian;
+ int century;
+
+ if (m > 2) {
+ m += 1;
+ y += 4800;
+ } else {
+ m += 13;
+ y += 4799;
+ }
+
+ century = y/100;
+ julian = y*365 - 32167;
+ julian += y/4 - century + century/4;
+ julian += 7834*m/256 + d;
- return ((1461 * (y + 4800 + m12)) / 4
- + (367 * (m - 2 - 12 * (m12))) / 12
- - (3 * ((y + 4900 + m12) / 100)) / 4
- + d - 32075);
+ return julian;
} /* date2j() */
void
j2date(int jd, int *year, int *month, int *day)
{
- int j,
- y,
- m,
- d;
+ unsigned int julian;
+ unsigned int quad;
+ unsigned int extra;
+ int y;
+
+ julian = jd;
+ julian += 32044;
+ quad = julian/146097;
+ extra = (julian - quad*146097)*4 + 3;
+ julian += 60 + quad*3 + extra/146097;
+ quad = julian/1461;
+ julian -= quad*1461;
+ y = julian * 4 / 1461;
+ julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366))
+ + 123;
+ y += quad*4;
+ *year = y - 4800;
+ quad = julian * 2141 / 65536;
+ *day = julian - 7834*quad/256;
+ *month = (quad + 10) % 12 + 1;
- 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)
{
- int day;
+ unsigned int day;
- day = (date + 1) % 7;
+ day = date;
+ day += 1;
+ day %= 7;
- return day;
+ return (int) day;
} /* j2day() */
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 3b81770152d..251691325a5 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: datetime.h,v 1.34 2003/01/16 00:26:49 tgl Exp $
+ * $Id: datetime.h,v 1.35 2003/02/19 03:48:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -239,7 +239,7 @@ extern int day_tab[2][13];
#define JULIAN_MINYEAR (-4713)
#define JULIAN_MINMONTH (11)
-#define JULIAN_MINDAY (23)
+#define JULIAN_MINDAY (24)
#define IS_VALID_JULIAN(y,m,d) (((y) > JULIAN_MINYEAR) \
|| (((y) == JULIAN_MINYEAR) && (((m) > JULIAN_MINMONTH) \
diff --git a/src/test/regress/expected/horology-no-DST-before-1970.out b/src/test/regress/expected/horology-no-DST-before-1970.out
index ab40a6404ca..5a191c9370c 100644
--- a/src/test/regress/expected/horology-no-DST-before-1970.out
+++ b/src/test/regress/expected/horology-no-DST-before-1970.out
@@ -328,6 +328,30 @@ SELECT timestamp without time zone '1999-12-01' + interval '1 month - 1 second'
Fri Dec 31 23:59:59 1999
(1 row)
+SELECT timestamp without time zone 'Nov 24, 4714 BC' + interval '1000000000 days' AS "Nov 27, 2733194";
+ Nov 27, 2733194
+-----------------------------
+ Sun Nov 27 00:00:00 2733194
+(1 row)
+
+SELECT timestamp without time zone 'Nov 24, 4714 BC' + interval '2000000000 days' AS "Nov 30, 5471101";
+ Nov 30, 5471101
+-----------------------------
+ Sat Nov 30 00:00:00 5471101
+(1 row)
+
+SELECT timestamp without time zone 'Nov 25, 4714 BC' + interval '2147483492 days' AS "Dec 31, 5874897";
+ Dec 31, 5874897
+-----------------------------
+ Tue Dec 31 00:00:00 5874897
+(1 row)
+
+SELECT timestamp without time zone '12/31/5874897' - timestamp without time zone 'Nov 24, 4714 BC' AS "2147483493 Days";
+ 2147483493 Days
+-------------------
+ @ 2147483493 days
+(1 row)
+
-- Shorthand values
-- Not directly usable for regression testing since these are not constants.
-- So, just try to test parser and hope for the best - thomas 97/04/26
diff --git a/src/test/regress/expected/horology-solaris-1947.out b/src/test/regress/expected/horology-solaris-1947.out
index 222ee303679..784900531d7 100644
--- a/src/test/regress/expected/horology-solaris-1947.out
+++ b/src/test/regress/expected/horology-solaris-1947.out
@@ -328,6 +328,30 @@ SELECT timestamp without time zone '1999-12-01' + interval '1 month - 1 second'
Fri Dec 31 23:59:59 1999
(1 row)
+SELECT timestamp without time zone 'Nov 24, 4714 BC' + interval '1000000000 days' AS "Nov 27, 2733194";
+ Nov 27, 2733194
+-----------------------------
+ Sun Nov 27 00:00:00 2733194
+(1 row)
+
+SELECT timestamp without time zone 'Nov 24, 4714 BC' + interval '2000000000 days' AS "Nov 30, 5471101";
+ Nov 30, 5471101
+-----------------------------
+ Sat Nov 30 00:00:00 5471101
+(1 row)
+
+SELECT timestamp without time zone 'Nov 25, 4714 BC' + interval '2147483492 days' AS "Dec 31, 5874897";
+ Dec 31, 5874897
+-----------------------------
+ Tue Dec 31 00:00:00 5874897
+(1 row)
+
+SELECT timestamp without time zone '12/31/5874897' - timestamp without time zone 'Nov 24, 4714 BC' AS "2147483493 Days";
+ 2147483493 Days
+-------------------
+ @ 2147483493 days
+(1 row)
+
-- Shorthand values
-- Not directly usable for regression testing since these are not constants.
-- So, just try to test parser and hope for the best - thomas 97/04/26
diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out
index 2735641e396..d91df1bd829 100644
--- a/src/test/regress/expected/horology.out
+++ b/src/test/regress/expected/horology.out
@@ -328,6 +328,30 @@ SELECT timestamp without time zone '1999-12-01' + interval '1 month - 1 second'
Fri Dec 31 23:59:59 1999
(1 row)
+SELECT timestamp without time zone 'Nov 24, 4714 BC' + interval '1000000000 days' AS "Nov 27, 2733194";
+ Nov 27, 2733194
+-----------------------------
+ Sun Nov 27 00:00:00 2733194
+(1 row)
+
+SELECT timestamp without time zone 'Nov 24, 4714 BC' + interval '2000000000 days' AS "Nov 30, 5471101";
+ Nov 30, 5471101
+-----------------------------
+ Sat Nov 30 00:00:00 5471101
+(1 row)
+
+SELECT timestamp without time zone 'Nov 25, 4714 BC' + interval '2147483492 days' AS "Dec 31, 5874897";
+ Dec 31, 5874897
+-----------------------------
+ Tue Dec 31 00:00:00 5874897
+(1 row)
+
+SELECT timestamp without time zone '12/31/5874897' - timestamp without time zone 'Nov 24, 4714 BC' AS "2147483493 Days";
+ 2147483493 Days
+-------------------
+ @ 2147483493 days
+(1 row)
+
-- Shorthand values
-- Not directly usable for regression testing since these are not constants.
-- So, just try to test parser and hope for the best - thomas 97/04/26
diff --git a/src/test/regress/sql/horology.sql b/src/test/regress/sql/horology.sql
index 0af769a8b2c..0ff15e9711f 100644
--- a/src/test/regress/sql/horology.sql
+++ b/src/test/regress/sql/horology.sql
@@ -76,6 +76,10 @@ SELECT timestamp without time zone '1996-03-01' - interval '1 second' AS "Feb 29
SELECT timestamp without time zone '1999-03-01' - interval '1 second' AS "Feb 28";
SELECT timestamp without time zone '2000-03-01' - interval '1 second' AS "Feb 29";
SELECT timestamp without time zone '1999-12-01' + interval '1 month - 1 second' AS "Dec 31";
+SELECT timestamp without time zone 'Nov 24, 4714 BC' + interval '1000000000 days' AS "Nov 27, 2733194";
+SELECT timestamp without time zone 'Nov 24, 4714 BC' + interval '2000000000 days' AS "Nov 30, 5471101";
+SELECT timestamp without time zone 'Nov 25, 4714 BC' + interval '2147483492 days' AS "Dec 31, 5874897";
+SELECT timestamp without time zone '12/31/5874897' - timestamp without time zone 'Nov 24, 4714 BC' AS "2147483493 Days";
-- Shorthand values
-- Not directly usable for regression testing since these are not constants.