Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 313ed1e

Browse files
committed
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.
1 parent 7754f76 commit 313ed1e

File tree

10 files changed

+125
-56
lines changed

10 files changed

+125
-56
lines changed

contrib/btree_gist/btree_ts.c

-2
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,7 @@ tstz_to_ts_gmt(Timestamp *gmt, TimestampTz *ts)
122122
*gmt -= (tz * INT64CONST(1000000));
123123
#else
124124
*gmt -= tz;
125-
*gmt = JROUND(*gmt);
126125
#endif
127-
128126
}
129127
return gmt;
130128
}

src/backend/utils/adt/date.c

+17-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.120 2005/09/09 02:31:49 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.121 2005/10/09 17:21:46 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -944,10 +944,18 @@ time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
944944
#else
945945
double trem;
946946

947+
recalc:
947948
trem = time;
948949
TMODULO(trem, tm->tm_hour, (double)SECS_PER_HOUR);
949950
TMODULO(trem, tm->tm_min, (double)SECS_PER_MINUTE);
950951
TMODULO(trem, tm->tm_sec, 1.0);
952+
trem = TIMEROUND(trem);
953+
/* roundoff may need to propagate to higher-order fields */
954+
if (trem >= 1.0)
955+
{
956+
time = ceil(time);
957+
goto recalc;
958+
}
951959
*fsec = trem;
952960
#endif
953961

@@ -1837,9 +1845,17 @@ timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
18371845
#else
18381846
double trem = time->time;
18391847

1848+
recalc:
18401849
TMODULO(trem, tm->tm_hour, (double)SECS_PER_HOUR);
18411850
TMODULO(trem, tm->tm_min, (double)SECS_PER_MINUTE);
18421851
TMODULO(trem, tm->tm_sec, 1.0);
1852+
trem = TIMEROUND(trem);
1853+
/* roundoff may need to propagate to higher-order fields */
1854+
if (trem >= 1.0)
1855+
{
1856+
trem = ceil(time->time);
1857+
goto recalc;
1858+
}
18431859
*fsec = trem;
18441860
#endif
18451861

src/backend/utils/adt/datetime.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.157 2005/07/23 14:25:33 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.158 2005/10/09 17:21:46 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -3488,16 +3488,16 @@ EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, int *tzp, int style, char *str)
34883488
sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min);
34893489

34903490
/*
3491-
* Print fractional seconds if any. The field widths here should be
3492-
* at least equal to the larger of MAX_TIME_PRECISION and
3491+
* Print fractional seconds if any. The fractional field widths
3492+
* here should be equal to the larger of MAX_TIME_PRECISION and
34933493
* MAX_TIMESTAMP_PRECISION.
34943494
*/
34953495
if (fsec != 0)
34963496
{
34973497
#ifdef HAVE_INT64_TIMESTAMP
34983498
sprintf(str + strlen(str), ":%02d.%06d", tm->tm_sec, fsec);
34993499
#else
3500-
sprintf(str + strlen(str), ":%012.9f", tm->tm_sec + fsec);
3500+
sprintf(str + strlen(str), ":%013.10f", tm->tm_sec + fsec);
35013501
#endif
35023502
TrimTrailingZeros(str);
35033503
}

src/backend/utils/adt/timestamp.c

+43-25
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.153 2005/09/09 06:46:14 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.154 2005/10/09 17:21:46 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -998,10 +998,8 @@ dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
998998
*min = time / SECS_PER_MINUTE;
999999
time -= (*min) * SECS_PER_MINUTE;
10001000
*sec = time;
1001-
*fsec = JROUND(time - *sec);
1001+
*fsec = time - *sec;
10021002
#endif
1003-
1004-
return;
10051003
} /* dt2time() */
10061004

10071005

@@ -1038,35 +1036,62 @@ timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char **tzn,
10381036
#endif
10391037
}
10401038

1041-
time = dt;
10421039
#ifdef HAVE_INT64_TIMESTAMP
1040+
time = dt;
10431041
TMODULO(time, date, USECS_PER_DAY);
10441042

10451043
if (time < INT64CONST(0))
10461044
{
10471045
time += USECS_PER_DAY;
10481046
date -= 1;
10491047
}
1048+
1049+
/* add offset to go from J2000 back to standard Julian date */
1050+
date += POSTGRES_EPOCH_JDATE;
1051+
1052+
/* Julian day routine does not work for negative Julian days */
1053+
if (date < 0 || date > (Timestamp) INT_MAX)
1054+
return -1;
1055+
1056+
j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1057+
dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
10501058
#else
1059+
time = dt;
10511060
TMODULO(time, date, (double)SECS_PER_DAY);
10521061

