aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/timestamp.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2004-06-03 02:08:07 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2004-06-03 02:08:07 +0000
commit921d749bd4c34c3349f1c254d5faa2f1cec03911 (patch)
treec349959cb92495a8231020062fa46ac1c2b57afd /src/backend/utils/adt/timestamp.c
parent473ac70acae41c5f1fecbb0b57e9f5be5b26ab68 (diff)
downloadpostgresql-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.c139
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)