Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * date.c
4 : : * implements DATE and TIME data types specified in SQL standard
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994-5, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/utils/adt/date.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres.h"
17 : :
18 : : #include <ctype.h>
19 : : #include <limits.h>
20 : : #include <float.h>
21 : : #include <math.h>
22 : : #include <time.h>
23 : :
24 : : #include "access/xact.h"
25 : : #include "catalog/pg_type.h"
26 : : #include "common/hashfn.h"
27 : : #include "common/int.h"
28 : : #include "libpq/pqformat.h"
29 : : #include "miscadmin.h"
30 : : #include "nodes/miscnodes.h"
31 : : #include "nodes/supportnodes.h"
32 : : #include "parser/scansup.h"
33 : : #include "utils/array.h"
34 : : #include "utils/builtins.h"
35 : : #include "utils/date.h"
36 : : #include "utils/datetime.h"
37 : : #include "utils/numeric.h"
38 : : #include "utils/skipsupport.h"
39 : : #include "utils/sortsupport.h"
40 : :
41 : :
42 : : /* common code for timetypmodin and timetztypmodin */
43 : : static int32
1109 michael@paquier.xyz 44 :CBC 28 : anytime_typmodin(bool istz, ArrayType *ta)
45 : : {
46 : : int32 *tl;
47 : : int n;
48 : :
49 : 28 : tl = ArrayGetIntegerTypmods(ta, &n);
50 : :
51 : : /*
52 : : * we're not too tense about good error message here because grammar
53 : : * shouldn't allow wrong number of modifiers for TIME
54 : : */
55 [ - + ]: 28 : if (n != 1)
1109 michael@paquier.xyz 56 [ # # ]:UBC 0 : ereport(ERROR,
57 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
58 : : errmsg("invalid type modifier")));
59 : :
1109 michael@paquier.xyz 60 :CBC 28 : return anytime_typmod_check(istz, tl[0]);
61 : : }
62 : :
63 : : /* exported so parse_expr.c can use it */
64 : : int32
3574 tgl@sss.pgh.pa.us 65 : 300 : anytime_typmod_check(bool istz, int32 typmod)
66 : : {
67 [ - + ]: 300 : if (typmod < 0)
7091 tgl@sss.pgh.pa.us 68 [ # # # # ]:UBC 0 : ereport(ERROR,
69 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
70 : : errmsg("TIME(%d)%s precision must not be negative",
71 : : typmod, (istz ? " WITH TIME ZONE" : ""))));
3574 tgl@sss.pgh.pa.us 72 [ + + ]:CBC 300 : if (typmod > MAX_TIME_PRECISION)
73 : : {
7091 74 [ + - + + ]: 24 : ereport(WARNING,
75 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
76 : : errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
77 : : typmod, (istz ? " WITH TIME ZONE" : ""),
78 : : MAX_TIME_PRECISION)));
79 : 24 : typmod = MAX_TIME_PRECISION;
80 : : }
81 : :
82 : 300 : return typmod;
83 : : }
84 : :
85 : : /* common code for timetypmodout and timetztypmodout */
86 : : static char *
87 : 10 : anytime_typmodout(bool istz, int32 typmod)
88 : : {
89 [ + + ]: 10 : const char *tz = istz ? " with time zone" : " without time zone";
90 : :
91 [ + - ]: 10 : if (typmod >= 0)
4527 peter_e@gmx.net 92 : 10 : return psprintf("(%d)%s", (int) typmod, tz);
93 : : else
1362 drowley@postgresql.o 94 :UBC 0 : return pstrdup(tz);
95 : : }
96 : :
97 : :
98 : : /*****************************************************************************
99 : : * Date ADT
100 : : *****************************************************************************/
101 : :
102 : :
103 : : /*
104 : : * date_in()
105 : : * Given date text string, convert to internal date format.
106 : : */
107 : : Datum
9486 tgl@sss.pgh.pa.us 108 :CBC 11481 : date_in(PG_FUNCTION_ARGS)
109 : : {
110 : 11481 : char *str = PG_GETARG_CSTRING(0);
1268 111 : 11481 : Node *escontext = fcinfo->context;
112 : : DateADT date;
113 : : fsec_t fsec;
114 : : struct pg_tm tt,
10491 bruce@momjian.us 115 : 11481 : *tm = &tt;
116 : : int tzp;
117 : : int dtype;
118 : : int nf;
119 : : int dterr;
120 : : char *field[MAXDATEFIELDS];
121 : : int ftype[MAXDATEFIELDS];
122 : : char workbuf[MAXDATELEN + 1];
123 : : DateTimeErrorExtra extra;
124 : :
7674 neilc@samurai.com 125 : 11481 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
126 : : field, ftype, MAXDATEFIELDS, &nf);
8312 tgl@sss.pgh.pa.us 127 [ + - ]: 11481 : if (dterr == 0)
1268 128 : 11481 : dterr = DecodeDateTime(field, ftype, nf,
129 : : &dtype, tm, &fsec, &tzp, &extra);
8312 130 [ + + ]: 11481 : if (dterr != 0)
131 : : {
1268 132 : 196 : DateTimeParseError(dterr, &extra, str, "date", escontext);
133 : 8 : PG_RETURN_NULL();
134 : : }
135 : :
10492 bruce@momjian.us 136 [ + + + + : 11285 : switch (dtype)
- ]
137 : : {
9600 lockhart@fourpalms.o 138 : 11049 : case DTK_DATE:
139 : 11049 : break;
140 : :
141 : 4 : case DTK_EPOCH:
9010 142 : 4 : GetEpochTime(tm);
9600 143 : 4 : break;
144 : :
6437 tgl@sss.pgh.pa.us 145 : 150 : case DTK_LATE:
146 : 150 : DATE_NOEND(date);
147 : 150 : PG_RETURN_DATEADT(date);
148 : :
149 : 82 : case DTK_EARLY:
150 : 82 : DATE_NOBEGIN(date);
151 : 82 : PG_RETURN_DATEADT(date);
152 : :
10491 bruce@momjian.us 153 :UBC 0 : default:
1268 tgl@sss.pgh.pa.us 154 : 0 : DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date", escontext);
155 : 0 : PG_RETURN_NULL();
156 : : }
157 : :
158 : : /* Prevent overflow in Julian-day routines */
7415 tgl@sss.pgh.pa.us 159 :CBC 11053 : if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
[ + + + -
+ - + + +
+ - + ]
1268 160 [ + + ]: 8 : ereturn(escontext, (Datum) 0,
161 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
162 : : errmsg("date out of range: \"%s\"", str)));
163 : :
8457 164 : 11045 : date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
165 : :
166 : : /* Now check for just-out-of-range dates */
3727 167 [ + + + + ]: 11045 : if (!IS_VALID_DATE(date))
1268 168 [ + - ]: 8 : ereturn(escontext, (Datum) 0,
169 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
170 : : errmsg("date out of range: \"%s\"", str)));
171 : :
9486 172 : 11037 : PG_RETURN_DATEADT(date);
173 : : }
174 : :
175 : : /*
176 : : * date_out()
177 : : * Given internal format date, convert to text string.
178 : : */
179 : : Datum
180 : 13829 : date_out(PG_FUNCTION_ARGS)
181 : : {
7532 bruce@momjian.us 182 : 13829 : DateADT date = PG_GETARG_DATEADT(0);
183 : : char *result;
184 : : struct pg_tm tt,
10491 185 : 13829 : *tm = &tt;
186 : : char buf[MAXDATELEN + 1];
187 : :
6437 tgl@sss.pgh.pa.us 188 [ + + + + ]: 13829 : if (DATE_NOT_FINITE(date))
189 : 124 : EncodeSpecialDate(date, buf);
190 : : else
191 : : {
192 : 13705 : j2date(date + POSTGRES_EPOCH_JDATE,
193 : : &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
194 : 13705 : EncodeDateOnly(tm, DateStyle, buf);
195 : : }
196 : :
9486 197 : 13829 : result = pstrdup(buf);
198 : 13829 : PG_RETURN_CSTRING(result);
199 : : }
200 : :
201 : : /*
202 : : * date_recv - converts external binary format to date
203 : : */
204 : : Datum
8419 tgl@sss.pgh.pa.us 205 :UBC 0 : date_recv(PG_FUNCTION_ARGS)
206 : : {
207 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
208 : : DateADT result;
209 : :
6112 heikki.linnakangas@i 210 : 0 : result = (DateADT) pq_getmsgint(buf, sizeof(DateADT));
211 : :
212 : : /* Limit to the same range that date_in() accepts. */
5945 itagaki.takahiro@gma 213 [ # # # # ]: 0 : if (DATE_NOT_FINITE(result))
214 : : /* ok */ ;
3727 tgl@sss.pgh.pa.us 215 [ # # # # ]: 0 : else if (!IS_VALID_DATE(result))
6112 heikki.linnakangas@i 216 [ # # ]: 0 : ereport(ERROR,
217 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
218 : : errmsg("date out of range")));
219 : :
220 : 0 : PG_RETURN_DATEADT(result);
221 : : }
222 : :
223 : : /*
224 : : * date_send - converts date to binary format
225 : : */
226 : : Datum
8419 tgl@sss.pgh.pa.us 227 : 0 : date_send(PG_FUNCTION_ARGS)
228 : : {
7532 bruce@momjian.us 229 : 0 : DateADT date = PG_GETARG_DATEADT(0);
230 : : StringInfoData buf;
231 : :
8419 tgl@sss.pgh.pa.us 232 : 0 : pq_begintypsend(&buf);
3153 andres@anarazel.de 233 : 0 : pq_sendint32(&buf, date);
8419 tgl@sss.pgh.pa.us 234 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
235 : : }
236 : :
237 : : /*
238 : : * make_date - date constructor
239 : : */
240 : : Datum
4577 tgl@sss.pgh.pa.us 241 :CBC 30 : make_date(PG_FUNCTION_ARGS)
242 : : {
243 : : struct pg_tm tm;
244 : : DateADT date;
245 : : int dterr;
3418 alvherre@alvh.no-ip. 246 : 30 : bool bc = false;
247 : :
4577 tgl@sss.pgh.pa.us 248 : 30 : tm.tm_year = PG_GETARG_INT32(0);
249 : 30 : tm.tm_mon = PG_GETARG_INT32(1);
250 : 30 : tm.tm_mday = PG_GETARG_INT32(2);
251 : :
252 : : /* Handle negative years as BC */
3418 alvherre@alvh.no-ip. 253 [ + + ]: 30 : if (tm.tm_year < 0)
254 : : {
537 nathan@postgresql.or 255 : 9 : int year = tm.tm_year;
256 : :
3418 alvherre@alvh.no-ip. 257 : 9 : bc = true;
537 nathan@postgresql.or 258 [ + + ]: 9 : if (pg_neg_s32_overflow(year, &year))
259 [ + - ]: 4 : ereport(ERROR,
260 : : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
261 : : errmsg("date field value out of range: %d-%02d-%02d",
262 : : tm.tm_year, tm.tm_mon, tm.tm_mday)));
263 : 5 : tm.tm_year = year;
264 : : }
265 : :
3418 alvherre@alvh.no-ip. 266 : 26 : dterr = ValidateDate(DTK_DATE_M, false, false, bc, &tm);
267 : :
4577 tgl@sss.pgh.pa.us 268 [ + + ]: 26 : if (dterr != 0)
269 [ + - ]: 16 : ereport(ERROR,
270 : : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
271 : : errmsg("date field value out of range: %d-%02d-%02d",
272 : : tm.tm_year, tm.tm_mon, tm.tm_mday)));
273 : :
274 : : /* Prevent overflow in Julian-day routines */
275 : 10 : if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
[ - + - -
- - - + -
- - - ]
4577 tgl@sss.pgh.pa.us 276 [ # # ]:UBC 0 : ereport(ERROR,
277 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
278 : : errmsg("date out of range: %d-%02d-%02d",
279 : : tm.tm_year, tm.tm_mon, tm.tm_mday)));
280 : :
4577 tgl@sss.pgh.pa.us 281 :CBC 10 : date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
282 : :
283 : : /* Now check for just-out-of-range dates */
3727 284 [ + - - + ]: 10 : if (!IS_VALID_DATE(date))
3727 tgl@sss.pgh.pa.us 285 [ # # ]:UBC 0 : ereport(ERROR,
286 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
287 : : errmsg("date out of range: %d-%02d-%02d",
288 : : tm.tm_year, tm.tm_mon, tm.tm_mday)));
289 : :
4577 tgl@sss.pgh.pa.us 290 :CBC 10 : PG_RETURN_DATEADT(date);
291 : : }
292 : :
293 : : /*
294 : : * Convert reserved date values to string.
295 : : */
296 : : void
6437 297 : 140 : EncodeSpecialDate(DateADT dt, char *str)
298 : : {
299 [ + + ]: 140 : if (DATE_IS_NOBEGIN(dt))
300 : 70 : strcpy(str, EARLY);
301 [ + - ]: 70 : else if (DATE_IS_NOEND(dt))
302 : 70 : strcpy(str, LATE);
303 : : else /* shouldn't happen */
6437 tgl@sss.pgh.pa.us 304 [ # # ]:UBC 0 : elog(ERROR, "invalid argument for EncodeSpecialDate");
6437 tgl@sss.pgh.pa.us 305 :CBC 140 : }
306 : :
307 : :
308 : : /*
309 : : * GetSQLCurrentDate -- implements CURRENT_DATE
310 : : */
311 : : DateADT
1109 michael@paquier.xyz 312 : 99 : GetSQLCurrentDate(void)
313 : : {
314 : : struct pg_tm tm;
315 : :
316 : : static int cache_year = 0;
317 : : static int cache_mon = 0;
318 : : static int cache_mday = 0;
319 : : static DateADT cache_date;
320 : :
2070 tgl@sss.pgh.pa.us 321 : 99 : GetCurrentDateTime(&tm);
322 : :
323 : : /*
324 : : * date2j involves several integer divisions; moreover, unless our session
325 : : * lives across local midnight, we don't really have to do it more than
326 : : * once. So it seems worth having a separate cache here.
327 : : */
328 [ + + ]: 99 : if (tm.tm_year != cache_year ||
329 [ + - ]: 74 : tm.tm_mon != cache_mon ||
330 [ - + ]: 74 : tm.tm_mday != cache_mday)
331 : : {
332 : 25 : cache_date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
333 : 25 : cache_year = tm.tm_year;
334 : 25 : cache_mon = tm.tm_mon;
335 : 25 : cache_mday = tm.tm_mday;
336 : : }
337 : :
1109 michael@paquier.xyz 338 : 99 : return cache_date;
339 : : }
340 : :
341 : : /*
342 : : * GetSQLCurrentTime -- implements CURRENT_TIME, CURRENT_TIME(n)
343 : : */
344 : : TimeTzADT *
345 : 16 : GetSQLCurrentTime(int32 typmod)
346 : : {
347 : : TimeTzADT *result;
348 : : struct pg_tm tt,
3574 tgl@sss.pgh.pa.us 349 : 16 : *tm = &tt;
350 : : fsec_t fsec;
351 : : int tz;
352 : :
2070 353 : 16 : GetCurrentTimeUsec(tm, &fsec, &tz);
354 : :
171 michael@paquier.xyz 355 :GNC 16 : result = palloc_object(TimeTzADT);
3574 tgl@sss.pgh.pa.us 356 :CBC 16 : tm2timetz(tm, fsec, tz, result);
357 : 16 : AdjustTimeForTypmod(&(result->time), typmod);
1109 michael@paquier.xyz 358 : 16 : return result;
359 : : }
360 : :
361 : : /*
362 : : * GetSQLLocalTime -- implements LOCALTIME, LOCALTIME(n)
363 : : */
364 : : TimeADT
365 : 16 : GetSQLLocalTime(int32 typmod)
366 : : {
367 : : TimeADT result;
368 : : struct pg_tm tt,
3574 tgl@sss.pgh.pa.us 369 : 16 : *tm = &tt;
370 : : fsec_t fsec;
371 : : int tz;
372 : :
2070 373 : 16 : GetCurrentTimeUsec(tm, &fsec, &tz);
374 : :
3574 375 : 16 : tm2time(tm, fsec, &result);
376 : 16 : AdjustTimeForTypmod(&result, typmod);
1109 michael@paquier.xyz 377 : 16 : return result;
378 : : }
379 : :
380 : :
381 : : /*
382 : : * Comparison functions for dates
383 : : */
384 : :
385 : : Datum
9486 tgl@sss.pgh.pa.us 386 : 42403 : date_eq(PG_FUNCTION_ARGS)
387 : : {
388 : 42403 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
389 : 42403 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
390 : :
391 : 42403 : PG_RETURN_BOOL(dateVal1 == dateVal2);
392 : : }
393 : :
394 : : Datum
9486 tgl@sss.pgh.pa.us 395 :GBC 348 : date_ne(PG_FUNCTION_ARGS)
396 : : {
397 : 348 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
398 : 348 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
399 : :
400 : 348 : PG_RETURN_BOOL(dateVal1 != dateVal2);
401 : : }
402 : :
403 : : Datum
9486 tgl@sss.pgh.pa.us 404 :CBC 105413 : date_lt(PG_FUNCTION_ARGS)
405 : : {
406 : 105413 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
407 : 105413 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
408 : :
409 : 105413 : PG_RETURN_BOOL(dateVal1 < dateVal2);
410 : : }
411 : :
412 : : Datum
413 : 4141 : date_le(PG_FUNCTION_ARGS)
414 : : {
415 : 4141 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
416 : 4141 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
417 : :
418 : 4141 : PG_RETURN_BOOL(dateVal1 <= dateVal2);
419 : : }
420 : :
421 : : Datum
422 : 5422 : date_gt(PG_FUNCTION_ARGS)
423 : : {
424 : 5422 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
425 : 5422 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
426 : :
427 : 5422 : PG_RETURN_BOOL(dateVal1 > dateVal2);
428 : : }
429 : :
430 : : Datum
431 : 4824 : date_ge(PG_FUNCTION_ARGS)
432 : : {
433 : 4824 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
434 : 4824 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
435 : :
436 : 4824 : PG_RETURN_BOOL(dateVal1 >= dateVal2);
437 : : }
438 : :
439 : : Datum
440 : 71808 : date_cmp(PG_FUNCTION_ARGS)
441 : : {
442 : 71808 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
443 : 71808 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
444 : :
9600 lockhart@fourpalms.o 445 [ + + ]: 71808 : if (dateVal1 < dateVal2)
9486 tgl@sss.pgh.pa.us 446 : 40993 : PG_RETURN_INT32(-1);
9600 lockhart@fourpalms.o 447 [ + + ]: 30815 : else if (dateVal1 > dateVal2)
9486 tgl@sss.pgh.pa.us 448 : 22716 : PG_RETURN_INT32(1);
449 : 8099 : PG_RETURN_INT32(0);
450 : : }
451 : :
452 : : Datum
5288 453 : 592 : date_sortsupport(PG_FUNCTION_ARGS)
454 : : {
5102 bruce@momjian.us 455 : 592 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
456 : :
1519 john.naylor@postgres 457 : 592 : ssup->comparator = ssup_datum_int32_cmp;
5288 tgl@sss.pgh.pa.us 458 : 592 : PG_RETURN_VOID();
459 : : }
460 : :
461 : : static Datum
421 pg@bowt.ie 462 :UBC 0 : date_decrement(Relation rel, Datum existing, bool *underflow)
463 : : {
464 : 0 : DateADT dexisting = DatumGetDateADT(existing);
465 : :
466 [ # # ]: 0 : if (dexisting == DATEVAL_NOBEGIN)
467 : : {
468 : : /* return value is undefined */
469 : 0 : *underflow = true;
470 : 0 : return (Datum) 0;
471 : : }
472 : :
473 : 0 : *underflow = false;
474 : 0 : return DateADTGetDatum(dexisting - 1);
475 : : }
476 : :
477 : : static Datum
478 : 0 : date_increment(Relation rel, Datum existing, bool *overflow)
479 : : {
480 : 0 : DateADT dexisting = DatumGetDateADT(existing);
481 : :
482 [ # # ]: 0 : if (dexisting == DATEVAL_NOEND)
483 : : {
484 : : /* return value is undefined */
485 : 0 : *overflow = true;
486 : 0 : return (Datum) 0;
487 : : }
488 : :
489 : 0 : *overflow = false;
490 : 0 : return DateADTGetDatum(dexisting + 1);
491 : : }
492 : :
493 : : Datum
494 : 0 : date_skipsupport(PG_FUNCTION_ARGS)
495 : : {
496 : 0 : SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
497 : :
498 : 0 : sksup->decrement = date_decrement;
499 : 0 : sksup->increment = date_increment;
500 : 0 : sksup->low_elem = DateADTGetDatum(DATEVAL_NOBEGIN);
501 : 0 : sksup->high_elem = DateADTGetDatum(DATEVAL_NOEND);
502 : :
503 : 0 : PG_RETURN_VOID();
504 : : }
505 : :
506 : : Datum
625 peter@eisentraut.org 507 :CBC 152 : hashdate(PG_FUNCTION_ARGS)
508 : : {
509 : 152 : return hash_uint32(PG_GETARG_DATEADT(0));
510 : : }
511 : :
512 : : Datum
625 peter@eisentraut.org 513 :UBC 0 : hashdateextended(PG_FUNCTION_ARGS)
514 : : {
515 : 0 : return hash_uint32_extended(PG_GETARG_DATEADT(0), PG_GETARG_INT64(1));
516 : : }
517 : :
518 : : Datum
6437 tgl@sss.pgh.pa.us 519 :CBC 15 : date_finite(PG_FUNCTION_ARGS)
520 : : {
521 : 15 : DateADT date = PG_GETARG_DATEADT(0);
522 : :
523 [ + + + + ]: 15 : PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
524 : : }
525 : :
526 : : Datum
9486 527 : 8 : date_larger(PG_FUNCTION_ARGS)
528 : : {
529 : 8 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
530 : 8 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
531 : :
532 : 8 : PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
533 : : }
534 : :
535 : : Datum
9486 tgl@sss.pgh.pa.us 536 :UBC 0 : date_smaller(PG_FUNCTION_ARGS)
537 : : {
538 : 0 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
539 : 0 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
540 : :
541 : 0 : PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
542 : : }
543 : :
544 : : /*
545 : : * Compute difference between two dates in days.
546 : : */
547 : : Datum
9486 tgl@sss.pgh.pa.us 548 :CBC 978 : date_mi(PG_FUNCTION_ARGS)
549 : : {
550 : 978 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
551 : 978 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
552 : :
6437 553 [ + - + - : 978 : if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
+ - - + ]
6437 tgl@sss.pgh.pa.us 554 [ # # ]:UBC 0 : ereport(ERROR,
555 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
556 : : errmsg("cannot subtract infinite dates")));
557 : :
9486 tgl@sss.pgh.pa.us 558 :CBC 978 : PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
559 : : }
560 : :
561 : : /*
562 : : * Add a number of days to a date, giving a new date.
563 : : * Must handle both positive and negative numbers of days.
564 : : */
565 : : Datum
566 : 1790 : date_pli(PG_FUNCTION_ARGS)
567 : : {
568 : 1790 : DateADT dateVal = PG_GETARG_DATEADT(0);
569 : 1790 : int32 days = PG_GETARG_INT32(1);
570 : : DateADT result;
571 : :
6437 572 [ + - - + ]: 1790 : if (DATE_NOT_FINITE(dateVal))
3265 tgl@sss.pgh.pa.us 573 :UBC 0 : PG_RETURN_DATEADT(dateVal); /* can't change infinity */
574 : :
3727 tgl@sss.pgh.pa.us 575 :CBC 1790 : result = dateVal + days;
576 : :
577 : : /* Check for integer overflow and out-of-allowed-range */
578 [ + - + - : 1790 : if ((days >= 0 ? (result < dateVal) : (result > dateVal)) ||
- - + - ]
579 [ - + ]: 1790 : !IS_VALID_DATE(result))
3727 tgl@sss.pgh.pa.us 580 [ # # ]:UBC 0 : ereport(ERROR,
581 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
582 : : errmsg("date out of range")));
583 : :
3727 tgl@sss.pgh.pa.us 584 :CBC 1790 : PG_RETURN_DATEADT(result);
585 : : }
586 : :
587 : : /*
588 : : * Subtract a number of days from a date, giving a new date.
589 : : */
590 : : Datum
9486 591 : 42 : date_mii(PG_FUNCTION_ARGS)
592 : : {
593 : 42 : DateADT dateVal = PG_GETARG_DATEADT(0);
594 : 42 : int32 days = PG_GETARG_INT32(1);
595 : : DateADT result;
596 : :
6437 597 [ + - - + ]: 42 : if (DATE_NOT_FINITE(dateVal))
3265 tgl@sss.pgh.pa.us 598 :UBC 0 : PG_RETURN_DATEADT(dateVal); /* can't change infinity */
599 : :
3727 tgl@sss.pgh.pa.us 600 :CBC 42 : result = dateVal - days;
601 : :
602 : : /* Check for integer overflow and out-of-allowed-range */
603 [ + - + - : 42 : if ((days >= 0 ? (result > dateVal) : (result < dateVal)) ||
- - + - ]
604 [ - + ]: 42 : !IS_VALID_DATE(result))
3727 tgl@sss.pgh.pa.us 605 [ # # ]:UBC 0 : ereport(ERROR,
606 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
607 : : errmsg("date out of range")));
608 : :
3727 tgl@sss.pgh.pa.us 609 :CBC 42 : PG_RETURN_DATEADT(result);
610 : : }
611 : :
612 : :
613 : : /*
614 : : * Promote date to timestamp.
615 : : *
616 : : * If the date falls out of the valid range for the timestamp type, error
617 : : * handling proceeds based on escontext.
618 : : *
619 : : * If escontext is NULL, we throw an out-of-range error (hard error).
620 : : * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or
621 : : * upper bound overflow, respectively, and record a soft error.
622 : : *
623 : : * Note: Lower bound overflow is currently not possible, as both date and
624 : : * timestamp datatypes share the same lower boundary: Julian day zero.
625 : : */
626 : : Timestamp
180 michael@paquier.xyz 627 :GNC 3137 : date2timestamp_safe(DateADT dateVal, Node *escontext)
628 : : {
629 : : Timestamp result;
630 : :
6437 tgl@sss.pgh.pa.us 631 [ + + ]:CBC 3137 : if (DATE_IS_NOBEGIN(dateVal))
632 : 54 : TIMESTAMP_NOBEGIN(result);
633 [ + + ]: 3083 : else if (DATE_IS_NOEND(dateVal))
634 : 52 : TIMESTAMP_NOEND(result);
635 : : else
636 : : {
637 : : /*
638 : : * Since dates have the same minimum values as timestamps, only upper
639 : : * boundary need be checked for overflow.
640 : : */
3727 641 [ + + ]: 3031 : if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
642 : : {
180 michael@paquier.xyz 643 :GNC 18 : TIMESTAMP_NOEND(result);
644 [ + + ]: 18 : ereturn(escontext, result,
645 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
646 : : errmsg("date out of range for timestamp")));
647 : : }
648 : :
649 : : /* date is days since 2000, timestamp is microseconds since same... */
3727 tgl@sss.pgh.pa.us 650 :CBC 3013 : result = dateVal * USECS_PER_DAY;
651 : : }
652 : :
6821 653 : 3119 : return result;
654 : : }
655 : :
656 : : /*
657 : : * Promote date to timestamp, throwing error for overflow.
658 : : */
659 : : static TimestampTz
2439 akorotkov@postgresql 660 : 1880 : date2timestamp(DateADT dateVal)
661 : : {
180 michael@paquier.xyz 662 :GNC 1880 : return date2timestamp_safe(dateVal, NULL);
663 : : }
664 : :
665 : : /*
666 : : * Promote date to timestamp with time zone.
667 : : *
668 : : * If the date falls out of the valid range for the timestamp type, error
669 : : * handling proceeds based on escontext.
670 : : *
671 : : * If escontext is NULL, we throw an out-of-range error (hard error).
672 : : * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or
673 : : * upper bound overflow, respectively, and record a soft error.
674 : : */
675 : : TimestampTz
676 : 356 : date2timestamptz_safe(DateADT dateVal, Node *escontext)
677 : : {
678 : : TimestampTz result;
679 : : struct pg_tm tt,
8141 tgl@sss.pgh.pa.us 680 :CBC 356 : *tm = &tt;
681 : : int tz;
682 : :
6437 683 [ + + ]: 356 : if (DATE_IS_NOBEGIN(dateVal))
6437 tgl@sss.pgh.pa.us 684 :GBC 9 : TIMESTAMP_NOBEGIN(result);
6437 tgl@sss.pgh.pa.us 685 [ + + ]:CBC 347 : else if (DATE_IS_NOEND(dateVal))
6437 tgl@sss.pgh.pa.us 686 :GBC 9 : TIMESTAMP_NOEND(result);
687 : : else
688 : : {
689 : : /*
690 : : * Since dates have the same minimum values as timestamps, only upper
691 : : * boundary need be checked for overflow.
692 : : */
3727 tgl@sss.pgh.pa.us 693 [ + + ]:CBC 338 : if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
694 : : {
180 michael@paquier.xyz 695 :GNC 12 : TIMESTAMP_NOEND(result);
696 [ + + ]: 12 : ereturn(escontext, result,
697 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
698 : : errmsg("date out of range for timestamp")));
699 : : }
700 : :
6437 tgl@sss.pgh.pa.us 701 :CBC 326 : j2date(dateVal + POSTGRES_EPOCH_JDATE,
702 : : &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
703 : 326 : tm->tm_hour = 0;
704 : 326 : tm->tm_min = 0;
705 : 326 : tm->tm_sec = 0;
706 : 326 : tz = DetermineTimeZoneOffset(tm, session_timezone);
707 : :
708 : 326 : result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
709 : :
710 : : /*
711 : : * Since it is possible to go beyond allowed timestamptz range because
712 : : * of time zone, check for allowed timestamp range after adding tz.
713 : : */
3727 714 [ + + - + ]: 326 : if (!IS_VALID_TIMESTAMP(result))
715 : : {
180 michael@paquier.xyz 716 [ + - ]:GNC 12 : if (result < MIN_TIMESTAMP)
717 : 12 : TIMESTAMP_NOBEGIN(result);
718 : : else
180 michael@paquier.xyz 719 :UNC 0 : TIMESTAMP_NOEND(result);
720 : :
180 michael@paquier.xyz 721 [ + + ]:GNC 12 : ereturn(escontext, result,
722 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
723 : : errmsg("date out of range for timestamp")));
724 : : }
725 : : }
726 : :
8141 tgl@sss.pgh.pa.us 727 :CBC 332 : return result;
728 : : }
729 : :
730 : : /*
731 : : * date2timestamp_no_overflow
732 : : *
733 : : * This is chartered to produce a double value that is numerically
734 : : * equivalent to the corresponding Timestamp value, if the date is in the
735 : : * valid range of Timestamps, but in any case not throw an overflow error.
736 : : * We can do this since the numerical range of double is greater than
737 : : * that of non-erroneous timestamps. The results are currently only
738 : : * used for statistical estimation purposes.
739 : : */
740 : : double
5632 tgl@sss.pgh.pa.us 741 :UBC 0 : date2timestamp_no_overflow(DateADT dateVal)
742 : : {
743 : : double result;
744 : :
745 [ # # ]: 0 : if (DATE_IS_NOBEGIN(dateVal))
746 : 0 : result = -DBL_MAX;
747 [ # # ]: 0 : else if (DATE_IS_NOEND(dateVal))
748 : 0 : result = DBL_MAX;
749 : : else
750 : : {
751 : : /* date is days since 2000, timestamp is microseconds since same... */
752 : 0 : result = dateVal * (double) USECS_PER_DAY;
753 : : }
754 : :
755 : 0 : return result;
756 : : }
757 : :
758 : :
759 : : /*
760 : : * Crosstype comparison functions for dates
761 : : */
762 : :
763 : : int32
2061 tgl@sss.pgh.pa.us 764 :CBC 267 : date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2)
765 : : {
766 : : Timestamp dt1;
180 michael@paquier.xyz 767 :GNC 267 : ErrorSaveContext escontext = {T_ErrorSaveContext};
768 : :
769 : 267 : dt1 = date2timestamp_safe(dateVal, (Node *) &escontext);
770 [ + + ]: 267 : if (escontext.error_occurred)
771 : : {
772 [ - + ]: 14 : Assert(TIMESTAMP_IS_NOEND(dt1)); /* NOBEGIN case cannot occur */
773 : :
774 : : /* dt1 is larger than any finite timestamp, but less than infinity */
2061 tgl@sss.pgh.pa.us 775 [ - + ]:CBC 14 : return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
776 : : }
777 : :
778 : 253 : return timestamp_cmp_internal(dt1, dt2);
779 : : }
780 : :
781 : : Datum
8141 tgl@sss.pgh.pa.us 782 :UBC 0 : date_eq_timestamp(PG_FUNCTION_ARGS)
783 : : {
784 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
785 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
786 : :
2061 787 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) == 0);
788 : : }
789 : :
790 : : Datum
8141 791 : 0 : date_ne_timestamp(PG_FUNCTION_ARGS)
792 : : {
793 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
794 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
795 : :
2061 796 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) != 0);
797 : : }
798 : :
799 : : Datum
8141 800 : 0 : date_lt_timestamp(PG_FUNCTION_ARGS)
801 : : {
802 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
803 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
804 : :
2061 805 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) < 0);
806 : : }
807 : :
808 : : Datum
8141 tgl@sss.pgh.pa.us 809 :CBC 5 : date_gt_timestamp(PG_FUNCTION_ARGS)
810 : : {
811 : 5 : DateADT dateVal = PG_GETARG_DATEADT(0);
812 : 5 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
813 : :
2061 814 : 5 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) > 0);
815 : : }
816 : :
817 : : Datum
8141 tgl@sss.pgh.pa.us 818 :UBC 0 : date_le_timestamp(PG_FUNCTION_ARGS)
819 : : {
820 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
821 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
822 : :
2061 823 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) <= 0);
824 : : }
825 : :
826 : : Datum
8141 827 : 0 : date_ge_timestamp(PG_FUNCTION_ARGS)
828 : : {
829 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
830 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
831 : :
2061 832 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) >= 0);
833 : : }
834 : :
835 : : Datum
8141 tgl@sss.pgh.pa.us 836 :GBC 55 : date_cmp_timestamp(PG_FUNCTION_ARGS)
837 : : {
838 : 55 : DateADT dateVal = PG_GETARG_DATEADT(0);
839 : 55 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
840 : :
2061 841 : 55 : PG_RETURN_INT32(date_cmp_timestamp_internal(dateVal, dt2));
842 : : }
843 : :
844 : : int32
2061 tgl@sss.pgh.pa.us 845 :CBC 205 : date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2)
846 : : {
847 : : TimestampTz dt1;
180 michael@paquier.xyz 848 :GNC 205 : ErrorSaveContext escontext = {T_ErrorSaveContext};
849 : :
850 : 205 : dt1 = date2timestamptz_safe(dateVal, (Node *) &escontext);
851 : :
852 [ + + ]: 205 : if (escontext.error_occurred)
853 : : {
854 [ + + ]: 16 : if (TIMESTAMP_IS_NOEND(dt1))
855 : : {
856 : : /* dt1 is larger than any finite timestamp, but less than infinity */
857 [ - + ]: 8 : return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
858 : : }
859 [ + - ]: 8 : if (TIMESTAMP_IS_NOBEGIN(dt1))
860 : : {
861 : : /* dt1 is less than any finite timestamp, but more than -infinity */
862 [ - + ]: 8 : return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1;
863 : : }
864 : : }
865 : :
2061 tgl@sss.pgh.pa.us 866 :CBC 189 : return timestamptz_cmp_internal(dt1, dt2);
867 : : }
868 : :
869 : : Datum
8141 tgl@sss.pgh.pa.us 870 :UBC 0 : date_eq_timestamptz(PG_FUNCTION_ARGS)
871 : : {
872 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
7944 bruce@momjian.us 873 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
874 : :
2061 tgl@sss.pgh.pa.us 875 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) == 0);
876 : : }
877 : :
878 : : Datum
8141 879 : 0 : date_ne_timestamptz(PG_FUNCTION_ARGS)
880 : : {
881 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
7944 bruce@momjian.us 882 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
883 : :
2061 tgl@sss.pgh.pa.us 884 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) != 0);
885 : : }
886 : :
887 : : Datum
8141 tgl@sss.pgh.pa.us 888 :CBC 4 : date_lt_timestamptz(PG_FUNCTION_ARGS)
889 : : {
890 : 4 : DateADT dateVal = PG_GETARG_DATEADT(0);
7944 bruce@momjian.us 891 : 4 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
892 : :
2061 tgl@sss.pgh.pa.us 893 : 4 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) < 0);
894 : : }
895 : :
896 : : Datum
8141 897 : 4 : date_gt_timestamptz(PG_FUNCTION_ARGS)
898 : : {
899 : 4 : DateADT dateVal = PG_GETARG_DATEADT(0);
7944 bruce@momjian.us 900 : 4 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
901 : :
2061 tgl@sss.pgh.pa.us 902 : 4 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) > 0);
903 : : }
904 : :
905 : : Datum
8141 tgl@sss.pgh.pa.us 906 :UBC 0 : date_le_timestamptz(PG_FUNCTION_ARGS)
907 : : {
908 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
7944 bruce@momjian.us 909 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
910 : :
2061 tgl@sss.pgh.pa.us 911 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) <= 0);
912 : : }
913 : :
914 : : Datum
8141 915 : 0 : date_ge_timestamptz(PG_FUNCTION_ARGS)
916 : : {
917 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
7944 bruce@momjian.us 918 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
919 : :
2061 tgl@sss.pgh.pa.us 920 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) >= 0);
921 : : }
922 : :
923 : : Datum
8141 tgl@sss.pgh.pa.us 924 :GBC 15 : date_cmp_timestamptz(PG_FUNCTION_ARGS)
925 : : {
926 : 15 : DateADT dateVal = PG_GETARG_DATEADT(0);
7944 bruce@momjian.us 927 : 15 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
928 : :
2061 tgl@sss.pgh.pa.us 929 : 15 : PG_RETURN_INT32(date_cmp_timestamptz_internal(dateVal, dt2));
930 : : }
931 : :
932 : : Datum
8141 tgl@sss.pgh.pa.us 933 :UBC 0 : timestamp_eq_date(PG_FUNCTION_ARGS)
934 : : {
935 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
936 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
937 : :
2061 938 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) == 0);
939 : : }
940 : :
941 : : Datum
8141 942 : 0 : timestamp_ne_date(PG_FUNCTION_ARGS)
943 : : {
944 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
945 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
946 : :
2061 947 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) != 0);
948 : : }
949 : :
950 : : Datum
8141 951 : 0 : timestamp_lt_date(PG_FUNCTION_ARGS)
952 : : {
953 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
954 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
955 : :
2061 956 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) > 0);
957 : : }
958 : :
959 : : Datum
8141 tgl@sss.pgh.pa.us 960 :CBC 5 : timestamp_gt_date(PG_FUNCTION_ARGS)
961 : : {
962 : 5 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
963 : 5 : DateADT dateVal = PG_GETARG_DATEADT(1);
964 : :
2061 965 : 5 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) < 0);
966 : : }
967 : :
968 : : Datum
8141 tgl@sss.pgh.pa.us 969 :UBC 0 : timestamp_le_date(PG_FUNCTION_ARGS)
970 : : {
971 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
972 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
973 : :
2061 974 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) >= 0);
975 : : }
976 : :
977 : : Datum
8141 978 : 0 : timestamp_ge_date(PG_FUNCTION_ARGS)
979 : : {
980 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
981 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
982 : :
2061 983 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) <= 0);
984 : : }
985 : :
986 : : Datum
8141 tgl@sss.pgh.pa.us 987 :GBC 78 : timestamp_cmp_date(PG_FUNCTION_ARGS)
988 : : {
989 : 78 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
990 : 78 : DateADT dateVal = PG_GETARG_DATEADT(1);
991 : :
2061 992 : 78 : PG_RETURN_INT32(-date_cmp_timestamp_internal(dateVal, dt1));
993 : : }
994 : :
995 : : Datum
8141 tgl@sss.pgh.pa.us 996 :UBC 0 : timestamptz_eq_date(PG_FUNCTION_ARGS)
997 : : {
7944 bruce@momjian.us 998 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
8141 tgl@sss.pgh.pa.us 999 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1000 : :
2061 1001 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) == 0);
1002 : : }
1003 : :
1004 : : Datum
8141 1005 : 0 : timestamptz_ne_date(PG_FUNCTION_ARGS)
1006 : : {
7944 bruce@momjian.us 1007 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
8141 tgl@sss.pgh.pa.us 1008 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1009 : :
2061 1010 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) != 0);
1011 : : }
1012 : :
1013 : : Datum
8141 1014 : 0 : timestamptz_lt_date(PG_FUNCTION_ARGS)
1015 : : {
7944 bruce@momjian.us 1016 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
8141 tgl@sss.pgh.pa.us 1017 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1018 : :
2061 1019 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) > 0);
1020 : : }
1021 : :
1022 : : Datum
8141 tgl@sss.pgh.pa.us 1023 :CBC 4 : timestamptz_gt_date(PG_FUNCTION_ARGS)
1024 : : {
7944 bruce@momjian.us 1025 : 4 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
8141 tgl@sss.pgh.pa.us 1026 : 4 : DateADT dateVal = PG_GETARG_DATEADT(1);
1027 : :
2061 1028 : 4 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) < 0);
1029 : : }
1030 : :
1031 : : Datum
8141 tgl@sss.pgh.pa.us 1032 :UBC 0 : timestamptz_le_date(PG_FUNCTION_ARGS)
1033 : : {
7944 bruce@momjian.us 1034 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
8141 tgl@sss.pgh.pa.us 1035 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1036 : :
2061 1037 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) >= 0);
1038 : : }
1039 : :
1040 : : Datum
8141 tgl@sss.pgh.pa.us 1041 :CBC 4 : timestamptz_ge_date(PG_FUNCTION_ARGS)
1042 : : {
7944 bruce@momjian.us 1043 : 4 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
8141 tgl@sss.pgh.pa.us 1044 : 4 : DateADT dateVal = PG_GETARG_DATEADT(1);
1045 : :
2061 1046 : 4 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) <= 0);
1047 : : }
1048 : :
1049 : : Datum
8141 tgl@sss.pgh.pa.us 1050 :GBC 78 : timestamptz_cmp_date(PG_FUNCTION_ARGS)
1051 : : {
7944 bruce@momjian.us 1052 : 78 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
8141 tgl@sss.pgh.pa.us 1053 : 78 : DateADT dateVal = PG_GETARG_DATEADT(1);
1054 : :
2061 1055 : 78 : PG_RETURN_INT32(-date_cmp_timestamptz_internal(dateVal, dt1));
1056 : : }
1057 : :
1058 : : /*
1059 : : * in_range support function for date.
1060 : : *
1061 : : * We implement this by promoting the dates to timestamp (without time zone)
1062 : : * and then using the timestamp-and-interval in_range function.
1063 : : */
1064 : : Datum
3034 tgl@sss.pgh.pa.us 1065 :CBC 892 : in_range_date_interval(PG_FUNCTION_ARGS)
1066 : : {
1067 : 892 : DateADT val = PG_GETARG_DATEADT(0);
1068 : 892 : DateADT base = PG_GETARG_DATEADT(1);
1069 : 892 : Interval *offset = PG_GETARG_INTERVAL_P(2);
1070 : 892 : bool sub = PG_GETARG_BOOL(3);
1071 : 892 : bool less = PG_GETARG_BOOL(4);
1072 : : Timestamp valStamp;
1073 : : Timestamp baseStamp;
1074 : :
1075 : : /* XXX we could support out-of-range cases here, perhaps */
1076 : 892 : valStamp = date2timestamp(val);
1077 : 892 : baseStamp = date2timestamp(base);
1078 : :
1079 : 892 : return DirectFunctionCall5(in_range_timestamp_interval,
1080 : : TimestampGetDatum(valStamp),
1081 : : TimestampGetDatum(baseStamp),
1082 : : IntervalPGetDatum(offset),
1083 : : BoolGetDatum(sub),
1084 : : BoolGetDatum(less));
1085 : : }
1086 : :
1087 : :
1088 : : /*
1089 : : * extract_date()
1090 : : * Extract specified field from date type.
1091 : : */
1092 : : Datum
1880 peter@eisentraut.org 1093 : 511 : extract_date(PG_FUNCTION_ARGS)
1094 : : {
1095 : 511 : text *units = PG_GETARG_TEXT_PP(0);
1096 : 511 : DateADT date = PG_GETARG_DATEADT(1);
1097 : : int64 intresult;
1098 : : int type,
1099 : : val;
1100 : : char *lowunits;
1101 : : int year,
1102 : : mon,
1103 : : mday;
1104 : :
1105 [ - + ]: 511 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
1106 [ - + - - : 511 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
1107 : : false);
1108 : :
1109 : 511 : type = DecodeUnits(0, lowunits, &val);
1110 [ + + ]: 511 : if (type == UNKNOWN_FIELD)
1111 : 94 : type = DecodeSpecial(0, lowunits, &val);
1112 : :
1113 [ + + + + : 511 : if (DATE_NOT_FINITE(date) && (type == UNITS || type == RESERV))
+ + + + ]
1114 : : {
1115 [ + + - ]: 90 : switch (val)
1116 : : {
1117 : : /* Oscillating units */
1118 : 45 : case DTK_DAY:
1119 : : case DTK_MONTH:
1120 : : case DTK_QUARTER:
1121 : : case DTK_WEEK:
1122 : : case DTK_DOW:
1123 : : case DTK_ISODOW:
1124 : : case DTK_DOY:
1125 : 45 : PG_RETURN_NULL();
1126 : : break;
1127 : :
1128 : : /* Monotonically-increasing units */
1129 : 45 : case DTK_YEAR:
1130 : : case DTK_DECADE:
1131 : : case DTK_CENTURY:
1132 : : case DTK_MILLENNIUM:
1133 : : case DTK_JULIAN:
1134 : : case DTK_ISOYEAR:
1135 : : case DTK_EPOCH:
1136 [ + + ]: 45 : if (DATE_IS_NOBEGIN(date))
1137 : 5 : PG_RETURN_NUMERIC(DatumGetNumeric(DirectFunctionCall3(numeric_in,
1138 : : CStringGetDatum("-Infinity"),
1139 : : ObjectIdGetDatum(InvalidOid),
1140 : : Int32GetDatum(-1))));
1141 : : else
1142 : 40 : PG_RETURN_NUMERIC(DatumGetNumeric(DirectFunctionCall3(numeric_in,
1143 : : CStringGetDatum("Infinity"),
1144 : : ObjectIdGetDatum(InvalidOid),
1145 : : Int32GetDatum(-1))));
1880 peter@eisentraut.org 1146 :UBC 0 : default:
1147 [ # # ]: 0 : ereport(ERROR,
1148 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1149 : : errmsg("unit \"%s\" not supported for type %s",
1150 : : lowunits, format_type_be(DATEOID))));
1151 : : }
1152 : : }
1880 peter@eisentraut.org 1153 [ + + ]:CBC 421 : else if (type == UNITS)
1154 : : {
1155 : 407 : j2date(date + POSTGRES_EPOCH_JDATE, &year, &mon, &mday);
1156 : :
1157 : 407 : switch (val)
[ + + + +
+ + + + +
+ + + + ]
1158 : : {
1159 : 5 : case DTK_DAY:
1160 : 5 : intresult = mday;
1161 : 5 : break;
1162 : :
1163 : 61 : case DTK_MONTH:
1164 : 61 : intresult = mon;
1165 : 61 : break;
1166 : :
1167 : 5 : case DTK_QUARTER:
1168 : 5 : intresult = (mon - 1) / 3 + 1;
1169 : 5 : break;
1170 : :
1171 : 5 : case DTK_WEEK:
1172 : 5 : intresult = date2isoweek(year, mon, mday);
1173 : 5 : break;
1174 : :
1175 : 126 : case DTK_YEAR:
1176 [ + + ]: 126 : if (year > 0)
1177 : 121 : intresult = year;
1178 : : else
1179 : : /* there is no year 0, just 1 BC and 1 AD */
1180 : 5 : intresult = year - 1;
1181 : 126 : break;
1182 : :
1183 : 40 : case DTK_DECADE:
1184 : : /* see comments in timestamp_part */
1185 [ + + ]: 40 : if (year >= 0)
1186 : 25 : intresult = year / 10;
1187 : : else
1188 : 15 : intresult = -((8 - (year - 1)) / 10);
1189 : 40 : break;
1190 : :
1191 : 54 : case DTK_CENTURY:
1192 : : /* see comments in timestamp_part */
1193 [ + + ]: 54 : if (year > 0)
1194 : 39 : intresult = (year + 99) / 100;
1195 : : else
1196 : 15 : intresult = -((99 - (year - 1)) / 100);
1197 : 54 : break;
1198 : :
1199 : 39 : case DTK_MILLENNIUM:
1200 : : /* see comments in timestamp_part */
1201 [ + + ]: 39 : if (year > 0)
1202 : 34 : intresult = (year + 999) / 1000;
1203 : : else
1204 : 5 : intresult = -((999 - (year - 1)) / 1000);
1205 : 39 : break;
1206 : :
1207 : 5 : case DTK_JULIAN:
1208 : 5 : intresult = date + POSTGRES_EPOCH_JDATE;
1209 : 5 : break;
1210 : :
1211 : 10 : case DTK_ISOYEAR:
1212 : 10 : intresult = date2isoyear(year, mon, mday);
1213 : : /* Adjust BC years */
1214 [ + + ]: 10 : if (intresult <= 0)
1215 : 5 : intresult -= 1;
1216 : 10 : break;
1217 : :
1218 : 20 : case DTK_DOW:
1219 : : case DTK_ISODOW:
1220 : 20 : intresult = j2day(date + POSTGRES_EPOCH_JDATE);
1221 [ + + + + ]: 20 : if (val == DTK_ISODOW && intresult == 0)
1222 : 5 : intresult = 7;
1223 : 20 : break;
1224 : :
1225 : 5 : case DTK_DOY:
1226 : 5 : intresult = date2j(year, mon, mday) - date2j(year, 1, 1) + 1;
1227 : 5 : break;
1228 : :
1229 : 32 : default:
1230 [ + - ]: 32 : ereport(ERROR,
1231 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1232 : : errmsg("unit \"%s\" not supported for type %s",
1233 : : lowunits, format_type_be(DATEOID))));
1234 : : intresult = 0;
1235 : : }
1236 : : }
1237 [ + + ]: 14 : else if (type == RESERV)
1238 : : {
1239 [ + - ]: 10 : switch (val)
1240 : : {
1241 : 10 : case DTK_EPOCH:
1242 : 10 : intresult = ((int64) date + POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
1243 : 10 : break;
1244 : :
1880 peter@eisentraut.org 1245 :UBC 0 : default:
1246 [ # # ]: 0 : ereport(ERROR,
1247 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1248 : : errmsg("unit \"%s\" not supported for type %s",
1249 : : lowunits, format_type_be(DATEOID))));
1250 : : intresult = 0;
1251 : : }
1252 : : }
1253 : : else
1254 : : {
1880 peter@eisentraut.org 1255 [ + - ]:CBC 4 : ereport(ERROR,
1256 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1257 : : errmsg("unit \"%s\" not recognized for type %s",
1258 : : lowunits, format_type_be(DATEOID))));
1259 : : intresult = 0;
1260 : : }
1261 : :
1262 : 385 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
1263 : : }
1264 : :
1265 : :
1266 : : /*
1267 : : * Add an interval to a date, giving a new date.
1268 : : * Must handle both positive and negative intervals.
1269 : : *
1270 : : * We implement this by promoting the date to timestamp (without time zone)
1271 : : * and then using the timestamp plus interval function.
1272 : : */
1273 : : Datum
8805 lockhart@fourpalms.o 1274 : 33 : date_pl_interval(PG_FUNCTION_ARGS)
1275 : : {
1276 : 33 : DateADT dateVal = PG_GETARG_DATEADT(0);
1277 : 33 : Interval *span = PG_GETARG_INTERVAL_P(1);
1278 : : Timestamp dateStamp;
1279 : :
8141 tgl@sss.pgh.pa.us 1280 : 33 : dateStamp = date2timestamp(dateVal);
1281 : :
1282 : 33 : return DirectFunctionCall2(timestamp_pl_interval,
1283 : : TimestampGetDatum(dateStamp),
1284 : : PointerGetDatum(span));
1285 : : }
1286 : :
1287 : : /*
1288 : : * Subtract an interval from a date, giving a new date.
1289 : : * Must handle both positive and negative intervals.
1290 : : *
1291 : : * We implement this by promoting the date to timestamp (without time zone)
1292 : : * and then using the timestamp minus interval function.
1293 : : */
1294 : : Datum
8805 lockhart@fourpalms.o 1295 : 38 : date_mi_interval(PG_FUNCTION_ARGS)
1296 : : {
1297 : 38 : DateADT dateVal = PG_GETARG_DATEADT(0);
1298 : 38 : Interval *span = PG_GETARG_INTERVAL_P(1);
1299 : : Timestamp dateStamp;
1300 : :
8141 tgl@sss.pgh.pa.us 1301 : 38 : dateStamp = date2timestamp(dateVal);
1302 : :
1303 : 38 : return DirectFunctionCall2(timestamp_mi_interval,
1304 : : TimestampGetDatum(dateStamp),
1305 : : PointerGetDatum(span));
1306 : : }
1307 : :
1308 : : /*
1309 : : * date_timestamp()
1310 : : * Convert date to timestamp data type.
1311 : : */
1312 : : Datum
9486 1313 : 972 : date_timestamp(PG_FUNCTION_ARGS)
1314 : : {
1315 : 972 : DateADT dateVal = PG_GETARG_DATEADT(0);
1316 : : Timestamp result;
1317 : :
67 peter@eisentraut.org 1318 :GNC 972 : result = date2timestamp_safe(dateVal, fcinfo->context);
1319 [ - + - - : 968 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
- - ]
67 peter@eisentraut.org 1320 :UNC 0 : PG_RETURN_NULL();
1321 : :
9010 lockhart@fourpalms.o 1322 :CBC 968 : PG_RETURN_TIMESTAMP(result);
1323 : : }
1324 : :
1325 : : /*
1326 : : * timestamp_date()
1327 : : * Convert timestamp to date data type.
1328 : : */
1329 : : Datum
1330 : 2022 : timestamp_date(PG_FUNCTION_ARGS)
1331 : : {
7532 bruce@momjian.us 1332 : 2022 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1333 : : DateADT result;
1334 : :
67 peter@eisentraut.org 1335 :GNC 2022 : result = timestamp2date_safe(timestamp, fcinfo->context);
1336 [ - + - - : 2022 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
- - ]
67 peter@eisentraut.org 1337 :UNC 0 : PG_RETURN_NULL();
1338 : :
331 tgl@sss.pgh.pa.us 1339 :GNC 2022 : PG_RETURN_DATEADT(result);
1340 : : }
1341 : :
1342 : : /*
1343 : : * Convert timestamp to date.
1344 : : *
1345 : : * If the timestamp falls out of the valid range for the date type, error
1346 : : * handling proceeds based on escontext.
1347 : : *
1348 : : * If escontext is NULL, we throw an out-of-range error (hard error).
1349 : : * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or
1350 : : * upper bound overflow, respectively, and record a soft error.
1351 : : *
1352 : : * Note: given the ranges of the types, overflow is only possible at
1353 : : * the lower bound of the range, but we don't assume that in this code.
1354 : : */
1355 : : DateADT
180 michael@paquier.xyz 1356 : 2046 : timestamp2date_safe(Timestamp timestamp, Node *escontext)
1357 : : {
1358 : : DateADT result;
1359 : : struct pg_tm tt,
8346 tgl@sss.pgh.pa.us 1360 :CBC 2046 : *tm = &tt;
1361 : : fsec_t fsec;
1362 : :
6437 1363 [ + + ]: 2046 : if (TIMESTAMP_IS_NOBEGIN(timestamp))
6437 tgl@sss.pgh.pa.us 1364 :GBC 6 : DATE_NOBEGIN(result);
6437 tgl@sss.pgh.pa.us 1365 [ + + ]:CBC 2040 : else if (TIMESTAMP_IS_NOEND(timestamp))
6437 tgl@sss.pgh.pa.us 1366 :GBC 6 : DATE_NOEND(result);
1367 : : else
1368 : : {
6437 tgl@sss.pgh.pa.us 1369 [ - + ]:CBC 2034 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1370 : : {
180 michael@paquier.xyz 1371 [ # # ]:UNC 0 : if (timestamp < 0)
1372 : 0 : DATE_NOBEGIN(result);
1373 : : else
1374 : 0 : DATE_NOEND(result); /* not actually reachable */
1375 : :
1376 [ # # ]: 0 : ereturn(escontext, result,
1377 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1378 : : errmsg("timestamp out of range")));
1379 : : }
1380 : :
6437 tgl@sss.pgh.pa.us 1381 :CBC 2034 : result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1382 : : }
1383 : :
331 tgl@sss.pgh.pa.us 1384 :GNC 2046 : return result;
1385 : : }
1386 : :
1387 : :
1388 : : /*
1389 : : * date_timestamptz()
1390 : : * Convert date to timestamp with time zone data type.
1391 : : */
1392 : : Datum
9010 lockhart@fourpalms.o 1393 :CBC 145 : date_timestamptz(PG_FUNCTION_ARGS)
1394 : : {
1395 : 145 : DateADT dateVal = PG_GETARG_DATEADT(0);
1396 : : TimestampTz result;
1397 : :
67 peter@eisentraut.org 1398 :GNC 145 : result = date2timestamptz_safe(dateVal, fcinfo->context);
1399 [ - + - - : 137 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
- - ]
67 peter@eisentraut.org 1400 :UNC 0 : PG_RETURN_NULL();
1401 : :
9486 tgl@sss.pgh.pa.us 1402 :CBC 137 : PG_RETURN_TIMESTAMP(result);
1403 : : }
1404 : :
1405 : :
1406 : : /*
1407 : : * timestamptz_date()
1408 : : * Convert timestamp with time zone to date data type.
1409 : : */
1410 : : Datum
9010 lockhart@fourpalms.o 1411 : 1998 : timestamptz_date(PG_FUNCTION_ARGS)
1412 : : {
8983 bruce@momjian.us 1413 : 1998 : TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1414 : : DateADT result;
1415 : :
67 peter@eisentraut.org 1416 :GNC 1998 : result = timestamptz2date_safe(timestamp, fcinfo->context);
66 1417 [ - + - - : 1998 : if (SOFT_ERROR_OCCURRED(fcinfo->context))
- - ]
67 peter@eisentraut.org 1418 :UNC 0 : PG_RETURN_NULL();
1419 : :
331 tgl@sss.pgh.pa.us 1420 :GNC 1998 : PG_RETURN_DATEADT(result);
1421 : : }
1422 : :
1423 : : /*
1424 : : * Convert timestamptz to date.
1425 : : *
1426 : : * If the timestamp falls out of the valid range for the date type, error
1427 : : * handling proceeds based on escontext.
1428 : : *
1429 : : * If escontext is NULL, we throw an out-of-range error (hard error).
1430 : : * If escontext is not NULL, we return NOBEGIN or NOEND for lower bound or
1431 : : * upper bound overflow, respectively, and record a soft error.
1432 : : *
1433 : : * Note: given the ranges of the types, overflow is only possible at
1434 : : * the lower bound of the range, but we don't assume that in this code.
1435 : : */
1436 : : DateADT
180 michael@paquier.xyz 1437 : 2022 : timestamptz2date_safe(TimestampTz timestamp, Node *escontext)
1438 : : {
1439 : : DateADT result;
1440 : : struct pg_tm tt,
9600 lockhart@fourpalms.o 1441 :CBC 2022 : *tm = &tt;
1442 : : fsec_t fsec;
1443 : : int tz;
1444 : :
6437 tgl@sss.pgh.pa.us 1445 [ + + ]: 2022 : if (TIMESTAMP_IS_NOBEGIN(timestamp))
6437 tgl@sss.pgh.pa.us 1446 :GBC 6 : DATE_NOBEGIN(result);
6437 tgl@sss.pgh.pa.us 1447 [ + + ]:CBC 2016 : else if (TIMESTAMP_IS_NOEND(timestamp))
6437 tgl@sss.pgh.pa.us 1448 :GBC 6 : DATE_NOEND(result);
1449 : : else
1450 : : {
5189 peter_e@gmx.net 1451 [ - + ]:CBC 2010 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
1452 : : {
180 michael@paquier.xyz 1453 [ # # ]:UNC 0 : if (timestamp < 0)
1454 : 0 : DATE_NOBEGIN(result);
1455 : : else
1456 : 0 : DATE_NOEND(result); /* not actually reachable */
1457 : :
1458 [ # # ]: 0 : ereturn(escontext, result,
1459 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1460 : : errmsg("timestamp out of range")));
1461 : : }
1462 : :
6437 tgl@sss.pgh.pa.us 1463 :CBC 2010 : result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1464 : : }
1465 : :
331 tgl@sss.pgh.pa.us 1466 :GNC 2022 : return result;
1467 : : }
1468 : :
1469 : :
1470 : : /*****************************************************************************
1471 : : * Time ADT
1472 : : *****************************************************************************/
1473 : :
1474 : : Datum
9486 tgl@sss.pgh.pa.us 1475 :CBC 1424 : time_in(PG_FUNCTION_ARGS)
1476 : : {
1477 : 1424 : char *str = PG_GETARG_CSTRING(0);
1478 : : #ifdef NOT_USED
1479 : : Oid typelem = PG_GETARG_OID(1);
1480 : : #endif
9005 lockhart@fourpalms.o 1481 : 1424 : int32 typmod = PG_GETARG_INT32(2);
1268 tgl@sss.pgh.pa.us 1482 : 1424 : Node *escontext = fcinfo->context;
1483 : : TimeADT result;
1484 : : fsec_t fsec;
1485 : : struct pg_tm tt,
9600 lockhart@fourpalms.o 1486 : 1424 : *tm = &tt;
1487 : : int tz;
1488 : : int nf;
1489 : : int dterr;
1490 : : char workbuf[MAXDATELEN + 1];
1491 : : char *field[MAXDATEFIELDS];
1492 : : int dtype;
1493 : : int ftype[MAXDATEFIELDS];
1494 : : DateTimeErrorExtra extra;
1495 : :
7674 neilc@samurai.com 1496 : 1424 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1497 : : field, ftype, MAXDATEFIELDS, &nf);
8312 tgl@sss.pgh.pa.us 1498 [ + - ]: 1424 : if (dterr == 0)
1268 1499 : 1424 : dterr = DecodeTimeOnly(field, ftype, nf,
1500 : : &dtype, tm, &fsec, &tz, &extra);
8312 1501 [ + + ]: 1424 : if (dterr != 0)
1502 : : {
1268 1503 : 40 : DateTimeParseError(dterr, &extra, str, "time", escontext);
1504 : 16 : PG_RETURN_NULL();
1505 : : }
1506 : :
8805 lockhart@fourpalms.o 1507 : 1384 : tm2time(tm, fsec, &result);
9005 1508 : 1384 : AdjustTimeForTypmod(&result, typmod);
1509 : :
1510 : 1384 : PG_RETURN_TIMEADT(result);
1511 : : }
1512 : :
1513 : : /*
1514 : : * tm2time()
1515 : : * Convert a tm structure to a time data type.
1516 : : */
1517 : : int
3265 tgl@sss.pgh.pa.us 1518 : 2309 : tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result)
1519 : : {
7618 bruce@momjian.us 1520 : 2309 : *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
7532 1521 : 2309 : * USECS_PER_SEC) + fsec;
8805 lockhart@fourpalms.o 1522 : 2309 : return 0;
1523 : : }
1524 : :
1525 : : /*
1526 : : * time_overflows()
1527 : : * Check to see if a broken-down time-of-day is out of range.
1528 : : */
1529 : : bool
2186 tgl@sss.pgh.pa.us 1530 : 31298 : time_overflows(int hour, int min, int sec, fsec_t fsec)
1531 : : {
1532 : : /* Range-check the fields individually. */
1533 [ + - + + : 31298 : if (hour < 0 || hour > HOURS_PER_DAY ||
+ - ]
1534 [ + - + - ]: 31274 : min < 0 || min >= MINS_PER_HOUR ||
1535 [ + - + - ]: 31274 : sec < 0 || sec > SECS_PER_MINUTE ||
1536 [ - + ]: 31274 : fsec < 0 || fsec > USECS_PER_SEC)
1537 : 24 : return true;
1538 : :
1539 : : /*
1540 : : * Because we allow, eg, hour = 24 or sec = 60, we must check separately
1541 : : * that the total time value doesn't exceed 24:00:00.
1542 : : */
1543 : 31274 : if ((((((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
1544 [ + + ]: 31274 : + sec) * USECS_PER_SEC) + fsec) > USECS_PER_DAY)
1545 : 24 : return true;
1546 : :
1547 : 31250 : return false;
1548 : : }
1549 : :
1550 : : /*
1551 : : * float_time_overflows()
1552 : : * Same, when we have seconds + fractional seconds as one "double" value.
1553 : : */
1554 : : bool
1555 : 168 : float_time_overflows(int hour, int min, double sec)
1556 : : {
1557 : : /* Range-check the fields individually. */
1558 [ + - + - : 168 : if (hour < 0 || hour > HOURS_PER_DAY ||
+ - ]
1559 [ - + ]: 168 : min < 0 || min >= MINS_PER_HOUR)
2186 tgl@sss.pgh.pa.us 1560 :UBC 0 : return true;
1561 : :
1562 : : /*
1563 : : * "sec", being double, requires extra care. Cope with NaN, and round off
1564 : : * before applying the range check to avoid unexpected errors due to
1565 : : * imprecise input. (We assume rint() behaves sanely with infinities.)
1566 : : */
2186 tgl@sss.pgh.pa.us 1567 [ - + ]:CBC 168 : if (isnan(sec))
2186 tgl@sss.pgh.pa.us 1568 :UBC 0 : return true;
2186 tgl@sss.pgh.pa.us 1569 :CBC 168 : sec = rint(sec * USECS_PER_SEC);
1570 [ + - + + ]: 168 : if (sec < 0 || sec > SECS_PER_MINUTE * USECS_PER_SEC)
1571 : 4 : return true;
1572 : :
1573 : : /*
1574 : : * Because we allow, eg, hour = 24 or sec = 60, we must check separately
1575 : : * that the total time value doesn't exceed 24:00:00. This must match the
1576 : : * way that callers will convert the fields to a time.
1577 : : */
1578 : 164 : if (((((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
1579 [ + + ]: 164 : * USECS_PER_SEC) + (int64) sec) > USECS_PER_DAY)
1580 : 4 : return true;
1581 : :
1582 : 160 : return false;
1583 : : }
1584 : :
1585 : :
1586 : : /*
1587 : : * time2tm()
1588 : : * Convert time data type to POSIX time structure.
1589 : : *
1590 : : * Note that only the hour/min/sec/fractional-sec fields are filled in.
1591 : : */
1592 : : int
3265 1593 : 4311 : time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
1594 : : {
7676 bruce@momjian.us 1595 : 4311 : tm->tm_hour = time / USECS_PER_HOUR;
1596 : 4311 : time -= tm->tm_hour * USECS_PER_HOUR;
1597 : 4311 : tm->tm_min = time / USECS_PER_MINUTE;
1598 : 4311 : time -= tm->tm_min * USECS_PER_MINUTE;
1599 : 4311 : tm->tm_sec = time / USECS_PER_SEC;
1600 : 4311 : time -= tm->tm_sec * USECS_PER_SEC;
8805 lockhart@fourpalms.o 1601 : 4311 : *fsec = time;
1602 : 4311 : return 0;
1603 : : }
1604 : :
1605 : : Datum
9486 tgl@sss.pgh.pa.us 1606 : 3875 : time_out(PG_FUNCTION_ARGS)
1607 : : {
1608 : 3875 : TimeADT time = PG_GETARG_TIMEADT(0);
1609 : : char *result;
1610 : : struct pg_tm tt,
9600 lockhart@fourpalms.o 1611 : 3875 : *tm = &tt;
1612 : : fsec_t fsec;
1613 : : char buf[MAXDATELEN + 1];
1614 : :
8805 1615 : 3875 : time2tm(time, tm, &fsec);
5190 peter_e@gmx.net 1616 : 3875 : EncodeTimeOnly(tm, fsec, false, 0, DateStyle, buf);
1617 : :
9486 tgl@sss.pgh.pa.us 1618 : 3875 : result = pstrdup(buf);
1619 : 3875 : PG_RETURN_CSTRING(result);
1620 : : }
1621 : :
1622 : : /*
1623 : : * time_recv - converts external binary format to time
1624 : : */
1625 : : Datum
8419 tgl@sss.pgh.pa.us 1626 :UBC 0 : time_recv(PG_FUNCTION_ARGS)
1627 : : {
1628 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1629 : :
1630 : : #ifdef NOT_USED
1631 : : Oid typelem = PG_GETARG_OID(1);
1632 : : #endif
7629 1633 : 0 : int32 typmod = PG_GETARG_INT32(2);
1634 : : TimeADT result;
1635 : :
1636 : 0 : result = pq_getmsgint64(buf);
1637 : :
6213 1638 [ # # # # ]: 0 : if (result < INT64CONST(0) || result > USECS_PER_DAY)
1639 [ # # ]: 0 : ereport(ERROR,
1640 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1641 : : errmsg("time out of range")));
1642 : :
7629 1643 : 0 : AdjustTimeForTypmod(&result, typmod);
1644 : :
1645 : 0 : PG_RETURN_TIMEADT(result);
1646 : : }
1647 : :
1648 : : /*
1649 : : * time_send - converts time to binary format
1650 : : */
1651 : : Datum
8419 1652 : 0 : time_send(PG_FUNCTION_ARGS)
1653 : : {
1654 : 0 : TimeADT time = PG_GETARG_TIMEADT(0);
1655 : : StringInfoData buf;
1656 : :
1657 : 0 : pq_begintypsend(&buf);
1658 : 0 : pq_sendint64(&buf, time);
1659 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1660 : : }
1661 : :
1662 : : Datum
7091 tgl@sss.pgh.pa.us 1663 :CBC 14 : timetypmodin(PG_FUNCTION_ARGS)
1664 : : {
6771 bruce@momjian.us 1665 : 14 : ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1666 : :
7091 tgl@sss.pgh.pa.us 1667 : 14 : PG_RETURN_INT32(anytime_typmodin(false, ta));
1668 : : }
1669 : :
1670 : : Datum
1671 : 5 : timetypmodout(PG_FUNCTION_ARGS)
1672 : : {
6771 bruce@momjian.us 1673 : 5 : int32 typmod = PG_GETARG_INT32(0);
1674 : :
7091 tgl@sss.pgh.pa.us 1675 : 5 : PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
1676 : : }
1677 : :
1678 : : /*
1679 : : * make_time - time constructor
1680 : : */
1681 : : Datum
4577 1682 : 13 : make_time(PG_FUNCTION_ARGS)
1683 : : {
1684 : 13 : int tm_hour = PG_GETARG_INT32(0);
1685 : 13 : int tm_min = PG_GETARG_INT32(1);
1686 : 13 : double sec = PG_GETARG_FLOAT8(2);
1687 : : TimeADT time;
1688 : :
1689 : : /* Check for time overflow */
2186 1690 [ + + ]: 13 : if (float_time_overflows(tm_hour, tm_min, sec))
4577 1691 [ + - ]: 8 : ereport(ERROR,
1692 : : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
1693 : : errmsg("time field value out of range: %d:%02d:%02g",
1694 : : tm_hour, tm_min, sec)));
1695 : :
1696 : : /* This should match tm2time */
1697 : 5 : time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
2186 1698 : 5 : * USECS_PER_SEC) + (int64) rint(sec * USECS_PER_SEC);
1699 : :
4577 1700 : 5 : PG_RETURN_TIMEADT(time);
1701 : : }
1702 : :
1703 : :
1704 : : /*
1705 : : * time_support()
1706 : : *
1707 : : * Planner support function for the time_scale() and timetz_scale()
1708 : : * length coercion functions (we need not distinguish them here).
1709 : : */
1710 : : Datum
2667 1711 : 20 : time_support(PG_FUNCTION_ARGS)
1712 : : {
1713 : 20 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
1714 : 20 : Node *ret = NULL;
1715 : :
1716 [ + + ]: 20 : if (IsA(rawreq, SupportRequestSimplify))
1717 : : {
1718 : 10 : SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
1719 : :
1720 : 10 : ret = TemporalSimplify(MAX_TIME_PRECISION, (Node *) req->fcall);
1721 : : }
1722 : :
1723 : 20 : PG_RETURN_POINTER(ret);
1724 : : }
1725 : :
1726 : : /*
1727 : : * time_scale()
1728 : : * Adjust time type for specified scale factor.
1729 : : * Used by PostgreSQL type system to stuff columns.
1730 : : */
1731 : : Datum
9005 lockhart@fourpalms.o 1732 : 54 : time_scale(PG_FUNCTION_ARGS)
1733 : : {
1734 : 54 : TimeADT time = PG_GETARG_TIMEADT(0);
1735 : 54 : int32 typmod = PG_GETARG_INT32(1);
1736 : : TimeADT result;
1737 : :
1738 : 54 : result = time;
1739 : 54 : AdjustTimeForTypmod(&result, typmod);
1740 : :
1741 : 54 : PG_RETURN_TIMEADT(result);
1742 : : }
1743 : :
1744 : : /*
1745 : : * AdjustTimeForTypmod()
1746 : : * Force the precision of the time value to a specified value.
1747 : : * Uses *exactly* the same code as in AdjustTimestampForTypmod()
1748 : : * but we make a separate copy because those types do not
1749 : : * have a fundamental tie together but rather a coincidence of
1750 : : * implementation. - thomas
1751 : : */
1752 : : void
1753 : 5176 : AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1754 : : {
1755 : : static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1756 : : INT64CONST(1000000),
1757 : : INT64CONST(100000),
1758 : : INT64CONST(10000),
1759 : : INT64CONST(1000),
1760 : : INT64CONST(100),
1761 : : INT64CONST(10),
1762 : : INT64CONST(1)
1763 : : };
1764 : :
1765 : : static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1766 : : INT64CONST(500000),
1767 : : INT64CONST(50000),
1768 : : INT64CONST(5000),
1769 : : INT64CONST(500),
1770 : : INT64CONST(50),
1771 : : INT64CONST(5),
1772 : : INT64CONST(0)
1773 : : };
1774 : :
7676 bruce@momjian.us 1775 [ + + + - ]: 5176 : if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1776 : : {
8700 lockhart@fourpalms.o 1777 [ + - ]: 374 : if (*time >= INT64CONST(0))
7676 bruce@momjian.us 1778 : 374 : *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
7532 1779 : 374 : TimeScales[typmod];
1780 : : else
7676 bruce@momjian.us 1781 :UBC 0 : *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
7532 1782 : 0 : TimeScales[typmod]);
1783 : : }
9005 lockhart@fourpalms.o 1784 :CBC 5176 : }
1785 : :
1786 : :
1787 : : Datum
9486 tgl@sss.pgh.pa.us 1788 : 25556 : time_eq(PG_FUNCTION_ARGS)
1789 : : {
1790 : 25556 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1791 : 25556 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1792 : :
1793 : 25556 : PG_RETURN_BOOL(time1 == time2);
1794 : : }
1795 : :
1796 : : Datum
9486 tgl@sss.pgh.pa.us 1797 :UBC 0 : time_ne(PG_FUNCTION_ARGS)
1798 : : {
1799 : 0 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1800 : 0 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1801 : :
1802 : 0 : PG_RETURN_BOOL(time1 != time2);
1803 : : }
1804 : :
1805 : : Datum
9486 tgl@sss.pgh.pa.us 1806 :CBC 62473 : time_lt(PG_FUNCTION_ARGS)
1807 : : {
1808 : 62473 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1809 : 62473 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1810 : :
1811 : 62473 : PG_RETURN_BOOL(time1 < time2);
1812 : : }
1813 : :
1814 : : Datum
1815 : 5515 : time_le(PG_FUNCTION_ARGS)
1816 : : {
1817 : 5515 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1818 : 5515 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1819 : :
1820 : 5515 : PG_RETURN_BOOL(time1 <= time2);
1821 : : }
1822 : :
1823 : : Datum
1824 : 6767 : time_gt(PG_FUNCTION_ARGS)
1825 : : {
1826 : 6767 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1827 : 6767 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1828 : :
1829 : 6767 : PG_RETURN_BOOL(time1 > time2);
1830 : : }
1831 : :
1832 : : Datum
1833 : 4151 : time_ge(PG_FUNCTION_ARGS)
1834 : : {
1835 : 4151 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1836 : 4151 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1837 : :
1838 : 4151 : PG_RETURN_BOOL(time1 >= time2);
1839 : : }
1840 : :
1841 : : Datum
1842 : 21364 : time_cmp(PG_FUNCTION_ARGS)
1843 : : {
1844 : 21364 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1845 : 21364 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1846 : :
1847 [ + + ]: 21364 : if (time1 < time2)
1848 : 10230 : PG_RETURN_INT32(-1);
1849 [ + + ]: 11134 : if (time1 > time2)
1850 : 9516 : PG_RETURN_INT32(1);
1851 : 1618 : PG_RETURN_INT32(0);
1852 : : }
1853 : :
1854 : : Datum
6903 1855 : 1516 : time_hash(PG_FUNCTION_ARGS)
1856 : : {
1857 : 1516 : return hashint8(fcinfo);
1858 : : }
1859 : :
1860 : : Datum
3194 rhaas@postgresql.org 1861 : 40 : time_hash_extended(PG_FUNCTION_ARGS)
1862 : : {
1863 : 40 : return hashint8extended(fcinfo);
1864 : : }
1865 : :
1866 : : Datum
9486 tgl@sss.pgh.pa.us 1867 :UBC 0 : time_larger(PG_FUNCTION_ARGS)
1868 : : {
1869 : 0 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1870 : 0 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1871 : :
1872 : 0 : PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1873 : : }
1874 : :
1875 : : Datum
1876 : 0 : time_smaller(PG_FUNCTION_ARGS)
1877 : : {
1878 : 0 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1879 : 0 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1880 : :
1881 : 0 : PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1882 : : }
1883 : :
1884 : : /*
1885 : : * overlaps_time() --- implements the SQL OVERLAPS operator.
1886 : : *
1887 : : * Algorithm is per SQL spec. This is much harder than you'd think
1888 : : * because the spec requires us to deliver a non-null answer in some cases
1889 : : * where some of the inputs are null.
1890 : : */
1891 : : Datum
9486 tgl@sss.pgh.pa.us 1892 :CBC 22 : overlaps_time(PG_FUNCTION_ARGS)
1893 : : {
1894 : : /*
1895 : : * The arguments are TimeADT, but we leave them as generic Datums to avoid
1896 : : * dereferencing nulls (TimeADT is pass-by-reference!)
1897 : : */
9305 1898 : 22 : Datum ts1 = PG_GETARG_DATUM(0);
1899 : 22 : Datum te1 = PG_GETARG_DATUM(1);
1900 : 22 : Datum ts2 = PG_GETARG_DATUM(2);
1901 : 22 : Datum te2 = PG_GETARG_DATUM(3);
1902 : 22 : bool ts1IsNull = PG_ARGISNULL(0);
1903 : 22 : bool te1IsNull = PG_ARGISNULL(1);
1904 : 22 : bool ts2IsNull = PG_ARGISNULL(2);
1905 : 22 : bool te2IsNull = PG_ARGISNULL(3);
1906 : :
1907 : : #define TIMEADT_GT(t1,t2) \
1908 : : (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1909 : : #define TIMEADT_LT(t1,t2) \
1910 : : (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1911 : :
1912 : : /*
1913 : : * If both endpoints of interval 1 are null, the result is null (unknown).
1914 : : * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1915 : : * take ts1 as the lesser endpoint.
1916 : : */
1917 [ - + ]: 22 : if (ts1IsNull)
1918 : : {
9305 tgl@sss.pgh.pa.us 1919 [ # # ]:UBC 0 : if (te1IsNull)
1920 : 0 : PG_RETURN_NULL();
1921 : : /* swap null for non-null */
9573 lockhart@fourpalms.o 1922 : 0 : ts1 = te1;
9305 tgl@sss.pgh.pa.us 1923 : 0 : te1IsNull = true;
1924 : : }
9305 tgl@sss.pgh.pa.us 1925 [ + - ]:CBC 22 : else if (!te1IsNull)
1926 : : {
1927 [ - + ]: 22 : if (TIMEADT_GT(ts1, te1))
1928 : : {
9200 bruce@momjian.us 1929 :UBC 0 : Datum tt = ts1;
1930 : :
9305 tgl@sss.pgh.pa.us 1931 : 0 : ts1 = te1;
1932 : 0 : te1 = tt;
1933 : : }
1934 : : }
1935 : :
1936 : : /* Likewise for interval 2. */
9305 tgl@sss.pgh.pa.us 1937 [ - + ]:CBC 22 : if (ts2IsNull)
1938 : : {
9305 tgl@sss.pgh.pa.us 1939 [ # # ]:UBC 0 : if (te2IsNull)
1940 : 0 : PG_RETURN_NULL();
1941 : : /* swap null for non-null */
9573 lockhart@fourpalms.o 1942 : 0 : ts2 = te2;
9305 tgl@sss.pgh.pa.us 1943 : 0 : te2IsNull = true;
1944 : : }
9305 tgl@sss.pgh.pa.us 1945 [ + - ]:CBC 22 : else if (!te2IsNull)
1946 : : {
1947 [ - + ]: 22 : if (TIMEADT_GT(ts2, te2))
1948 : : {
9200 bruce@momjian.us 1949 :UBC 0 : Datum tt = ts2;
1950 : :
9305 tgl@sss.pgh.pa.us 1951 : 0 : ts2 = te2;
1952 : 0 : te2 = tt;
1953 : : }
1954 : : }
1955 : :
1956 : : /*
1957 : : * At this point neither ts1 nor ts2 is null, so we can consider three
1958 : : * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1959 : : */
9305 tgl@sss.pgh.pa.us 1960 [ - + ]:CBC 22 : if (TIMEADT_GT(ts1, ts2))
1961 : : {
1962 : : /*
1963 : : * This case is ts1 < te2 OR te1 < te2, which may look redundant but
1964 : : * in the presence of nulls it's not quite completely so.
1965 : : */
9305 tgl@sss.pgh.pa.us 1966 [ # # ]:UBC 0 : if (te2IsNull)
1967 : 0 : PG_RETURN_NULL();
1968 [ # # ]: 0 : if (TIMEADT_LT(ts1, te2))
1969 : 0 : PG_RETURN_BOOL(true);
1970 [ # # ]: 0 : if (te1IsNull)
1971 : 0 : PG_RETURN_NULL();
1972 : :
1973 : : /*
1974 : : * If te1 is not null then we had ts1 <= te1 above, and we just found
1975 : : * ts1 >= te2, hence te1 >= te2.
1976 : : */
1977 : 0 : PG_RETURN_BOOL(false);
1978 : : }
9305 tgl@sss.pgh.pa.us 1979 [ + - ]:CBC 22 : else if (TIMEADT_LT(ts1, ts2))
1980 : : {
1981 : : /* This case is ts2 < te1 OR te2 < te1 */
1982 [ - + ]: 22 : if (te1IsNull)
9305 tgl@sss.pgh.pa.us 1983 :UBC 0 : PG_RETURN_NULL();
9305 tgl@sss.pgh.pa.us 1984 [ + + ]:CBC 22 : if (TIMEADT_LT(ts2, te1))
1985 : 11 : PG_RETURN_BOOL(true);
1986 [ - + ]: 11 : if (te2IsNull)
9305 tgl@sss.pgh.pa.us 1987 :UBC 0 : PG_RETURN_NULL();
1988 : :
1989 : : /*
1990 : : * If te2 is not null then we had ts2 <= te2 above, and we just found
1991 : : * ts2 >= te1, hence te2 >= te1.
1992 : : */
9305 tgl@sss.pgh.pa.us 1993 :CBC 11 : PG_RETURN_BOOL(false);
1994 : : }
1995 : : else
1996 : : {
1997 : : /*
1998 : : * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1999 : : * rather silly way of saying "true if both are nonnull, else null".
2000 : : */
9305 tgl@sss.pgh.pa.us 2001 [ # # # # ]:UBC 0 : if (te1IsNull || te2IsNull)
2002 : 0 : PG_RETURN_NULL();
2003 : 0 : PG_RETURN_BOOL(true);
2004 : : }
2005 : :
2006 : : #undef TIMEADT_GT
2007 : : #undef TIMEADT_LT
2008 : : }
2009 : :
2010 : : /*
2011 : : * timestamp_time()
2012 : : * Convert timestamp to time data type.
2013 : : */
2014 : : Datum
9486 tgl@sss.pgh.pa.us 2015 :CBC 24 : timestamp_time(PG_FUNCTION_ARGS)
2016 : : {
7532 bruce@momjian.us 2017 : 24 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
2018 : : TimeADT result;
2019 : : struct pg_tm tt,
10491 2020 : 24 : *tm = &tt;
2021 : : fsec_t fsec;
2022 : :
9486 tgl@sss.pgh.pa.us 2023 [ + - - + ]: 24 : if (TIMESTAMP_NOT_FINITE(timestamp))
9010 lockhart@fourpalms.o 2024 :UBC 0 : PG_RETURN_NULL();
2025 : :
7618 bruce@momjian.us 2026 [ - + ]:CBC 24 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
67 peter@eisentraut.org 2027 [ # # ]:UNC 0 : ereturn(fcinfo->context, (Datum) 0,
2028 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2029 : : errmsg("timestamp out of range")));
2030 : :
2031 : : /*
2032 : : * Could also do this with time = (timestamp / USECS_PER_DAY *
2033 : : * USECS_PER_DAY) - timestamp;
2034 : : */
7618 bruce@momjian.us 2035 :CBC 24 : result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
7532 2036 : 24 : USECS_PER_SEC) + fsec;
2037 : :
8805 lockhart@fourpalms.o 2038 : 24 : PG_RETURN_TIMEADT(result);
2039 : : }
2040 : :
2041 : : /*
2042 : : * timestamptz_time()
2043 : : * Convert timestamptz to time data type.
2044 : : */
2045 : : Datum
2046 : 36 : timestamptz_time(PG_FUNCTION_ARGS)
2047 : : {
2048 : 36 : TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2049 : : TimeADT result;
2050 : : struct pg_tm tt,
2051 : 36 : *tm = &tt;
2052 : : int tz;
2053 : : fsec_t fsec;
2054 : :
2055 [ + - - + ]: 36 : if (TIMESTAMP_NOT_FINITE(timestamp))
8805 lockhart@fourpalms.o 2056 :UBC 0 : PG_RETURN_NULL();
2057 : :
5189 peter_e@gmx.net 2058 [ - + ]:CBC 36 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
67 peter@eisentraut.org 2059 [ # # ]:UNC 0 : ereturn(fcinfo->context, (Datum) 0,
2060 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2061 : : errmsg("timestamp out of range")));
2062 : :
2063 : : /*
2064 : : * Could also do this with time = (timestamp / USECS_PER_DAY *
2065 : : * USECS_PER_DAY) - timestamp;
2066 : : */
7618 bruce@momjian.us 2067 :CBC 36 : result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
7532 2068 : 36 : USECS_PER_SEC) + fsec;
2069 : :
9486 tgl@sss.pgh.pa.us 2070 : 36 : PG_RETURN_TIMEADT(result);
2071 : : }
2072 : :
2073 : : /*
2074 : : * datetime_timestamp()
2075 : : * Convert date and time to timestamp data type.
2076 : : */
2077 : : Datum
2078 : 25 : datetime_timestamp(PG_FUNCTION_ARGS)
2079 : : {
7532 bruce@momjian.us 2080 : 25 : DateADT date = PG_GETARG_DATEADT(0);
9486 tgl@sss.pgh.pa.us 2081 : 25 : TimeADT time = PG_GETARG_TIMEADT(1);
2082 : : Timestamp result;
2083 : :
6437 2084 : 25 : result = date2timestamp(date);
2085 [ + - + - ]: 25 : if (!TIMESTAMP_NOT_FINITE(result))
2086 : : {
2087 : 25 : result += time;
3727 2088 [ + - - + ]: 25 : if (!IS_VALID_TIMESTAMP(result))
3727 tgl@sss.pgh.pa.us 2089 [ # # ]:UBC 0 : ereport(ERROR,
2090 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2091 : : errmsg("timestamp out of range")));
2092 : : }
2093 : :
9486 tgl@sss.pgh.pa.us 2094 :CBC 25 : PG_RETURN_TIMESTAMP(result);
2095 : : }
2096 : :
2097 : : /*
2098 : : * time_interval()
2099 : : * Convert time to interval data type.
2100 : : */
2101 : : Datum
2102 : 10 : time_interval(PG_FUNCTION_ARGS)
2103 : : {
2104 : 10 : TimeADT time = PG_GETARG_TIMEADT(0);
2105 : : Interval *result;
2106 : :
171 michael@paquier.xyz 2107 :GNC 10 : result = palloc_object(Interval);
2108 : :
9486 tgl@sss.pgh.pa.us 2109 :CBC 10 : result->time = time;
7619 bruce@momjian.us 2110 : 10 : result->day = 0;
9573 lockhart@fourpalms.o 2111 : 10 : result->month = 0;
2112 : :
9486 tgl@sss.pgh.pa.us 2113 : 10 : PG_RETURN_INTERVAL_P(result);
2114 : : }
2115 : :
2116 : : /*
2117 : : * interval_time()
2118 : : * Convert interval to time data type.
2119 : : *
2120 : : * This is defined as producing the fractional-day portion of the interval.
2121 : : * Therefore, we can just ignore the months field. It is not real clear
2122 : : * what to do with negative intervals, but we choose to subtract the floor,
2123 : : * so that, say, '-2 hours' becomes '22:00:00'.
2124 : : */
2125 : : Datum
9309 lockhart@fourpalms.o 2126 : 23 : interval_time(PG_FUNCTION_ARGS)
2127 : : {
2128 : 23 : Interval *span = PG_GETARG_INTERVAL_P(0);
2129 : : TimeADT result;
2130 : :
928 dean.a.rasheed@gmail 2131 : 23 : if (INTERVAL_NOT_FINITE(span))
[ + + + -
- + + + +
- + - ]
67 peter@eisentraut.org 2132 [ + - ]:GNC 8 : ereturn(fcinfo->context, (Datum) 0,
2133 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2134 : : errmsg("cannot convert infinite interval to time")));
2135 : :
933 dean.a.rasheed@gmail 2136 :CBC 15 : result = span->time % USECS_PER_DAY;
2137 [ + + ]: 15 : if (result < 0)
2138 : 5 : result += USECS_PER_DAY;
2139 : :
9309 lockhart@fourpalms.o 2140 : 15 : PG_RETURN_TIMEADT(result);
2141 : : }
2142 : :
2143 : : /*
2144 : : * time_mi_time()
2145 : : * Subtract two times to produce an interval.
2146 : : */
2147 : : Datum
9010 2148 : 820 : time_mi_time(PG_FUNCTION_ARGS)
2149 : : {
2150 : 820 : TimeADT time1 = PG_GETARG_TIMEADT(0);
2151 : 820 : TimeADT time2 = PG_GETARG_TIMEADT(1);
2152 : : Interval *result;
2153 : :
171 michael@paquier.xyz 2154 :GNC 820 : result = palloc_object(Interval);
2155 : :
9010 lockhart@fourpalms.o 2156 :CBC 820 : result->month = 0;
7619 bruce@momjian.us 2157 : 820 : result->day = 0;
2158 : 820 : result->time = time1 - time2;
2159 : :
9010 lockhart@fourpalms.o 2160 : 820 : PG_RETURN_INTERVAL_P(result);
2161 : : }
2162 : :
2163 : : /*
2164 : : * time_pl_interval()
2165 : : * Add interval to time.
2166 : : */
2167 : : Datum
9309 2168 : 1777 : time_pl_interval(PG_FUNCTION_ARGS)
2169 : : {
2170 : 1777 : TimeADT time = PG_GETARG_TIMEADT(0);
2171 : 1777 : Interval *span = PG_GETARG_INTERVAL_P(1);
2172 : : TimeADT result;
2173 : :
928 dean.a.rasheed@gmail 2174 : 1777 : if (INTERVAL_NOT_FINITE(span))
[ + + + -
- + + + +
- + - ]
2175 [ + - ]: 8 : ereport(ERROR,
2176 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2177 : : errmsg("cannot add infinite interval to time")));
2178 : :
7618 bruce@momjian.us 2179 : 1769 : result = time + span->time;
2180 : 1769 : result -= result / USECS_PER_DAY * USECS_PER_DAY;
8805 lockhart@fourpalms.o 2181 [ + + ]: 1769 : if (result < INT64CONST(0))
7677 bruce@momjian.us 2182 : 4 : result += USECS_PER_DAY;
2183 : :
9309 lockhart@fourpalms.o 2184 : 1769 : PG_RETURN_TIMEADT(result);
2185 : : }
2186 : :
2187 : : /*
2188 : : * time_mi_interval()
2189 : : * Subtract interval from time.
2190 : : */
2191 : : Datum
2192 : 413 : time_mi_interval(PG_FUNCTION_ARGS)
2193 : : {
2194 : 413 : TimeADT time = PG_GETARG_TIMEADT(0);
2195 : 413 : Interval *span = PG_GETARG_INTERVAL_P(1);
2196 : : TimeADT result;
2197 : :
928 dean.a.rasheed@gmail 2198 : 413 : if (INTERVAL_NOT_FINITE(span))
[ + + + -
- + + + +
- + - ]
2199 [ + - ]: 8 : ereport(ERROR,
2200 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2201 : : errmsg("cannot subtract infinite interval from time")));
2202 : :
7618 bruce@momjian.us 2203 : 405 : result = time - span->time;
2204 : 405 : result -= result / USECS_PER_DAY * USECS_PER_DAY;
8805 lockhart@fourpalms.o 2205 [ + + ]: 405 : if (result < INT64CONST(0))
7677 bruce@momjian.us 2206 : 49 : result += USECS_PER_DAY;
2207 : :
9309 lockhart@fourpalms.o 2208 : 405 : PG_RETURN_TIMEADT(result);
2209 : : }
2210 : :
2211 : : /*
2212 : : * in_range support function for time.
2213 : : */
2214 : : Datum
3034 tgl@sss.pgh.pa.us 2215 : 640 : in_range_time_interval(PG_FUNCTION_ARGS)
2216 : : {
2217 : 640 : TimeADT val = PG_GETARG_TIMEADT(0);
2218 : 640 : TimeADT base = PG_GETARG_TIMEADT(1);
2219 : 640 : Interval *offset = PG_GETARG_INTERVAL_P(2);
2220 : 640 : bool sub = PG_GETARG_BOOL(3);
2221 : 640 : bool less = PG_GETARG_BOOL(4);
2222 : : TimeADT sum;
2223 : :
2224 : : /*
2225 : : * Like time_pl_interval/time_mi_interval, we disregard the month and day
2226 : : * fields of the offset. So our test for negative should too. This also
2227 : : * catches -infinity, so we only need worry about +infinity below.
2228 : : */
2229 [ + + ]: 640 : if (offset->time < 0)
2230 [ + - ]: 8 : ereport(ERROR,
2231 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2232 : : errmsg("invalid preceding or following size in window function")));
2233 : :
2234 : : /*
2235 : : * We can't use time_pl_interval/time_mi_interval here, because their
2236 : : * wraparound behavior would give wrong (or at least undesirable) answers.
2237 : : * Fortunately the equivalent non-wrapping behavior is trivial, except
2238 : : * that adding an infinite (or very large) interval might cause integer
2239 : : * overflow. Subtraction cannot overflow here.
2240 : : */
2241 [ + + ]: 632 : if (sub)
2242 : 316 : sum = base - offset->time;
928 dean.a.rasheed@gmail 2243 [ + + ]: 316 : else if (pg_add_s64_overflow(base, offset->time, &sum))
2244 : 144 : PG_RETURN_BOOL(less);
2245 : :
3034 tgl@sss.pgh.pa.us 2246 [ + + ]: 488 : if (less)
2247 : 220 : PG_RETURN_BOOL(val <= sum);
2248 : : else
2249 : 268 : PG_RETURN_BOOL(val >= sum);
2250 : : }
2251 : :
2252 : :
2253 : : /*
2254 : : * time_part() and extract_time()
2255 : : * Extract specified field from time type.
2256 : : */
2257 : : static Datum
1880 peter@eisentraut.org 2258 : 62 : time_part_common(PG_FUNCTION_ARGS, bool retnumeric)
2259 : : {
6640 tgl@sss.pgh.pa.us 2260 : 62 : text *units = PG_GETARG_TEXT_PP(0);
8805 lockhart@fourpalms.o 2261 : 62 : TimeADT time = PG_GETARG_TIMEADT(1);
2262 : : int64 intresult;
2263 : : int type,
2264 : : val;
2265 : : char *lowunits;
2266 : :
6640 tgl@sss.pgh.pa.us 2267 [ - + ]: 62 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
2268 [ - + - - : 62 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
2269 : : false);
2270 : :
8805 lockhart@fourpalms.o 2271 : 62 : type = DecodeUnits(0, lowunits, &val);
2272 [ + + ]: 62 : if (type == UNKNOWN_FIELD)
2273 : 14 : type = DecodeSpecial(0, lowunits, &val);
2274 : :
2275 [ + + ]: 62 : if (type == UNITS)
2276 : : {
2277 : : fsec_t fsec;
2278 : : struct pg_tm tt,
2279 : 48 : *tm = &tt;
2280 : :
2281 : 48 : time2tm(time, tm, &fsec);
2282 : :
2283 [ + + + + : 48 : switch (val)
+ + ]
2284 : : {
2285 : 10 : case DTK_MICROSEC:
1875 tgl@sss.pgh.pa.us 2286 : 10 : intresult = tm->tm_sec * INT64CONST(1000000) + fsec;
8805 lockhart@fourpalms.o 2287 : 10 : break;
2288 : :
2289 : 10 : case DTK_MILLISEC:
1880 peter@eisentraut.org 2290 [ + + ]: 10 : if (retnumeric)
2291 : : /*---
2292 : : * tm->tm_sec * 1000 + fsec / 1000
2293 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1000
2294 : : */
1875 tgl@sss.pgh.pa.us 2295 : 20 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 3));
2296 : : else
1880 peter@eisentraut.org 2297 : 5 : PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0);
2298 : : break;
2299 : :
8805 lockhart@fourpalms.o 2300 : 10 : case DTK_SECOND:
1880 peter@eisentraut.org 2301 [ + + ]: 10 : if (retnumeric)
2302 : : /*---
2303 : : * tm->tm_sec + fsec / 1'000'000
2304 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
2305 : : */
1875 tgl@sss.pgh.pa.us 2306 : 5 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 6));
2307 : : else
1880 peter@eisentraut.org 2308 : 5 : PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0);
2309 : : break;
2310 : :
8805 lockhart@fourpalms.o 2311 : 5 : case DTK_MINUTE:
1880 peter@eisentraut.org 2312 : 5 : intresult = tm->tm_min;
8805 lockhart@fourpalms.o 2313 : 5 : break;
2314 : :
2315 : 5 : case DTK_HOUR:
1880 peter@eisentraut.org 2316 : 5 : intresult = tm->tm_hour;
8805 lockhart@fourpalms.o 2317 : 5 : break;
2318 : :
2319 : 8 : case DTK_TZ:
2320 : : case DTK_TZ_MINUTE:
2321 : : case DTK_TZ_HOUR:
2322 : : case DTK_DAY:
2323 : : case DTK_MONTH:
2324 : : case DTK_QUARTER:
2325 : : case DTK_YEAR:
2326 : : case DTK_DECADE:
2327 : : case DTK_CENTURY:
2328 : : case DTK_MILLENNIUM:
2329 : : case DTK_ISOYEAR:
2330 : : default:
8343 tgl@sss.pgh.pa.us 2331 [ + - ]: 8 : ereport(ERROR,
2332 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2333 : : errmsg("unit \"%s\" not supported for type %s",
2334 : : lowunits, format_type_be(TIMEOID))));
2335 : : intresult = 0;
2336 : : }
2337 : : }
7676 bruce@momjian.us 2338 [ + + + - ]: 14 : else if (type == RESERV && val == DTK_EPOCH)
2339 : : {
1880 peter@eisentraut.org 2340 [ + + ]: 10 : if (retnumeric)
2341 : 5 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(time, 6));
2342 : : else
2343 : 5 : PG_RETURN_FLOAT8(time / 1000000.0);
2344 : : }
2345 : : else
2346 : : {
8343 tgl@sss.pgh.pa.us 2347 [ + - ]: 4 : ereport(ERROR,
2348 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2349 : : errmsg("unit \"%s\" not recognized for type %s",
2350 : : lowunits, format_type_be(TIMEOID))));
2351 : : intresult = 0;
2352 : : }
2353 : :
1880 peter@eisentraut.org 2354 [ + + ]: 20 : if (retnumeric)
2355 : 15 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
2356 : : else
2357 : 5 : PG_RETURN_FLOAT8(intresult);
2358 : : }
2359 : :
2360 : : Datum
2361 : 20 : time_part(PG_FUNCTION_ARGS)
2362 : : {
2363 : 20 : return time_part_common(fcinfo, false);
2364 : : }
2365 : :
2366 : : Datum
2367 : 42 : extract_time(PG_FUNCTION_ARGS)
2368 : : {
2369 : 42 : return time_part_common(fcinfo, true);
2370 : : }
2371 : :
2372 : :
2373 : : /*****************************************************************************
2374 : : * Time With Time Zone ADT
2375 : : *****************************************************************************/
2376 : :
2377 : : /*
2378 : : * tm2timetz()
2379 : : * Convert a tm structure to a time data type.
2380 : : */
2381 : : int
3265 tgl@sss.pgh.pa.us 2382 : 2549 : tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result)
2383 : : {
7618 bruce@momjian.us 2384 : 2549 : result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
7676 2385 : 2549 : USECS_PER_SEC) + fsec;
8805 lockhart@fourpalms.o 2386 : 2549 : result->zone = tz;
2387 : :
2388 : 2549 : return 0;
2389 : : }
2390 : :
2391 : : Datum
9486 tgl@sss.pgh.pa.us 2392 : 1364 : timetz_in(PG_FUNCTION_ARGS)
2393 : : {
2394 : 1364 : char *str = PG_GETARG_CSTRING(0);
2395 : : #ifdef NOT_USED
2396 : : Oid typelem = PG_GETARG_OID(1);
2397 : : #endif
9005 lockhart@fourpalms.o 2398 : 1364 : int32 typmod = PG_GETARG_INT32(2);
1268 tgl@sss.pgh.pa.us 2399 : 1364 : Node *escontext = fcinfo->context;
2400 : : TimeTzADT *result;
2401 : : fsec_t fsec;
2402 : : struct pg_tm tt,
9573 lockhart@fourpalms.o 2403 : 1364 : *tm = &tt;
2404 : : int tz;
2405 : : int nf;
2406 : : int dterr;
2407 : : char workbuf[MAXDATELEN + 1];
2408 : : char *field[MAXDATEFIELDS];
2409 : : int dtype;
2410 : : int ftype[MAXDATEFIELDS];
2411 : : DateTimeErrorExtra extra;
2412 : :
7674 neilc@samurai.com 2413 : 1364 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
2414 : : field, ftype, MAXDATEFIELDS, &nf);
8312 tgl@sss.pgh.pa.us 2415 [ + - ]: 1364 : if (dterr == 0)
1268 2416 : 1364 : dterr = DecodeTimeOnly(field, ftype, nf,
2417 : : &dtype, tm, &fsec, &tz, &extra);
8312 2418 [ + + ]: 1364 : if (dterr != 0)
2419 : : {
1268 2420 : 52 : DateTimeParseError(dterr, &extra, str, "time with time zone",
2421 : : escontext);
2422 : 16 : PG_RETURN_NULL();
2423 : : }
2424 : :
171 michael@paquier.xyz 2425 :GNC 1312 : result = palloc_object(TimeTzADT);
8805 lockhart@fourpalms.o 2426 :CBC 1312 : tm2timetz(tm, fsec, tz, result);
9005 2427 : 1312 : AdjustTimeForTypmod(&(result->time), typmod);
2428 : :
2429 : 1312 : PG_RETURN_TIMETZADT_P(result);
2430 : : }
2431 : :
2432 : : Datum
9486 tgl@sss.pgh.pa.us 2433 : 4207 : timetz_out(PG_FUNCTION_ARGS)
2434 : : {
2435 : 4207 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2436 : : char *result;
2437 : : struct pg_tm tt,
9573 lockhart@fourpalms.o 2438 : 4207 : *tm = &tt;
2439 : : fsec_t fsec;
2440 : : int tz;
2441 : : char buf[MAXDATELEN + 1];
2442 : :
8805 2443 : 4207 : timetz2tm(time, tm, &fsec, &tz);
5190 peter_e@gmx.net 2444 : 4207 : EncodeTimeOnly(tm, fsec, true, tz, DateStyle, buf);
2445 : :
8805 lockhart@fourpalms.o 2446 : 4207 : result = pstrdup(buf);
2447 : 4207 : PG_RETURN_CSTRING(result);
2448 : : }
2449 : :
2450 : : /*
2451 : : * timetz_recv - converts external binary format to timetz
2452 : : */
2453 : : Datum
8419 tgl@sss.pgh.pa.us 2454 :UBC 0 : timetz_recv(PG_FUNCTION_ARGS)
2455 : : {
2456 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
2457 : :
2458 : : #ifdef NOT_USED
2459 : : Oid typelem = PG_GETARG_OID(1);
2460 : : #endif
7629 2461 : 0 : int32 typmod = PG_GETARG_INT32(2);
2462 : : TimeTzADT *result;
2463 : :
171 michael@paquier.xyz 2464 :UNC 0 : result = palloc_object(TimeTzADT);
2465 : :
7629 tgl@sss.pgh.pa.us 2466 :UBC 0 : result->time = pq_getmsgint64(buf);
2467 : :
6213 2468 [ # # # # ]: 0 : if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
2469 [ # # ]: 0 : ereport(ERROR,
2470 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2471 : : errmsg("time out of range")));
2472 : :
7629 2473 : 0 : result->zone = pq_getmsgint(buf, sizeof(result->zone));
2474 : :
2475 : : /* Check for sane GMT displacement; see notes in datatype/timestamp.h */
5113 2476 [ # # # # ]: 0 : if (result->zone <= -TZDISP_LIMIT || result->zone >= TZDISP_LIMIT)
6213 2477 [ # # ]: 0 : ereport(ERROR,
2478 : : (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
2479 : : errmsg("time zone displacement out of range")));
2480 : :
7629 2481 : 0 : AdjustTimeForTypmod(&(result->time), typmod);
2482 : :
2483 : 0 : PG_RETURN_TIMETZADT_P(result);
2484 : : }
2485 : :
2486 : : /*
2487 : : * timetz_send - converts timetz to binary format
2488 : : */
2489 : : Datum
8419 2490 : 0 : timetz_send(PG_FUNCTION_ARGS)
2491 : : {
2492 : 0 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2493 : : StringInfoData buf;
2494 : :
2495 : 0 : pq_begintypsend(&buf);
2496 : 0 : pq_sendint64(&buf, time->time);
3153 andres@anarazel.de 2497 : 0 : pq_sendint32(&buf, time->zone);
8419 tgl@sss.pgh.pa.us 2498 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
2499 : : }
2500 : :
2501 : : Datum
7091 tgl@sss.pgh.pa.us 2502 :CBC 14 : timetztypmodin(PG_FUNCTION_ARGS)
2503 : : {
6771 bruce@momjian.us 2504 : 14 : ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
2505 : :
7091 tgl@sss.pgh.pa.us 2506 : 14 : PG_RETURN_INT32(anytime_typmodin(true, ta));
2507 : : }
2508 : :
2509 : : Datum
2510 : 5 : timetztypmodout(PG_FUNCTION_ARGS)
2511 : : {
6771 bruce@momjian.us 2512 : 5 : int32 typmod = PG_GETARG_INT32(0);
2513 : :
7091 tgl@sss.pgh.pa.us 2514 : 5 : PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
2515 : : }
2516 : :
2517 : :
2518 : : /*
2519 : : * timetz2tm()
2520 : : * Convert TIME WITH TIME ZONE data type to POSIX time structure.
2521 : : */
2522 : : int
3265 2523 : 4494 : timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
2524 : : {
6644 2525 : 4494 : TimeOffset trem = time->time;
2526 : :
7676 bruce@momjian.us 2527 : 4494 : tm->tm_hour = trem / USECS_PER_HOUR;
2528 : 4494 : trem -= tm->tm_hour * USECS_PER_HOUR;
2529 : 4494 : tm->tm_min = trem / USECS_PER_MINUTE;
2530 : 4494 : trem -= tm->tm_min * USECS_PER_MINUTE;
2531 : 4494 : tm->tm_sec = trem / USECS_PER_SEC;
2532 : 4494 : *fsec = trem - tm->tm_sec * USECS_PER_SEC;
2533 : :
8805 lockhart@fourpalms.o 2534 [ + - ]: 4494 : if (tzp != NULL)
2535 : 4494 : *tzp = time->zone;
2536 : :
2537 : 4494 : return 0;
2538 : : }
2539 : :
2540 : : /*
2541 : : * timetz_scale()
2542 : : * Adjust time type for specified scale factor.
2543 : : * Used by PostgreSQL type system to stuff columns.
2544 : : */
2545 : : Datum
9005 2546 : 64 : timetz_scale(PG_FUNCTION_ARGS)
2547 : : {
2548 : 64 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2549 : 64 : int32 typmod = PG_GETARG_INT32(1);
2550 : : TimeTzADT *result;
2551 : :
171 michael@paquier.xyz 2552 :GNC 64 : result = palloc_object(TimeTzADT);
2553 : :
9005 lockhart@fourpalms.o 2554 :CBC 64 : result->time = time->time;
2555 : 64 : result->zone = time->zone;
2556 : :
2557 : 64 : AdjustTimeForTypmod(&(result->time), typmod);
2558 : :
2559 : 64 : PG_RETURN_TIMETZADT_P(result);
2560 : : }
2561 : :
2562 : :
2563 : : static int
9158 tgl@sss.pgh.pa.us 2564 : 138364 : timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
2565 : : {
2566 : : TimeOffset t1,
2567 : : t2;
2568 : :
2569 : : /* Primary sort is by true (GMT-equivalent) time */
7677 bruce@momjian.us 2570 : 138364 : t1 = time1->time + (time1->zone * USECS_PER_SEC);
2571 : 138364 : t2 = time2->time + (time2->zone * USECS_PER_SEC);
2572 : :
9158 tgl@sss.pgh.pa.us 2573 [ + + ]: 138364 : if (t1 > t2)
2574 : 66170 : return 1;
2575 [ + + ]: 72194 : if (t1 < t2)
2576 : 65290 : return -1;
2577 : :
2578 : : /*
2579 : : * If same GMT time, sort by timezone; we only want to say that two
2580 : : * timetz's are equal if both the time and zone parts are equal.
2581 : : */
9010 lockhart@fourpalms.o 2582 [ + + ]: 6904 : if (time1->zone > time2->zone)
2583 : 52 : return 1;
2584 [ + + ]: 6852 : if (time1->zone < time2->zone)
2585 : 24 : return -1;
2586 : :
2587 : 6828 : return 0;
2588 : : }
2589 : :
2590 : : Datum
9486 tgl@sss.pgh.pa.us 2591 : 19247 : timetz_eq(PG_FUNCTION_ARGS)
2592 : : {
9200 bruce@momjian.us 2593 : 19247 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2594 : 19247 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2595 : :
9158 tgl@sss.pgh.pa.us 2596 : 19247 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
2597 : : }
2598 : :
2599 : : Datum
9486 tgl@sss.pgh.pa.us 2600 :UBC 0 : timetz_ne(PG_FUNCTION_ARGS)
2601 : : {
9200 bruce@momjian.us 2602 : 0 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2603 : 0 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2604 : :
9158 tgl@sss.pgh.pa.us 2605 : 0 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
2606 : : }
2607 : :
2608 : : Datum
9486 tgl@sss.pgh.pa.us 2609 :CBC 93134 : timetz_lt(PG_FUNCTION_ARGS)
2610 : : {
9200 bruce@momjian.us 2611 : 93134 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2612 : 93134 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2613 : :
9158 tgl@sss.pgh.pa.us 2614 : 93134 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
2615 : : }
2616 : :
2617 : : Datum
9486 2618 : 4859 : timetz_le(PG_FUNCTION_ARGS)
2619 : : {
9200 bruce@momjian.us 2620 : 4859 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2621 : 4859 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2622 : :
9158 tgl@sss.pgh.pa.us 2623 : 4859 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
2624 : : }
2625 : :
2626 : : Datum
9486 2627 : 5472 : timetz_gt(PG_FUNCTION_ARGS)
2628 : : {
9200 bruce@momjian.us 2629 : 5472 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2630 : 5472 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2631 : :
9158 tgl@sss.pgh.pa.us 2632 : 5472 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
2633 : : }
2634 : :
2635 : : Datum
9486 2636 : 4753 : timetz_ge(PG_FUNCTION_ARGS)
2637 : : {
9200 bruce@momjian.us 2638 : 4753 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2639 : 4753 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2640 : :
9158 tgl@sss.pgh.pa.us 2641 : 4753 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
2642 : : }
2643 : :
2644 : : Datum
9486 2645 : 10407 : timetz_cmp(PG_FUNCTION_ARGS)
2646 : : {
9200 bruce@momjian.us 2647 : 10407 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2648 : 10407 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2649 : :
9158 tgl@sss.pgh.pa.us 2650 : 10407 : PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
2651 : : }
2652 : :
2653 : : Datum
9476 2654 : 1516 : timetz_hash(PG_FUNCTION_ARGS)
2655 : : {
9200 bruce@momjian.us 2656 : 1516 : TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2657 : : uint32 thash;
2658 : :
2659 : : /*
2660 : : * To avoid any problems with padding bytes in the struct, we figure the
2661 : : * field hashes separately and XOR them.
2662 : : */
6903 tgl@sss.pgh.pa.us 2663 : 1516 : thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
2664 : : Int64GetDatumFast(key->time)));
2665 : 1516 : thash ^= DatumGetUInt32(hash_uint32(key->zone));
2666 : 1516 : PG_RETURN_UINT32(thash);
2667 : : }
2668 : :
2669 : : Datum
3194 rhaas@postgresql.org 2670 : 40 : timetz_hash_extended(PG_FUNCTION_ARGS)
2671 : : {
2672 : 40 : TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
3193 2673 : 40 : Datum seed = PG_GETARG_DATUM(1);
2674 : : uint64 thash;
2675 : :
2676 : : /* Same approach as timetz_hash */
3194 2677 : 40 : thash = DatumGetUInt64(DirectFunctionCall2(hashint8extended,
2678 : : Int64GetDatumFast(key->time),
2679 : : seed));
3193 2680 : 40 : thash ^= DatumGetUInt64(hash_uint32_extended(key->zone,
3104 2681 : 40 : DatumGetInt64(seed)));
3194 2682 : 40 : PG_RETURN_UINT64(thash);
2683 : : }
2684 : :
2685 : : Datum
9486 tgl@sss.pgh.pa.us 2686 :UBC 0 : timetz_larger(PG_FUNCTION_ARGS)
2687 : : {
9200 bruce@momjian.us 2688 : 0 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2689 : 0 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2690 : : TimeTzADT *result;
2691 : :
8331 tgl@sss.pgh.pa.us 2692 [ # # ]: 0 : if (timetz_cmp_internal(time1, time2) > 0)
2693 : 0 : result = time1;
2694 : : else
2695 : 0 : result = time2;
2696 : 0 : PG_RETURN_TIMETZADT_P(result);
2697 : : }
2698 : :
2699 : : Datum
9486 2700 : 0 : timetz_smaller(PG_FUNCTION_ARGS)
2701 : : {
9200 bruce@momjian.us 2702 : 0 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2703 : 0 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2704 : : TimeTzADT *result;
2705 : :
8331 tgl@sss.pgh.pa.us 2706 [ # # ]: 0 : if (timetz_cmp_internal(time1, time2) < 0)
2707 : 0 : result = time1;
2708 : : else
2709 : 0 : result = time2;
2710 : 0 : PG_RETURN_TIMETZADT_P(result);
2711 : : }
2712 : :
2713 : : /*
2714 : : * timetz_pl_interval()
2715 : : * Add interval to timetz.
2716 : : */
2717 : : Datum
9309 lockhart@fourpalms.o 2718 :CBC 1813 : timetz_pl_interval(PG_FUNCTION_ARGS)
2719 : : {
2720 : 1813 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2721 : 1813 : Interval *span = PG_GETARG_INTERVAL_P(1);
2722 : : TimeTzADT *result;
2723 : :
928 dean.a.rasheed@gmail 2724 : 1813 : if (INTERVAL_NOT_FINITE(span))
[ + + + -
- + + + +
- + - ]
2725 [ + - ]: 8 : ereport(ERROR,
2726 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2727 : : errmsg("cannot add infinite interval to time")));
2728 : :
171 michael@paquier.xyz 2729 :GNC 1805 : result = palloc_object(TimeTzADT);
2730 : :
7618 bruce@momjian.us 2731 :CBC 1805 : result->time = time->time + span->time;
2732 : 1805 : result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
8805 lockhart@fourpalms.o 2733 [ - + ]: 1805 : if (result->time < INT64CONST(0))
7677 bruce@momjian.us 2734 :UBC 0 : result->time += USECS_PER_DAY;
2735 : :
9309 lockhart@fourpalms.o 2736 :CBC 1805 : result->zone = time->zone;
2737 : :
2738 : 1805 : PG_RETURN_TIMETZADT_P(result);
2739 : : }
2740 : :
2741 : : /*
2742 : : * timetz_mi_interval()
2743 : : * Subtract interval from timetz.
2744 : : */
2745 : : Datum
2746 : 493 : timetz_mi_interval(PG_FUNCTION_ARGS)
2747 : : {
2748 : 493 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2749 : 493 : Interval *span = PG_GETARG_INTERVAL_P(1);
2750 : : TimeTzADT *result;
2751 : :
928 dean.a.rasheed@gmail 2752 : 493 : if (INTERVAL_NOT_FINITE(span))
[ + + + -
- + + + +
- + - ]
2753 [ + - ]: 8 : ereport(ERROR,
2754 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2755 : : errmsg("cannot subtract infinite interval from time")));
2756 : :
171 michael@paquier.xyz 2757 :GNC 485 : result = palloc_object(TimeTzADT);
2758 : :
7618 bruce@momjian.us 2759 :CBC 485 : result->time = time->time - span->time;
2760 : 485 : result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
8805 lockhart@fourpalms.o 2761 [ + + ]: 485 : if (result->time < INT64CONST(0))
7677 bruce@momjian.us 2762 : 53 : result->time += USECS_PER_DAY;
2763 : :
9309 lockhart@fourpalms.o 2764 : 485 : result->zone = time->zone;
2765 : :
2766 : 485 : PG_RETURN_TIMETZADT_P(result);
2767 : : }
2768 : :
2769 : : /*
2770 : : * in_range support function for timetz.
2771 : : */
2772 : : Datum
3034 tgl@sss.pgh.pa.us 2773 : 692 : in_range_timetz_interval(PG_FUNCTION_ARGS)
2774 : : {
2775 : 692 : TimeTzADT *val = PG_GETARG_TIMETZADT_P(0);
2776 : 692 : TimeTzADT *base = PG_GETARG_TIMETZADT_P(1);
2777 : 692 : Interval *offset = PG_GETARG_INTERVAL_P(2);
2778 : 692 : bool sub = PG_GETARG_BOOL(3);
2779 : 692 : bool less = PG_GETARG_BOOL(4);
2780 : : TimeTzADT sum;
2781 : :
2782 : : /*
2783 : : * Like timetz_pl_interval/timetz_mi_interval, we disregard the month and
2784 : : * day fields of the offset. So our test for negative should too. This
2785 : : * also catches -infinity, so we only need worry about +infinity below.
2786 : : */
2787 [ + + ]: 692 : if (offset->time < 0)
2788 [ + - ]: 8 : ereport(ERROR,
2789 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2790 : : errmsg("invalid preceding or following size in window function")));
2791 : :
2792 : : /*
2793 : : * We can't use timetz_pl_interval/timetz_mi_interval here, because their
2794 : : * wraparound behavior would give wrong (or at least undesirable) answers.
2795 : : * Fortunately the equivalent non-wrapping behavior is trivial, except
2796 : : * that adding an infinite (or very large) interval might cause integer
2797 : : * overflow. Subtraction cannot overflow here.
2798 : : */
2799 [ + + ]: 684 : if (sub)
2800 : 316 : sum.time = base->time - offset->time;
928 dean.a.rasheed@gmail 2801 [ + + ]: 368 : else if (pg_add_s64_overflow(base->time, offset->time, &sum.time))
2802 : 192 : PG_RETURN_BOOL(less);
3034 tgl@sss.pgh.pa.us 2803 : 492 : sum.zone = base->zone;
2804 : :
2805 [ + + ]: 492 : if (less)
2806 : 224 : PG_RETURN_BOOL(timetz_cmp_internal(val, &sum) <= 0);
2807 : : else
2808 : 268 : PG_RETURN_BOOL(timetz_cmp_internal(val, &sum) >= 0);
2809 : : }
2810 : :
2811 : : /*
2812 : : * overlaps_timetz() --- implements the SQL OVERLAPS operator.
2813 : : *
2814 : : * Algorithm is per SQL spec. This is much harder than you'd think
2815 : : * because the spec requires us to deliver a non-null answer in some cases
2816 : : * where some of the inputs are null.
2817 : : */
2818 : : Datum
9486 tgl@sss.pgh.pa.us 2819 :UBC 0 : overlaps_timetz(PG_FUNCTION_ARGS)
2820 : : {
2821 : : /*
2822 : : * The arguments are TimeTzADT *, but we leave them as generic Datums for
2823 : : * convenience of notation --- and to avoid dereferencing nulls.
2824 : : */
2825 : 0 : Datum ts1 = PG_GETARG_DATUM(0);
2826 : 0 : Datum te1 = PG_GETARG_DATUM(1);
2827 : 0 : Datum ts2 = PG_GETARG_DATUM(2);
2828 : 0 : Datum te2 = PG_GETARG_DATUM(3);
9305 2829 : 0 : bool ts1IsNull = PG_ARGISNULL(0);
2830 : 0 : bool te1IsNull = PG_ARGISNULL(1);
2831 : 0 : bool ts2IsNull = PG_ARGISNULL(2);
2832 : 0 : bool te2IsNull = PG_ARGISNULL(3);
2833 : :
2834 : : #define TIMETZ_GT(t1,t2) \
2835 : : DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2836 : : #define TIMETZ_LT(t1,t2) \
2837 : : DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2838 : :
2839 : : /*
2840 : : * If both endpoints of interval 1 are null, the result is null (unknown).
2841 : : * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2842 : : * take ts1 as the lesser endpoint.
2843 : : */
2844 [ # # ]: 0 : if (ts1IsNull)
2845 : : {
2846 [ # # ]: 0 : if (te1IsNull)
2847 : 0 : PG_RETURN_NULL();
2848 : : /* swap null for non-null */
9573 lockhart@fourpalms.o 2849 : 0 : ts1 = te1;
9305 tgl@sss.pgh.pa.us 2850 : 0 : te1IsNull = true;
2851 : : }
2852 [ # # ]: 0 : else if (!te1IsNull)
2853 : : {
2854 [ # # ]: 0 : if (TIMETZ_GT(ts1, te1))
2855 : : {
9200 bruce@momjian.us 2856 : 0 : Datum tt = ts1;
2857 : :
9305 tgl@sss.pgh.pa.us 2858 : 0 : ts1 = te1;
2859 : 0 : te1 = tt;
2860 : : }
2861 : : }
2862 : :
2863 : : /* Likewise for interval 2. */
2864 [ # # ]: 0 : if (ts2IsNull)
2865 : : {
2866 [ # # ]: 0 : if (te2IsNull)
2867 : 0 : PG_RETURN_NULL();
2868 : : /* swap null for non-null */
9573 lockhart@fourpalms.o 2869 : 0 : ts2 = te2;
9305 tgl@sss.pgh.pa.us 2870 : 0 : te2IsNull = true;
2871 : : }
2872 [ # # ]: 0 : else if (!te2IsNull)
2873 : : {
2874 [ # # ]: 0 : if (TIMETZ_GT(ts2, te2))
2875 : : {
9200 bruce@momjian.us 2876 : 0 : Datum tt = ts2;
2877 : :
9305 tgl@sss.pgh.pa.us 2878 : 0 : ts2 = te2;
2879 : 0 : te2 = tt;
2880 : : }
2881 : : }
2882 : :
2883 : : /*
2884 : : * At this point neither ts1 nor ts2 is null, so we can consider three
2885 : : * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2886 : : */
2887 [ # # ]: 0 : if (TIMETZ_GT(ts1, ts2))
2888 : : {
2889 : : /*
2890 : : * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2891 : : * in the presence of nulls it's not quite completely so.
2892 : : */
2893 [ # # ]: 0 : if (te2IsNull)
2894 : 0 : PG_RETURN_NULL();
2895 [ # # ]: 0 : if (TIMETZ_LT(ts1, te2))
2896 : 0 : PG_RETURN_BOOL(true);
2897 [ # # ]: 0 : if (te1IsNull)
2898 : 0 : PG_RETURN_NULL();
2899 : :
2900 : : /*
2901 : : * If te1 is not null then we had ts1 <= te1 above, and we just found
2902 : : * ts1 >= te2, hence te1 >= te2.
2903 : : */
2904 : 0 : PG_RETURN_BOOL(false);
2905 : : }
2906 [ # # ]: 0 : else if (TIMETZ_LT(ts1, ts2))
2907 : : {
2908 : : /* This case is ts2 < te1 OR te2 < te1 */
2909 [ # # ]: 0 : if (te1IsNull)
2910 : 0 : PG_RETURN_NULL();
2911 [ # # ]: 0 : if (TIMETZ_LT(ts2, te1))
2912 : 0 : PG_RETURN_BOOL(true);
2913 [ # # ]: 0 : if (te2IsNull)
2914 : 0 : PG_RETURN_NULL();
2915 : :
2916 : : /*
2917 : : * If te2 is not null then we had ts2 <= te2 above, and we just found
2918 : : * ts2 >= te1, hence te2 >= te1.
2919 : : */
2920 : 0 : PG_RETURN_BOOL(false);
2921 : : }
2922 : : else
2923 : : {
2924 : : /*
2925 : : * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2926 : : * rather silly way of saying "true if both are nonnull, else null".
2927 : : */
2928 [ # # # # ]: 0 : if (te1IsNull || te2IsNull)
2929 : 0 : PG_RETURN_NULL();
2930 : 0 : PG_RETURN_BOOL(true);
2931 : : }
2932 : :
2933 : : #undef TIMETZ_GT
2934 : : #undef TIMETZ_LT
2935 : : }
2936 : :
2937 : :
2938 : : Datum
9010 lockhart@fourpalms.o 2939 :CBC 56 : timetz_time(PG_FUNCTION_ARGS)
2940 : : {
2941 : 56 : TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
2942 : : TimeADT result;
2943 : :
2944 : : /* swallow the time zone and just return the time */
2945 : 56 : result = timetz->time;
2946 : :
2947 : 56 : PG_RETURN_TIMEADT(result);
2948 : : }
2949 : :
2950 : :
2951 : : Datum
2952 : 208 : time_timetz(PG_FUNCTION_ARGS)
2953 : : {
2954 : 208 : TimeADT time = PG_GETARG_TIMEADT(0);
2955 : : TimeTzADT *result;
2956 : : struct pg_tm tt,
2957 : 208 : *tm = &tt;
2958 : : fsec_t fsec;
2959 : : int tz;
2960 : :
8754 JanWieck@Yahoo.com 2961 : 208 : GetCurrentDateTime(tm);
8805 lockhart@fourpalms.o 2962 : 208 : time2tm(time, tm, &fsec);
6874 tgl@sss.pgh.pa.us 2963 : 208 : tz = DetermineTimeZoneOffset(tm, session_timezone);
2964 : :
171 michael@paquier.xyz 2965 :GNC 208 : result = palloc_object(TimeTzADT);
2966 : :
9010 lockhart@fourpalms.o 2967 :CBC 208 : result->time = time;
2968 : 208 : result->zone = tz;
2969 : :
2970 : 208 : PG_RETURN_TIMETZADT_P(result);
2971 : : }
2972 : :
2973 : :
2974 : : /*
2975 : : * timestamptz_timetz()
2976 : : * Convert timestamp to timetz data type.
2977 : : */
2978 : : Datum
2979 : 40 : timestamptz_timetz(PG_FUNCTION_ARGS)
2980 : : {
8983 bruce@momjian.us 2981 : 40 : TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2982 : : TimeTzADT *result;
2983 : : struct pg_tm tt,
9573 lockhart@fourpalms.o 2984 : 40 : *tm = &tt;
2985 : : int tz;
2986 : : fsec_t fsec;
2987 : :
9486 tgl@sss.pgh.pa.us 2988 [ + - - + ]: 40 : if (TIMESTAMP_NOT_FINITE(timestamp))
9010 lockhart@fourpalms.o 2989 :UBC 0 : PG_RETURN_NULL();
2990 : :
5189 peter_e@gmx.net 2991 [ - + ]:CBC 40 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
67 peter@eisentraut.org 2992 [ # # ]:UNC 0 : ereturn(fcinfo->context, (Datum) 0,
2993 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2994 : : errmsg("timestamp out of range")));
2995 : :
171 michael@paquier.xyz 2996 :GNC 40 : result = palloc_object(TimeTzADT);
2997 : :
8805 lockhart@fourpalms.o 2998 :CBC 40 : tm2timetz(tm, fsec, tz, result);
2999 : :
9486 tgl@sss.pgh.pa.us 3000 : 40 : PG_RETURN_TIMETZADT_P(result);
3001 : : }
3002 : :
3003 : :
3004 : : /*
3005 : : * datetimetz_timestamptz()
3006 : : * Convert date and timetz to timestamp with time zone data type.
3007 : : * Timestamp is stored in GMT, so add the time zone
3008 : : * stored with the timetz to the result.
3009 : : * - thomas 2000-03-10
3010 : : */
3011 : : Datum
9010 lockhart@fourpalms.o 3012 : 45 : datetimetz_timestamptz(PG_FUNCTION_ARGS)
3013 : : {
7532 bruce@momjian.us 3014 : 45 : DateADT date = PG_GETARG_DATEADT(0);
9486 tgl@sss.pgh.pa.us 3015 : 45 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
3016 : : TimestampTz result;
3017 : :
6437 3018 [ - + ]: 45 : if (DATE_IS_NOBEGIN(date))
6437 tgl@sss.pgh.pa.us 3019 :UBC 0 : TIMESTAMP_NOBEGIN(result);
6437 tgl@sss.pgh.pa.us 3020 [ - + ]:CBC 45 : else if (DATE_IS_NOEND(date))
6437 tgl@sss.pgh.pa.us 3021 :UBC 0 : TIMESTAMP_NOEND(result);
3022 : : else
3023 : : {
3024 : : /*
3025 : : * Date's range is wider than timestamp's, so check for boundaries.
3026 : : * Since dates have the same minimum values as timestamps, only upper
3027 : : * boundary need be checked for overflow.
3028 : : */
3727 tgl@sss.pgh.pa.us 3029 [ - + ]:CBC 45 : if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
3727 tgl@sss.pgh.pa.us 3030 [ # # ]:UBC 0 : ereport(ERROR,
3031 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3032 : : errmsg("date out of range for timestamp")));
6437 tgl@sss.pgh.pa.us 3033 :CBC 45 : result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
3034 : :
3035 : : /*
3036 : : * Since it is possible to go beyond allowed timestamptz range because
3037 : : * of time zone, check for allowed timestamp range after adding tz.
3038 : : */
3727 3039 [ + - - + ]: 45 : if (!IS_VALID_TIMESTAMP(result))
3727 tgl@sss.pgh.pa.us 3040 [ # # ]:UBC 0 : ereport(ERROR,
3041 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3042 : : errmsg("date out of range for timestamp")));
3043 : : }
3044 : :
9486 tgl@sss.pgh.pa.us 3045 :CBC 45 : PG_RETURN_TIMESTAMP(result);
3046 : : }
3047 : :
3048 : :
3049 : : /*
3050 : : * timetz_part() and extract_timetz()
3051 : : * Extract specified field from time type.
3052 : : */
3053 : : static Datum
1880 peter@eisentraut.org 3054 : 73 : timetz_part_common(PG_FUNCTION_ARGS, bool retnumeric)
3055 : : {
6640 tgl@sss.pgh.pa.us 3056 : 73 : text *units = PG_GETARG_TEXT_PP(0);
8990 lockhart@fourpalms.o 3057 : 73 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
3058 : : int64 intresult;
3059 : : int type,
3060 : : val;
3061 : : char *lowunits;
3062 : :
6640 tgl@sss.pgh.pa.us 3063 [ - + ]: 73 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
3064 [ - + - - : 73 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
3065 : : false);
3066 : :
8990 lockhart@fourpalms.o 3067 : 73 : type = DecodeUnits(0, lowunits, &val);
3068 [ + + ]: 73 : if (type == UNKNOWN_FIELD)
3069 : 14 : type = DecodeSpecial(0, lowunits, &val);
3070 : :
3071 [ + + ]: 73 : if (type == UNITS)
3072 : : {
3073 : : int tz;
3074 : : fsec_t fsec;
3075 : : struct pg_tm tt,
3076 : 59 : *tm = &tt;
3077 : :
8805 3078 : 59 : timetz2tm(time, tm, &fsec, &tz);
3079 : :
8990 3080 [ + + + + : 59 : switch (val)
+ + + +
+ ]
3081 : : {
3082 : 5 : case DTK_TZ:
1880 peter@eisentraut.org 3083 : 5 : intresult = -tz;
8990 lockhart@fourpalms.o 3084 : 5 : break;
3085 : :
3086 : 5 : case DTK_TZ_MINUTE:
1880 peter@eisentraut.org 3087 : 5 : intresult = (-tz / SECS_PER_MINUTE) % MINS_PER_HOUR;
8990 lockhart@fourpalms.o 3088 : 5 : break;
3089 : :
3090 : 5 : case DTK_TZ_HOUR:
1880 peter@eisentraut.org 3091 : 5 : intresult = -tz / SECS_PER_HOUR;
8990 lockhart@fourpalms.o 3092 : 5 : break;
3093 : :
3094 : 10 : case DTK_MICROSEC:
1875 tgl@sss.pgh.pa.us 3095 : 10 : intresult = tm->tm_sec * INT64CONST(1000000) + fsec;
8990 lockhart@fourpalms.o 3096 : 10 : break;
3097 : :
3098 : 10 : case DTK_MILLISEC:
1880 peter@eisentraut.org 3099 [ + + ]: 10 : if (retnumeric)
3100 : : /*---
3101 : : * tm->tm_sec * 1000 + fsec / 1000
3102 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1000
3103 : : */
1875 tgl@sss.pgh.pa.us 3104 : 20 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 3));
3105 : : else
1880 peter@eisentraut.org 3106 : 5 : PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0);
3107 : : break;
3108 : :
8990 lockhart@fourpalms.o 3109 : 10 : case DTK_SECOND:
1880 peter@eisentraut.org 3110 [ + + ]: 10 : if (retnumeric)
3111 : : /*---
3112 : : * tm->tm_sec + fsec / 1'000'000
3113 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
3114 : : */
1875 tgl@sss.pgh.pa.us 3115 : 5 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 6));
3116 : : else
1880 peter@eisentraut.org 3117 : 5 : PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0);
3118 : : break;
3119 : :
8990 lockhart@fourpalms.o 3120 : 5 : case DTK_MINUTE:
1880 peter@eisentraut.org 3121 : 5 : intresult = tm->tm_min;
8990 lockhart@fourpalms.o 3122 : 5 : break;
3123 : :
3124 : 5 : case DTK_HOUR:
1880 peter@eisentraut.org 3125 : 5 : intresult = tm->tm_hour;
8990 lockhart@fourpalms.o 3126 : 5 : break;
3127 : :
3128 : 4 : case DTK_DAY:
3129 : : case DTK_MONTH:
3130 : : case DTK_QUARTER:
3131 : : case DTK_YEAR:
3132 : : case DTK_DECADE:
3133 : : case DTK_CENTURY:
3134 : : case DTK_MILLENNIUM:
3135 : : default:
8343 tgl@sss.pgh.pa.us 3136 [ + - ]: 4 : ereport(ERROR,
3137 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3138 : : errmsg("unit \"%s\" not supported for type %s",
3139 : : lowunits, format_type_be(TIMETZOID))));
3140 : : intresult = 0;
3141 : : }
3142 : : }
7676 bruce@momjian.us 3143 [ + + + - ]: 14 : else if (type == RESERV && val == DTK_EPOCH)
3144 : : {
1880 peter@eisentraut.org 3145 [ + + ]: 10 : if (retnumeric)
3146 : : /*---
3147 : : * time->time / 1'000'000 + time->zone
3148 : : * = (time->time + time->zone * 1'000'000) / 1'000'000
3149 : : */
1875 tgl@sss.pgh.pa.us 3150 : 5 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(time->time + time->zone * INT64CONST(1000000), 6));
3151 : : else
1880 peter@eisentraut.org 3152 : 5 : PG_RETURN_FLOAT8(time->time / 1000000.0 + time->zone);
3153 : : }
3154 : : else
3155 : : {
8343 tgl@sss.pgh.pa.us 3156 [ + - ]: 4 : ereport(ERROR,
3157 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3158 : : errmsg("unit \"%s\" not recognized for type %s",
3159 : : lowunits, format_type_be(TIMETZOID))));
3160 : : intresult = 0;
3161 : : }
3162 : :
1880 peter@eisentraut.org 3163 [ + + ]: 35 : if (retnumeric)
3164 : 30 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
3165 : : else
3166 : 5 : PG_RETURN_FLOAT8(intresult);
3167 : : }
3168 : :
3169 : :
3170 : : Datum
3171 : 20 : timetz_part(PG_FUNCTION_ARGS)
3172 : : {
3173 : 20 : return timetz_part_common(fcinfo, false);
3174 : : }
3175 : :
3176 : : Datum
3177 : 53 : extract_timetz(PG_FUNCTION_ARGS)
3178 : : {
3179 : 53 : return timetz_part_common(fcinfo, true);
3180 : : }
3181 : :
3182 : : /*
3183 : : * timetz_zone()
3184 : : * Encode time with time zone type with specified time zone.
3185 : : * Applies DST rules as of the transaction start time.
3186 : : */
3187 : : Datum
9010 lockhart@fourpalms.o 3188 : 192 : timetz_zone(PG_FUNCTION_ARGS)
3189 : : {
6640 tgl@sss.pgh.pa.us 3190 : 192 : text *zone = PG_GETARG_TEXT_PP(0);
7654 bruce@momjian.us 3191 : 192 : TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
3192 : : TimeTzADT *result;
3193 : : int tz;
3194 : : char tzname[TZ_STRLEN_MAX + 1];
3195 : : int type,
3196 : : val;
3197 : : pg_tz *tzp;
3198 : :
3199 : : /*
3200 : : * Look up the requested timezone.
3201 : : */
6640 tgl@sss.pgh.pa.us 3202 : 192 : text_to_cstring_buffer(zone, tzname, sizeof(tzname));
3203 : :
1170 3204 : 192 : type = DecodeTimezoneName(tzname, &val, &tzp);
3205 : :
3206 [ + + ]: 192 : if (type == TZNAME_FIXED_OFFSET)
3207 : : {
3208 : : /* fixed-offset abbreviation */
4244 3209 : 144 : tz = -val;
3210 : : }
1170 3211 [ - + ]: 48 : else if (type == TZNAME_DYNTZ)
3212 : : {
3213 : : /* dynamic-offset abbreviation, resolve using transaction start time */
1727 tgl@sss.pgh.pa.us 3214 :UBC 0 : TimestampTz now = GetCurrentTransactionStartTimestamp();
3215 : : int isdst;
3216 : :
3217 : 0 : tz = DetermineTimeZoneAbbrevOffsetTS(now, tzname, tzp, &isdst);
3218 : : }
3219 : : else
3220 : : {
3221 : : /* Get the offset-from-GMT that is valid now for the zone name */
1170 tgl@sss.pgh.pa.us 3222 :CBC 48 : TimestampTz now = GetCurrentTransactionStartTimestamp();
3223 : : struct pg_tm tm;
3224 : : fsec_t fsec;
3225 : :
3226 [ - + ]: 48 : if (timestamp2tm(now, &tz, &tm, &fsec, NULL, tzp) != 0)
7568 tgl@sss.pgh.pa.us 3227 [ # # ]:UBC 0 : ereport(ERROR,
3228 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3229 : : errmsg("timestamp out of range")));
3230 : : }
3231 : :
171 michael@paquier.xyz 3232 :GNC 192 : result = palloc_object(TimeTzADT);
3233 : :
7654 bruce@momjian.us 3234 :CBC 192 : result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
3235 : : /* C99 modulo has the wrong sign convention for negative input */
3236 [ + + ]: 204 : while (result->time < INT64CONST(0))
3237 : 12 : result->time += USECS_PER_DAY;
956 tgl@sss.pgh.pa.us 3238 [ + + ]: 192 : if (result->time >= USECS_PER_DAY)
3239 : 24 : result->time %= USECS_PER_DAY;
3240 : :
7654 bruce@momjian.us 3241 : 192 : result->zone = tz;
3242 : :
9010 lockhart@fourpalms.o 3243 : 192 : PG_RETURN_TIMETZADT_P(result);
3244 : : }
3245 : :
3246 : : /*
3247 : : * timetz_izone()
3248 : : * Encode time with time zone type with specified time interval as time zone.
3249 : : */
3250 : : Datum
3251 : 112 : timetz_izone(PG_FUNCTION_ARGS)
3252 : : {
3253 : 112 : Interval *zone = PG_GETARG_INTERVAL_P(0);
3254 : 112 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
3255 : : TimeTzADT *result;
3256 : : int tz;
3257 : :
928 dean.a.rasheed@gmail 3258 : 112 : if (INTERVAL_NOT_FINITE(zone))
[ + + + -
- + + + +
- + - ]
3259 [ + - ]: 16 : ereport(ERROR,
3260 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3261 : : errmsg("interval time zone \"%s\" must be finite",
3262 : : DatumGetCString(DirectFunctionCall1(interval_out,
3263 : : PointerGetDatum(zone))))));
3264 : :
4867 tgl@sss.pgh.pa.us 3265 [ + - - + ]: 96 : if (zone->month != 0 || zone->day != 0)
8343 tgl@sss.pgh.pa.us 3266 [ # # ]:UBC 0 : ereport(ERROR,
3267 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3268 : : errmsg("interval time zone \"%s\" must not include months or days",
3269 : : DatumGetCString(DirectFunctionCall1(interval_out,
3270 : : PointerGetDatum(zone))))));
3271 : :
7677 bruce@momjian.us 3272 :CBC 96 : tz = -(zone->time / USECS_PER_SEC);
3273 : :
171 michael@paquier.xyz 3274 :GNC 96 : result = palloc_object(TimeTzADT);
3275 : :
7676 bruce@momjian.us 3276 :CBC 96 : result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
3277 : : /* C99 modulo has the wrong sign convention for negative input */
8805 lockhart@fourpalms.o 3278 [ + + ]: 108 : while (result->time < INT64CONST(0))
7677 bruce@momjian.us 3279 : 12 : result->time += USECS_PER_DAY;
956 tgl@sss.pgh.pa.us 3280 [ + + ]: 96 : if (result->time >= USECS_PER_DAY)
3281 : 8 : result->time %= USECS_PER_DAY;
3282 : :
9010 lockhart@fourpalms.o 3283 : 96 : result->zone = tz;
3284 : :
3285 : 96 : PG_RETURN_TIMETZADT_P(result);
3286 : : }
3287 : :
3288 : : /*
3289 : : * timetz_at_local()
3290 : : *
3291 : : * Unlike for timestamp[tz]_at_local, the type for timetz does not flip between
3292 : : * time with/without time zone, so we cannot just call the conversion function.
3293 : : */
3294 : : Datum
960 michael@paquier.xyz 3295 : 96 : timetz_at_local(PG_FUNCTION_ARGS)
3296 : : {
3297 : 96 : Datum time = PG_GETARG_DATUM(0);
3298 : 96 : const char *tzn = pg_get_timezone_name(session_timezone);
3299 : 96 : Datum zone = PointerGetDatum(cstring_to_text(tzn));
3300 : :
3301 : 96 : return DirectFunctionCall2(timetz_zone, zone, time);
3302 : : }
|