summaryrefslogtreecommitdiff
path: root/src/interfaces
diff options
context:
space:
mode:
authorTom Lane2005-10-09 17:21:47 +0000
committerTom Lane2005-10-09 17:21:47 +0000
commit313ed1ed9498f977262e180a080c7748197ced5c (patch)
tree17780e929d9a0d710d84de261b27ac26e9a5adcf /src/interfaces
parent7754f7634cbc837702428bbb40a0efbeec7a51d1 (diff)
Fix (hopefully for the last time) problems with datetime values displaying
like '23:59:60' because of fractional-second roundoff problems. Trying to control this upstream of the actual display code was hopeless; the right way is to explicitly round fractional seconds in the display code and then refigure the results if the fraction rounds up to 1. Per bug #1927.
Diffstat (limited to 'src/interfaces')
-rw-r--r--src/interfaces/ecpg/pgtypeslib/dt.h7
-rw-r--r--src/interfaces/ecpg/pgtypeslib/dt_common.c3
-rw-r--r--src/interfaces/ecpg/pgtypeslib/interval.c11
-rw-r--r--src/interfaces/ecpg/pgtypeslib/timestamp.c49
4 files changed, 50 insertions, 20 deletions
diff --git a/src/interfaces/ecpg/pgtypeslib/dt.h b/src/interfaces/ecpg/pgtypeslib/dt.h
index 3c9dd4e0186..d7ca2d5bf2f 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt.h
+++ b/src/interfaces/ecpg/pgtypeslib/dt.h
@@ -13,8 +13,11 @@ typedef int32 fsec_t;
typedef double fsec_t;
-#define TIME_PREC_INV 1000000.0
-#define JROUND(j) (rint(((double) (j)) * TIME_PREC_INV) / TIME_PREC_INV)
+/* round off to MAX_TIMESTAMP_PRECISION decimal places */
+/* note: this is also used for rounding off intervals */
+#define TS_PREC_INV 1000000.0
+#define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)
+
#endif
#define USE_POSTGRES_DATES 0
diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c
index 7cb65f7e962..305f192a7bd 100644
--- a/src/interfaces/ecpg/pgtypeslib/dt_common.c
+++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c
@@ -1255,9 +1255,8 @@ dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
*min = time / SECS_PER_MINUTE;
time -= (*min) * SECS_PER_MINUTE;
*sec = time;
- *fsec = JROUND(time - *sec);
+ *fsec = time - *sec;
#endif
- return;
} /* dt2time() */
diff --git a/src/interfaces/ecpg/pgtypeslib/interval.c b/src/interfaces/ecpg/pgtypeslib/interval.c
index a9ed260e70a..93a9d3b45e1 100644
--- a/src/interfaces/ecpg/pgtypeslib/interval.c
+++ b/src/interfaces/ecpg/pgtypeslib/interval.c
@@ -702,10 +702,18 @@ interval2tm(interval span, struct tm *tm, fsec_t *fsec)
tm->tm_sec = time / USECS_PER_SEC;
*fsec = time - (tm->tm_sec * USECS_PER_SEC);
#else
+recalc:
TMODULO(time, tm->tm_mday, (double)SECS_PER_DAY);
TMODULO(time, tm->tm_hour, (double)SECS_PER_HOUR);
TMODULO(time, tm->tm_min, (double)SECS_PER_MINUTE);
TMODULO(time, tm->tm_sec, 1.0);
+ time = TSROUND(time);
+ /* roundoff may need to propagate to higher-order fields */
+ if (time >= 1.0)
+ {
+ time = ceil(span.time);
+ goto recalc;
+ }
*fsec = time;
#endif
@@ -725,8 +733,7 @@ tm2interval(struct tm *tm, fsec_t fsec, interval *span)
span->time = (((((tm->tm_mday * (double)HOURS_PER_DAY) +
tm->tm_hour) * (double)MINS_PER_HOUR) +
tm->tm_min) * (double)SECS_PER_MINUTE) +
- tm->tm_sec;
- span->time = JROUND(span->time + fsec);
+ tm->tm_sec + fsec;
#endif
return 0;
diff --git a/src/interfaces/ecpg/pgtypeslib/timestamp.c b/src/interfaces/ecpg/pgtypeslib/timestamp.c
index 74b024d0a9b..5b7928b182e 100644
--- a/src/interfaces/ecpg/pgtypeslib/timestamp.c
+++ b/src/interfaces/ecpg/pgtypeslib/timestamp.c
@@ -38,7 +38,6 @@ dt2local(timestamp dt, int tz)
dt -= (tz * USECS_PER_SEC);
#else
dt -= tz;
- dt = JROUND(dt);
#endif
return dt;
} /* dt2local() */
@@ -124,9 +123,8 @@ dt2time(timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
*min = time / SECS_PER_MINUTE;
time -= (*min) * SECS_PER_MINUTE;
*sec = time;
- *fsec = JROUND(time - *sec);
+ *fsec = time - *sec;
#endif
- return;
} /* dt2time() */
/* timestamp2tm()
@@ -144,7 +142,7 @@ static int
timestamp2tm(timestamp dt, int *tzp, struct tm *tm, fsec_t *fsec, char **tzn)
{
#ifdef HAVE_INT64_TIMESTAMP
- int dDate,
+ int64 dDate,
date0;
int64 time;
#else
@@ -160,8 +158,8 @@ timestamp2tm(timestamp dt, int *tzp, struct tm *tm, fsec_t *fsec, char **tzn)
date0 = date2j(2000, 1, 1);
- time = dt;
#ifdef HAVE_INT64_TIMESTAMP
+ time = dt;
TMODULO(time, dDate, USECS_PER_DAY);
if (time < INT64CONST(0))
@@ -169,7 +167,18 @@ timestamp2tm(timestamp dt, int *tzp, struct tm *tm, fsec_t *fsec, char **tzn)
time += USECS_PER_DAY;
dDate -= 1;
}
+
+ /* add offset to go from J2000 back to standard Julian date */
+ dDate += date0;
+
+ /* Julian day routine does not work for negative Julian days */
+ if (dDate < 0 || dDate > (timestamp) INT_MAX)
+ return -1;
+
+ j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+ dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
#else
+ time = dt;
TMODULO(time, dDate, (double)SECS_PER_DAY);
if (time < 0)
@@ -177,18 +186,34 @@ timestamp2tm(timestamp dt, int *tzp, struct tm *tm, fsec_t *fsec, char **tzn)
time += SECS_PER_DAY;
dDate -= 1;
}
-#endif
-
- /* Julian day routine does not work for negative Julian days */
- if (dDate < -date0)
- return -1;
/* add offset to go from J2000 back to standard Julian date */
dDate += date0;
+recalc_d:
+ /* Julian day routine does not work for negative Julian days */
+ if (dDate < 0 || dDate > (timestamp) INT_MAX)
+ return -1;
+
j2date((int) dDate, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
+recalc_t:
dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
+ *fsec = TSROUND(*fsec);
+ /* roundoff may need to propagate to higher-order fields */
+ if (*fsec >= 1.0)
+ {
+ time = ceil(time);
+ if (time >= (double)SECS_PER_DAY)
+ {
+ time = 0;
+ dDate += 1;
+ goto recalc_d;
+ }
+ goto recalc_t;
+ }
+#endif
+
if (tzp != NULL)
{
/*
@@ -791,11 +816,7 @@ PGTYPEStimestamp_sub(timestamp *ts1, timestamp *ts2, interval *iv)
if (TIMESTAMP_NOT_FINITE(*ts1) || TIMESTAMP_NOT_FINITE(*ts2))
return PGTYPES_TS_ERR_EINFTIME;
else
-#ifdef HAVE_INT64_TIMESTAMP
iv->time = (ts1 - ts2);
-#else
- iv->time = JROUND(ts1 - ts2);
-#endif
iv->month = 0;