From 921d749bd4c34c3349f1c254d5faa2f1cec03911 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 3 Jun 2004 02:08:07 +0000 Subject: [PATCH] Adjust our timezone library to use pg_time_t (typedef'd as int64) in place of time_t, as per prior discussion. The behavior does not change on machines without a 64-bit-int type, but on machines with one, which is most, we are rid of the bizarre boundary behavior at the edges of the 32-bit-time_t range (1901 and 2038). The system will now treat times over the full supported timestamp range as being in your local time zone. It may seem a little bizarre to consider that times in 4000 BC are PST or EST, but this is surely at least as reasonable as propagating Gregorian calendar rules back that far. I did not modify the format of the zic timezone database files, which means that for the moment the system will not know about daylight-savings periods outside the range 1901-2038. Given the way the files are set up, it's not a simple decision like 'widen to 64 bits'; we have to actually think about the range of years that need to be supported. We should probably inquire what the plans of the upstream zic people are before making any decisions of our own. --- src/backend/access/transam/xact.c | 7 +- src/backend/access/transam/xlog.c | 7 +- src/backend/bootstrap/bootparse.y | 3 +- src/backend/bootstrap/bootscanner.l | 3 +- src/backend/bootstrap/bootstrap.c | 3 +- src/backend/optimizer/geqo/geqo_main.c | 3 +- src/backend/postmaster/bgwriter.c | 3 +- src/backend/postmaster/pgstat.c | 3 +- src/backend/postmaster/postmaster.c | 4 +- src/backend/storage/buffer/freelist.c | 4 +- src/backend/tcop/postgres.c | 3 +- src/backend/utils/adt/date.c | 32 +- src/backend/utils/adt/datetime.c | 201 +++++------ src/backend/utils/adt/nabstime.c | 11 +- src/backend/utils/adt/timestamp.c | 137 ++++---- src/backend/utils/error/elog.c | 14 +- src/include/catalog/pg_control.h | 5 +- src/include/commands/vacuum.h | 3 +- src/include/pgtime.h | 46 +-- src/include/utils/datetime.h | 24 +- src/include/utils/nabstime.h | 6 +- src/test/regress/expected/horology.out | 32 +- src/test/regress/expected/timestamp.out | 24 +- src/test/regress/expected/timestamptz.out | 96 +++--- src/timezone/localtime.c | 385 +++------------------- src/timezone/pgtz.c | 38 +-- src/timezone/strftime.c | 17 +- src/timezone/zic.c | 91 +++-- 28 files changed, 419 insertions(+), 786 deletions(-) diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 1764000e5e..2ae0fc5b21 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.167 2004/05/22 23:14:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.168 2004/06/03 02:08:00 tgl Exp $ * * NOTES * Transaction aborts can now occur two ways: @@ -141,6 +141,7 @@ #include "postgres.h" +#include #include #include "access/gistscan.h" @@ -1858,7 +1859,7 @@ xact_desc(char *buf, uint8 xl_info, char *rec) if (info == XLOG_XACT_COMMIT) { xl_xact_commit *xlrec = (xl_xact_commit *) rec; - struct pg_tm *tm = pg_localtime(&xlrec->xtime); + struct tm *tm = localtime(&xlrec->xtime); sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, @@ -1868,7 +1869,7 @@ xact_desc(char *buf, uint8 xl_info, char *rec) else if (info == XLOG_XACT_ABORT) { xl_xact_abort *xlrec = (xl_xact_abort *) rec; - struct pg_tm *tm = pg_localtime(&xlrec->xtime); + struct tm *tm = localtime(&xlrec->xtime); sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index cfb864d094..f120564061 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.145 2004/05/29 22:48:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.146 2004/06/03 02:08:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -2761,9 +2762,9 @@ str_time(time_t tnow) { static char buf[128]; - pg_strftime(buf, sizeof(buf), + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z", - pg_localtime(&tnow)); + localtime(&tnow)); return buf; } diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 4047958b1d..cbc8ff6e53 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.68 2004/05/26 04:41:05 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.69 2004/06/03 02:08:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,7 +38,6 @@ #include "nodes/parsenodes.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" -#include "pgtime.h" #include "rewrite/prs2lock.h" #include "storage/block.h" #include "storage/fd.h" diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l index 3f8777ea66..57b872231d 100644 --- a/src/backend/bootstrap/bootscanner.l +++ b/src/backend/bootstrap/bootscanner.l @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootscanner.l,v 1.34 2004/05/21 05:07:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootscanner.l,v 1.35 2004/06/03 02:08:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,7 +29,6 @@ #include "nodes/pg_list.h" #include "nodes/primnodes.h" #include "parser/scansup.h" -#include "pgtime.h" #include "rewrite/prs2lock.h" #include "storage/block.h" #include "storage/fd.h" diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index a19217a47b..02f67339b1 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.182 2004/05/29 22:48:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.183 2004/06/03 02:08:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,7 +34,6 @@ #include "libpq/pqsignal.h" #include "miscadmin.h" #include "postmaster/bgwriter.h" -#include "pgtime.h" #include "storage/freespace.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" diff --git a/src/backend/optimizer/geqo/geqo_main.c b/src/backend/optimizer/geqo/geqo_main.c index 902fcbcd26..fe73a42ea0 100644 --- a/src/backend/optimizer/geqo/geqo_main.c +++ b/src/backend/optimizer/geqo/geqo_main.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_main.c,v 1.44 2004/05/21 05:07:57 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_main.c,v 1.45 2004/06/03 02:08:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,7 +31,6 @@ #include "optimizer/geqo_mutation.h" #include "optimizer/geqo_pool.h" #include "optimizer/geqo_selection.h" -#include "pgtime.h" /* diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index 6bb683386a..f0ecb13d27 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -37,13 +37,14 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.2 2004/05/31 03:47:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/bgwriter.c,v 1.3 2004/06/03 02:08:03 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include +#include #include "access/xlog.h" #include "libpq/pqsignal.h" diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index c90749ca12..1a904cc8db 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -13,7 +13,7 @@ * * Copyright (c) 2001-2003, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.73 2004/05/29 22:48:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.74 2004/06/03 02:08:03 tgl Exp $ * ---------- */ #include "postgres.h" @@ -29,6 +29,7 @@ #include #include #include +#include #include "pgstat.h" diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 6a0cfbf052..85ba8309d4 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.401 2004/05/30 03:50:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.402 2004/06/03 02:08:03 tgl Exp $ * * NOTES * @@ -62,6 +62,7 @@ #include #include +#include #include #include #include @@ -97,7 +98,6 @@ #include "miscadmin.h" #include "nodes/nodes.h" #include "postmaster/postmaster.h" -#include "pgtime.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c index b59bfb9543..b4702e7ceb 100644 --- a/src/backend/storage/buffer/freelist.c +++ b/src/backend/storage/buffer/freelist.c @@ -12,12 +12,14 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/buffer/freelist.c,v 1.43 2004/04/21 18:06:30 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/buffer/freelist.c,v 1.44 2004/06/03 02:08:03 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include + #include "access/xact.h" #include "storage/buf_internals.h" #include "storage/bufmgr.h" diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 556528d6d2..29d457e94e 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.417 2004/05/29 22:48:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.418 2004/06/03 02:08:03 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -46,7 +46,6 @@ #include "optimizer/planner.h" #include "parser/analyze.h" #include "parser/parser.h" -#include "pgtime.h" #include "rewrite/rewriteHandler.h" #include "storage/freespace.h" #include "storage/ipc.h" diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 91ef127a94..1caa68d774 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.98 2004/05/31 18:53:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.99 2004/06/03 02:08:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,7 +23,6 @@ #include "libpq/pqformat.h" #include "miscadmin.h" #include "parser/scansup.h" -#include "pgtime.h" #include "utils/builtins.h" #include "utils/date.h" #include "utils/nabstime.h" @@ -295,35 +294,22 @@ date2timestamptz(DateADT dateVal) TimestampTz result; struct pg_tm tt, *tm = &tt; + int tz; j2date(dateVal + POSTGRES_EPOCH_JDATE, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); - if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { - int tz; - - tm->tm_hour = 0; - tm->tm_min = 0; - tm->tm_sec = 0; - tz = DetermineLocalTimeZone(tm); + tm->tm_hour = 0; + tm->tm_min = 0; + tm->tm_sec = 0; + tz = DetermineLocalTimeZone(tm); #ifdef HAVE_INT64_TIMESTAMP - result = (dateVal * INT64CONST(86400000000)) - + (tz * INT64CONST(1000000)); + result = (dateVal * INT64CONST(86400000000)) + + (tz * INT64CONST(1000000)); #else - result = dateVal * 86400.0 + tz; + result = dateVal * 86400.0 + tz; #endif - } - else - { - /* Outside of range for timezone support, so assume UTC */ -#ifdef HAVE_INT64_TIMESTAMP - result = (dateVal * INT64CONST(86400000000)); -#else - result = dateVal * 86400.0; -#endif - } return result; } diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 4284e4c5e3..6fdefc536e 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.129 2004/05/31 18:53:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.130 2004/06/03 02:08:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,9 +40,11 @@ static int DecodeDate(char *str, int fmask, int *tmask, struct pg_tm * tm); static void TrimTrailingZeros(char *str); -int day_tab[2][13] = { +const int day_tab[2][13] = +{ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}, -{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}}; + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0} +}; char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}; @@ -1572,115 +1574,126 @@ DecodeDateTime(char **field, int *ftype, int nf, * (ie, regular or daylight-savings time) at that time. Set the struct pg_tm's * tm_isdst field accordingly, and return the actual timezone offset. * - * Note: this subroutine exists because mktime() has such a spectacular - * variety of, ahem, odd behaviors on various platforms. We used to try to - * use mktime() here, but finally gave it up as a bad job. Avoid using - * mktime() anywhere else. + * Note: it might seem that we should use mktime() for this, but bitter + * experience teaches otherwise. In particular, mktime() is generally + * incapable of coping reasonably with "impossible" times within a + * spring-forward DST transition. Typical implementations of mktime() + * turn out to be loops around localtime() anyway, so they're not even + * any faster than this code. */ int -DetermineLocalTimeZone(struct pg_tm * tm) +DetermineLocalTimeZone(struct pg_tm *tm) { int tz; + int date, + sec; + pg_time_t day, + mysec, + locsec, + delta1, + delta2; + struct pg_tm *tx; if (HasCTZSet) { tm->tm_isdst = 0; /* for lack of a better idea */ - tz = CTimeZone; + return CTimeZone; } - else if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) + + /* + * First, generate the pg_time_t value corresponding to the given + * y/m/d/h/m/s taken as GMT time. If this overflows, punt and + * decide the timezone is GMT. (We only need to worry about overflow + * on machines where pg_time_t is 32 bits.) + */ + if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) + goto overflow; + date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE; + day = ((pg_time_t) date) * 86400; + if (day / 86400 != date) + goto overflow; + sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * 60) * 60; + mysec = day + sec; + /* since sec >= 0, overflow could only be from +day to -mysec */ + if (mysec < 0 && day > 0) + goto overflow; + + /* + * Use pg_localtime to convert that pg_time_t to broken-down time, + * and reassemble to get a representation of local time. (We could get + * overflow of a few hours in the result, but the delta calculation + * should still work.) + */ + tx = pg_localtime(&mysec); + if (!tx) + goto overflow; /* probably can't happen */ + day = date2j(tx->tm_year + 1900, tx->tm_mon + 1, tx->tm_mday) - + UNIX_EPOCH_JDATE; + locsec = tx->tm_sec + (tx->tm_min + (day * 24 + tx->tm_hour) * 60) * 60; + + /* + * The local time offset corresponding to that GMT time is now + * computable as mysec - locsec. + */ + delta1 = mysec - locsec; + + /* + * However, if that GMT time and the local time we are + * actually interested in are on opposite sides of a + * daylight-savings-time transition, then this is not the time + * offset we want. So, adjust the pg_time_t to be what we think + * the GMT time corresponding to our target local time is, and + * repeat the pg_localtime() call and delta calculation. + * + * We have to watch out for overflow while adjusting the pg_time_t. + */ + if ((delta1 < 0) ? (mysec < 0 && (mysec + delta1) > 0) : + (mysec > 0 && (mysec + delta1) < 0)) + goto overflow; + mysec += delta1; + tx = pg_localtime(&mysec); + if (!tx) + goto overflow; /* probably can't happen */ + day = date2j(tx->tm_year + 1900, tx->tm_mon + 1, tx->tm_mday) - + UNIX_EPOCH_JDATE; + locsec = tx->tm_sec + (tx->tm_min + (day * 24 + tx->tm_hour) * 60) * 60; + delta2 = mysec - locsec; + + /* + * We may have to do it again to get the correct delta. + * + * It might seem we should just loop until we get the same delta + * twice in a row, but if we've been given an "impossible" local + * time (in the gap during a spring-forward transition) we'd never + * get out of the loop. The behavior we want is that "impossible" + * times are taken as standard time, and also that ambiguous times + * (during a fall-back transition) are taken as standard time. + * Therefore, we bias the code to prefer the standard-time solution. + */ + if (delta2 != delta1 && tx->tm_isdst != 0) { - /* - * First, generate the time_t value corresponding to the given - * y/m/d/h/m/s taken as GMT time. This will not overflow (at - * least not for time_t taken as signed) because of the range - * check we did above. - */ - long day, - mysec, - locsec, - delta1, - delta2; - time_t mytime; - struct pg_tm *tx; - - day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE; - mysec = tm->tm_sec + (tm->tm_min + (day * 24 + tm->tm_hour) * 60) * 60; - mytime = (time_t) mysec; - - /* - * Use localtime to convert that time_t to broken-down time, - * and reassemble to get a representation of local time. - */ - tx = pg_localtime(&mytime); + delta2 -= delta1; + if ((delta2 < 0) ? (mysec < 0 && (mysec + delta2) > 0) : + (mysec > 0 && (mysec + delta2) < 0)) + goto overflow; + mysec += delta2; + tx = pg_localtime(&mysec); if (!tx) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("timestamp out of range"))); - day = date2j(tx->tm_year + 1900, tx->tm_mon + 1, tx->tm_mday) - - UNIX_EPOCH_JDATE; - locsec = tx->tm_sec + (tx->tm_min + (day * 24 + tx->tm_hour) * 60) * 60; - - /* - * The local time offset corresponding to that GMT time is now - * computable as mysec - locsec. - */ - delta1 = mysec - locsec; - /* - * However, if that GMT time and the local time we are - * actually interested in are on opposite sides of a - * daylight-savings-time transition, then this is not the time - * offset we want. So, adjust the time_t to be what we think - * the GMT time corresponding to our target local time is, and - * repeat the localtime() call and delta calculation. - */ - mysec += delta1; - mytime = (time_t) mysec; - tx = pg_localtime(&mytime); - if (!tx) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("timestamp out of range"))); + goto overflow; /* probably can't happen */ day = date2j(tx->tm_year + 1900, tx->tm_mon + 1, tx->tm_mday) - UNIX_EPOCH_JDATE; locsec = tx->tm_sec + (tx->tm_min + (day * 24 + tx->tm_hour) * 60) * 60; delta2 = mysec - locsec; - - /* - * We may have to do it again to get the correct delta. - * - * It might seem we should just loop until we get the same delta - * twice in a row, but if we've been given an "impossible" local - * time (in the gap during a spring-forward transition) we'd never - * get out of the loop. The behavior we want is that "impossible" - * times are taken as standard time, and also that ambiguous times - * (during a fall-back transition) are taken as standard time. - * Therefore, we bias the code to prefer the standard-time solution. - */ - if (delta2 != delta1 && tx->tm_isdst != 0) - { - mysec += (delta2 - delta1); - mytime = (time_t) mysec; - tx = pg_localtime(&mytime); - if (!tx) - ereport(ERROR, - (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), - errmsg("timestamp out of range"))); - day = date2j(tx->tm_year + 1900, tx->tm_mon + 1, tx->tm_mday) - - UNIX_EPOCH_JDATE; - locsec = tx->tm_sec + (tx->tm_min + (day * 24 + tx->tm_hour) * 60) * 60; - delta2 = mysec - locsec; - } - tm->tm_isdst = tx->tm_isdst; - tz = (int) delta2; - } - else - { - /* Given date is out of range, so assume UTC */ - tm->tm_isdst = 0; - tz = 0; } + tm->tm_isdst = tx->tm_isdst; + tz = (int) delta2; return tz; + +overflow: + /* Given date is out of range, so assume UTC */ + tm->tm_isdst = 0; + return 0; } diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c index 0443072e8e..8353fa2658 100644 --- a/src/backend/utils/adt/nabstime.c +++ b/src/backend/utils/adt/nabstime.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.122 2004/05/21 16:08:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/nabstime.c,v 1.123 2004/06/03 02:08:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "access/xact.h" @@ -179,7 +180,7 @@ GetCurrentTimeUsec(struct pg_tm * tm, fsec_t *fsec, int *tzp) void abstime2tm(AbsoluteTime _time, int *tzp, struct pg_tm * tm, char **tzn) { - time_t time = (time_t) _time; + pg_time_t time = (pg_time_t) _time; struct pg_tm *tx; /* @@ -1667,12 +1668,12 @@ timeofday(PG_FUNCTION_ARGS) char buf[128]; text *result; int len; - time_t tt; + pg_time_t tt; gettimeofday(&tp, &tpz); - tt = (time_t) tp.tv_sec; + tt = (pg_time_t) tp.tv_sec; pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z", - pg_localtime(&tt)); + pg_localtime(&tt)); snprintf(buf, sizeof(buf), templ, tp.tv_usec); len = VARHDRSZ + strlen(buf); diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index d40715b7e4..91a34ed538 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.107 2004/05/31 18:31:51 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.108 2004/06/03 02:08:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -921,16 +921,14 @@ dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec) } /* dt2time() */ -/* timestamp2tm() - * Convert timestamp data type to POSIX time structure. +/* + * timestamp2tm() - Convert timestamp data type to POSIX time structure. + * * Note that year is _not_ 1900-based, but is an explicit full value. * Also, month is one-based, _not_ zero-based. * Returns: * 0 on success * -1 on out of range - * - * For dates within the system-supported time_t range, convert to the - * local time zone. If out of this range, leave as GMT. - tgl 97/05/27 */ int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char **tzn) @@ -942,8 +940,7 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char **tzn) double date; double time; #endif - time_t utime; - struct pg_tm *tx; + pg_time_t utime; /* * If HasCTZSet is true then we have a brute force time zone @@ -988,70 +985,77 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char **tzn) j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec); - if (tzp != NULL) + /* Done if no TZ conversion wanted */ + if (tzp == NULL) { - /* - * We have a brute force time zone per SQL99? Then use it without - * change since we have already rotated to the time zone. - */ - if (HasCTZSet) - { - *tzp = CTimeZone; - tm->tm_isdst = 0; - tm->tm_gmtoff = CTimeZone; - tm->tm_zone = NULL; - if (tzn != NULL) - *tzn = NULL; - } + tm->tm_isdst = -1; + tm->tm_gmtoff = 0; + tm->tm_zone = NULL; + if (tzn != NULL) + *tzn = NULL; + return 0; + } - /* - * Does this fall within the capabilities of the localtime() - * interface? Then use this to rotate to the local time zone. - */ - else if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday)) - { - /* - * Convert to integer, avoiding platform-specific - * roundoff-in-wrong-direction errors, and adjust to - * Unix epoch. Note we have to do this in one step - * because the intermediate result before adjustment - * won't necessarily fit in an int32. - */ + /* + * We have a brute force time zone per SQL99? Then use it without + * change since we have already rotated to the time zone. + */ + if (HasCTZSet) + { + *tzp = CTimeZone; + tm->tm_isdst = 0; + tm->tm_gmtoff = CTimeZone; + tm->tm_zone = NULL; + if (tzn != NULL) + *tzn = NULL; + return 0; + } + + /* + * If the time falls within the range of pg_time_t, use pg_localtime() + * to rotate to the local time zone. + * + * First, convert to an integral timestamp, avoiding possibly + * platform-specific roundoff-in-wrong-direction errors, and adjust to + * Unix epoch. Then see if we can convert to pg_time_t without loss. + * This coding avoids hardwiring any assumptions about the width of + * pg_time_t, so it should behave sanely on machines without int64. + */ #ifdef HAVE_INT64_TIMESTAMP - utime = (dt - *fsec) / INT64CONST(1000000) + - (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400; + dt = (dt - *fsec) / INT64CONST(1000000) + + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400; #else - utime = rint(dt - *fsec + - (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400); + dt = rint(dt - *fsec + + (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400); #endif + utime = (pg_time_t) dt; + if ((Timestamp) utime == dt) + { + struct pg_tm *tx = pg_localtime(&utime); - tx = pg_localtime(&utime); - tm->tm_year = tx->tm_year + 1900; - tm->tm_mon = tx->tm_mon + 1; - tm->tm_mday = tx->tm_mday; - tm->tm_hour = tx->tm_hour; - tm->tm_min = tx->tm_min; - tm->tm_sec = tx->tm_sec; - tm->tm_isdst = tx->tm_isdst; - tm->tm_gmtoff = tx->tm_gmtoff; - tm->tm_zone = tx->tm_zone; - - *tzp = -(tm->tm_gmtoff); - if (tzn != NULL) - *tzn = (char *) tm->tm_zone; - } - else - { - *tzp = 0; - /* Mark this as *no* time zone available */ - tm->tm_isdst = -1; - if (tzn != NULL) - *tzn = NULL; - } + tm->tm_year = tx->tm_year + 1900; + tm->tm_mon = tx->tm_mon + 1; + tm->tm_mday = tx->tm_mday; + tm->tm_hour = tx->tm_hour; + tm->tm_min = tx->tm_min; + tm->tm_sec = tx->tm_sec; + tm->tm_isdst = tx->tm_isdst; + tm->tm_gmtoff = tx->tm_gmtoff; + tm->tm_zone = tx->tm_zone; + *tzp = -(tm->tm_gmtoff); + if (tzn != NULL) + *tzn = (char *) tm->tm_zone; } else { + /* + * When out of range of pg_time_t, treat as GMT + */ + *tzp = 0; + /* Mark this as *no* time zone available */ tm->tm_isdst = -1; + tm->tm_gmtoff = 0; + tm->tm_zone = NULL; if (tzn != NULL) *tzn = NULL; } @@ -1224,7 +1228,7 @@ void GetEpochTime(struct pg_tm * tm) { struct pg_tm *t0; - time_t epoch = 0; + pg_time_t epoch = 0; t0 = pg_gmtime(&epoch); @@ -1235,12 +1239,9 @@ GetEpochTime(struct pg_tm * tm) tm->tm_min = t0->tm_min; tm->tm_sec = t0->tm_sec; - if (tm->tm_year < 1900) - tm->tm_year += 1900; + tm->tm_year += 1900; tm->tm_mon++; - - return; -} /* GetEpochTime() */ +} Timestamp SetEpochTimestamp(void) diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index e3adab8297..dc881a0067 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.139 2004/05/29 22:48:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.140 2004/06/03 02:08:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -56,7 +57,6 @@ #include "libpq/pqformat.h" #include "mb/pg_wchar.h" #include "miscadmin.h" -#include "pgtime.h" #include "postmaster/postmaster.h" #include "storage/ipc.h" #include "tcop/tcopprot.h" @@ -1217,8 +1217,9 @@ log_line_prefix(StringInfo buf) time_t stamp_time = time(NULL); char strfbuf[128]; - pg_strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", - pg_localtime(&stamp_time)); + strftime(strfbuf, sizeof(strfbuf), + "%Y-%m-%d %H:%M:%S %Z", + localtime(&stamp_time)); appendStringInfoString(buf, strfbuf); } break; @@ -1228,8 +1229,9 @@ log_line_prefix(StringInfo buf) time_t stamp_time = MyProcPort->session_start.tv_sec; char strfbuf[128]; - pg_strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", - pg_localtime(&stamp_time)); + strftime(strfbuf, sizeof(strfbuf), + "%Y-%m-%d %H:%M:%S %Z", + localtime(&stamp_time)); appendStringInfoString(buf, strfbuf); } break; diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index a4aba75985..2319da9b8e 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -8,15 +8,16 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_control.h,v 1.14 2004/05/21 05:08:04 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_control.h,v 1.15 2004/06/03 02:08:05 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef PG_CONTROL_H #define PG_CONTROL_H +#include + #include "access/xlogdefs.h" -#include "pgtime.h" #include "utils/pg_crc.h" diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 1a3f759840..8b1a15ee5a 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.53 2004/05/21 16:22:38 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/vacuum.h,v 1.54 2004/06/03 02:08:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,7 +27,6 @@ #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" #include "nodes/parsenodes.h" -#include "pgtime.h" #include "utils/rel.h" diff --git a/src/include/pgtime.h b/src/include/pgtime.h index 8493c8c6f7..e772d76bd7 100644 --- a/src/include/pgtime.h +++ b/src/include/pgtime.h @@ -6,50 +6,29 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.1 2004/05/21 05:08:03 tgl Exp $ + * $PostgreSQL: pgsql/src/include/pgtime.h,v 1.2 2004/06/03 02:08:06 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef _PGTIME_H #define _PGTIME_H -#ifdef FRONTEND - -/* Don't mess with anything for the frontends */ -#include - -#else - -/* - * Redefine functions and defines we implement, so we cause an - * error if someone tries to use the "base functions" - */ -#ifndef NO_REDEFINE_TIMEFUNCS -#define localtime DONOTUSETHIS_localtime -#define gmtime DONOTUSETHIS_gmtime -#define asctime DONOTUSETHIS_asctime -#define ctime DONOTUSETHIS_ctime -#define tzset DONOTUSETHIS_tzset -#define mktime DONOTUSETHIS_mktime -#define tzname DONOTUSETHIS_tzname -#define daylight DONOTUSETHIS_daylight -#define strftime DONOTUSETHIS_strftime -#endif - -/* Then pull in default declarations, particularly time_t */ -#include /* - * Now define prototype for our own timezone implementation - * structs and functions. + * The API of this library is generally similar to the corresponding + * C library functions, except that we use pg_time_t which (we hope) is + * 64 bits wide, and which is most definitely signed not unsigned. */ + +typedef int64 pg_time_t; + struct pg_tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; - int tm_mon; - int tm_year; + int tm_mon; /* origin 0, not 1 */ + int tm_year; /* relative to 1900 */ int tm_wday; int tm_yday; int tm_isdst; @@ -57,9 +36,8 @@ struct pg_tm { const char *tm_zone; }; -extern struct pg_tm *pg_localtime(const time_t *); -extern struct pg_tm *pg_gmtime(const time_t *); -extern time_t pg_mktime(struct pg_tm *); +extern struct pg_tm *pg_localtime(const pg_time_t *); +extern struct pg_tm *pg_gmtime(const pg_time_t *); extern bool pg_tzset(const char *tzname); extern size_t pg_strftime(char *s, size_t max, const char *format, const struct pg_tm *tm); @@ -68,6 +46,4 @@ extern bool tz_acceptable(void); extern const char *select_default_timezone(void); extern const char *pg_get_current_timezone(void); -#endif /* FRONTEND */ - #endif /* _PGTIME_H */ diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index 35eec95eeb..1dd379fd21 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.48 2004/05/21 05:08:05 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/datetime.h,v 1.49 2004/06/03 02:08:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,6 @@ #include #include -#include "pgtime.h" #include "utils/timestamp.h" @@ -232,10 +231,11 @@ do { \ * Include check for leap year. */ -extern int day_tab[2][13]; +extern const int day_tab[2][13]; #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + /* Julian date support for date2j() and j2date() * * IS_VALID_JULIAN checks the minimum date exactly, but is a bit sloppy @@ -257,24 +257,6 @@ extern int day_tab[2][13]; #define UNIX_EPOCH_JDATE 2440588 /* == date2j(1970, 1, 1) */ #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */ -/* - * Info about limits of the Unix time_t data type. We assume that time_t - * is a signed int32 with origin 1970-01-01. Note this is only relevant - * when we use the C library's time routines for timezone processing. - */ -#define UTIME_MINYEAR (1901) -#define UTIME_MINMONTH (12) -#define UTIME_MINDAY (14) -#define UTIME_MAXYEAR (2038) -#define UTIME_MAXMONTH (01) -#define UTIME_MAXDAY (18) - -#define IS_VALID_UTIME(y,m,d) ((((y) > UTIME_MINYEAR) \ - || (((y) == UTIME_MINYEAR) && (((m) > UTIME_MINMONTH) \ - || (((m) == UTIME_MINMONTH) && ((d) >= UTIME_MINDAY))))) \ - && (((y) < UTIME_MAXYEAR) \ - || (((y) == UTIME_MAXYEAR) && (((m) < UTIME_MAXMONTH) \ - || (((m) == UTIME_MAXMONTH) && ((d) <= UTIME_MAXDAY)))))) /* * Datetime input parsing routines (ParseDateTime, DecodeDateTime, etc) diff --git a/src/include/utils/nabstime.h b/src/include/utils/nabstime.h index 704e7de583..914dd9ebd7 100644 --- a/src/include/utils/nabstime.h +++ b/src/include/utils/nabstime.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/nabstime.h,v 1.42 2004/05/21 05:08:05 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/nabstime.h,v 1.43 2004/06/03 02:08:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,6 @@ #include #include "fmgr.h" -#include "pgtime.h" #include "utils/timestamp.h" #include "utils/datetime.h" @@ -28,9 +27,10 @@ * * ---------------------------------------------------------------- */ + /* * Although time_t generally is a long int on 64 bit systems, these two - * types must be 4 bytes, because that's what the system assumes. They + * types must be 4 bytes, because that's what pg_type.h assumes. They * should be yanked (long) before 2038 and be replaced by timestamp and * interval. */ diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out index b135eb5b91..4523947b56 100644 --- a/src/test/regress/expected/horology.out +++ b/src/test/regress/expected/horology.out @@ -672,15 +672,15 @@ SELECT '' AS "64", d1 + interval '1 year' AS one_year FROM TIMESTAMPTZ_TBL; | Sat Feb 14 17:32:01 1998 PST | Sun Feb 15 17:32:01 1998 PST | Mon Feb 16 17:32:01 1998 PST - | Thu Feb 16 17:32:01 0096 BC - | Sun Feb 16 17:32:01 0098 - | Fri Feb 16 17:32:01 0598 - | Wed Feb 16 17:32:01 1098 - | Sun Feb 16 17:32:01 1698 - | Fri Feb 16 17:32:01 1798 - | Wed Feb 16 17:32:01 1898 + | Thu Feb 16 17:32:01 0096 BC PST + | Sun Feb 16 17:32:01 0098 PST + | Fri Feb 16 17:32:01 0598 PST + | Wed Feb 16 17:32:01 1098 PST + | Sun Feb 16 17:32:01 1698 PST + | Fri Feb 16 17:32:01 1798 PST + | Wed Feb 16 17:32:01 1898 PST | Mon Feb 16 17:32:01 1998 PST - | Sun Feb 16 17:32:01 2098 + | Sun Feb 16 17:32:01 2098 PST | Fri Feb 28 17:32:01 1997 PST | Fri Feb 28 17:32:01 1997 PST | Sat Mar 01 17:32:01 1997 PST @@ -741,15 +741,15 @@ SELECT '' AS "64", d1 - interval '1 year' AS one_year FROM TIMESTAMPTZ_TBL; | Wed Feb 14 17:32:01 1996 PST | Thu Feb 15 17:32:01 1996 PST | Fri Feb 16 17:32:01 1996 PST - | Mon Feb 16 17:32:01 0098 BC - | Thu Feb 16 17:32:01 0096 - | Tue Feb 16 17:32:01 0596 - | Sun Feb 16 17:32:01 1096 - | Thu Feb 16 17:32:01 1696 - | Tue Feb 16 17:32:01 1796 - | Sun Feb 16 17:32:01 1896 + | Mon Feb 16 17:32:01 0098 BC PST + | Thu Feb 16 17:32:01 0096 PST + | Tue Feb 16 17:32:01 0596 PST + | Sun Feb 16 17:32:01 1096 PST + | Thu Feb 16 17:32:01 1696 PST + | Tue Feb 16 17:32:01 1796 PST + | Sun Feb 16 17:32:01 1896 PST | Fri Feb 16 17:32:01 1996 PST - | Thu Feb 16 17:32:01 2096 + | Thu Feb 16 17:32:01 2096 PST | Tue Feb 28 17:32:01 1995 PST | Tue Feb 28 17:32:01 1995 PST | Wed Mar 01 17:32:01 1995 PST diff --git a/src/test/regress/expected/timestamp.out b/src/test/regress/expected/timestamp.out index 7557bf2900..d6f14ff6ef 100644 --- a/src/test/regress/expected/timestamp.out +++ b/src/test/regress/expected/timestamp.out @@ -1327,15 +1327,15 @@ SELECT '' AS to_char_9, to_char(d1, 'YYYY A.D. YYYY a.d. YYYY bc HH:MI:SS P.M. H -- TO_TIMESTAMP() SELECT '' AS to_timestamp_1, to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); - to_timestamp_1 | to_timestamp -----------------+-------------------------- - | Sat Feb 16 08:14:30 0097 + to_timestamp_1 | to_timestamp +----------------+------------------------------ + | Sat Feb 16 08:14:30 0097 PST (1 row) SELECT '' AS to_timestamp_2, to_timestamp('97/2/16 8:14:30', 'FMYYYY/FMMM/FMDD FMHH:FMMI:FMSS'); - to_timestamp_2 | to_timestamp -----------------+-------------------------- - | Sat Feb 16 08:14:30 0097 + to_timestamp_2 | to_timestamp +----------------+------------------------------ + | Sat Feb 16 08:14:30 0097 PST (1 row) SELECT '' AS to_timestamp_3, to_timestamp('1985 January 12', 'YYYY FMMonth DD'); @@ -1352,9 +1352,9 @@ SELECT '' AS to_timestamp_4, to_timestamp('My birthday-> Year: 1976, Month: May, (1 row) SELECT '' AS to_timestamp_5, to_timestamp('1,582nd VIII 21', 'Y,YYYth FMRM DD'); - to_timestamp_5 | to_timestamp -----------------+-------------------------- - | Sat Aug 21 00:00:00 1582 + to_timestamp_5 | to_timestamp +----------------+------------------------------ + | Sat Aug 21 00:00:00 1582 PST (1 row) SELECT '' AS to_timestamp_6, to_timestamp('15 "text between quote marks" 98 54 45', @@ -1386,9 +1386,9 @@ SELECT '' AS to_timestamp_10, to_timestamp('19971116', 'YYYYMMDD'); (1 row) SELECT '' AS to_timestamp_11, to_timestamp('20000-1116', 'YYYY-MMDD'); - to_timestamp_11 | to_timestamp ------------------+--------------------------- - | Thu Nov 16 00:00:00 20000 + to_timestamp_11 | to_timestamp +-----------------+------------------------------- + | Thu Nov 16 00:00:00 20000 PST (1 row) SELECT '' AS to_timestamp_12, to_timestamp('9-1116', 'Y-MMDD'); diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out index 8a9bccf0a3..c07604ff72 100644 --- a/src/test/regress/expected/timestamptz.out +++ b/src/test/regress/expected/timestamptz.out @@ -180,15 +180,15 @@ SELECT '' AS "64", d1 FROM TIMESTAMPTZ_TBL; | Fri Feb 14 17:32:01 1997 PST | Sat Feb 15 17:32:01 1997 PST | Sun Feb 16 17:32:01 1997 PST - | Tue Feb 16 17:32:01 0097 BC - | Sat Feb 16 17:32:01 0097 - | Thu Feb 16 17:32:01 0597 - | Tue Feb 16 17:32:01 1097 - | Sat Feb 16 17:32:01 1697 - | Thu Feb 16 17:32:01 1797 - | Tue Feb 16 17:32:01 1897 + | Tue Feb 16 17:32:01 0097 BC PST + | Sat Feb 16 17:32:01 0097 PST + | Thu Feb 16 17:32:01 0597 PST + | Tue Feb 16 17:32:01 1097 PST + | Sat Feb 16 17:32:01 1697 PST + | Thu Feb 16 17:32:01 1797 PST + | Tue Feb 16 17:32:01 1897 PST | Sun Feb 16 17:32:01 1997 PST - | Sat Feb 16 17:32:01 2097 + | Sat Feb 16 17:32:01 2097 PST | Wed Feb 28 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST @@ -249,7 +249,7 @@ SELECT '' AS "48", d1 FROM TIMESTAMPTZ_TBL | Sat Feb 15 17:32:01 1997 PST | Sun Feb 16 17:32:01 1997 PST | Sun Feb 16 17:32:01 1997 PST - | Sat Feb 16 17:32:01 2097 + | Sat Feb 16 17:32:01 2097 PST | Fri Feb 28 17:32:01 1997 PST | Sat Mar 01 17:32:01 1997 PST | Tue Dec 30 17:32:01 1997 PST @@ -262,17 +262,17 @@ SELECT '' AS "48", d1 FROM TIMESTAMPTZ_TBL SELECT '' AS "15", d1 FROM TIMESTAMPTZ_TBL WHERE d1 < timestamp with time zone '1997-01-02'; - 15 | d1 -----+------------------------------ + 15 | d1 +----+--------------------------------- | -infinity | Wed Dec 31 16:00:00 1969 PST - | Tue Feb 16 17:32:01 0097 BC - | Sat Feb 16 17:32:01 0097 - | Thu Feb 16 17:32:01 0597 - | Tue Feb 16 17:32:01 1097 - | Sat Feb 16 17:32:01 1697 - | Thu Feb 16 17:32:01 1797 - | Tue Feb 16 17:32:01 1897 + | Tue Feb 16 17:32:01 0097 BC PST + | Sat Feb 16 17:32:01 0097 PST + | Thu Feb 16 17:32:01 0597 PST + | Tue Feb 16 17:32:01 1097 PST + | Sat Feb 16 17:32:01 1697 PST + | Thu Feb 16 17:32:01 1797 PST + | Tue Feb 16 17:32:01 1897 PST | Wed Feb 28 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST @@ -332,15 +332,15 @@ SELECT '' AS "63", d1 FROM TIMESTAMPTZ_TBL | Fri Feb 14 17:32:01 1997 PST | Sat Feb 15 17:32:01 1997 PST | Sun Feb 16 17:32:01 1997 PST - | Tue Feb 16 17:32:01 0097 BC - | Sat Feb 16 17:32:01 0097 - | Thu Feb 16 17:32:01 0597 - | Tue Feb 16 17:32:01 1097 - | Sat Feb 16 17:32:01 1697 - | Thu Feb 16 17:32:01 1797 - | Tue Feb 16 17:32:01 1897 + | Tue Feb 16 17:32:01 0097 BC PST + | Sat Feb 16 17:32:01 0097 PST + | Thu Feb 16 17:32:01 0597 PST + | Tue Feb 16 17:32:01 1097 PST + | Sat Feb 16 17:32:01 1697 PST + | Thu Feb 16 17:32:01 1797 PST + | Tue Feb 16 17:32:01 1897 PST | Sun Feb 16 17:32:01 1997 PST - | Sat Feb 16 17:32:01 2097 + | Sat Feb 16 17:32:01 2097 PST | Wed Feb 28 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST @@ -359,18 +359,18 @@ SELECT '' AS "63", d1 FROM TIMESTAMPTZ_TBL SELECT '' AS "16", d1 FROM TIMESTAMPTZ_TBL WHERE d1 <= timestamp with time zone '1997-01-02'; - 16 | d1 -----+------------------------------ + 16 | d1 +----+--------------------------------- | -infinity | Wed Dec 31 16:00:00 1969 PST | Thu Jan 02 00:00:00 1997 PST - | Tue Feb 16 17:32:01 0097 BC - | Sat Feb 16 17:32:01 0097 - | Thu Feb 16 17:32:01 0597 - | Tue Feb 16 17:32:01 1097 - | Sat Feb 16 17:32:01 1697 - | Thu Feb 16 17:32:01 1797 - | Tue Feb 16 17:32:01 1897 + | Tue Feb 16 17:32:01 0097 BC PST + | Sat Feb 16 17:32:01 0097 PST + | Thu Feb 16 17:32:01 0597 PST + | Tue Feb 16 17:32:01 1097 PST + | Sat Feb 16 17:32:01 1697 PST + | Thu Feb 16 17:32:01 1797 PST + | Tue Feb 16 17:32:01 1897 PST | Wed Feb 28 17:32:01 1996 PST | Thu Feb 29 17:32:01 1996 PST | Fri Mar 01 17:32:01 1996 PST @@ -423,7 +423,7 @@ SELECT '' AS "49", d1 FROM TIMESTAMPTZ_TBL | Sat Feb 15 17:32:01 1997 PST | Sun Feb 16 17:32:01 1997 PST | Sun Feb 16 17:32:01 1997 PST - | Sat Feb 16 17:32:01 2097 + | Sat Feb 16 17:32:01 2097 PST | Fri Feb 28 17:32:01 1997 PST | Sat Mar 01 17:32:01 1997 PST | Tue Dec 30 17:32:01 1997 PST @@ -1395,16 +1395,16 @@ SELECT '' AS to_char_10, to_char(d1, 'YYYY WW IYYY IYY IY I IW') -- TO_TIMESTAMP() SELECT '' AS to_timestamp_1, to_timestamp('0097/Feb/16 --> 08:14:30', 'YYYY/Mon/DD --> HH:MI:SS'); - to_timestamp_1 | to_timestamp -----------------+-------------------------- - | Sat Feb 16 08:14:30 0097 + to_timestamp_1 | to_timestamp +----------------+------------------------------ + | Sat Feb 16 08:14:30 0097 PST (1 row) SELECT '' AS to_timestamp_2, to_timestamp('97/2/16 8:14:30', 'FMYYYY/FMMM/FMDD FMHH:FMMI:FMSS'); - to_timestamp_2 | to_timestamp -----------------+-------------------------- - | Sat Feb 16 08:14:30 0097 + to_timestamp_2 | to_timestamp +----------------+------------------------------ + | Sat Feb 16 08:14:30 0097 PST (1 row) SELECT '' AS to_timestamp_3, to_timestamp('1985 January 12', 'YYYY FMMonth DD'); @@ -1421,9 +1421,9 @@ SELECT '' AS to_timestamp_4, to_timestamp('My birthday-> Year: 1976, Month: May, (1 row) SELECT '' AS to_timestamp_5, to_timestamp('1,582nd VIII 21', 'Y,YYYth FMRM DD'); - to_timestamp_5 | to_timestamp -----------------+-------------------------- - | Sat Aug 21 00:00:00 1582 + to_timestamp_5 | to_timestamp +----------------+------------------------------ + | Sat Aug 21 00:00:00 1582 PST (1 row) SELECT '' AS to_timestamp_6, to_timestamp('15 "text between quote marks" 98 54 45', @@ -1455,9 +1455,9 @@ SELECT '' AS to_timestamp_10, to_timestamp('19971116', 'YYYYMMDD'); (1 row) SELECT '' AS to_timestamp_11, to_timestamp('20000-1116', 'YYYY-MMDD'); - to_timestamp_11 | to_timestamp ------------------+--------------------------- - | Thu Nov 16 00:00:00 20000 + to_timestamp_11 | to_timestamp +-----------------+------------------------------- + | Thu Nov 16 00:00:00 20000 PST (1 row) SELECT '' AS to_timestamp_12, to_timestamp('9-1116', 'Y-MMDD'); diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c index 5ce8664f27..61c58d05e7 100644 --- a/src/timezone/localtime.c +++ b/src/timezone/localtime.c @@ -3,7 +3,7 @@ * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.6 2004/05/21 20:59:10 tgl Exp $ + * $PostgreSQL: pgsql/src/timezone/localtime.c,v 1.7 2004/06/03 02:08:07 tgl Exp $ */ /* @@ -69,7 +69,7 @@ struct ttinfo struct lsinfo { /* leap second information */ - time_t ls_trans; /* transition time */ + pg_time_t ls_trans; /* transition time */ long ls_corr; /* correction to apply */ }; @@ -81,7 +81,7 @@ struct state int timecnt; int typecnt; int charcnt; - time_t ats[TZ_MAX_TIMES]; + pg_time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt), @@ -114,23 +114,11 @@ static const char *getsecs(const char *strp, long *secsp); static const char *getoffset(const char *strp, long *offsetp); static const char *getrule(const char *strp, struct rule * rulep); static void gmtload(struct state * sp); -static void gmtsub(const time_t *timep, long offset, struct pg_tm * tmp); -static void localsub(const time_t *timep, long offset, struct pg_tm * tmp); -static int increment_overflow(int *number, int delta); -static int normalize_overflow(int *tensptr, int *unitsptr, int base); -static time_t time1(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - long offset); -static time_t time2(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - long offset, int *okayp); -static time_t time2sub(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - long offset, int *okayp, int do_norm_secs); -static void timesub(const time_t *timep, long offset, +static void gmtsub(const pg_time_t *timep, long offset, struct pg_tm * tmp); +static void localsub(const pg_time_t *timep, long offset, struct pg_tm * tmp); +static void timesub(const pg_time_t *timep, long offset, const struct state * sp, struct pg_tm * tmp); -static int tmcomp(const struct pg_tm * atmp, const struct pg_tm * btmp); -static time_t transtime(time_t janfirst, int year, +static pg_time_t transtime(pg_time_t janfirst, int year, const struct rule * rulep, long offset); static int tzload(const char *name, struct state * sp); static int tzparse(const char *name, struct state * sp, int lastditch); @@ -503,12 +491,12 @@ getrule(const char *strp, register struct rule * rulep) * year, a rule, and the offset from UTC at the time that rule takes effect, * calculate the Epoch-relative time that rule takes effect. */ -static time_t -transtime(const time_t janfirst, const int year, +static pg_time_t +transtime(const pg_time_t janfirst, const int year, register const struct rule * rulep, const long offset) { register int leapyear; - register time_t value = 0; + register pg_time_t value = 0; register int i; int d, m1, @@ -612,7 +600,7 @@ tzparse(const char *name, register struct state * sp, const int lastditch) size_t dstlen; long stdoffset; long dstoffset; - register time_t *atp; + register pg_time_t *atp; register unsigned char *typep; register char *cp; register int load_result; @@ -663,9 +651,9 @@ tzparse(const char *name, register struct state * sp, const int lastditch) struct rule start; struct rule end; register int year; - register time_t janfirst; - time_t starttime; - time_t endtime; + register pg_time_t janfirst; + pg_time_t starttime; + pg_time_t endtime; ++name; if ((name = getrule(name, &start)) == NULL) @@ -886,12 +874,12 @@ pg_tzset(const char *name) * The unused offset argument is for the benefit of mktime variants. */ static void -localsub(const time_t *timep, const long offset, struct pg_tm * tmp) +localsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp) { register struct state *sp; register const struct ttinfo *ttisp; register int i; - const time_t t = *timep; + const pg_time_t t = *timep; sp = lclptr; if (sp->timecnt == 0 || t < sp->ats[0]) @@ -920,7 +908,7 @@ localsub(const time_t *timep, const long offset, struct pg_tm * tmp) struct pg_tm * -pg_localtime(const time_t *timep) +pg_localtime(const pg_time_t *timep) { localsub(timep, 0L, &tm); return &tm; @@ -931,7 +919,7 @@ pg_localtime(const time_t *timep) * gmtsub is to gmtime as localsub is to localtime. */ static void -gmtsub(const time_t *timep, const long offset, struct pg_tm * tmp) +gmtsub(const pg_time_t *timep, const long offset, struct pg_tm * tmp) { if (!gmt_is_set) { @@ -952,7 +940,7 @@ gmtsub(const time_t *timep, const long offset, struct pg_tm * tmp) } struct pg_tm * -pg_gmtime(const time_t *timep) +pg_gmtime(const pg_time_t *timep) { gmtsub(timep, 0L, &tm); return &tm; @@ -960,11 +948,13 @@ pg_gmtime(const time_t *timep) static void -timesub(const time_t *timep, const long offset, +timesub(const pg_time_t *timep, const long offset, register const struct state * sp, register struct pg_tm * tmp) { register const struct lsinfo *lp; - register long days; + /* expand days to 64 bits to support full Julian-day range */ + register int64 days; + register int idays; register long rem; register int y; register int yleap; @@ -1036,335 +1026,38 @@ timesub(const time_t *timep, const long offset, if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; y = EPOCH_YEAR; -#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) - while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) + /* + * Note: the point of adding 4800 is to ensure we make the same assumptions + * as Postgres' Julian-date routines about the placement of leap years + * in centuries BC, at least back to 4713BC which is as far as we'll go. + * This is effectively extending Gregorian timekeeping into pre-Gregorian + * centuries, which is a tad bogus but it conforms to the SQL spec... + */ +#define LEAPS_THRU_END_OF(y) (((y) + 4800) / 4 - ((y) + 4800) / 100 + ((y) + 4800) / 400) + while (days < 0 || days >= (int64) year_lengths[yleap = isleap(y)]) { register int newy; newy = y + days / DAYSPERNYEAR; if (days < 0) --newy; - days -= (newy - y) * DAYSPERNYEAR + + days -= ((int64) (newy - y)) * DAYSPERNYEAR + LEAPS_THRU_END_OF(newy - 1) - LEAPS_THRU_END_OF(y - 1); y = newy; } tmp->tm_year = y - TM_YEAR_BASE; - tmp->tm_yday = (int) days; + idays = (int) days; /* no longer have a range problem */ + tmp->tm_yday = idays; ip = mon_lengths[yleap]; - for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon)) - days = days - (long) ip[tmp->tm_mon]; - tmp->tm_mday = (int) (days + 1); + for (i = 0; idays >= ip[i]; ++i) + idays -= ip[i]; + tmp->tm_mon = i; + tmp->tm_mday = idays + 1; tmp->tm_isdst = 0; tmp->tm_gmtoff = offset; } -/* - * Adapted from code provided by Robert Elz, who writes: - * The "best" way to do mktime I think is based on an idea of Bob - * Kridle's (so its said...) from a long time ago. - * [kridle@xinet.com as of 1996-01-16.] - * It does a binary search of the time_t space. Since time_t's are - * just 32 bits, its a max of 32 iterations (even at 64 bits it - * would still be very reasonable). - */ - -#define WRONG (-1) - -/* - * Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com). - */ - -static int -increment_overflow(int *number, int delta) -{ - int number0; - - number0 = *number; - *number += delta; - return (*number < number0) != (delta < 0); -} - -static int -normalize_overflow(int *tensptr, int *unitsptr, const int base) -{ - register int tensdelta; - - tensdelta = (*unitsptr >= 0) ? - (*unitsptr / base) : - (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow(tensptr, tensdelta); -} - -static int -tmcomp(register const struct pg_tm * atmp, register const struct pg_tm * btmp) -{ - register int result; - - if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && - (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && - (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && - (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && - (result = (atmp->tm_min - btmp->tm_min)) == 0) - result = atmp->tm_sec - btmp->tm_sec; - return result; -} - -static time_t -time2sub(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - const long offset, int *okayp, const int do_norm_secs) -{ - register const struct state *sp; - register int dir; - register int bits; - register int i, - j; - register int saved_seconds; - time_t newt; - time_t t; - struct pg_tm yourtm, - mytm; - - *okayp = FALSE; - yourtm = *tmp; - if (do_norm_secs) - { - if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, - SECSPERMIN)) - return WRONG; - } - if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) - return WRONG; - if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) - return WRONG; - if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR)) - return WRONG; - - /* - * Turn yourtm.tm_year into an actual year number for now. It is - * converted back to an offset from TM_YEAR_BASE later. - */ - if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE)) - return WRONG; - while (yourtm.tm_mday <= 0) - { - if (increment_overflow(&yourtm.tm_year, -1)) - return WRONG; - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(i)]; - } - while (yourtm.tm_mday > DAYSPERLYEAR) - { - i = yourtm.tm_year + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap(i)]; - if (increment_overflow(&yourtm.tm_year, 1)) - return WRONG; - } - for (;;) - { - i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon]; - if (yourtm.tm_mday <= i) - break; - yourtm.tm_mday -= i; - if (++yourtm.tm_mon >= MONSPERYEAR) - { - yourtm.tm_mon = 0; - if (increment_overflow(&yourtm.tm_year, 1)) - return WRONG; - } - } - if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE)) - return WRONG; - if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) - saved_seconds = 0; - else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) - { - /* - * We can't set tm_sec to 0, because that might push the time - * below the minimum representable time. Set tm_sec to 59 - * instead. This assumes that the minimum representable time is - * not in the same minute that a leap second was deleted from, - * which is a safer assumption than using 58 would be. - */ - if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) - return WRONG; - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = SECSPERMIN - 1; - } - else - { - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = 0; - } - - /* - * Divide the search space in half (this works whether time_t is - * signed or unsigned). - */ - bits = TYPE_BIT(time_t) -1; - - /* - * If time_t is signed, then 0 is just above the median, assuming - * two's complement arithmetic. If time_t is unsigned, then (1 << - * bits) is just above the median. - */ - t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits); - for (;;) - { - (*funcp) (&t, offset, &mytm); - dir = tmcomp(&mytm, &yourtm); - if (dir != 0) - { - if (bits-- < 0) - return WRONG; - if (bits < 0) - --t; /* may be needed if new t is minimal */ - else if (dir > 0) - t -= ((time_t) 1) << bits; - else - t += ((time_t) 1) << bits; - continue; - } - if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) - break; - - /* - * Right time, wrong type. Hunt for right time, right type. - * It's okay to guess wrong since the guess gets checked. - */ - - /* - * The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) - (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); - for (i = sp->typecnt - 1; i >= 0; --i) - { - if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) - continue; - for (j = sp->typecnt - 1; j >= 0; --j) - { - if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) - continue; - newt = t + sp->ttis[j].tt_gmtoff - - sp->ttis[i].tt_gmtoff; - (*funcp) (&newt, offset, &mytm); - if (tmcomp(&mytm, &yourtm) != 0) - continue; - if (mytm.tm_isdst != yourtm.tm_isdst) - continue; - - /* - * We have a match. - */ - t = newt; - goto label; - } - } - return WRONG; - } -label: - newt = t + saved_seconds; - if ((newt < t) != (saved_seconds < 0)) - return WRONG; - t = newt; - (*funcp) (&t, offset, tmp); - *okayp = TRUE; - return t; -} - -static time_t -time2(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - const long offset, int *okayp) -{ - time_t t; - - /* - * First try without normalization of seconds (in case tm_sec - * contains a value associated with a leap second). If that fails, - * try with normalization of seconds. - */ - t = time2sub(tmp, funcp, offset, okayp, FALSE); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE); -} - -static time_t -time1(struct pg_tm * tmp, - void (*funcp) (const time_t *, long, struct pg_tm *), - const long offset) -{ - register time_t t; - register const struct state *sp; - register int samei, - otheri; - register int sameind, - otherind; - register int i; - register int nseen; - int seen[TZ_MAX_TYPES]; - int types[TZ_MAX_TYPES]; - int okay; - - if (tmp->tm_isdst > 1) - tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay); - if (okay || tmp->tm_isdst < 0) - return t; - - /* - * We're supposed to assume that somebody took a time of one type - * and did some math on it that yielded a "struct pg_tm" that's bad. - * We try to divine the type they started from and adjust to the - * type they need. - */ - - /* - * The (void *) casts are the benefit of SunOS 3.3 on Sun 2's. - */ - sp = (const struct state *) (((void *) funcp == (void *) localsub) ? - lclptr : gmtptr); - for (i = 0; i < sp->typecnt; ++i) - seen[i] = FALSE; - nseen = 0; - for (i = sp->timecnt - 1; i >= 0; --i) - if (!seen[sp->types[i]]) - { - seen[sp->types[i]] = TRUE; - types[nseen++] = sp->types[i]; - } - for (sameind = 0; sameind < nseen; ++sameind) - { - samei = types[sameind]; - if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) - continue; - for (otherind = 0; otherind < nseen; ++otherind) - { - otheri = types[otherind]; - if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) - continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay); - if (okay) - return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - } - } - return WRONG; -} - -time_t -pg_mktime(struct pg_tm * tmp) -{ - return time1(tmp, localsub, 0L); -} /* * Return the name of the current timezone diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index 9222cc143e..3da41363d5 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.16 2004/05/25 19:46:21 tgl Exp $ + * $PostgreSQL: pgsql/src/timezone/pgtz.c,v 1.17 2004/06/03 02:08:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,12 +16,14 @@ #include #include +#include #include "miscadmin.h" #include "pgtime.h" #include "pgtz.h" #include "storage/fd.h" #include "tzfile.h" +#include "utils/datetime.h" #include "utils/elog.h" #include "utils/guc.h" @@ -107,7 +109,7 @@ win32_get_timezone_abbrev(const char *tz) #endif /* - * Convenience subroutine to convert y/m/d to time_t + * Convenience subroutine to convert y/m/d to time_t (NOT pg_time_t) */ static time_t build_time_t(int year, int month, int day) @@ -148,6 +150,7 @@ static bool try_timezone(const char *tzname, struct tztry *tt) { int i; + pg_time_t pgtt; struct tm *systm; struct pg_tm *pgtm; char cbuf[TZ_STRLEN_MAX + 1]; @@ -158,14 +161,15 @@ try_timezone(const char *tzname, struct tztry *tt) /* Check for match at all the test times */ for (i = 0; i < tt->n_test_times; i++) { - pgtm = pg_localtime(&(tt->test_times[i])); + pgtt = (pg_time_t) (tt->test_times[i]); + pgtm = pg_localtime(&pgtt); if (!pgtm) return false; /* probably shouldn't happen */ systm = localtime(&(tt->test_times[i])); if (!compare_tm(systm, pgtm)) { - elog(DEBUG4, "Reject TZ \"%s\": at %lu %04d-%02d-%02d %02d:%02d:%02d %s versus %04d-%02d-%02d %02d:%02d:%02d %s", - tzname, (unsigned long) tt->test_times[i], + elog(DEBUG4, "Reject TZ \"%s\": at %ld %04d-%02d-%02d %02d:%02d:%02d %s versus %04d-%02d-%02d %02d:%02d:%02d %s", + tzname, (long) pgtt, pgtm->tm_year + 1900, pgtm->tm_mon + 1, pgtm->tm_mday, pgtm->tm_hour, pgtm->tm_min, pgtm->tm_sec, pgtm->tm_isdst ? "dst" : "std", @@ -183,8 +187,8 @@ try_timezone(const char *tzname, struct tztry *tt) strftime(cbuf, sizeof(cbuf) - 1, "%Z", systm); /* zone abbr */ if (strcmp(TZABBREV(cbuf), pgtm->tm_zone) != 0) { - elog(DEBUG4, "Reject TZ \"%s\": at %lu \"%s\" versus \"%s\"", - tzname, (unsigned long) tt->test_times[i], + elog(DEBUG4, "Reject TZ \"%s\": at %ld \"%s\" versus \"%s\"", + tzname, (long) pgtt, pgtm->tm_zone, cbuf); return false; } @@ -470,21 +474,17 @@ scan_available_timezones(char *tzdir, char *tzdirsub, struct tztry *tt) bool tz_acceptable(void) { - struct pg_tm tt; - time_t time2000; + struct pg_tm *tt; + pg_time_t time2000; /* - * To detect leap-second timekeeping, compute the time_t value for - * local midnight, 2000-01-01. Insist that this be a multiple of 60; - * any partial-minute offset has to be due to leap seconds. + * To detect leap-second timekeeping, run pg_localtime for what should + * be GMT midnight, 2000-01-01. Insist that the tm_sec value be zero; + * any other result has to be due to leap seconds. */ - MemSet(&tt, 0, sizeof(tt)); - tt.tm_year = 100; - tt.tm_mon = 0; - tt.tm_mday = 1; - tt.tm_isdst = -1; - time2000 = pg_mktime(&tt); - if ((time2000 % 60) != 0) + time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * 86400; + tt = pg_localtime(&time2000); + if (tt->tm_sec != 0) return false; return true; diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c index 3b505364ef..791b076e8e 100644 --- a/src/timezone/strftime.c +++ b/src/timezone/strftime.c @@ -15,7 +15,7 @@ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.3 2004/05/21 20:59:10 tgl Exp $ + * $PostgreSQL: pgsql/src/timezone/strftime.c,v 1.4 2004/06/03 02:08:07 tgl Exp $ */ #include "postgres.h" @@ -265,21 +265,6 @@ _fmt(const char *format, const struct pg_tm * t, char *pt, const char *ptlim, case 'S': pt = _conv(t->tm_sec, "%02d", pt, ptlim); continue; - case 's': - { - struct pg_tm tm; - char buf[INT_STRLEN_MAXIMUM(time_t) +1]; - time_t mkt; - - tm = *t; - mkt = pg_mktime(&tm); - if (TYPE_SIGNED(time_t)) - (void) sprintf(buf, "%ld", (long) mkt); - else - (void) sprintf(buf, "%lu", (unsigned long) mkt); - pt = _add(buf, pt, ptlim); - } - continue; case 'T': pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); continue; diff --git a/src/timezone/zic.c b/src/timezone/zic.c index a427e75686..90fe89ea08 100644 --- a/src/timezone/zic.c +++ b/src/timezone/zic.c @@ -3,12 +3,13 @@ * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). * * IDENTIFICATION - * $PostgreSQL: pgsql/src/timezone/zic.c,v 1.7 2004/05/21 20:59:10 tgl Exp $ + * $PostgreSQL: pgsql/src/timezone/zic.c,v 1.8 2004/06/03 02:08:07 tgl Exp $ */ #include "postgres.h" #include +#include #include "pgtz.h" #include "private.h" @@ -71,7 +72,7 @@ struct rule const char *r_abbrvar; /* variable part of abbreviation */ int r_todo; /* a rule to do (used in outzone) */ - time_t r_temp; /* used in outzone */ + pg_time_t r_temp; /* used in outzone */ }; /* @@ -98,14 +99,14 @@ struct zone int z_nrules; struct rule z_untilrule; - time_t z_untiltime; + pg_time_t z_untiltime; }; extern int link(const char *fromname, const char *toname); -static void addtt(time_t starttime, int type); +static void addtt(pg_time_t starttime, int type); static int addtype(long gmtoff, const char *abbr, int isdst, int ttisstd, int ttisgmt); -static void leapadd(time_t t, int positive, int rolling, int count); +static void leapadd(pg_time_t t, int positive, int rolling, int count); static void adjleap(void); static void associate(void); static int ciequal(const char *ap, const char *bp); @@ -138,13 +139,13 @@ static long oadd(long t1, long t2); static void outzone(const struct zone * zp, int ntzones); static void puttzcode(long code, FILE *fp); static int rcomp(const void *leftp, const void *rightp); -static time_t rpytime(const struct rule * rp, int wantedy); +static pg_time_t rpytime(const struct rule * rp, int wantedy); static void rulesub(struct rule * rp, const char *loyearp, const char *hiyearp, const char *typep, const char *monthp, const char *dayp, const char *timep); static void setboundaries(void); -static time_t tadd(time_t t1, long t2); +static pg_time_t tadd(pg_time_t t1, long t2); static void usage(void); static void writezone(const char *name); static int yearistype(int year, const char *type); @@ -158,10 +159,10 @@ static int errors; static const char *filename; static int leapcnt; static int linenum; -static time_t max_time; +static pg_time_t max_time; static int max_year; static int max_year_representable; -static time_t min_time; +static pg_time_t min_time; static int min_year; static int min_year_representable; static int noise; @@ -354,7 +355,7 @@ static const int len_years[2] = { static struct attype { - time_t at; + pg_time_t at; unsigned char type; } attypes[TZ_MAX_TIMES]; static long gmtoffs[TZ_MAX_TYPES]; @@ -363,7 +364,7 @@ static unsigned char abbrinds[TZ_MAX_TYPES]; static char ttisstds[TZ_MAX_TYPES]; static char ttisgmts[TZ_MAX_TYPES]; static char chars[TZ_MAX_CHARS]; -static time_t trans[TZ_MAX_LEAPS]; +static pg_time_t trans[TZ_MAX_LEAPS]; static long corr[TZ_MAX_LEAPS]; static char roll[TZ_MAX_LEAPS]; @@ -684,27 +685,26 @@ dolink(const char *fromfile, const char *tofile) * change to the tz file format. */ -#define MAX_BITS_IN_FILE 32 -#define TIME_T_BITS_IN_FILE ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE) +#define TIME_T_BITS_IN_FILE 32 static void setboundaries(void) { - if (TYPE_SIGNED(time_t)) - { - min_time = ~(time_t) 0; - min_time <<= TIME_T_BITS_IN_FILE - 1; - max_time = ~(time_t) 0 - min_time; - if (sflag) - min_time = 0; - } - else - { - min_time = 0; - max_time = 2 - sflag; - max_time <<= TIME_T_BITS_IN_FILE - 1; - --max_time; - } + /* + * pg_time_t is always signed, but might be only 32 bits ... + */ + min_time = ~(pg_time_t) 0; + min_time <<= TYPE_BIT(pg_time_t) - 1; + max_time = ~(pg_time_t) 0 - min_time; + + /* + * For the moment, hard-wire the range as 1901 to 2038. We cannot + * go wider without adopting an incompatible zone file format, which + * is a step I'd just as soon not take just yet. + */ + min_time = Max(min_time, (pg_time_t) INT_MIN); + max_time = Min(max_time, (pg_time_t) INT_MAX); + min_year = TM_YEAR_BASE + pg_gmtime(&min_time)->tm_year; max_year = TM_YEAR_BASE + pg_gmtime(&max_time)->tm_year; min_year_representable = min_year; @@ -1170,7 +1170,7 @@ inleap(register char **fields, const int nfields) day; long dayoff, tod; - time_t t; + pg_time_t t; if (nfields != LEAP_FIELDS) { @@ -1223,11 +1223,6 @@ inleap(register char **fields, const int nfields) return; } dayoff = oadd(dayoff, eitol(day - 1)); - if (dayoff < 0 && !TYPE_SIGNED(time_t)) - { - error(_("time before zero")); - return; - } if (dayoff < min_time / SECSPERDAY) { error(_("time too small")); @@ -1238,7 +1233,7 @@ inleap(register char **fields, const int nfields) error(_("time too large")); return; } - t = (time_t) dayoff *SECSPERDAY; + t = (pg_time_t) dayoff *SECSPERDAY; tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE); cp = fields[LP_CORR]; @@ -1525,7 +1520,7 @@ writezone(const char *name) j; static char *fullname; static struct tzhead tzh; - time_t ats[TZ_MAX_TIMES]; + pg_time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; /* @@ -1711,8 +1706,8 @@ outzone(const struct zone * zpfirst, const int zonecount) j; register int usestart, useuntil; - register time_t starttime = 0; - register time_t untiltime = 0; + register pg_time_t starttime = 0; + register pg_time_t untiltime = 0; register long gmtoff; register long stdoff; register int year; @@ -1790,7 +1785,7 @@ outzone(const struct zone * zpfirst, const int zonecount) for (;;) { register int k; - register time_t jtime, + register pg_time_t jtime, ktime = 0; register long offset; char buf[BUFSIZ]; @@ -1911,7 +1906,7 @@ outzone(const struct zone * zpfirst, const int zonecount) } static void -addtt(const time_t starttime, int type) +addtt(const pg_time_t starttime, int type) { if (starttime <= min_time || (timecnt == 1 && attypes[0].at < min_time)) @@ -1999,7 +1994,7 @@ addtype(const long gmtoff, const char *abbr, const int isdst, } static void -leapadd(const time_t t, const int positive, const int rolling, int count) +leapadd(const pg_time_t t, const int positive, const int rolling, int count) { register int i, j; @@ -2191,10 +2186,10 @@ oadd(const long t1, const long t2) return t; } -static time_t -tadd(const time_t t1, const long t2) +static pg_time_t +tadd(const pg_time_t t1, const long t2) { - register time_t t; + register pg_time_t t; if (t1 == max_time && t2 > 0) return max_time; @@ -2214,14 +2209,14 @@ tadd(const time_t t1, const long t2) * 1970, 00:00 LOCAL time - in that year that the rule refers to. */ -static time_t +static pg_time_t rpytime(register const struct rule * rp, register const int wantedy) { register int y, m, i; register long dayoff; /* with a nod to Margaret O. */ - register time_t t; + register pg_time_t t; if (wantedy == INT_MIN) return min_time; @@ -2302,13 +2297,11 @@ rpytime(register const struct rule * rp, register const int wantedy) (void) exit(EXIT_FAILURE); } } - if (dayoff < 0 && !TYPE_SIGNED(time_t)) - return min_time; if (dayoff < min_time / SECSPERDAY) return min_time; if (dayoff > max_time / SECSPERDAY) return max_time; - t = (time_t) dayoff *SECSPERDAY; + t = (pg_time_t) dayoff *SECSPERDAY; return tadd(t, rp->r_tod); }