Skip to content

Commit 6be39d7

Browse files
committed
Fix extraction of week and quarter fields from intervals.
"EXTRACT(WEEK FROM interval_value)" formerly threw an error. Define it as "tm->tm_mday / 7". (With C99 division semantics, this gives consistent results for negative intervals.) "EXTRACT(QUARTER FROM interval_value)" has been implemented all along, but it formerly gave extremely strange results for negative intervals. Fix it so that the output for -N months is the negative of the output for N months. Per bug #18348 from Michael Bondarenko and subsequent discussion. Discussion: https://postgr.es/m/18348-b097a3587dfde8a4@postgresql.org
1 parent 108d2ad commit 6be39d7

File tree

4 files changed

+94
-16
lines changed

4 files changed

+94
-16
lines changed

doc/src/sgml/func.sgml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10432,12 +10432,16 @@ SELECT EXTRACT(MONTH FROM INTERVAL '2 years 13 months');
1043210432
<term><literal>quarter</literal></term>
1043310433
<listitem>
1043410434
<para>
10435-
The quarter of the year (1&ndash;4) that the date is in
10435+
The quarter of the year (1&ndash;4) that the date is in;
10436+
for <type>interval</type> values, the month field divided by 3
10437+
plus 1
1043610438
</para>
1043710439

1043810440
<screen>
1043910441
SELECT EXTRACT(QUARTER FROM TIMESTAMP '2001-02-16 20:38:40');
1044010442
<lineannotation>Result: </lineannotation><computeroutput>1</computeroutput>
10443+
SELECT EXTRACT(QUARTER FROM INTERVAL '1 year 6 months');
10444+
<lineannotation>Result: </lineannotation><computeroutput>3</computeroutput>
1044110445
</screen>
1044210446
</listitem>
1044310447
</varlistentry>
@@ -10508,9 +10512,16 @@ SELECT EXTRACT(SECOND FROM TIME '17:12:28.5');
1050810512
<literal>week</literal> to get consistent results.
1050910513
</para>
1051010514

10515+
<para>
10516+
For <type>interval</type> values, the week field is simply the number
10517+
of integral days divided by 7.
10518+
</para>
10519+
1051110520
<screen>
1051210521
SELECT EXTRACT(WEEK FROM TIMESTAMP '2001-02-16 20:38:40');
1051310522
<lineannotation>Result: </lineannotation><computeroutput>7</computeroutput>
10523+
SELECT EXTRACT(WEEK FROM INTERVAL '13 days 24 hours');
10524+
<lineannotation>Result: </lineannotation><computeroutput>1</computeroutput>
1051410525
</screen>
1051510526
</listitem>
1051610527
</varlistentry>

src/backend/utils/adt/timestamp.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5899,6 +5899,7 @@ NonFiniteIntervalPart(int type, int unit, char *lowunits, bool isNegative)
58995899
case DTK_MILLISEC:
59005900
case DTK_SECOND:
59015901
case DTK_MINUTE:
5902+
case DTK_WEEK:
59025903
case DTK_MONTH:
59035904
case DTK_QUARTER:
59045905
return 0.0;
@@ -6018,12 +6019,27 @@ interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
60186019
intresult = tm->tm_mday;
60196020
break;
60206021

6022+
case DTK_WEEK:
6023+
intresult = tm->tm_mday / 7;
6024+
break;
6025+
60216026
case DTK_MONTH:
60226027
intresult = tm->tm_mon;
60236028
break;
60246029

60256030
case DTK_QUARTER:
6026-
intresult = (tm->tm_mon / 3) + 1;
6031+
6032+
/*
6033+
* We want to maintain the rule that a field extracted from a
6034+
* negative interval is the negative of the field's value for
6035+
* the sign-reversed interval. The broken-down tm_year and
6036+
* tm_mon aren't very helpful for that, so work from
6037+
* interval->month.
6038+
*/
6039+
if (interval->month >= 0)
6040+
intresult = (tm->tm_mon / 3) + 1;
6041+
else
6042+
intresult = -(((-interval->month % MONTHS_PER_YEAR) / 3) + 1);
60276043
break;
60286044

60296045
case DTK_YEAR:

src/test/regress/expected/interval.out

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1834,6 +1834,7 @@ SELECT f1,
18341834
EXTRACT(MINUTE FROM f1) AS MINUTE,
18351835
EXTRACT(HOUR FROM f1) AS HOUR,
18361836
EXTRACT(DAY FROM f1) AS DAY,
1837+
EXTRACT(WEEK FROM f1) AS WEEK,
18371838
EXTRACT(MONTH FROM f1) AS MONTH,
18381839
EXTRACT(QUARTER FROM f1) AS QUARTER,
18391840
EXTRACT(YEAR FROM f1) AS YEAR,
@@ -1842,20 +1843,52 @@ SELECT f1,
18421843
EXTRACT(MILLENNIUM FROM f1) AS MILLENNIUM,
18431844
EXTRACT(EPOCH FROM f1) AS EPOCH
18441845
FROM INTERVAL_TBL;
1845-
f1 | microsecond | millisecond | second | minute | hour | day | month | quarter | year | decade | century | millennium | epoch
1846-
-------------------------------+-------------+-------------+------------+--------+-----------+-----------+-------+---------+-----------+-----------+-----------+------------+-------------------
1847-
@ 1 min | 0 | 0.000 | 0.000000 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 60.000000
1848-
@ 5 hours | 0 | 0.000 | 0.000000 | 0 | 5 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 18000.000000
1849-
@ 10 days | 0 | 0.000 | 0.000000 | 0 | 0 | 10 | 0 | 1 | 0 | 0 | 0 | 0 | 864000.000000
1850-
@ 34 years | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | 1 | 34 | 3 | 0 | 0 | 1072958400.000000
1851-
@ 3 mons | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 3 | 2 | 0 | 0 | 0 | 0 | 7776000.000000
1852-
@ 14 secs ago | -14000000 | -14000.000 | -14.000000 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | -14.000000
1853-
@ 1 day 2 hours 3 mins 4 secs | 4000000 | 4000.000 | 4.000000 | 3 | 2 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 93784.000000
1854-
@ 6 years | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | 1 | 6 | 0 | 0 | 0 | 189345600.000000
1855-
@ 5 mons | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 5 | 2 | 0 | 0 | 0 | 0 | 12960000.000000
1856-
@ 5 mons 12 hours | 0 | 0.000 | 0.000000 | 0 | 12 | 0 | 5 | 2 | 0 | 0 | 0 | 0 | 13003200.000000
1857-
infinity | | | | | Infinity | Infinity | | | Infinity | Infinity | Infinity | Infinity | Infinity
1858-
-infinity | | | | | -Infinity | -Infinity | | | -Infinity | -Infinity | -Infinity | -Infinity | -Infinity
1846+
f1 | microsecond | millisecond | second | minute | hour | day | week | month | quarter | year | decade | century | millennium | epoch
1847+
-------------------------------+-------------+-------------+------------+--------+-----------+-----------+------+-------+---------+-----------+-----------+-----------+------------+-------------------
1848+
@ 1 min | 0 | 0.000 | 0.000000 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 60.000000
1849+
@ 5 hours | 0 | 0.000 | 0.000000 | 0 | 5 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 18000.000000
1850+
@ 10 days | 0 | 0.000 | 0.000000 | 0 | 0 | 10 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 864000.000000
1851+
@ 34 years | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | 0 | 1 | 34 | 3 | 0 | 0 | 1072958400.000000
1852+
@ 3 mons | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | 3 | 2 | 0 | 0 | 0 | 0 | 7776000.000000
1853+
@ 14 secs ago | -14000000 | -14000.000 | -14.000000 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | -14.000000
1854+
@ 1 day 2 hours 3 mins 4 secs | 4000000 | 4000.000 | 4.000000 | 3 | 2 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 93784.000000
1855+
@ 6 years | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | 0 | 1 | 6 | 0 | 0 | 0 | 189345600.000000
1856+
@ 5 mons | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | 5 | 2 | 0 | 0 | 0 | 0 | 12960000.000000
1857+
@ 5 mons 12 hours | 0 | 0.000 | 0.000000 | 0 | 12 | 0 | 0 | 5 | 2 | 0 | 0 | 0 | 0 | 13003200.000000
1858+
infinity | | | | | Infinity | Infinity | | | | Infinity | Infinity | Infinity | Infinity | Infinity
1859+
-infinity | | | | | -Infinity | -Infinity | | | | -Infinity | -Infinity | -Infinity | -Infinity | -Infinity
1860+
(12 rows)
1861+
1862+
SELECT -f1,
1863+
EXTRACT(MICROSECOND FROM -f1) AS MICROSECOND,
1864+
EXTRACT(MILLISECOND FROM -f1) AS MILLISECOND,
1865+
EXTRACT(SECOND FROM -f1) AS SECOND,
1866+
EXTRACT(MINUTE FROM -f1) AS MINUTE,
1867+
EXTRACT(HOUR FROM -f1) AS HOUR,
1868+
EXTRACT(DAY FROM -f1) AS DAY,
1869+
EXTRACT(WEEK FROM -f1) AS WEEK,
1870+
EXTRACT(MONTH FROM -f1) AS MONTH,
1871+
EXTRACT(QUARTER FROM -f1) AS QUARTER,
1872+
EXTRACT(YEAR FROM -f1) AS YEAR,
1873+
EXTRACT(DECADE FROM -f1) AS DECADE,
1874+
EXTRACT(CENTURY FROM -f1) AS CENTURY,
1875+
EXTRACT(MILLENNIUM FROM -f1) AS MILLENNIUM,
1876+
EXTRACT(EPOCH FROM -f1) AS EPOCH
1877+
FROM INTERVAL_TBL;
1878+
?column? | microsecond | millisecond | second | minute | hour | day | week | month | quarter | year | decade | century | millennium | epoch
1879+
-----------------------------------+-------------+-------------+-----------+--------+-----------+-----------+------+-------+---------+-----------+-----------+-----------+------------+--------------------
1880+
@ 1 min ago | 0 | 0.000 | 0.000000 | -1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | -60.000000
1881+
@ 5 hours ago | 0 | 0.000 | 0.000000 | 0 | -5 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | -18000.000000
1882+
@ 10 days ago | 0 | 0.000 | 0.000000 | 0 | 0 | -10 | -1 | 0 | 1 | 0 | 0 | 0 | 0 | -864000.000000
1883+
@ 34 years ago | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | 0 | -1 | -34 | -3 | 0 | 0 | -1072958400.000000
1884+
@ 3 mons ago | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | -3 | -2 | 0 | 0 | 0 | 0 | -7776000.000000
1885+
@ 14 secs | 14000000 | 14000.000 | 14.000000 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 14.000000
1886+
@ 1 day 2 hours 3 mins 4 secs ago | -4000000 | -4000.000 | -4.000000 | -3 | -2 | -1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | -93784.000000
1887+
@ 6 years ago | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | 0 | -1 | -6 | 0 | 0 | 0 | -189345600.000000
1888+
@ 5 mons ago | 0 | 0.000 | 0.000000 | 0 | 0 | 0 | 0 | -5 | -2 | 0 | 0 | 0 | 0 | -12960000.000000
1889+
@ 5 mons 12 hours ago | 0 | 0.000 | 0.000000 | 0 | -12 | 0 | 0 | -5 | -2 | 0 | 0 | 0 | 0 | -13003200.000000
1890+
-infinity | | | | | -Infinity | -Infinity | | | | -Infinity | -Infinity | -Infinity | -Infinity | -Infinity
1891+
infinity | | | | | Infinity | Infinity | | | | Infinity | Infinity | Infinity | Infinity | Infinity
18591892
(12 rows)
18601893