10531062
if (time < 0)
10541063
{
10551064
time += SECS_PER_DAY;
1056-
date -=1;
1065+
date -= 1;
10571066
}
1058-
#endif
10591067

10601068
/* add offset to go from J2000 back to standard Julian date */
10611069
date += POSTGRES_EPOCH_JDATE;
10621070

1071+
recalc_d:
10631072
/* Julian day routine does not work for negative Julian days */
1064-
if (date <0 || date >(Timestamp) INT_MAX)
1073+
if (date < 0 || date > (Timestamp) INT_MAX)
10651074
return -1;
10661075

10671076
j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1077+
recalc_t:
10681078
dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
10691079

1080+
*fsec = TSROUND(*fsec);
1081+
/* roundoff may need to propagate to higher-order fields */
1082+
if (*fsec >= 1.0)
1083+
{
1084+
time = ceil(time);
1085+
if (time >= (double)SECS_PER_DAY)
1086+
{
1087+
time = 0;
1088+
date += 1;
1089+
goto recalc_d;
1090+
}
1091+
goto recalc_t;
1092+
}
1093+
#endif
1094+
10701095
/* Done if no TZ conversion wanted */
10711096
if (tzp == NULL)
10721097
{
@@ -1216,9 +1241,17 @@ interval2tm(Interval span, struct pg_tm *tm, fsec_t *fsec)
12161241
tm->tm_sec = time / USECS_PER_SEC;
12171242
*fsec = time - (tm->tm_sec * USECS_PER_SEC);
12181243
#else
1244+
recalc:
12191245
TMODULO(time, tm->tm_hour, (double)SECS_PER_HOUR);
12201246
TMODULO(time, tm->tm_min, (double)SECS_PER_MINUTE);
12211247
TMODULO(time, tm->tm_sec, 1.0);
1248+
time = TSROUND(time);
1249+
/* roundoff may need to propagate to higher-order fields */
1250+
if (time >= 1.0)
1251+
{
1252+
time = ceil(span.time);
1253+
goto recalc;
1254+
}
12221255
*fsec = time;
12231256
#endif
12241257

@@ -1237,8 +1270,7 @@ tm2interval(struct pg_tm *tm, fsec_t fsec, Interval *span)
12371270
#else
12381271
span->time = (((tm->tm_hour * (double)MINS_PER_HOUR) +
12391272
tm->tm_min) * (double)SECS_PER_MINUTE) +
1240-
tm->tm_sec;
1241-
span->time = JROUND(span->time + fsec);
1273+
tm->tm_sec + fsec;
12421274
#endif
12431275

12441276
return 0;
@@ -1266,7 +1298,6 @@ dt2local(Timestamp dt, int tz)
12661298
dt -= (tz * USECS_PER_SEC);
12671299
#else
12681300
dt -= tz;
1269-
dt = JROUND(dt);
12701301
#endif
12711302
return dt;
12721303
} /* dt2local() */
@@ -1901,11 +1932,7 @@ timestamp_mi(PG_FUNCTION_ARGS)
19011932
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
19021933
errmsg("cannot subtract infinite timestamps")));
19031934

1904-
#ifdef HAVE_INT64_TIMESTAMP
19051935
result->time = dt1 - dt2;
1906-
#else
1907-
result->time = JROUND(dt1 - dt2);
1908-
#endif
19091936

19101937
result->month = 0;
19111938
result->day = 0;
@@ -2224,11 +2251,7 @@ interval_pl(PG_FUNCTION_ARGS)
22242251

22252252
result->month = span1->month + span2->month;
22262253
result->day = span1->day + span2->day;
2227-
#ifdef HAVE_INT64_TIMESTAMP
22282254
result->time = span1->time + span2->time;
2229-
#else
2230-
result->time = JROUND(span1->time + span2->time);
2231-
#endif
22322255

22332256
PG_RETURN_INTERVAL_P(result);
22342257
}
@@ -2244,11 +2267,7 @@ interval_mi(PG_FUNCTION_ARGS)
22442267

22452268
result->month = span1->month - span2->month;
22462269
result->day = span1->day - span2->day;
2247-
#ifdef HAVE_INT64_TIMESTAMP
22482270
result->time = span1->time - span2->time;
2249-
#else
2250-
result->time = JROUND(span1->time - span2->time);
2251-
#endif
22522271

22532272
PG_RETURN_INTERVAL_P(result);
22542273
}
@@ -2280,7 +2299,7 @@ interval_mul(PG_FUNCTION_ARGS)
22802299
#ifdef HAVE_INT64_TIMESTAMP
22812300
result->time = rint(span->time * factor + day_remainder * USECS_PER_DAY);
22822301
#else
2283-
result->time = JROUND(span->time * factor + day_remainder * SECS_PER_DAY);
2302+
result->time = span->time * factor + day_remainder * SECS_PER_DAY;
22842303
#endif
22852304

