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