18611894
SELECT EXTRACT(FORTNIGHT FROM INTERVAL '2 days'); -- error

src/test/regress/sql/interval.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,7 @@ SELECT f1,
592592
EXTRACT(MINUTE FROM f1) AS MINUTE,
593593
EXTRACT(HOUR FROM f1) AS HOUR,
594594
EXTRACT(DAY FROM f1) AS DAY,
595+
EXTRACT(WEEK FROM f1) AS WEEK,
595596
EXTRACT(MONTH FROM f1) AS MONTH,
596597
EXTRACT(QUARTER FROM f1) AS QUARTER,
597598
EXTRACT(YEAR FROM f1) AS YEAR,
@@ -601,6 +602,23 @@ SELECT f1,
601602
EXTRACT(EPOCH FROM f1) AS EPOCH
602603
FROM INTERVAL_TBL;
603604

605+
SELECT -f1,
606+
EXTRACT(MICROSECOND FROM -f1) AS MICROSECOND,
607+
EXTRACT(MILLISECOND FROM -f1) AS MILLISECOND,
608+
EXTRACT(SECOND FROM -f1) AS SECOND,
609+
EXTRACT(MINUTE FROM -f1) AS MINUTE,
610+
EXTRACT(HOUR FROM -f1) AS HOUR,
611+
EXTRACT(DAY FROM -f1) AS DAY,
612+
EXTRACT(WEEK FROM -f1) AS WEEK,
613+
EXTRACT(MONTH FROM -f1) AS MONTH,
614+
EXTRACT(QUARTER FROM -f1) AS QUARTER,
615+
EXTRACT(YEAR FROM -f1) AS YEAR,
616+
EXTRACT(DECADE FROM -f1) AS DECADE,
617+
EXTRACT(CENTURY FROM -f1) AS CENTURY,
618+
EXTRACT(MILLENNIUM FROM -f1) AS MILLENNIUM,
619+
EXTRACT(EPOCH FROM -f1) AS EPOCH
620+
FROM INTERVAL_TBL;
621+
604622
SELECT EXTRACT(FORTNIGHT FROM INTERVAL '2 days'); -- error
605623
SELECT EXTRACT(TIMEZONE FROM INTERVAL '2 days'); -- error
606624

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy