aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/timestamp.c
diff options
context:
space:
mode:
authorPeter Eisentraut <peter@eisentraut.org>2022-04-19 20:38:53 +0200
committerPeter Eisentraut <peter@eisentraut.org>2022-04-19 21:04:52 +0200
commitf2a2bf66c87e14f07aefe23cbbe2f2d9edcd9734 (patch)
tree8ee986db7380f57b38eee0d5d368caa0d2662d6e /src/backend/utils/adt/timestamp.c
parenta62bff74b135b191de0417d1cc8ec52f144c8fe7 (diff)
downloadpostgresql-f2a2bf66c87e14f07aefe23cbbe2f2d9edcd9734.tar.gz
postgresql-f2a2bf66c87e14f07aefe23cbbe2f2d9edcd9734.zip
Fix extract epoch from interval calculation
The new numeric code for extract epoch from interval accidentally truncated the DAYS_PER_YEAR value to an integer, leading to results that mismatched the floating-point interval_part calculations. The commit a2da77cdb4661826482ebf2ddba1f953bc74afe4 that introduced this actually contains the regression test change that this reverts. I suppose this was missed at the time. Reported-by: Joseph Koshakow <koshy44@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://www.postgresql.org/message-id/flat/CAAvxfHd5n%3D13NYA2q_tUq%3D3%3DSuWU-CufmTf-Ozj%3DfrEgt7pXwQ%40mail.gmail.com
Diffstat (limited to 'src/backend/utils/adt/timestamp.c')
-rw-r--r--src/backend/utils/adt/timestamp.c14
1 files changed, 10 insertions, 4 deletions
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index da73796eac8..552b631ba78 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -5308,10 +5308,16 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
int64 secs_from_day_month;
int64 val;
- /* this always fits into int64 */
- secs_from_day_month = ((int64) DAYS_PER_YEAR * (interval->month / MONTHS_PER_YEAR) +
- (int64) DAYS_PER_MONTH * (interval->month % MONTHS_PER_YEAR) +
- interval->day) * SECS_PER_DAY;
+ /*
+ * To do this calculation in integer arithmetic even though
+ * DAYS_PER_YEAR is fractional, multiply everything by 4 and then
+ * divide by 4 again at the end. This relies on DAYS_PER_YEAR
+ * being a multiple of 0.25 and on SECS_PER_DAY being a multiple
+ * of 4.
+ */
+ secs_from_day_month = ((int64) (4 * DAYS_PER_YEAR) * (interval->month / MONTHS_PER_YEAR) +
+ (int64) (4 * DAYS_PER_MONTH) * (interval->month % MONTHS_PER_YEAR) +
+ (int64) 4 * interval->day) * (SECS_PER_DAY / 4);
/*---
* result = secs_from_day_month + interval->time / 1'000'000