1 /*-------------------------------------------------------------------------
2 *
3 * date.c
4 * implements DATE and TIME data types specified in SQL standard
5 *
6 * Portions Copyright (c) 1996-2020, 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/sortsupport.h"
35
36 /*
37 * gcc's -ffast-math switch breaks routines that expect exact results from
38 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
39 */
40 #ifdef __FAST_MATH__
41 #error -ffast-math is known to break this code
42 #endif
43
44
45 /* common code for timetypmodin and timetztypmodin */
46 static int32
anytime_typmodin(bool istz,ArrayType * ta)47 anytime_typmodin(bool istz, ArrayType *ta)
48 {
49 int32 *tl;
50 int n;
51
52 tl = ArrayGetIntegerTypmods(ta, &n);
53
54 /*
55 * we're not too tense about good error message here because grammar
56 * shouldn't allow wrong number of modifiers for TIME
57 */
58 if (n != 1)
59 ereport(ERROR,
60 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
61 errmsg("invalid type modifier")));
62
63 return anytime_typmod_check(istz, tl[0]);
64 }
65
66 /* exported so parse_expr.c can use it */
67 int32
anytime_typmod_check(bool istz,int32 typmod)68 anytime_typmod_check(bool istz, int32 typmod)
69 {
70 if (typmod < 0)
71 ereport(ERROR,
72 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
73 errmsg("TIME(%d)%s precision must not be negative",
74 typmod, (istz ? " WITH TIME ZONE" : ""))));
75 if (typmod > MAX_TIME_PRECISION)
76 {
77 ereport(WARNING,
78 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
79 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
80 typmod, (istz ? " WITH TIME ZONE" : ""),
81 MAX_TIME_PRECISION)));
82 typmod = MAX_TIME_PRECISION;
83 }
84
85 return typmod;
86 }
87
88 /* common code for timetypmodout and timetztypmodout */
89 static char *
anytime_typmodout(bool istz,int32 typmod)90 anytime_typmodout(bool istz, int32 typmod)
91 {
92 const char *tz = istz ? " with time zone" : " without time zone";
93
94 if (typmod >= 0)
95 return psprintf("(%d)%s", (int) typmod, tz);
96 else
97 return psprintf("%s", tz);
98 }
99
100
101 /*****************************************************************************
102 * Date ADT
103 *****************************************************************************/
104
105
106 /* date_in()
107 * Given date text string, convert to internal date format.
108 */
109 Datum
date_in(PG_FUNCTION_ARGS)110 date_in(PG_FUNCTION_ARGS)
111 {
112 char *str = PG_GETARG_CSTRING(0);
113 DateADT date;
114 fsec_t fsec;
115 struct pg_tm tt,
116 *tm = &tt;
117 int tzp;
118 int dtype;
119 int nf;
120 int dterr;
121 char *field[MAXDATEFIELDS];
122 int ftype[MAXDATEFIELDS];
123 char workbuf[MAXDATELEN + 1];
124
125 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
126 field, ftype, MAXDATEFIELDS, &nf);
127 if (dterr == 0)
128 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
129 if (dterr != 0)
130 DateTimeParseError(dterr, str, "date");
131
132 switch (dtype)
133 {
134 case DTK_DATE:
135 break;
136
137 case DTK_EPOCH:
138 GetEpochTime(tm);
139 break;
140
141 case DTK_LATE:
142 DATE_NOEND(date);
143 PG_RETURN_DATEADT(date);
144
145 case DTK_EARLY:
146 DATE_NOBEGIN(date);
147 PG_RETURN_DATEADT(date);
148
149 default:
150 DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
151 break;
152 }
153
154 /* Prevent overflow in Julian-day routines */
155 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
156 ereport(ERROR,
157 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
158 errmsg("date out of range: \"%s\"", str)));
159
160 date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
161
162 /* Now check for just-out-of-range dates */
163 if (!IS_VALID_DATE(date))
164 ereport(ERROR,
165 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
166 errmsg("date out of range: \"%s\"", str)));
167
168 PG_RETURN_DATEADT(date);
169 }
170
171 /* date_out()
172 * Given internal format date, convert to text string.
173 */
174 Datum
date_out(PG_FUNCTION_ARGS)175 date_out(PG_FUNCTION_ARGS)
176 {
177 DateADT date = PG_GETARG_DATEADT(0);
178 char *result;
179 struct pg_tm tt,
180 *tm = &tt;
181 char buf[MAXDATELEN + 1];
182
183 if (DATE_NOT_FINITE(date))
184 EncodeSpecialDate(date, buf);
185 else
186 {
187 j2date(date + POSTGRES_EPOCH_JDATE,
188 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
189 EncodeDateOnly(tm, DateStyle, buf);
190 }
191
192 result = pstrdup(buf);
193 PG_RETURN_CSTRING(result);
194 }
195
196 /*
197 * date_recv - converts external binary format to date
198 */
199 Datum
date_recv(PG_FUNCTION_ARGS)200 date_recv(PG_FUNCTION_ARGS)
201 {
202 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
203 DateADT result;
204
205 result = (DateADT) pq_getmsgint(buf, sizeof(DateADT));
206
207 /* Limit to the same range that date_in() accepts. */
208 if (DATE_NOT_FINITE(result))
209 /* ok */ ;
210 else if (!IS_VALID_DATE(result))
211 ereport(ERROR,
212 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
213 errmsg("date out of range")));
214
215 PG_RETURN_DATEADT(result);
216 }
217
218 /*
219 * date_send - converts date to binary format
220 */
221 Datum
date_send(PG_FUNCTION_ARGS)222 date_send(PG_FUNCTION_ARGS)
223 {
224 DateADT date = PG_GETARG_DATEADT(0);
225 StringInfoData buf;
226
227 pq_begintypsend(&buf);
228 pq_sendint32(&buf, date);
229 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
230 }
231
232 /*
233 * make_date - date constructor
234 */
235 Datum
make_date(PG_FUNCTION_ARGS)236 make_date(PG_FUNCTION_ARGS)
237 {
238 struct pg_tm tm;
239 DateADT date;
240 int dterr;
241 bool bc = false;
242
243 tm.tm_year = PG_GETARG_INT32(0);
244 tm.tm_mon = PG_GETARG_INT32(1);
245 tm.tm_mday = PG_GETARG_INT32(2);
246
247 /* Handle negative years as BC */
248 if (tm.tm_year < 0)
249 {
250 bc = true;
251 tm.tm_year = -tm.tm_year;
252 }
253
254 dterr = ValidateDate(DTK_DATE_M, false, false, bc, &tm);
255
256 if (dterr != 0)
257 ereport(ERROR,
258 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
259 errmsg("date field value out of range: %d-%02d-%02d",
260 tm.tm_year, tm.tm_mon, tm.tm_mday)));
261
262 /* Prevent overflow in Julian-day routines */
263 if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
264 ereport(ERROR,
265 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
266 errmsg("date out of range: %d-%02d-%02d",
267 tm.tm_year, tm.tm_mon, tm.tm_mday)));
268
269 date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
270
271 /* Now check for just-out-of-range dates */
272 if (!IS_VALID_DATE(date))
273 ereport(ERROR,
274 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
275 errmsg("date out of range: %d-%02d-%02d",
276 tm.tm_year, tm.tm_mon, tm.tm_mday)));
277
278 PG_RETURN_DATEADT(date);
279 }
280
281 /*
282 * Convert reserved date values to string.
283 */
284 void
EncodeSpecialDate(DateADT dt,char * str)285 EncodeSpecialDate(DateADT dt, char *str)
286 {
287 if (DATE_IS_NOBEGIN(dt))
288 strcpy(str, EARLY);
289 else if (DATE_IS_NOEND(dt))
290 strcpy(str, LATE);
291 else /* shouldn't happen */
292 elog(ERROR, "invalid argument for EncodeSpecialDate");
293 }
294
295
296 /*
297 * GetSQLCurrentDate -- implements CURRENT_DATE
298 */
299 DateADT
GetSQLCurrentDate(void)300 GetSQLCurrentDate(void)
301 {
302 TimestampTz ts;
303 struct pg_tm tt,
304 *tm = &tt;
305 fsec_t fsec;
306 int tz;
307
308 ts = GetCurrentTransactionStartTimestamp();
309
310 if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
311 ereport(ERROR,
312 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
313 errmsg("timestamp out of range")));
314
315 return date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
316 }
317
318 /*
319 * GetSQLCurrentTime -- implements CURRENT_TIME, CURRENT_TIME(n)
320 */
321 TimeTzADT *
GetSQLCurrentTime(int32 typmod)322 GetSQLCurrentTime(int32 typmod)
323 {
324 TimeTzADT *result;
325 TimestampTz ts;
326 struct pg_tm tt,
327 *tm = &tt;
328 fsec_t fsec;
329 int tz;
330
331 ts = GetCurrentTransactionStartTimestamp();
332
333 if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
334 ereport(ERROR,
335 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
336 errmsg("timestamp out of range")));
337
338 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
339 tm2timetz(tm, fsec, tz, result);
340 AdjustTimeForTypmod(&(result->time), typmod);
341 return result;
342 }
343
344 /*
345 * GetSQLLocalTime -- implements LOCALTIME, LOCALTIME(n)
346 */
347 TimeADT
GetSQLLocalTime(int32 typmod)348 GetSQLLocalTime(int32 typmod)
349 {
350 TimeADT result;
351 TimestampTz ts;
352 struct pg_tm tt,
353 *tm = &tt;
354 fsec_t fsec;
355 int tz;
356
357 ts = GetCurrentTransactionStartTimestamp();
358
359 if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
360 ereport(ERROR,
361 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
362 errmsg("timestamp out of range")));
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 /* Add an interval to a date, giving a new date.
1068 * Must handle both positive and negative intervals.
1069 *
1070 * We implement this by promoting the date to timestamp (without time zone)
1071 * and then using the timestamp plus interval function.
1072 */
1073 Datum
date_pl_interval(PG_FUNCTION_ARGS)1074 date_pl_interval(PG_FUNCTION_ARGS)
1075 {
1076 DateADT dateVal = PG_GETARG_DATEADT(0);
1077 Interval *span = PG_GETARG_INTERVAL_P(1);
1078 Timestamp dateStamp;
1079
1080 dateStamp = date2timestamp(dateVal);
1081
1082 return DirectFunctionCall2(timestamp_pl_interval,
1083 TimestampGetDatum(dateStamp),
1084 PointerGetDatum(span));
1085 }
1086
1087 /* Subtract an interval from a date, giving a new date.
1088 * Must handle both positive and negative intervals.
1089 *
1090 * We implement this by promoting the date to timestamp (without time zone)
1091 * and then using the timestamp minus interval function.
1092 */
1093 Datum
date_mi_interval(PG_FUNCTION_ARGS)1094 date_mi_interval(PG_FUNCTION_ARGS)
1095 {
1096 DateADT dateVal = PG_GETARG_DATEADT(0);
1097 Interval *span = PG_GETARG_INTERVAL_P(1);
1098 Timestamp dateStamp;
1099
1100 dateStamp = date2timestamp(dateVal);
1101
1102 return DirectFunctionCall2(timestamp_mi_interval,
1103 TimestampGetDatum(dateStamp),
1104 PointerGetDatum(span));
1105 }
1106
1107 /* date_timestamp()
1108 * Convert date to timestamp data type.
1109 */
1110 Datum
date_timestamp(PG_FUNCTION_ARGS)1111 date_timestamp(PG_FUNCTION_ARGS)
1112 {
1113 DateADT dateVal = PG_GETARG_DATEADT(0);
1114 Timestamp result;
1115
1116 result = date2timestamp(dateVal);
1117
1118 PG_RETURN_TIMESTAMP(result);
1119 }
1120
1121 /* timestamp_date()
1122 * Convert timestamp to date data type.
1123 */
1124 Datum
timestamp_date(PG_FUNCTION_ARGS)1125 timestamp_date(PG_FUNCTION_ARGS)
1126 {
1127 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1128 DateADT result;
1129 struct pg_tm tt,
1130 *tm = &tt;
1131 fsec_t fsec;
1132
1133 if (TIMESTAMP_IS_NOBEGIN(timestamp))
1134 DATE_NOBEGIN(result);
1135 else if (TIMESTAMP_IS_NOEND(timestamp))
1136 DATE_NOEND(result);
1137 else
1138 {
1139 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1140 ereport(ERROR,
1141 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1142 errmsg("timestamp out of range")));
1143
1144 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1145 }
1146
1147 PG_RETURN_DATEADT(result);
1148 }
1149
1150
1151 /* date_timestamptz()
1152 * Convert date to timestamp with time zone data type.
1153 */
1154 Datum
date_timestamptz(PG_FUNCTION_ARGS)1155 date_timestamptz(PG_FUNCTION_ARGS)
1156 {
1157 DateADT dateVal = PG_GETARG_DATEADT(0);
1158 TimestampTz result;
1159
1160 result = date2timestamptz(dateVal);
1161
1162 PG_RETURN_TIMESTAMP(result);
1163 }
1164
1165
1166 /* timestamptz_date()
1167 * Convert timestamp with time zone to date data type.
1168 */
1169 Datum
timestamptz_date(PG_FUNCTION_ARGS)1170 timestamptz_date(PG_FUNCTION_ARGS)
1171 {
1172 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1173 DateADT result;
1174 struct pg_tm tt,
1175 *tm = &tt;
1176 fsec_t fsec;
1177 int tz;
1178
1179 if (TIMESTAMP_IS_NOBEGIN(timestamp))
1180 DATE_NOBEGIN(result);
1181 else if (TIMESTAMP_IS_NOEND(timestamp))
1182 DATE_NOEND(result);
1183 else
1184 {
1185 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
1186 ereport(ERROR,
1187 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1188 errmsg("timestamp out of range")));
1189
1190 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1191 }
1192
1193 PG_RETURN_DATEADT(result);
1194 }
1195
1196
1197 /*****************************************************************************
1198 * Time ADT
1199 *****************************************************************************/
1200
1201 Datum
time_in(PG_FUNCTION_ARGS)1202 time_in(PG_FUNCTION_ARGS)
1203 {
1204 char *str = PG_GETARG_CSTRING(0);
1205
1206 #ifdef NOT_USED
1207 Oid typelem = PG_GETARG_OID(1);
1208 #endif
1209 int32 typmod = PG_GETARG_INT32(2);
1210 TimeADT result;
1211 fsec_t fsec;
1212 struct pg_tm tt,
1213 *tm = &tt;
1214 int tz;
1215 int nf;
1216 int dterr;
1217 char workbuf[MAXDATELEN + 1];
1218 char *field[MAXDATEFIELDS];
1219 int dtype;
1220 int ftype[MAXDATEFIELDS];
1221
1222 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1223 field, ftype, MAXDATEFIELDS, &nf);
1224 if (dterr == 0)
1225 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1226 if (dterr != 0)
1227 DateTimeParseError(dterr, str, "time");
1228
1229 tm2time(tm, fsec, &result);
1230 AdjustTimeForTypmod(&result, typmod);
1231
1232 PG_RETURN_TIMEADT(result);
1233 }
1234
1235 /* tm2time()
1236 * Convert a tm structure to a time data type.
1237 */
1238 int
tm2time(struct pg_tm * tm,fsec_t fsec,TimeADT * result)1239 tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result)
1240 {
1241 *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
1242 * USECS_PER_SEC) + fsec;
1243 return 0;
1244 }
1245
1246 /* time_overflows()
1247 * Check to see if a broken-down time-of-day is out of range.
1248 */
1249 bool
time_overflows(int hour,int min,int sec,fsec_t fsec)1250 time_overflows(int hour, int min, int sec, fsec_t fsec)
1251 {
1252 /* Range-check the fields individually. */
1253 if (hour < 0 || hour > HOURS_PER_DAY ||
1254 min < 0 || min >= MINS_PER_HOUR ||
1255 sec < 0 || sec > SECS_PER_MINUTE ||
1256 fsec < 0 || fsec > USECS_PER_SEC)
1257 return true;
1258
1259 /*
1260 * Because we allow, eg, hour = 24 or sec = 60, we must check separately
1261 * that the total time value doesn't exceed 24:00:00.
1262 */
1263 if ((((((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
1264 + sec) * USECS_PER_SEC) + fsec) > USECS_PER_DAY)
1265 return true;
1266
1267 return false;
1268 }
1269
1270 /* float_time_overflows()
1271 * Same, when we have seconds + fractional seconds as one "double" value.
1272 */
1273 bool
float_time_overflows(int hour,int min,double sec)1274 float_time_overflows(int hour, int min, double sec)
1275 {
1276 /* Range-check the fields individually. */
1277 if (hour < 0 || hour > HOURS_PER_DAY ||
1278 min < 0 || min >= MINS_PER_HOUR)
1279 return true;
1280
1281 /*
1282 * "sec", being double, requires extra care. Cope with NaN, and round off
1283 * before applying the range check to avoid unexpected errors due to
1284 * imprecise input. (We assume rint() behaves sanely with infinities.)
1285 */
1286 if (isnan(sec))
1287 return true;
1288 sec = rint(sec * USECS_PER_SEC);
1289 if (sec < 0 || sec > SECS_PER_MINUTE * USECS_PER_SEC)
1290 return true;
1291
1292 /*
1293 * Because we allow, eg, hour = 24 or sec = 60, we must check separately
1294 * that the total time value doesn't exceed 24:00:00. This must match the
1295 * way that callers will convert the fields to a time.
1296 */
1297 if (((((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
1298 * USECS_PER_SEC) + (int64) sec) > USECS_PER_DAY)
1299 return true;
1300
1301 return false;
1302 }
1303
1304
1305 /* time2tm()
1306 * Convert time data type to POSIX time structure.
1307 *
1308 * For dates within the range of pg_time_t, convert to the local time zone.
1309 * If out of this range, leave as UTC (in practice that could only happen
1310 * if pg_time_t is just 32 bits) - thomas 97/05/27
1311 */
1312 int
time2tm(TimeADT time,struct pg_tm * tm,fsec_t * fsec)1313 time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
1314 {
1315 tm->tm_hour = time / USECS_PER_HOUR;
1316 time -= tm->tm_hour * USECS_PER_HOUR;
1317 tm->tm_min = time / USECS_PER_MINUTE;
1318 time -= tm->tm_min * USECS_PER_MINUTE;
1319 tm->tm_sec = time / USECS_PER_SEC;
1320 time -= tm->tm_sec * USECS_PER_SEC;
1321 *fsec = time;
1322 return 0;
1323 }
1324
1325 Datum
time_out(PG_FUNCTION_ARGS)1326 time_out(PG_FUNCTION_ARGS)
1327 {
1328 TimeADT time = PG_GETARG_TIMEADT(0);
1329 char *result;
1330 struct pg_tm tt,
1331 *tm = &tt;
1332 fsec_t fsec;
1333 char buf[MAXDATELEN + 1];
1334
1335 time2tm(time, tm, &fsec);
1336 EncodeTimeOnly(tm, fsec, false, 0, DateStyle, buf);
1337
1338 result = pstrdup(buf);
1339 PG_RETURN_CSTRING(result);
1340 }
1341
1342 /*
1343 * time_recv - converts external binary format to time
1344 */
1345 Datum
time_recv(PG_FUNCTION_ARGS)1346 time_recv(PG_FUNCTION_ARGS)
1347 {
1348 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1349
1350 #ifdef NOT_USED
1351 Oid typelem = PG_GETARG_OID(1);
1352 #endif
1353 int32 typmod = PG_GETARG_INT32(2);
1354 TimeADT result;
1355
1356 result = pq_getmsgint64(buf);
1357
1358 if (result < INT64CONST(0) || result > USECS_PER_DAY)
1359 ereport(ERROR,
1360 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1361 errmsg("time out of range")));
1362
1363 AdjustTimeForTypmod(&result, typmod);
1364
1365 PG_RETURN_TIMEADT(result);
1366 }
1367
1368 /*
1369 * time_send - converts time to binary format
1370 */
1371 Datum
time_send(PG_FUNCTION_ARGS)1372 time_send(PG_FUNCTION_ARGS)
1373 {
1374 TimeADT time = PG_GETARG_TIMEADT(0);
1375 StringInfoData buf;
1376
1377 pq_begintypsend(&buf);
1378 pq_sendint64(&buf, time);
1379 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1380 }
1381
1382 Datum
timetypmodin(PG_FUNCTION_ARGS)1383 timetypmodin(PG_FUNCTION_ARGS)
1384 {
1385 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1386
1387 PG_RETURN_INT32(anytime_typmodin(false, ta));
1388 }
1389
1390 Datum
timetypmodout(PG_FUNCTION_ARGS)1391 timetypmodout(PG_FUNCTION_ARGS)
1392 {
1393 int32 typmod = PG_GETARG_INT32(0);
1394
1395 PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
1396 }
1397
1398 /*
1399 * make_time - time constructor
1400 */
1401 Datum
make_time(PG_FUNCTION_ARGS)1402 make_time(PG_FUNCTION_ARGS)
1403 {
1404 int tm_hour = PG_GETARG_INT32(0);
1405 int tm_min = PG_GETARG_INT32(1);
1406 double sec = PG_GETARG_FLOAT8(2);
1407 TimeADT time;
1408
1409 /* Check for time overflow */
1410 if (float_time_overflows(tm_hour, tm_min, sec))
1411 ereport(ERROR,
1412 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
1413 errmsg("time field value out of range: %d:%02d:%02g",
1414 tm_hour, tm_min, sec)));
1415
1416 /* This should match tm2time */
1417 time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
1418 * USECS_PER_SEC) + (int64) rint(sec * USECS_PER_SEC);
1419
1420 PG_RETURN_TIMEADT(time);
1421 }
1422
1423
1424 /* time_support()
1425 *
1426 * Planner support function for the time_scale() and timetz_scale()
1427 * length coercion functions (we need not distinguish them here).
1428 */
1429 Datum
time_support(PG_FUNCTION_ARGS)1430 time_support(PG_FUNCTION_ARGS)
1431 {
1432 Node *rawreq = (Node *) PG_GETARG_POINTER(0);
1433 Node *ret = NULL;
1434
1435 if (IsA(rawreq, SupportRequestSimplify))
1436 {
1437 SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
1438
1439 ret = TemporalSimplify(MAX_TIME_PRECISION, (Node *) req->fcall);
1440 }
1441
1442 PG_RETURN_POINTER(ret);
1443 }
1444
1445 /* time_scale()
1446 * Adjust time type for specified scale factor.
1447 * Used by PostgreSQL type system to stuff columns.
1448 */
1449 Datum
time_scale(PG_FUNCTION_ARGS)1450 time_scale(PG_FUNCTION_ARGS)
1451 {
1452 TimeADT time = PG_GETARG_TIMEADT(0);
1453 int32 typmod = PG_GETARG_INT32(1);
1454 TimeADT result;
1455
1456 result = time;
1457 AdjustTimeForTypmod(&result, typmod);
1458
1459 PG_RETURN_TIMEADT(result);
1460 }
1461
1462 /* AdjustTimeForTypmod()
1463 * Force the precision of the time value to a specified value.
1464 * Uses *exactly* the same code as in AdjustTimestampForTypmod()
1465 * but we make a separate copy because those types do not
1466 * have a fundamental tie together but rather a coincidence of
1467 * implementation. - thomas
1468 */
1469 void
AdjustTimeForTypmod(TimeADT * time,int32 typmod)1470 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1471 {
1472 static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1473 INT64CONST(1000000),
1474 INT64CONST(100000),
1475 INT64CONST(10000),
1476 INT64CONST(1000),
1477 INT64CONST(100),
1478 INT64CONST(10),
1479 INT64CONST(1)
1480 };
1481
1482 static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1483 INT64CONST(500000),
1484 INT64CONST(50000),
1485 INT64CONST(5000),
1486 INT64CONST(500),
1487 INT64CONST(50),
1488 INT64CONST(5),
1489 INT64CONST(0)
1490 };
1491
1492 if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1493 {
1494 if (*time >= INT64CONST(0))
1495 *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
1496 TimeScales[typmod];
1497 else
1498 *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
1499 TimeScales[typmod]);
1500 }
1501 }
1502
1503
1504 Datum
time_eq(PG_FUNCTION_ARGS)1505 time_eq(PG_FUNCTION_ARGS)
1506 {
1507 TimeADT time1 = PG_GETARG_TIMEADT(0);
1508 TimeADT time2 = PG_GETARG_TIMEADT(1);
1509
1510 PG_RETURN_BOOL(time1 == time2);
1511 }
1512
1513 Datum
time_ne(PG_FUNCTION_ARGS)1514 time_ne(PG_FUNCTION_ARGS)
1515 {
1516 TimeADT time1 = PG_GETARG_TIMEADT(0);
1517 TimeADT time2 = PG_GETARG_TIMEADT(1);
1518
1519 PG_RETURN_BOOL(time1 != time2);
1520 }
1521
1522 Datum
time_lt(PG_FUNCTION_ARGS)1523 time_lt(PG_FUNCTION_ARGS)
1524 {
1525 TimeADT time1 = PG_GETARG_TIMEADT(0);
1526 TimeADT time2 = PG_GETARG_TIMEADT(1);
1527
1528 PG_RETURN_BOOL(time1 < time2);
1529 }
1530
1531 Datum
time_le(PG_FUNCTION_ARGS)1532 time_le(PG_FUNCTION_ARGS)
1533 {
1534 TimeADT time1 = PG_GETARG_TIMEADT(0);
1535 TimeADT time2 = PG_GETARG_TIMEADT(1);
1536
1537 PG_RETURN_BOOL(time1 <= time2);
1538 }
1539
1540 Datum
time_gt(PG_FUNCTION_ARGS)1541 time_gt(PG_FUNCTION_ARGS)
1542 {
1543 TimeADT time1 = PG_GETARG_TIMEADT(0);
1544 TimeADT time2 = PG_GETARG_TIMEADT(1);
1545
1546 PG_RETURN_BOOL(time1 > time2);
1547 }
1548
1549 Datum
time_ge(PG_FUNCTION_ARGS)1550 time_ge(PG_FUNCTION_ARGS)
1551 {
1552 TimeADT time1 = PG_GETARG_TIMEADT(0);
1553 TimeADT time2 = PG_GETARG_TIMEADT(1);
1554
1555 PG_RETURN_BOOL(time1 >= time2);
1556 }
1557
1558 Datum
time_cmp(PG_FUNCTION_ARGS)1559 time_cmp(PG_FUNCTION_ARGS)
1560 {
1561 TimeADT time1 = PG_GETARG_TIMEADT(0);
1562 TimeADT time2 = PG_GETARG_TIMEADT(1);
1563
1564 if (time1 < time2)
1565 PG_RETURN_INT32(-1);
1566 if (time1 > time2)
1567 PG_RETURN_INT32(1);
1568 PG_RETURN_INT32(0);
1569 }
1570
1571 Datum
time_hash(PG_FUNCTION_ARGS)1572 time_hash(PG_FUNCTION_ARGS)
1573 {
1574 return hashint8(fcinfo);
1575 }
1576
1577 Datum
time_hash_extended(PG_FUNCTION_ARGS)1578 time_hash_extended(PG_FUNCTION_ARGS)
1579 {
1580 return hashint8extended(fcinfo);
1581 }
1582
1583 Datum
time_larger(PG_FUNCTION_ARGS)1584 time_larger(PG_FUNCTION_ARGS)
1585 {
1586 TimeADT time1 = PG_GETARG_TIMEADT(0);
1587 TimeADT time2 = PG_GETARG_TIMEADT(1);
1588
1589 PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1590 }
1591
1592 Datum
time_smaller(PG_FUNCTION_ARGS)1593 time_smaller(PG_FUNCTION_ARGS)
1594 {
1595 TimeADT time1 = PG_GETARG_TIMEADT(0);
1596 TimeADT time2 = PG_GETARG_TIMEADT(1);
1597
1598 PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1599 }
1600
1601 /* overlaps_time() --- implements the SQL OVERLAPS operator.
1602 *
1603 * Algorithm is per SQL spec. This is much harder than you'd think
1604 * because the spec requires us to deliver a non-null answer in some cases
1605 * where some of the inputs are null.
1606 */
1607 Datum
overlaps_time(PG_FUNCTION_ARGS)1608 overlaps_time(PG_FUNCTION_ARGS)
1609 {
1610 /*
1611 * The arguments are TimeADT, but we leave them as generic Datums to avoid
1612 * dereferencing nulls (TimeADT is pass-by-reference!)
1613 */
1614 Datum ts1 = PG_GETARG_DATUM(0);
1615 Datum te1 = PG_GETARG_DATUM(1);
1616 Datum ts2 = PG_GETARG_DATUM(2);
1617 Datum te2 = PG_GETARG_DATUM(3);
1618 bool ts1IsNull = PG_ARGISNULL(0);
1619 bool te1IsNull = PG_ARGISNULL(1);
1620 bool ts2IsNull = PG_ARGISNULL(2);
1621 bool te2IsNull = PG_ARGISNULL(3);
1622
1623 #define TIMEADT_GT(t1,t2) \
1624 (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1625 #define TIMEADT_LT(t1,t2) \
1626 (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1627
1628 /*
1629 * If both endpoints of interval 1 are null, the result is null (unknown).
1630 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1631 * take ts1 as the lesser endpoint.
1632 */
1633 if (ts1IsNull)
1634 {
1635 if (te1IsNull)
1636 PG_RETURN_NULL();
1637 /* swap null for non-null */
1638 ts1 = te1;
1639 te1IsNull = true;
1640 }
1641 else if (!te1IsNull)
1642 {
1643 if (TIMEADT_GT(ts1, te1))
1644 {
1645 Datum tt = ts1;
1646
1647 ts1 = te1;
1648 te1 = tt;
1649 }
1650 }
1651
1652 /* Likewise for interval 2. */
1653 if (ts2IsNull)
1654 {
1655 if (te2IsNull)
1656 PG_RETURN_NULL();
1657 /* swap null for non-null */
1658 ts2 = te2;
1659 te2IsNull = true;
1660 }
1661 else if (!te2IsNull)
1662 {
1663 if (TIMEADT_GT(ts2, te2))
1664 {
1665 Datum tt = ts2;
1666
1667 ts2 = te2;
1668 te2 = tt;
1669 }
1670 }
1671
1672 /*
1673 * At this point neither ts1 nor ts2 is null, so we can consider three
1674 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1675 */
1676 if (TIMEADT_GT(ts1, ts2))
1677 {
1678 /*
1679 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
1680 * in the presence of nulls it's not quite completely so.
1681 */
1682 if (te2IsNull)
1683 PG_RETURN_NULL();
1684 if (TIMEADT_LT(ts1, te2))
1685 PG_RETURN_BOOL(true);
1686 if (te1IsNull)
1687 PG_RETURN_NULL();
1688
1689 /*
1690 * If te1 is not null then we had ts1 <= te1 above, and we just found
1691 * ts1 >= te2, hence te1 >= te2.
1692 */
1693 PG_RETURN_BOOL(false);
1694 }
1695 else if (TIMEADT_LT(ts1, ts2))
1696 {
1697 /* This case is ts2 < te1 OR te2 < te1 */
1698 if (te1IsNull)
1699 PG_RETURN_NULL();
1700 if (TIMEADT_LT(ts2, te1))
1701 PG_RETURN_BOOL(true);
1702 if (te2IsNull)
1703 PG_RETURN_NULL();
1704
1705 /*
1706 * If te2 is not null then we had ts2 <= te2 above, and we just found
1707 * ts2 >= te1, hence te2 >= te1.
1708 */
1709 PG_RETURN_BOOL(false);
1710 }
1711 else
1712 {
1713 /*
1714 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1715 * rather silly way of saying "true if both are nonnull, else null".
1716 */
1717 if (te1IsNull || te2IsNull)
1718 PG_RETURN_NULL();
1719 PG_RETURN_BOOL(true);
1720 }
1721
1722 #undef TIMEADT_GT
1723 #undef TIMEADT_LT
1724 }
1725
1726 /* timestamp_time()
1727 * Convert timestamp to time data type.
1728 */
1729 Datum
timestamp_time(PG_FUNCTION_ARGS)1730 timestamp_time(PG_FUNCTION_ARGS)
1731 {
1732 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1733 TimeADT result;
1734 struct pg_tm tt,
1735 *tm = &tt;
1736 fsec_t fsec;
1737
1738 if (TIMESTAMP_NOT_FINITE(timestamp))
1739 PG_RETURN_NULL();
1740
1741 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1742 ereport(ERROR,
1743 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1744 errmsg("timestamp out of range")));
1745
1746 /*
1747 * Could also do this with time = (timestamp / USECS_PER_DAY *
1748 * USECS_PER_DAY) - timestamp;
1749 */
1750 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1751 USECS_PER_SEC) + fsec;
1752
1753 PG_RETURN_TIMEADT(result);
1754 }
1755
1756 /* timestamptz_time()
1757 * Convert timestamptz to time data type.
1758 */
1759 Datum
timestamptz_time(PG_FUNCTION_ARGS)1760 timestamptz_time(PG_FUNCTION_ARGS)
1761 {
1762 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1763 TimeADT result;
1764 struct pg_tm tt,
1765 *tm = &tt;
1766 int tz;
1767 fsec_t fsec;
1768
1769 if (TIMESTAMP_NOT_FINITE(timestamp))
1770 PG_RETURN_NULL();
1771
1772 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
1773 ereport(ERROR,
1774 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1775 errmsg("timestamp out of range")));
1776
1777 /*
1778 * Could also do this with time = (timestamp / USECS_PER_DAY *
1779 * USECS_PER_DAY) - timestamp;
1780 */
1781 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1782 USECS_PER_SEC) + fsec;
1783
1784 PG_RETURN_TIMEADT(result);
1785 }
1786
1787 /* datetime_timestamp()
1788 * Convert date and time to timestamp data type.
1789 */
1790 Datum
datetime_timestamp(PG_FUNCTION_ARGS)1791 datetime_timestamp(PG_FUNCTION_ARGS)
1792 {
1793 DateADT date = PG_GETARG_DATEADT(0);
1794 TimeADT time = PG_GETARG_TIMEADT(1);
1795 Timestamp result;
1796
1797 result = date2timestamp(date);
1798 if (!TIMESTAMP_NOT_FINITE(result))
1799 {
1800 result += time;
1801 if (!IS_VALID_TIMESTAMP(result))
1802 ereport(ERROR,
1803 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1804 errmsg("timestamp out of range")));
1805 }
1806
1807 PG_RETURN_TIMESTAMP(result);
1808 }
1809
1810 /* time_interval()
1811 * Convert time to interval data type.
1812 */
1813 Datum
time_interval(PG_FUNCTION_ARGS)1814 time_interval(PG_FUNCTION_ARGS)
1815 {
1816 TimeADT time = PG_GETARG_TIMEADT(0);
1817 Interval *result;
1818
1819 result = (Interval *) palloc(sizeof(Interval));
1820
1821 result->time = time;
1822 result->day = 0;
1823 result->month = 0;
1824
1825 PG_RETURN_INTERVAL_P(result);
1826 }
1827
1828 /* interval_time()
1829 * Convert interval to time data type.
1830 *
1831 * This is defined as producing the fractional-day portion of the interval.
1832 * Therefore, we can just ignore the months field. It is not real clear
1833 * what to do with negative intervals, but we choose to subtract the floor,
1834 * so that, say, '-2 hours' becomes '22:00:00'.
1835 */
1836 Datum
interval_time(PG_FUNCTION_ARGS)1837 interval_time(PG_FUNCTION_ARGS)
1838 {
1839 Interval *span = PG_GETARG_INTERVAL_P(0);
1840 TimeADT result;
1841 int64 days;
1842
1843 result = span->time;
1844 if (result >= USECS_PER_DAY)
1845 {
1846 days = result / USECS_PER_DAY;
1847 result -= days * USECS_PER_DAY;
1848 }
1849 else if (result < 0)
1850 {
1851 days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
1852 result += days * USECS_PER_DAY;
1853 }
1854
1855 PG_RETURN_TIMEADT(result);
1856 }
1857
1858 /* time_mi_time()
1859 * Subtract two times to produce an interval.
1860 */
1861 Datum
time_mi_time(PG_FUNCTION_ARGS)1862 time_mi_time(PG_FUNCTION_ARGS)
1863 {
1864 TimeADT time1 = PG_GETARG_TIMEADT(0);
1865 TimeADT time2 = PG_GETARG_TIMEADT(1);
1866 Interval *result;
1867
1868 result = (Interval *) palloc(sizeof(Interval));
1869
1870 result->month = 0;
1871 result->day = 0;
1872 result->time = time1 - time2;
1873
1874 PG_RETURN_INTERVAL_P(result);
1875 }
1876
1877 /* time_pl_interval()
1878 * Add interval to time.
1879 */
1880 Datum
time_pl_interval(PG_FUNCTION_ARGS)1881 time_pl_interval(PG_FUNCTION_ARGS)
1882 {
1883 TimeADT time = PG_GETARG_TIMEADT(0);
1884 Interval *span = PG_GETARG_INTERVAL_P(1);
1885 TimeADT result;
1886
1887 result = time + span->time;
1888 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1889 if (result < INT64CONST(0))
1890 result += USECS_PER_DAY;
1891
1892 PG_RETURN_TIMEADT(result);
1893 }
1894
1895 /* time_mi_interval()
1896 * Subtract interval from time.
1897 */
1898 Datum
time_mi_interval(PG_FUNCTION_ARGS)1899 time_mi_interval(PG_FUNCTION_ARGS)
1900 {
1901 TimeADT time = PG_GETARG_TIMEADT(0);
1902 Interval *span = PG_GETARG_INTERVAL_P(1);
1903 TimeADT result;
1904
1905 result = time - span->time;
1906 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1907 if (result < INT64CONST(0))
1908 result += USECS_PER_DAY;
1909
1910 PG_RETURN_TIMEADT(result);
1911 }
1912
1913 /*
1914 * in_range support function for time.
1915 */
1916 Datum
in_range_time_interval(PG_FUNCTION_ARGS)1917 in_range_time_interval(PG_FUNCTION_ARGS)
1918 {
1919 TimeADT val = PG_GETARG_TIMEADT(0);
1920 TimeADT base = PG_GETARG_TIMEADT(1);
1921 Interval *offset = PG_GETARG_INTERVAL_P(2);
1922 bool sub = PG_GETARG_BOOL(3);
1923 bool less = PG_GETARG_BOOL(4);
1924 TimeADT sum;
1925
1926 /*
1927 * Like time_pl_interval/time_mi_interval, we disregard the month and day
1928 * fields of the offset. So our test for negative should too.
1929 */
1930 if (offset->time < 0)
1931 ereport(ERROR,
1932 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
1933 errmsg("invalid preceding or following size in window function")));
1934
1935 /*
1936 * We can't use time_pl_interval/time_mi_interval here, because their
1937 * wraparound behavior would give wrong (or at least undesirable) answers.
1938 * Fortunately the equivalent non-wrapping behavior is trivial, especially
1939 * since we don't worry about integer overflow.
1940 */
1941 if (sub)
1942 sum = base - offset->time;
1943 else
1944 sum = base + offset->time;
1945
1946 if (less)
1947 PG_RETURN_BOOL(val <= sum);
1948 else
1949 PG_RETURN_BOOL(val >= sum);
1950 }
1951
1952
1953 /* time_part()
1954 * Extract specified field from time type.
1955 */
1956 Datum
time_part(PG_FUNCTION_ARGS)1957 time_part(PG_FUNCTION_ARGS)
1958 {
1959 text *units = PG_GETARG_TEXT_PP(0);
1960 TimeADT time = PG_GETARG_TIMEADT(1);
1961 float8 result;
1962 int type,
1963 val;
1964 char *lowunits;
1965
1966 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
1967 VARSIZE_ANY_EXHDR(units),
1968 false);
1969
1970 type = DecodeUnits(0, lowunits, &val);
1971 if (type == UNKNOWN_FIELD)
1972 type = DecodeSpecial(0, lowunits, &val);
1973
1974 if (type == UNITS)
1975 {
1976 fsec_t fsec;
1977 struct pg_tm tt,
1978 *tm = &tt;
1979
1980 time2tm(time, tm, &fsec);
1981
1982 switch (val)
1983 {
1984 case DTK_MICROSEC:
1985 result = tm->tm_sec * 1000000.0 + fsec;
1986 break;
1987
1988 case DTK_MILLISEC:
1989 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
1990 break;
1991
1992 case DTK_SECOND:
1993 result = tm->tm_sec + fsec / 1000000.0;
1994 break;
1995
1996 case DTK_MINUTE:
1997 result = tm->tm_min;
1998 break;
1999
2000 case DTK_HOUR:
2001 result = tm->tm_hour;
2002 break;
2003
2004 case DTK_TZ:
2005 case DTK_TZ_MINUTE:
2006 case DTK_TZ_HOUR:
2007 case DTK_DAY:
2008 case DTK_MONTH:
2009 case DTK_QUARTER:
2010 case DTK_YEAR:
2011 case DTK_DECADE:
2012 case DTK_CENTURY:
2013 case DTK_MILLENNIUM:
2014 case DTK_ISOYEAR:
2015 default:
2016 ereport(ERROR,
2017 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2018 errmsg("\"time\" units \"%s\" not recognized",
2019 lowunits)));
2020 result = 0;
2021 }
2022 }
2023 else if (type == RESERV && val == DTK_EPOCH)
2024 {
2025 result = time / 1000000.0;
2026 }
2027 else
2028 {
2029 ereport(ERROR,
2030 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2031 errmsg("\"time\" units \"%s\" not recognized",
2032 lowunits)));
2033 result = 0;
2034 }
2035
2036 PG_RETURN_FLOAT8(result);
2037 }
2038
2039
2040 /*****************************************************************************
2041 * Time With Time Zone ADT
2042 *****************************************************************************/
2043
2044 /* tm2timetz()
2045 * Convert a tm structure to a time data type.
2046 */
2047 int
tm2timetz(struct pg_tm * tm,fsec_t fsec,int tz,TimeTzADT * result)2048 tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result)
2049 {
2050 result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
2051 USECS_PER_SEC) + fsec;
2052 result->zone = tz;
2053
2054 return 0;
2055 }
2056
2057 Datum
timetz_in(PG_FUNCTION_ARGS)2058 timetz_in(PG_FUNCTION_ARGS)
2059 {
2060 char *str = PG_GETARG_CSTRING(0);
2061
2062 #ifdef NOT_USED
2063 Oid typelem = PG_GETARG_OID(1);
2064 #endif
2065 int32 typmod = PG_GETARG_INT32(2);
2066 TimeTzADT *result;
2067 fsec_t fsec;
2068 struct pg_tm tt,
2069 *tm = &tt;
2070 int tz;
2071 int nf;
2072 int dterr;
2073 char workbuf[MAXDATELEN + 1];
2074 char *field[MAXDATEFIELDS];
2075 int dtype;
2076 int ftype[MAXDATEFIELDS];
2077
2078 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
2079 field, ftype, MAXDATEFIELDS, &nf);
2080 if (dterr == 0)
2081 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
2082 if (dterr != 0)
2083 DateTimeParseError(dterr, str, "time with time zone");
2084
2085 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2086 tm2timetz(tm, fsec, tz, result);
2087 AdjustTimeForTypmod(&(result->time), typmod);
2088
2089 PG_RETURN_TIMETZADT_P(result);
2090 }
2091
2092 Datum
timetz_out(PG_FUNCTION_ARGS)2093 timetz_out(PG_FUNCTION_ARGS)
2094 {
2095 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2096 char *result;
2097 struct pg_tm tt,
2098 *tm = &tt;
2099 fsec_t fsec;
2100 int tz;
2101 char buf[MAXDATELEN + 1];
2102
2103 timetz2tm(time, tm, &fsec, &tz);
2104 EncodeTimeOnly(tm, fsec, true, tz, DateStyle, buf);
2105
2106 result = pstrdup(buf);
2107 PG_RETURN_CSTRING(result);
2108 }
2109
2110 /*
2111 * timetz_recv - converts external binary format to timetz
2112 */
2113 Datum
timetz_recv(PG_FUNCTION_ARGS)2114 timetz_recv(PG_FUNCTION_ARGS)
2115 {
2116 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
2117
2118 #ifdef NOT_USED
2119 Oid typelem = PG_GETARG_OID(1);
2120 #endif
2121 int32 typmod = PG_GETARG_INT32(2);
2122 TimeTzADT *result;
2123
2124 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2125
2126 result->time = pq_getmsgint64(buf);
2127
2128 if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
2129 ereport(ERROR,
2130 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2131 errmsg("time out of range")));
2132
2133 result->zone = pq_getmsgint(buf, sizeof(result->zone));
2134
2135 /* Check for sane GMT displacement; see notes in datatype/timestamp.h */
2136 if (result->zone <= -TZDISP_LIMIT || result->zone >= TZDISP_LIMIT)
2137 ereport(ERROR,
2138 (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
2139 errmsg("time zone displacement out of range")));
2140
2141 AdjustTimeForTypmod(&(result->time), typmod);
2142
2143 PG_RETURN_TIMETZADT_P(result);
2144 }
2145
2146 /*
2147 * timetz_send - converts timetz to binary format
2148 */
2149 Datum
timetz_send(PG_FUNCTION_ARGS)2150 timetz_send(PG_FUNCTION_ARGS)
2151 {
2152 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2153 StringInfoData buf;
2154
2155 pq_begintypsend(&buf);
2156 pq_sendint64(&buf, time->time);
2157 pq_sendint32(&buf, time->zone);
2158 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
2159 }
2160
2161 Datum
timetztypmodin(PG_FUNCTION_ARGS)2162 timetztypmodin(PG_FUNCTION_ARGS)
2163 {
2164 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
2165
2166 PG_RETURN_INT32(anytime_typmodin(true, ta));
2167 }
2168
2169 Datum
timetztypmodout(PG_FUNCTION_ARGS)2170 timetztypmodout(PG_FUNCTION_ARGS)
2171 {
2172 int32 typmod = PG_GETARG_INT32(0);
2173
2174 PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
2175 }
2176
2177
2178 /* timetz2tm()
2179 * Convert TIME WITH TIME ZONE data type to POSIX time structure.
2180 */
2181 int
timetz2tm(TimeTzADT * time,struct pg_tm * tm,fsec_t * fsec,int * tzp)2182 timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
2183 {
2184 TimeOffset trem = time->time;
2185
2186 tm->tm_hour = trem / USECS_PER_HOUR;
2187 trem -= tm->tm_hour * USECS_PER_HOUR;
2188 tm->tm_min = trem / USECS_PER_MINUTE;
2189 trem -= tm->tm_min * USECS_PER_MINUTE;
2190 tm->tm_sec = trem / USECS_PER_SEC;
2191 *fsec = trem - tm->tm_sec * USECS_PER_SEC;
2192
2193 if (tzp != NULL)
2194 *tzp = time->zone;
2195
2196 return 0;
2197 }
2198
2199 /* timetz_scale()
2200 * Adjust time type for specified scale factor.
2201 * Used by PostgreSQL type system to stuff columns.
2202 */
2203 Datum
timetz_scale(PG_FUNCTION_ARGS)2204 timetz_scale(PG_FUNCTION_ARGS)
2205 {
2206 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2207 int32 typmod = PG_GETARG_INT32(1);
2208 TimeTzADT *result;
2209
2210 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2211
2212 result->time = time->time;
2213 result->zone = time->zone;
2214
2215 AdjustTimeForTypmod(&(result->time), typmod);
2216
2217 PG_RETURN_TIMETZADT_P(result);
2218 }
2219
2220
2221 static int
timetz_cmp_internal(TimeTzADT * time1,TimeTzADT * time2)2222 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
2223 {
2224 TimeOffset t1,
2225 t2;
2226
2227 /* Primary sort is by true (GMT-equivalent) time */
2228 t1 = time1->time + (time1->zone * USECS_PER_SEC);
2229 t2 = time2->time + (time2->zone * USECS_PER_SEC);
2230
2231 if (t1 > t2)
2232 return 1;
2233 if (t1 < t2)
2234 return -1;
2235
2236 /*
2237 * If same GMT time, sort by timezone; we only want to say that two
2238 * timetz's are equal if both the time and zone parts are equal.
2239 */
2240 if (time1->zone > time2->zone)
2241 return 1;
2242 if (time1->zone < time2->zone)
2243 return -1;
2244
2245 return 0;
2246 }
2247
2248 Datum
timetz_eq(PG_FUNCTION_ARGS)2249 timetz_eq(PG_FUNCTION_ARGS)
2250 {
2251 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2252 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2253
2254 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
2255 }
2256
2257 Datum
timetz_ne(PG_FUNCTION_ARGS)2258 timetz_ne(PG_FUNCTION_ARGS)
2259 {
2260 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2261 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2262
2263 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
2264 }
2265
2266 Datum
timetz_lt(PG_FUNCTION_ARGS)2267 timetz_lt(PG_FUNCTION_ARGS)
2268 {
2269 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2270 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2271
2272 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
2273 }
2274
2275 Datum
timetz_le(PG_FUNCTION_ARGS)2276 timetz_le(PG_FUNCTION_ARGS)
2277 {
2278 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2279 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2280
2281 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
2282 }
2283
2284 Datum
timetz_gt(PG_FUNCTION_ARGS)2285 timetz_gt(PG_FUNCTION_ARGS)
2286 {
2287 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2288 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2289
2290 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
2291 }
2292
2293 Datum
timetz_ge(PG_FUNCTION_ARGS)2294 timetz_ge(PG_FUNCTION_ARGS)
2295 {
2296 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2297 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2298
2299 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
2300 }
2301
2302 Datum
timetz_cmp(PG_FUNCTION_ARGS)2303 timetz_cmp(PG_FUNCTION_ARGS)
2304 {
2305 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2306 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2307
2308 PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
2309 }
2310
2311 Datum
timetz_hash(PG_FUNCTION_ARGS)2312 timetz_hash(PG_FUNCTION_ARGS)
2313 {
2314 TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2315 uint32 thash;
2316
2317 /*
2318 * To avoid any problems with padding bytes in the struct, we figure the
2319 * field hashes separately and XOR them.
2320 */
2321 thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
2322 Int64GetDatumFast(key->time)));
2323 thash ^= DatumGetUInt32(hash_uint32(key->zone));
2324 PG_RETURN_UINT32(thash);
2325 }
2326
2327 Datum
timetz_hash_extended(PG_FUNCTION_ARGS)2328 timetz_hash_extended(PG_FUNCTION_ARGS)
2329 {
2330 TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2331 Datum seed = PG_GETARG_DATUM(1);
2332 uint64 thash;
2333
2334 /* Same approach as timetz_hash */
2335 thash = DatumGetUInt64(DirectFunctionCall2(hashint8extended,
2336 Int64GetDatumFast(key->time),
2337 seed));
2338 thash ^= DatumGetUInt64(hash_uint32_extended(key->zone,
2339 DatumGetInt64(seed)));
2340 PG_RETURN_UINT64(thash);
2341 }
2342
2343 Datum
timetz_larger(PG_FUNCTION_ARGS)2344 timetz_larger(PG_FUNCTION_ARGS)
2345 {
2346 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2347 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2348 TimeTzADT *result;
2349
2350 if (timetz_cmp_internal(time1, time2) > 0)
2351 result = time1;
2352 else
2353 result = time2;
2354 PG_RETURN_TIMETZADT_P(result);
2355 }
2356
2357 Datum
timetz_smaller(PG_FUNCTION_ARGS)2358 timetz_smaller(PG_FUNCTION_ARGS)
2359 {
2360 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2361 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2362 TimeTzADT *result;
2363
2364 if (timetz_cmp_internal(time1, time2) < 0)
2365 result = time1;
2366 else
2367 result = time2;
2368 PG_RETURN_TIMETZADT_P(result);
2369 }
2370
2371 /* timetz_pl_interval()
2372 * Add interval to timetz.
2373 */
2374 Datum
timetz_pl_interval(PG_FUNCTION_ARGS)2375 timetz_pl_interval(PG_FUNCTION_ARGS)
2376 {
2377 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2378 Interval *span = PG_GETARG_INTERVAL_P(1);
2379 TimeTzADT *result;
2380
2381 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2382
2383 result->time = time->time + span->time;
2384 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2385 if (result->time < INT64CONST(0))
2386 result->time += USECS_PER_DAY;
2387
2388 result->zone = time->zone;
2389
2390 PG_RETURN_TIMETZADT_P(result);
2391 }
2392
2393 /* timetz_mi_interval()
2394 * Subtract interval from timetz.
2395 */
2396 Datum
timetz_mi_interval(PG_FUNCTION_ARGS)2397 timetz_mi_interval(PG_FUNCTION_ARGS)
2398 {
2399 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2400 Interval *span = PG_GETARG_INTERVAL_P(1);
2401 TimeTzADT *result;
2402
2403 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2404
2405 result->time = time->time - span->time;
2406 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2407 if (result->time < INT64CONST(0))
2408 result->time += USECS_PER_DAY;
2409
2410 result->zone = time->zone;
2411
2412 PG_RETURN_TIMETZADT_P(result);
2413 }
2414
2415 /*
2416 * in_range support function for timetz.
2417 */
2418 Datum
in_range_timetz_interval(PG_FUNCTION_ARGS)2419 in_range_timetz_interval(PG_FUNCTION_ARGS)
2420 {
2421 TimeTzADT *val = PG_GETARG_TIMETZADT_P(0);
2422 TimeTzADT *base = PG_GETARG_TIMETZADT_P(1);
2423 Interval *offset = PG_GETARG_INTERVAL_P(2);
2424 bool sub = PG_GETARG_BOOL(3);
2425 bool less = PG_GETARG_BOOL(4);
2426 TimeTzADT sum;
2427
2428 /*
2429 * Like timetz_pl_interval/timetz_mi_interval, we disregard the month and
2430 * day fields of the offset. So our test for negative should too.
2431 */
2432 if (offset->time < 0)
2433 ereport(ERROR,
2434 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2435 errmsg("invalid preceding or following size in window function")));
2436
2437 /*
2438 * We can't use timetz_pl_interval/timetz_mi_interval here, because their
2439 * wraparound behavior would give wrong (or at least undesirable) answers.
2440 * Fortunately the equivalent non-wrapping behavior is trivial, especially
2441 * since we don't worry about integer overflow.
2442 */
2443 if (sub)
2444 sum.time = base->time - offset->time;
2445 else
2446 sum.time = base->time + offset->time;
2447 sum.zone = base->zone;
2448
2449 if (less)
2450 PG_RETURN_BOOL(timetz_cmp_internal(val, &sum) <= 0);
2451 else
2452 PG_RETURN_BOOL(timetz_cmp_internal(val, &sum) >= 0);
2453 }
2454
2455 /* overlaps_timetz() --- implements the SQL OVERLAPS operator.
2456 *
2457 * Algorithm is per SQL spec. This is much harder than you'd think
2458 * because the spec requires us to deliver a non-null answer in some cases
2459 * where some of the inputs are null.
2460 */
2461 Datum
overlaps_timetz(PG_FUNCTION_ARGS)2462 overlaps_timetz(PG_FUNCTION_ARGS)
2463 {
2464 /*
2465 * The arguments are TimeTzADT *, but we leave them as generic Datums for
2466 * convenience of notation --- and to avoid dereferencing nulls.
2467 */
2468 Datum ts1 = PG_GETARG_DATUM(0);
2469 Datum te1 = PG_GETARG_DATUM(1);
2470 Datum ts2 = PG_GETARG_DATUM(2);
2471 Datum te2 = PG_GETARG_DATUM(3);
2472 bool ts1IsNull = PG_ARGISNULL(0);
2473 bool te1IsNull = PG_ARGISNULL(1);
2474 bool ts2IsNull = PG_ARGISNULL(2);
2475 bool te2IsNull = PG_ARGISNULL(3);
2476
2477 #define TIMETZ_GT(t1,t2) \
2478 DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2479 #define TIMETZ_LT(t1,t2) \
2480 DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2481
2482 /*
2483 * If both endpoints of interval 1 are null, the result is null (unknown).
2484 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2485 * take ts1 as the lesser endpoint.
2486 */
2487 if (ts1IsNull)
2488 {
2489 if (te1IsNull)
2490 PG_RETURN_NULL();
2491 /* swap null for non-null */
2492 ts1 = te1;
2493 te1IsNull = true;
2494 }
2495 else if (!te1IsNull)
2496 {
2497 if (TIMETZ_GT(ts1, te1))
2498 {
2499 Datum tt = ts1;
2500
2501 ts1 = te1;
2502 te1 = tt;
2503 }
2504 }
2505
2506 /* Likewise for interval 2. */
2507 if (ts2IsNull)
2508 {
2509 if (te2IsNull)
2510 PG_RETURN_NULL();
2511 /* swap null for non-null */
2512 ts2 = te2;
2513 te2IsNull = true;
2514 }
2515 else if (!te2IsNull)
2516 {
2517 if (TIMETZ_GT(ts2, te2))
2518 {
2519 Datum tt = ts2;
2520
2521 ts2 = te2;
2522 te2 = tt;
2523 }
2524 }
2525
2526 /*
2527 * At this point neither ts1 nor ts2 is null, so we can consider three
2528 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2529 */
2530 if (TIMETZ_GT(ts1, ts2))
2531 {
2532 /*
2533 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2534 * in the presence of nulls it's not quite completely so.
2535 */
2536 if (te2IsNull)
2537 PG_RETURN_NULL();
2538 if (TIMETZ_LT(ts1, te2))
2539 PG_RETURN_BOOL(true);
2540 if (te1IsNull)
2541 PG_RETURN_NULL();
2542
2543 /*
2544 * If te1 is not null then we had ts1 <= te1 above, and we just found
2545 * ts1 >= te2, hence te1 >= te2.
2546 */
2547 PG_RETURN_BOOL(false);
2548 }
2549 else if (TIMETZ_LT(ts1, ts2))
2550 {
2551 /* This case is ts2 < te1 OR te2 < te1 */
2552 if (te1IsNull)
2553 PG_RETURN_NULL();
2554 if (TIMETZ_LT(ts2, te1))
2555 PG_RETURN_BOOL(true);
2556 if (te2IsNull)
2557 PG_RETURN_NULL();
2558
2559 /*
2560 * If te2 is not null then we had ts2 <= te2 above, and we just found
2561 * ts2 >= te1, hence te2 >= te1.
2562 */
2563 PG_RETURN_BOOL(false);
2564 }
2565 else
2566 {
2567 /*
2568 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2569 * rather silly way of saying "true if both are nonnull, else null".
2570 */
2571 if (te1IsNull || te2IsNull)
2572 PG_RETURN_NULL();
2573 PG_RETURN_BOOL(true);
2574 }
2575
2576 #undef TIMETZ_GT
2577 #undef TIMETZ_LT
2578 }
2579
2580
2581 Datum
timetz_time(PG_FUNCTION_ARGS)2582 timetz_time(PG_FUNCTION_ARGS)
2583 {
2584 TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
2585 TimeADT result;
2586
2587 /* swallow the time zone and just return the time */
2588 result = timetz->time;
2589
2590 PG_RETURN_TIMEADT(result);
2591 }
2592
2593
2594 Datum
time_timetz(PG_FUNCTION_ARGS)2595 time_timetz(PG_FUNCTION_ARGS)
2596 {
2597 TimeADT time = PG_GETARG_TIMEADT(0);
2598 TimeTzADT *result;
2599 struct pg_tm tt,
2600 *tm = &tt;
2601 fsec_t fsec;
2602 int tz;
2603
2604 GetCurrentDateTime(tm);
2605 time2tm(time, tm, &fsec);
2606 tz = DetermineTimeZoneOffset(tm, session_timezone);
2607
2608 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2609
2610 result->time = time;
2611 result->zone = tz;
2612
2613 PG_RETURN_TIMETZADT_P(result);
2614 }
2615
2616
2617 /* timestamptz_timetz()
2618 * Convert timestamp to timetz data type.
2619 */
2620 Datum
timestamptz_timetz(PG_FUNCTION_ARGS)2621 timestamptz_timetz(PG_FUNCTION_ARGS)
2622 {
2623 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2624 TimeTzADT *result;
2625 struct pg_tm tt,
2626 *tm = &tt;
2627 int tz;
2628 fsec_t fsec;
2629
2630 if (TIMESTAMP_NOT_FINITE(timestamp))
2631 PG_RETURN_NULL();
2632
2633 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
2634 ereport(ERROR,
2635 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2636 errmsg("timestamp out of range")));
2637
2638 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2639
2640 tm2timetz(tm, fsec, tz, result);
2641
2642 PG_RETURN_TIMETZADT_P(result);
2643 }
2644
2645
2646 /* datetimetz_timestamptz()
2647 * Convert date and timetz to timestamp with time zone data type.
2648 * Timestamp is stored in GMT, so add the time zone
2649 * stored with the timetz to the result.
2650 * - thomas 2000-03-10
2651 */
2652 Datum
datetimetz_timestamptz(PG_FUNCTION_ARGS)2653 datetimetz_timestamptz(PG_FUNCTION_ARGS)
2654 {
2655 DateADT date = PG_GETARG_DATEADT(0);
2656 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2657 TimestampTz result;
2658
2659 if (DATE_IS_NOBEGIN(date))
2660 TIMESTAMP_NOBEGIN(result);
2661 else if (DATE_IS_NOEND(date))
2662 TIMESTAMP_NOEND(result);
2663 else
2664 {
2665 /*
2666 * Date's range is wider than timestamp's, so check for boundaries.
2667 * Since dates have the same minimum values as timestamps, only upper
2668 * boundary need be checked for overflow.
2669 */
2670 if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
2671 ereport(ERROR,
2672 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2673 errmsg("date out of range for timestamp")));
2674 result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2675
2676 /*
2677 * Since it is possible to go beyond allowed timestamptz range because
2678 * of time zone, check for allowed timestamp range after adding tz.
2679 */
2680 if (!IS_VALID_TIMESTAMP(result))
2681 ereport(ERROR,
2682 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2683 errmsg("date out of range for timestamp")));
2684 }
2685
2686 PG_RETURN_TIMESTAMP(result);
2687 }
2688
2689
2690 /* timetz_part()
2691 * Extract specified field from time type.
2692 */
2693 Datum
timetz_part(PG_FUNCTION_ARGS)2694 timetz_part(PG_FUNCTION_ARGS)
2695 {
2696 text *units = PG_GETARG_TEXT_PP(0);
2697 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2698 float8 result;
2699 int type,
2700 val;
2701 char *lowunits;
2702
2703 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
2704 VARSIZE_ANY_EXHDR(units),
2705 false);
2706
2707 type = DecodeUnits(0, lowunits, &val);
2708 if (type == UNKNOWN_FIELD)
2709 type = DecodeSpecial(0, lowunits, &val);
2710
2711 if (type == UNITS)
2712 {
2713 double dummy;
2714 int tz;
2715 fsec_t fsec;
2716 struct pg_tm tt,
2717 *tm = &tt;
2718
2719 timetz2tm(time, tm, &fsec, &tz);
2720
2721 switch (val)
2722 {
2723 case DTK_TZ:
2724 result = -tz;
2725 break;
2726
2727 case DTK_TZ_MINUTE:
2728 result = -tz;
2729 result /= SECS_PER_MINUTE;
2730 FMODULO(result, dummy, (double) SECS_PER_MINUTE);
2731 break;
2732
2733 case DTK_TZ_HOUR:
2734 dummy = -tz;
2735 FMODULO(dummy, result, (double) SECS_PER_HOUR);
2736 break;
2737
2738 case DTK_MICROSEC:
2739 result = tm->tm_sec * 1000000.0 + fsec;
2740 break;
2741
2742 case DTK_MILLISEC:
2743 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
2744 break;
2745
2746 case DTK_SECOND:
2747 result = tm->tm_sec + fsec / 1000000.0;
2748 break;
2749
2750 case DTK_MINUTE:
2751 result = tm->tm_min;
2752 break;
2753
2754 case DTK_HOUR:
2755 result = tm->tm_hour;
2756 break;
2757
2758 case DTK_DAY:
2759 case DTK_MONTH:
2760 case DTK_QUARTER:
2761 case DTK_YEAR:
2762 case DTK_DECADE:
2763 case DTK_CENTURY:
2764 case DTK_MILLENNIUM:
2765 default:
2766 ereport(ERROR,
2767 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2768 errmsg("\"time with time zone\" units \"%s\" not recognized",
2769 lowunits)));
2770 result = 0;
2771 }
2772 }
2773 else if (type == RESERV && val == DTK_EPOCH)
2774 {
2775 result = time->time / 1000000.0 + time->zone;
2776 }
2777 else
2778 {
2779 ereport(ERROR,
2780 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2781 errmsg("\"time with time zone\" units \"%s\" not recognized",
2782 lowunits)));
2783 result = 0;
2784 }
2785
2786 PG_RETURN_FLOAT8(result);
2787 }
2788
2789 /* timetz_zone()
2790 * Encode time with time zone type with specified time zone.
2791 * Applies DST rules as of the current date.
2792 */
2793 Datum
timetz_zone(PG_FUNCTION_ARGS)2794 timetz_zone(PG_FUNCTION_ARGS)
2795 {
2796 text *zone = PG_GETARG_TEXT_PP(0);
2797 TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
2798 TimeTzADT *result;
2799 int tz;
2800 char tzname[TZ_STRLEN_MAX + 1];
2801 char *lowzone;
2802 int type,
2803 val;
2804 pg_tz *tzp;
2805
2806 /*
2807 * Look up the requested timezone. First we look in the timezone
2808 * abbreviation table (to handle cases like "EST"), and if that fails, we
2809 * look in the timezone database (to handle cases like
2810 * "America/New_York"). (This matches the order in which timestamp input
2811 * checks the cases; it's important because the timezone database unwisely
2812 * uses a few zone names that are identical to offset abbreviations.)
2813 */
2814 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
2815
2816 /* DecodeTimezoneAbbrev requires lowercase input */
2817 lowzone = downcase_truncate_identifier(tzname,
2818 strlen(tzname),
2819 false);
2820
2821 type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
2822
2823 if (type == TZ || type == DTZ)
2824 {
2825 /* fixed-offset abbreviation */
2826 tz = -val;
2827 }
2828 else if (type == DYNTZ)
2829 {
2830 /* dynamic-offset abbreviation, resolve using current time */
2831 pg_time_t now = (pg_time_t) time(NULL);
2832 struct pg_tm *tm;
2833
2834 tm = pg_localtime(&now, tzp);
2835 tm->tm_year += 1900; /* adjust to PG conventions */
2836 tm->tm_mon += 1;
2837 tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
2838 }
2839 else
2840 {
2841 /* try it as a full zone name */
2842 tzp = pg_tzset(tzname);
2843 if (tzp)
2844 {
2845 /* Get the offset-from-GMT that is valid today for the zone */
2846 pg_time_t now = (pg_time_t) time(NULL);
2847 struct pg_tm *tm;
2848
2849 tm = pg_localtime(&now, tzp);
2850 tz = -tm->tm_gmtoff;
2851 }
2852 else
2853 {
2854 ereport(ERROR,
2855 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2856 errmsg("time zone \"%s\" not recognized", tzname)));
2857 tz = 0; /* keep compiler quiet */
2858 }
2859 }
2860
2861 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2862
2863 result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
2864 while (result->time < INT64CONST(0))
2865 result->time += USECS_PER_DAY;
2866 while (result->time >= USECS_PER_DAY)
2867 result->time -= USECS_PER_DAY;
2868
2869 result->zone = tz;
2870
2871 PG_RETURN_TIMETZADT_P(result);
2872 }
2873
2874 /* timetz_izone()
2875 * Encode time with time zone type with specified time interval as time zone.
2876 */
2877 Datum
timetz_izone(PG_FUNCTION_ARGS)2878 timetz_izone(PG_FUNCTION_ARGS)
2879 {
2880 Interval *zone = PG_GETARG_INTERVAL_P(0);
2881 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2882 TimeTzADT *result;
2883 int tz;
2884
2885 if (zone->month != 0 || zone->day != 0)
2886 ereport(ERROR,
2887 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2888 errmsg("interval time zone \"%s\" must not include months or days",
2889 DatumGetCString(DirectFunctionCall1(interval_out,
2890 PointerGetDatum(zone))))));
2891
2892 tz = -(zone->time / USECS_PER_SEC);
2893
2894 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2895
2896 result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
2897 while (result->time < INT64CONST(0))
2898 result->time += USECS_PER_DAY;
2899 while (result->time >= USECS_PER_DAY)
2900 result->time -= USECS_PER_DAY;
2901
2902 result->zone = tz;
2903
2904 PG_RETURN_TIMETZADT_P(result);
2905 }
2906