diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2004-06-03 02:08:07 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2004-06-03 02:08:07 +0000 |
commit | 921d749bd4c34c3349f1c254d5faa2f1cec03911 (patch) | |
tree | c349959cb92495a8231020062fa46ac1c2b57afd /src/backend/utils/adt/timestamp.c | |
parent | 473ac70acae41c5f1fecbb0b57e9f5be5b26ab68 (diff) | |
download | postgresql-921d749bd4c34c3349f1c254d5faa2f1cec03911.tar.gz postgresql-921d749bd4c34c3349f1c254d5faa2f1cec03911.zip |
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.
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 139 |
1 files changed, 70 insertions, 69 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index d40715b7e44..91a34ed5388 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 - - 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; - } + utime = (pg_time_t) dt; + if ((Timestamp) utime == dt) + { + struct pg_tm *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 { + /* + * 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) |