1-- 2-- INTERVAL 3-- 4 5SET DATESTYLE = 'ISO'; 6SET IntervalStyle to postgres; 7 8-- check acceptance of "time zone style" 9SELECT INTERVAL '01:00' AS "One hour"; 10SELECT INTERVAL '+02:00' AS "Two hours"; 11SELECT INTERVAL '-08:00' AS "Eight hours"; 12SELECT INTERVAL '-1 +02:03' AS "22 hours ago..."; 13SELECT INTERVAL '-1 days +02:03' AS "22 hours ago..."; 14SELECT INTERVAL '1.5 weeks' AS "Ten days twelve hours"; 15SELECT INTERVAL '1.5 months' AS "One month 15 days"; 16SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years..."; 17 18CREATE TABLE INTERVAL_TBL (f1 interval); 19 20INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 1 minute'); 21INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 5 hour'); 22INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 10 day'); 23INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 34 year'); 24INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 3 months'); 25INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 14 seconds ago'); 26INSERT INTO INTERVAL_TBL (f1) VALUES ('1 day 2 hours 3 minutes 4 seconds'); 27INSERT INTO INTERVAL_TBL (f1) VALUES ('6 years'); 28INSERT INTO INTERVAL_TBL (f1) VALUES ('5 months'); 29INSERT INTO INTERVAL_TBL (f1) VALUES ('5 months 12 hours'); 30 31-- badly formatted interval 32INSERT INTO INTERVAL_TBL (f1) VALUES ('badly formatted interval'); 33INSERT INTO INTERVAL_TBL (f1) VALUES ('@ 30 eons ago'); 34 35-- test interval operators 36 37SELECT '' AS ten, * FROM INTERVAL_TBL; 38 39SELECT '' AS nine, * FROM INTERVAL_TBL 40 WHERE INTERVAL_TBL.f1 <> interval '@ 10 days'; 41 42SELECT '' AS three, * FROM INTERVAL_TBL 43 WHERE INTERVAL_TBL.f1 <= interval '@ 5 hours'; 44 45SELECT '' AS three, * FROM INTERVAL_TBL 46 WHERE INTERVAL_TBL.f1 < interval '@ 1 day'; 47 48SELECT '' AS one, * FROM INTERVAL_TBL 49 WHERE INTERVAL_TBL.f1 = interval '@ 34 years'; 50 51SELECT '' AS five, * FROM INTERVAL_TBL 52 WHERE INTERVAL_TBL.f1 >= interval '@ 1 month'; 53 54SELECT '' AS nine, * FROM INTERVAL_TBL 55 WHERE INTERVAL_TBL.f1 > interval '@ 3 seconds ago'; 56 57SELECT '' AS fortyfive, r1.*, r2.* 58 FROM INTERVAL_TBL r1, INTERVAL_TBL r2 59 WHERE r1.f1 > r2.f1 60 ORDER BY r1.f1, r2.f1; 61 62-- Test intervals that are large enough to overflow 64 bits in comparisons 63CREATE TEMP TABLE INTERVAL_TBL_OF (f1 interval); 64INSERT INTO INTERVAL_TBL_OF (f1) VALUES 65 ('2147483647 days 2147483647 months'), 66 ('2147483647 days -2147483648 months'), 67 ('1 year'), 68 ('-2147483648 days 2147483647 months'), 69 ('-2147483648 days -2147483648 months'); 70-- these should fail as out-of-range 71INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483648 days'); 72INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483649 days'); 73INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('2147483647 years'); 74INSERT INTO INTERVAL_TBL_OF (f1) VALUES ('-2147483648 years'); 75 76-- Test edge-case overflow detection in interval multiplication 77select extract(epoch from '256 microseconds'::interval * (2^55)::float8); 78 79SELECT r1.*, r2.* 80 FROM INTERVAL_TBL_OF r1, INTERVAL_TBL_OF r2 81 WHERE r1.f1 > r2.f1 82 ORDER BY r1.f1, r2.f1; 83 84CREATE INDEX ON INTERVAL_TBL_OF USING btree (f1); 85SET enable_seqscan TO false; 86EXPLAIN (COSTS OFF) 87SELECT f1 FROM INTERVAL_TBL_OF r1 ORDER BY f1; 88SELECT f1 FROM INTERVAL_TBL_OF r1 ORDER BY f1; 89RESET enable_seqscan; 90 91DROP TABLE INTERVAL_TBL_OF; 92 93-- Test multiplication and division with intervals. 94-- Floating point arithmetic rounding errors can lead to unexpected results, 95-- though the code attempts to do the right thing and round up to days and 96-- minutes to avoid results such as '3 days 24:00 hours' or '14:20:60'. 97-- Note that it is expected for some day components to be greater than 29 and 98-- some time components be greater than 23:59:59 due to how intervals are 99-- stored internally. 100 101CREATE TABLE INTERVAL_MULDIV_TBL (span interval); 102COPY INTERVAL_MULDIV_TBL FROM STDIN; 10341 mon 12 days 360:00 104-41 mon -12 days +360:00 105-12 days 1069 mon -27 days 12:34:56 107-3 years 482 days 76:54:32.189 1084 mon 10914 mon 110999 mon 999 days 111\. 112 113SELECT span * 0.3 AS product 114FROM INTERVAL_MULDIV_TBL; 115 116SELECT span * 8.2 AS product 117FROM INTERVAL_MULDIV_TBL; 118 119SELECT span / 10 AS quotient 120FROM INTERVAL_MULDIV_TBL; 121 122SELECT span / 100 AS quotient 123FROM INTERVAL_MULDIV_TBL; 124 125DROP TABLE INTERVAL_MULDIV_TBL; 126 127SET DATESTYLE = 'postgres'; 128SET IntervalStyle to postgres_verbose; 129 130SELECT '' AS ten, * FROM INTERVAL_TBL; 131 132-- test avg(interval), which is somewhat fragile since people have been 133-- known to change the allowed input syntax for type interval without 134-- updating pg_aggregate.agginitval 135 136select avg(f1) from interval_tbl; 137 138-- test long interval input 139select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 seconds'::interval; 140 141-- test long interval output 142-- Note: the actual maximum length of the interval output is longer, 143-- but we need the test to work for both integer and floating-point 144-- timestamps. 145select '100000000y 10mon -1000000000d -100000h -10min -10.000001s ago'::interval; 146 147-- test justify_hours() and justify_days() 148 149SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds"; 150SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as "7 mons 6 days 5 hours 4 mins 3 seconds"; 151 152-- test justify_interval() 153 154SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour"; 155 156-- test fractional second input, and detection of duplicate units 157SET DATESTYLE = 'ISO'; 158SET IntervalStyle TO postgres; 159 160SELECT '1 millisecond'::interval, '1 microsecond'::interval, 161 '500 seconds 99 milliseconds 51 microseconds'::interval; 162SELECT '3 days 5 milliseconds'::interval; 163 164SELECT '1 second 2 seconds'::interval; -- error 165SELECT '10 milliseconds 20 milliseconds'::interval; -- error 166SELECT '5.5 seconds 3 milliseconds'::interval; -- error 167SELECT '1:20:05 5 microseconds'::interval; -- error 168SELECT '1 day 1 day'::interval; -- error 169SELECT interval '1-2'; -- SQL year-month literal 170SELECT interval '999' second; -- oversize leading field is ok 171SELECT interval '999' minute; 172SELECT interval '999' hour; 173SELECT interval '999' day; 174SELECT interval '999' month; 175 176-- test SQL-spec syntaxes for restricted field sets 177SELECT interval '1' year; 178SELECT interval '2' month; 179SELECT interval '3' day; 180SELECT interval '4' hour; 181SELECT interval '5' minute; 182SELECT interval '6' second; 183SELECT interval '1' year to month; 184SELECT interval '1-2' year to month; 185SELECT interval '1 2' day to hour; 186SELECT interval '1 2:03' day to hour; 187SELECT interval '1 2:03:04' day to hour; 188SELECT interval '1 2' day to minute; 189SELECT interval '1 2:03' day to minute; 190SELECT interval '1 2:03:04' day to minute; 191SELECT interval '1 2' day to second; 192SELECT interval '1 2:03' day to second; 193SELECT interval '1 2:03:04' day to second; 194SELECT interval '1 2' hour to minute; 195SELECT interval '1 2:03' hour to minute; 196SELECT interval '1 2:03:04' hour to minute; 197SELECT interval '1 2' hour to second; 198SELECT interval '1 2:03' hour to second; 199SELECT interval '1 2:03:04' hour to second; 200SELECT interval '1 2' minute to second; 201SELECT interval '1 2:03' minute to second; 202SELECT interval '1 2:03:04' minute to second; 203SELECT interval '1 +2:03' minute to second; 204SELECT interval '1 +2:03:04' minute to second; 205SELECT interval '1 -2:03' minute to second; 206SELECT interval '1 -2:03:04' minute to second; 207SELECT interval '123 11' day to hour; -- ok 208SELECT interval '123 11' day; -- not ok 209SELECT interval '123 11'; -- not ok, too ambiguous 210SELECT interval '123 2:03 -2:04'; -- not ok, redundant hh:mm fields 211 212-- test syntaxes for restricted precision 213SELECT interval(0) '1 day 01:23:45.6789'; 214SELECT interval(2) '1 day 01:23:45.6789'; 215SELECT interval '12:34.5678' minute to second(2); -- per SQL spec 216SELECT interval '1.234' second; 217SELECT interval '1.234' second(2); 218SELECT interval '1 2.345' day to second(2); 219SELECT interval '1 2:03' day to second(2); 220SELECT interval '1 2:03.4567' day to second(2); 221SELECT interval '1 2:03:04.5678' day to second(2); 222SELECT interval '1 2.345' hour to second(2); 223SELECT interval '1 2:03.45678' hour to second(2); 224SELECT interval '1 2:03:04.5678' hour to second(2); 225SELECT interval '1 2.3456' minute to second(2); 226SELECT interval '1 2:03.5678' minute to second(2); 227SELECT interval '1 2:03:04.5678' minute to second(2); 228 229-- test casting to restricted precision (bug #14479) 230SELECT f1, f1::INTERVAL DAY TO MINUTE AS "minutes", 231 (f1 + INTERVAL '1 month')::INTERVAL MONTH::INTERVAL YEAR AS "years" 232 FROM interval_tbl; 233 234-- test inputting and outputting SQL standard interval literals 235SET IntervalStyle TO sql_standard; 236SELECT interval '0' AS "zero", 237 interval '1-2' year to month AS "year-month", 238 interval '1 2:03:04' day to second AS "day-time", 239 - interval '1-2' AS "negative year-month", 240 - interval '1 2:03:04' AS "negative day-time"; 241 242-- test input of some not-quite-standard interval values in the sql style 243SET IntervalStyle TO postgres; 244SELECT interval '+1 -1:00:00', 245 interval '-1 +1:00:00', 246 interval '+1-2 -3 +4:05:06.789', 247 interval '-1-2 +3 -4:05:06.789'; 248 249-- test output of couple non-standard interval values in the sql style 250SET IntervalStyle TO sql_standard; 251SELECT interval '1 day -1 hours', 252 interval '-1 days +1 hours', 253 interval '1 years 2 months -3 days 4 hours 5 minutes 6.789 seconds', 254 - interval '1 years 2 months -3 days 4 hours 5 minutes 6.789 seconds'; 255 256-- test outputting iso8601 intervals 257SET IntervalStyle to iso_8601; 258select interval '0' AS "zero", 259 interval '1-2' AS "a year 2 months", 260 interval '1 2:03:04' AS "a bit over a day", 261 interval '2:03:04.45679' AS "a bit over 2 hours", 262 (interval '1-2' + interval '3 4:05:06.7') AS "all fields", 263 (interval '1-2' - interval '3 4:05:06.7') AS "mixed sign", 264 (- interval '1-2' + interval '3 4:05:06.7') AS "negative"; 265 266-- test inputting ISO 8601 4.4.2.1 "Format With Time Unit Designators" 267SET IntervalStyle to sql_standard; 268select interval 'P0Y' AS "zero", 269 interval 'P1Y2M' AS "a year 2 months", 270 interval 'P1W' AS "a week", 271 interval 'P1DT2H3M4S' AS "a bit over a day", 272 interval 'P1Y2M3DT4H5M6.7S' AS "all fields", 273 interval 'P-1Y-2M-3DT-4H-5M-6.7S' AS "negative", 274 interval 'PT-0.1S' AS "fractional second"; 275 276-- test inputting ISO 8601 4.4.2.2 "Alternative Format" 277SET IntervalStyle to postgres; 278select interval 'P00021015T103020' AS "ISO8601 Basic Format", 279 interval 'P0002-10-15T10:30:20' AS "ISO8601 Extended Format"; 280 281-- Make sure optional ISO8601 alternative format fields are optional. 282select interval 'P0002' AS "year only", 283 interval 'P0002-10' AS "year month", 284 interval 'P0002-10-15' AS "year month day", 285 interval 'P0002T1S' AS "year only plus time", 286 interval 'P0002-10T1S' AS "year month plus time", 287 interval 'P0002-10-15T1S' AS "year month day plus time", 288 interval 'PT10' AS "hour only", 289 interval 'PT10:30' AS "hour minute"; 290 291-- test a couple rounding cases that changed since 8.3 w/ HAVE_INT64_TIMESTAMP. 292SET IntervalStyle to postgres_verbose; 293select interval '-10 mons -3 days +03:55:06.70'; 294select interval '1 year 2 mons 3 days 04:05:06.699999'; 295select interval '0:0:0.7', interval '@ 0.70 secs', interval '0.7 seconds'; 296 297-- check that '30 days' equals '1 month' according to the hash function 298select '30 days'::interval = '1 month'::interval as t; 299select interval_hash('30 days'::interval) = interval_hash('1 month'::interval) as t; 300 301-- numeric constructor 302select make_interval(years := 2); 303select make_interval(years := 1, months := 6); 304select make_interval(years := 1, months := -1, weeks := 5, days := -7, hours := 25, mins := -180); 305 306select make_interval() = make_interval(years := 0, months := 0, weeks := 0, days := 0, mins := 0, secs := 0.0); 307select make_interval(hours := -2, mins := -10, secs := -25.3); 308 309select make_interval(years := 'inf'::float::int); 310select make_interval(months := 'NaN'::float::int); 311select make_interval(secs := 'inf'); 312select make_interval(secs := 'NaN'); 313select make_interval(secs := 7e12); 314