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