22862305
result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
@@ -2332,7 +2351,6 @@ interval_div(PG_FUNCTION_ARGS)
23322351
result->time += rint(day_remainder * USECS_PER_DAY);
23332352
#else
23342353
result->time += day_remainder * SECS_PER_DAY;
2335-
result->time = JROUND(result->time);
23362354
#endif
23372355

23382356
result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,

src/include/utils/date.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/utils/date.h,v 1.30 2005/02/25 16:13:29 teodor Exp $
10+
* $PostgreSQL: pgsql/src/include/utils/date.h,v 1.31 2005/10/09 17:21:47 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -60,6 +60,10 @@ typedef struct
6060

6161
#define MAX_TIME_PRECISION 10
6262

63+
/* round off to MAX_TIME_PRECISION decimal places */
64+
#define TIME_PREC_INV 10000000000.0
65+
#define TIMEROUND(j) (rint(((double) (j)) * TIME_PREC_INV) / TIME_PREC_INV)
66+
6367
#define DatumGetDateADT(X) ((DateADT) DatumGetInt32(X))
6468
#define DatumGetTimeADT(X) ((TimeADT) DatumGetFloat8(X))
6569
#define DatumGetTimeTzADTP(X) ((TimeTzADT *) DatumGetPointer(X))

src/include/utils/timestamp.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
9-
* $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.55 2005/10/07 20:13:16 momjian Exp $
9+
* $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.56 2005/10/09 17:21:47 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -163,8 +163,11 @@ typedef int32 fsec_t;
163163

164164
typedef double fsec_t;
165165

166-
#define TIME_PREC_INV 1000000.0
167-
#define JROUND(j) (rint(((double) (j)) * TIME_PREC_INV) / TIME_PREC_INV)
166+
/* round off to MAX_TIMESTAMP_PRECISION decimal places */
167+
/* note: this is also used for rounding off intervals */
168+
#define TS_PREC_INV 1000000.0
169+
#define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)
170+
168171
#endif
169172

170173
#define TIMESTAMP_MASK(b) (1 << (b))

src/interfaces/ecpg/pgtypeslib/dt.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ typedef int32 fsec_t;
1313

1414
typedef double fsec_t;
1515

16-
#define TIME_PREC_INV 1000000.0
17-
#define JROUND(j) (rint(((double) (j)) * TIME_PREC_INV) / TIME_PREC_INV)
16+
/* round off to MAX_TIMESTAMP_PRECISION decimal places */
17+
/* note: this is also used for rounding off intervals */
18+
#define TS_PREC_INV 1000000.0
19+
#define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)
20+
1821
#endif
1922

2023
#define USE_POSTGRES_DATES 0

src/interfaces/ecpg/pgtypeslib/dt_common.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -1255,9 +1255,8 @@ dt2time(double jd, int *hour, int *min, int *sec, fsec_t *fsec)
12551255
*min = time / SECS_PER_MINUTE;
12561256
time -= (*min) * SECS_PER_MINUTE;
12571257
*sec = time;
1258-
*fsec = JROUND(time - *sec);
1258+
*fsec = time - *sec;
12591259
#endif
1260-
return;
12611260
} /* dt2time() */
12621261

12631262

src/interfaces/ecpg/pgtypeslib/interval.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -702,10 +702,18 @@ interval2tm(interval span, struct tm *tm, fsec_t *fsec)
702702
tm->tm_sec = time / USECS_PER_SEC;
703703
*fsec = time - (tm->tm_sec * USECS_PER_SEC);
704704
#else
705+
recalc:
705706
TMODULO(time, tm->tm_mday, (double)SECS_PER_DAY);
706707
TMODULO(time, tm->tm_hour, (double)SECS_PER_HOUR);
707708
TMODULO(time, tm->tm_min, (double)SECS_PER_MINUTE);
708709
TMODULO(time, tm->tm_sec, 1.0);
710+
time = TSROUND(time);
711+
/* roundoff may need to propagate to higher-order fields */
712+
if (time >= 1.0)
713+
{
714+
time = ceil(span.time);
715+
goto recalc;
716+
}
709717
*fsec = time;
710718
#endif
711719

@@ -725,8 +733,7 @@ tm2interval(struct tm *tm, fsec_t fsec, interval *span)
725733
span->time = (((((tm->tm_mday * (double)HOURS_PER_DAY) +
726734
tm->tm_hour) * (double)MINS_PER_HOUR) +
727735
tm->tm_min) * (double)SECS_PER_MINUTE) +
728-
tm->tm_sec;
729-
span->time = JROUND(span->time + fsec);
736+
tm->tm_sec + fsec;
730737
#endif
731738

732739
return 0;

0 commit comments

Comments
 (0)