diff options
| author | Tom Lane | 2005-10-09 17:21:47 +0000 |
|---|---|---|
| committer | Tom Lane | 2005-10-09 17:21:47 +0000 |
| commit | 313ed1ed9498f977262e180a080c7748197ced5c (patch) | |
| tree | 17780e929d9a0d710d84de261b27ac26e9a5adcf /src/interfaces | |
| parent | 7754f7634cbc837702428bbb40a0efbeec7a51d1 (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.h | 7 | ||||
| -rw-r--r-- | src/interfaces/ecpg/pgtypeslib/dt_common.c | 3 | ||||
| -rw-r--r-- | src/interfaces/ecpg/pgtypeslib/interval.c | 11 | ||||
| -rw-r--r-- | src/interfaces/ecpg/pgtypeslib/timestamp.c | 49 |
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; |
