Skip to content

Commit 57bfb27

Browse files
committed
Fix interval input parser so that fractional weeks and months are
cascaded first to days and only what is leftover into seconds. This seems to satisfy the principle of least surprise given the general conversion to three-part interval values --- it was an oversight that these cases weren't dealt with in 8.1. Michael Glaesemann
1 parent 091fe03 commit 57bfb27

File tree

4 files changed

+72
-30
lines changed

4 files changed

+72
-30
lines changed

src/backend/utils/adt/datetime.c

Lines changed: 29 additions & 15 deletions
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.169 2006/07/25 03:51:21 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.170 2006/09/04 01:26:27 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -2920,16 +2920,23 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm,
29202920
tm->tm_mday += val * 7;
29212921
if (fval != 0)
29222922
{
2923-
int sec;
2924-
2925-
fval *= 7 * SECS_PER_DAY;
2926-
sec = fval;
2927-
tm->tm_sec += sec;
2923+
int extra_days;
2924+
fval *= 7;
2925+
extra_days = (int32) fval;
2926+
tm->tm_mday += extra_days;
2927+
fval -= extra_days;
2928+
if (fval != 0)
2929+
{
2930+
int sec;
2931+
fval *= SECS_PER_DAY;
2932+
sec = fval;
2933+
tm->tm_sec += sec;
29282934
#ifdef HAVE_INT64_TIMESTAMP
2929-
*fsec += (fval - sec) * 1000000;
2935+
*fsec += (fval - sec) * 1000000;
29302936
#else
2931-
*fsec += fval - sec;
2937+
*fsec += fval - sec;
29322938
#endif
2939+
}
29332940
}
29342941
tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
29352942
break;
@@ -2938,16 +2945,23 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm,
29382945
tm->tm_mon += val;
29392946
if (fval != 0)
29402947
{
2941-
int sec;
2942-
2943-
fval *= DAYS_PER_MONTH * SECS_PER_DAY;
2944-
sec = fval;
2945-
tm->tm_sec += sec;
2948+
int day;
2949+
fval *= DAYS_PER_MONTH;
2950+
day = fval;
2951+
tm->tm_mday += day;
2952+
fval -= day;
2953+
if (fval != 0)
2954+
{
2955+
int sec;
2956+
fval *= SECS_PER_DAY;
2957+
sec = fval;
2958+
tm->tm_sec += sec;
29462959
#ifdef HAVE_INT64_TIMESTAMP
2947-
*fsec += (fval - sec) * 1000000;
2960+
*fsec += (fval - sec) * 1000000;
29482961
#else
2949-
*fsec += fval - sec;
2962+
*fsec += fval - sec;
29502963
#endif
2964+
}
29512965
}
29522966
tmask = DTK_M(MONTH);
29532967
break;

src/interfaces/ecpg/pgtypeslib/interval.c

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/interval.c,v 1.32 2006/06/06 11:31:55 meskes Exp $ */
1+
/* $PostgreSQL: pgsql/src/interfaces/ecpg/pgtypeslib/interval.c,v 1.33 2006/09/04 01:26:28 tgl Exp $ */
22

33
#include "postgres_fe.h"
44
#include <time.h>
@@ -307,16 +307,23 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
307307
tm->tm_mday += val * 7;
308308
if (fval != 0)
309309
{
310-
int sec;
311-
312-
fval *= 7 * SECS_PER_DAY;
313-
sec = fval;
314-
tm->tm_sec += sec;
310+
int extra_days;
311+
fval *= 7;
312+
extra_days = (int32) fval;
313+
tm->tm_mday += extra_days;
314+
fval -= extra_days;
315+
if (fval != 0)
316+
{
317+
int sec;
318+
fval *= SECS_PER_DAY;
319+
sec = fval;
320+
tm->tm_sec += sec;
315321
#ifdef HAVE_INT64_TIMESTAMP
316-
*fsec += (fval - sec) * 1000000;
322+
*fsec += (fval - sec) * 1000000;
317323
#else
318-
*fsec += fval - sec;
324+
*fsec += fval - sec;
319325
#endif
326+
}
320327
}
321328
tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY);
322329
break;
@@ -325,16 +332,23 @@ DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fse
325332
tm->tm_mon += val;
326333
if (fval != 0)
327334
{
328-
int sec;
329-
330-
fval *= DAYS_PER_MONTH * SECS_PER_DAY;
331-
sec = fval;
332-
tm->tm_sec += sec;
335+
int day;
336+
fval *= DAYS_PER_MONTH;
337+
day = fval;
338+
tm->tm_mday += day;
339+
fval -= day;
340+
if (fval != 0)
341+
{
342+
int sec;
343+
fval *= SECS_PER_DAY;
344+
sec = fval;
345+
tm->tm_sec += sec;
333346
#ifdef HAVE_INT64_TIMESTAMP
334-
*fsec += (fval - sec) * 1000000;
347+
*fsec += (fval - sec) * 1000000;
335348
#else
336-
*fsec += fval - sec;
349+
*fsec += fval - sec;
337350
#endif
351+
}
338352
}
339353
tmask = DTK_M(MONTH);
340354
break;

src/test/regress/expected/interval.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ SELECT INTERVAL '-1 days +02:03' AS "22 hours ago...";
3939
-1 days +02:03:00
4040
(1 row)
4141

42+
SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours";
43+
Ten days twelve hours
44+
-----------------------
45+
10 days 12:00:00
46+
(1 row)
47+
48+
SELECT INTERVAL '1.5 months' AS "One month 15 days";
49+
One month 15 days
50+
-------------------
51+
1 mon 15 days
52+
(1 row)
53+
4254
SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years...";
4355
9 years...
4456
----------------------------------

src/test/regress/sql/interval.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ SELECT INTERVAL '-08:00' AS "Eight hours";
1111
SELECT INTERVAL '-05' AS "Five hours";
1212
SELECT INTERVAL '-1 +02:03' AS "22 hours ago...";
1313
SELECT INTERVAL '-1 days +02:03' AS "22 hours ago...";
14+
SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours";
15+
SELECT INTERVAL '1.5 months' AS "One month 15 days";
1416
SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years...";
1517

1618
CREATE TABLE INTERVAL_TBL (f1 interval);

0 commit comments

Comments
 (0)