mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
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.
This commit is contained in:
@ -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
|
||||
|
@ -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() */
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Reference in New Issue
Block a user