1 /*-------------------------------------------------------------------------
4 * implements DATE and TIME data types specified in SQL standard
6 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994-5, Regents of the University of California
11 * src/backend/utils/adt/date.c
13 *-------------------------------------------------------------------------
23 #include "access/hash.h"
24 #include "access/xact.h"
25 #include "libpq/pqformat.h"
26 #include "miscadmin.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/nabstime.h"
33 #include "utils/sortsupport.h"
36 * gcc's -ffast-math switch breaks routines that expect exact results from
37 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
40 #error -ffast-math is known to break this code
44 static int time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
45 static int timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
46 static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
47 static int tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
48 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
51 /* common code for timetypmodin and timetztypmodin */
53 anytime_typmodin(bool istz, ArrayType *ta)
58 tl = ArrayGetIntegerTypmods(ta, &n);
61 * we're not too tense about good error message here because grammar
62 * shouldn't allow wrong number of modifiers for TIME
66 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
67 errmsg("invalid type modifier")));
69 return anytime_typmod_check(istz, tl[0]);
72 /* exported so parse_expr.c can use it */
74 anytime_typmod_check(bool istz, int32 typmod)
78 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
79 errmsg("TIME(%d)%s precision must not be negative",
80 typmod, (istz ? " WITH TIME ZONE" : ""))));
81 if (typmod > MAX_TIME_PRECISION)
84 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
85 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
86 typmod, (istz ? " WITH TIME ZONE" : ""),
87 MAX_TIME_PRECISION)));
88 typmod = MAX_TIME_PRECISION;
94 /* common code for timetypmodout and timetztypmodout */
96 anytime_typmodout(bool istz, int32 typmod)
98 const char *tz = istz ? " with time zone" : " without time zone";
101 return psprintf("(%d)%s", (int) typmod, tz);
103 return psprintf("%s", tz);
107 /*****************************************************************************
109 *****************************************************************************/
113 * Given date text string, convert to internal date format.
116 date_in(PG_FUNCTION_ARGS)
118 char *str = PG_GETARG_CSTRING(0);
127 char *field[MAXDATEFIELDS];
128 int ftype[MAXDATEFIELDS];
129 char workbuf[MAXDATELEN + 1];
131 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
132 field, ftype, MAXDATEFIELDS, &nf);
134 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
136 DateTimeParseError(dterr, str, "date");
145 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
146 errmsg("date/time value \"current\" is no longer supported")));
148 GetCurrentDateTime(tm);
157 PG_RETURN_DATEADT(date);
161 PG_RETURN_DATEADT(date);
164 DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
168 /* Prevent overflow in Julian-day routines */
169 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
171 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
172 errmsg("date out of range: \"%s\"", str)));
174 date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
176 /* Now check for just-out-of-range dates */
177 if (!IS_VALID_DATE(date))
179 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
180 errmsg("date out of range: \"%s\"", str)));
182 PG_RETURN_DATEADT(date);
186 * Given internal format date, convert to text string.
189 date_out(PG_FUNCTION_ARGS)
191 DateADT date = PG_GETARG_DATEADT(0);
195 char buf[MAXDATELEN + 1];
197 if (DATE_NOT_FINITE(date))
198 EncodeSpecialDate(date, buf);
201 j2date(date + POSTGRES_EPOCH_JDATE,
202 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
203 EncodeDateOnly(tm, DateStyle, buf);
206 result = pstrdup(buf);
207 PG_RETURN_CSTRING(result);
211 * date_recv - converts external binary format to date
214 date_recv(PG_FUNCTION_ARGS)
216 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
219 result = (DateADT) pq_getmsgint(buf, sizeof(DateADT));
221 /* Limit to the same range that date_in() accepts. */
222 if (DATE_NOT_FINITE(result))
224 else if (!IS_VALID_DATE(result))
226 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
227 errmsg("date out of range")));
229 PG_RETURN_DATEADT(result);
233 * date_send - converts date to binary format
236 date_send(PG_FUNCTION_ARGS)
238 DateADT date = PG_GETARG_DATEADT(0);
241 pq_begintypsend(&buf);
242 pq_sendint(&buf, date, sizeof(date));
243 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
247 * make_date - date constructor
250 make_date(PG_FUNCTION_ARGS)
256 tm.tm_year = PG_GETARG_INT32(0);
257 tm.tm_mon = PG_GETARG_INT32(1);
258 tm.tm_mday = PG_GETARG_INT32(2);
261 * Note: we'll reject zero or negative year values. Perhaps negatives
262 * should be allowed to represent BC years?
264 dterr = ValidateDate(DTK_DATE_M, false, false, false, &tm);
268 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
269 errmsg("date field value out of range: %d-%02d-%02d",
270 tm.tm_year, tm.tm_mon, tm.tm_mday)));
272 /* Prevent overflow in Julian-day routines */
273 if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
275 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
276 errmsg("date out of range: %d-%02d-%02d",
277 tm.tm_year, tm.tm_mon, tm.tm_mday)));
279 date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
281 /* Now check for just-out-of-range dates */
282 if (!IS_VALID_DATE(date))
284 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
285 errmsg("date out of range: %d-%02d-%02d",
286 tm.tm_year, tm.tm_mon, tm.tm_mday)));
288 PG_RETURN_DATEADT(date);
292 * Convert reserved date values to string.
295 EncodeSpecialDate(DateADT dt, char *str)
297 if (DATE_IS_NOBEGIN(dt))
299 else if (DATE_IS_NOEND(dt))
301 else /* shouldn't happen */
302 elog(ERROR, "invalid argument for EncodeSpecialDate");
307 * GetSQLCurrentDate -- implements CURRENT_DATE
310 GetSQLCurrentDate(void)
318 ts = GetCurrentTransactionStartTimestamp();
320 if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
322 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
323 errmsg("timestamp out of range")));
325 return date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
329 * GetSQLCurrentTime -- implements CURRENT_TIME, CURRENT_TIME(n)
332 GetSQLCurrentTime(int32 typmod)
341 ts = GetCurrentTransactionStartTimestamp();
343 if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
345 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
346 errmsg("timestamp out of range")));
348 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
349 tm2timetz(tm, fsec, tz, result);
350 AdjustTimeForTypmod(&(result->time), typmod);
355 * GetSQLLocalTime -- implements LOCALTIME, LOCALTIME(n)
358 GetSQLLocalTime(int32 typmod)
367 ts = GetCurrentTransactionStartTimestamp();
369 if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
371 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
372 errmsg("timestamp out of range")));
374 tm2time(tm, fsec, &result);
375 AdjustTimeForTypmod(&result, typmod);
381 * Comparison functions for dates
385 date_eq(PG_FUNCTION_ARGS)
387 DateADT dateVal1 = PG_GETARG_DATEADT(0);
388 DateADT dateVal2 = PG_GETARG_DATEADT(1);
390 PG_RETURN_BOOL(dateVal1 == dateVal2);
394 date_ne(PG_FUNCTION_ARGS)
396 DateADT dateVal1 = PG_GETARG_DATEADT(0);
397 DateADT dateVal2 = PG_GETARG_DATEADT(1);
399 PG_RETURN_BOOL(dateVal1 != dateVal2);
403 date_lt(PG_FUNCTION_ARGS)
405 DateADT dateVal1 = PG_GETARG_DATEADT(0);
406 DateADT dateVal2 = PG_GETARG_DATEADT(1);
408 PG_RETURN_BOOL(dateVal1 < dateVal2);
412 date_le(PG_FUNCTION_ARGS)
414 DateADT dateVal1 = PG_GETARG_DATEADT(0);
415 DateADT dateVal2 = PG_GETARG_DATEADT(1);
417 PG_RETURN_BOOL(dateVal1 <= dateVal2);
421 date_gt(PG_FUNCTION_ARGS)
423 DateADT dateVal1 = PG_GETARG_DATEADT(0);
424 DateADT dateVal2 = PG_GETARG_DATEADT(1);
426 PG_RETURN_BOOL(dateVal1 > dateVal2);
430 date_ge(PG_FUNCTION_ARGS)
432 DateADT dateVal1 = PG_GETARG_DATEADT(0);
433 DateADT dateVal2 = PG_GETARG_DATEADT(1);
435 PG_RETURN_BOOL(dateVal1 >= dateVal2);
439 date_cmp(PG_FUNCTION_ARGS)
441 DateADT dateVal1 = PG_GETARG_DATEADT(0);
442 DateADT dateVal2 = PG_GETARG_DATEADT(1);
444 if (dateVal1 < dateVal2)
446 else if (dateVal1 > dateVal2)
452 date_fastcmp(Datum x, Datum y, SortSupport ssup)
454 DateADT a = DatumGetDateADT(x);
455 DateADT b = DatumGetDateADT(y);
465 date_sortsupport(PG_FUNCTION_ARGS)
467 SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
469 ssup->comparator = date_fastcmp;
474 date_finite(PG_FUNCTION_ARGS)
476 DateADT date = PG_GETARG_DATEADT(0);
478 PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
482 date_larger(PG_FUNCTION_ARGS)
484 DateADT dateVal1 = PG_GETARG_DATEADT(0);
485 DateADT dateVal2 = PG_GETARG_DATEADT(1);
487 PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
491 date_smaller(PG_FUNCTION_ARGS)
493 DateADT dateVal1 = PG_GETARG_DATEADT(0);
494 DateADT dateVal2 = PG_GETARG_DATEADT(1);
496 PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
499 /* Compute difference between two dates in days.
502 date_mi(PG_FUNCTION_ARGS)
504 DateADT dateVal1 = PG_GETARG_DATEADT(0);
505 DateADT dateVal2 = PG_GETARG_DATEADT(1);
507 if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
509 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
510 errmsg("cannot subtract infinite dates")));
512 PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
515 /* Add a number of days to a date, giving a new date.
516 * Must handle both positive and negative numbers of days.
519 date_pli(PG_FUNCTION_ARGS)
521 DateADT dateVal = PG_GETARG_DATEADT(0);
522 int32 days = PG_GETARG_INT32(1);
525 if (DATE_NOT_FINITE(dateVal))
526 PG_RETURN_DATEADT(dateVal); /* can't change infinity */
528 result = dateVal + days;
530 /* Check for integer overflow and out-of-allowed-range */
531 if ((days >= 0 ? (result < dateVal) : (result > dateVal)) ||
532 !IS_VALID_DATE(result))
534 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
535 errmsg("date out of range")));
537 PG_RETURN_DATEADT(result);
540 /* Subtract a number of days from a date, giving a new date.
543 date_mii(PG_FUNCTION_ARGS)
545 DateADT dateVal = PG_GETARG_DATEADT(0);
546 int32 days = PG_GETARG_INT32(1);
549 if (DATE_NOT_FINITE(dateVal))
550 PG_RETURN_DATEADT(dateVal); /* can't change infinity */
552 result = dateVal - days;
554 /* Check for integer overflow and out-of-allowed-range */
555 if ((days >= 0 ? (result > dateVal) : (result < dateVal)) ||
556 !IS_VALID_DATE(result))
558 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
559 errmsg("date out of range")));
561 PG_RETURN_DATEADT(result);
565 * Internal routines for promoting date to timestamp and timestamp with
570 date2timestamp(DateADT dateVal)
574 if (DATE_IS_NOBEGIN(dateVal))
575 TIMESTAMP_NOBEGIN(result);
576 else if (DATE_IS_NOEND(dateVal))
577 TIMESTAMP_NOEND(result);
581 * Date's range is wider than timestamp's, so check for boundaries.
582 * Since dates have the same minimum values as timestamps, only upper
583 * boundary need be checked for overflow.
585 if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
587 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
588 errmsg("date out of range for timestamp")));
589 #ifdef HAVE_INT64_TIMESTAMP
590 /* date is days since 2000, timestamp is microseconds since same... */
591 result = dateVal * USECS_PER_DAY;
593 /* date is days since 2000, timestamp is seconds since same... */
594 result = dateVal * (double) SECS_PER_DAY;
602 date2timestamptz(DateADT dateVal)
609 if (DATE_IS_NOBEGIN(dateVal))
610 TIMESTAMP_NOBEGIN(result);
611 else if (DATE_IS_NOEND(dateVal))
612 TIMESTAMP_NOEND(result);
616 * Date's range is wider than timestamp's, so check for boundaries.
617 * Since dates have the same minimum values as timestamps, only upper
618 * boundary need be checked for overflow.
620 if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
622 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
623 errmsg("date out of range for timestamp")));
625 j2date(dateVal + POSTGRES_EPOCH_JDATE,
626 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
630 tz = DetermineTimeZoneOffset(tm, session_timezone);
632 #ifdef HAVE_INT64_TIMESTAMP
633 result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
635 result = dateVal * (double) SECS_PER_DAY + tz;
639 * Since it is possible to go beyond allowed timestamptz range because
640 * of time zone, check for allowed timestamp range after adding tz.
642 if (!IS_VALID_TIMESTAMP(result))
644 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
645 errmsg("date out of range for timestamp")));
652 * date2timestamp_no_overflow
654 * This is chartered to produce a double value that is numerically
655 * equivalent to the corresponding Timestamp value, if the date is in the
656 * valid range of Timestamps, but in any case not throw an overflow error.
657 * We can do this since the numerical range of double is greater than
658 * that of non-erroneous timestamps. The results are currently only
659 * used for statistical estimation purposes.
662 date2timestamp_no_overflow(DateADT dateVal)
666 if (DATE_IS_NOBEGIN(dateVal))
668 else if (DATE_IS_NOEND(dateVal))
672 #ifdef HAVE_INT64_TIMESTAMP
673 /* date is days since 2000, timestamp is microseconds since same... */
674 result = dateVal * (double) USECS_PER_DAY;
676 /* date is days since 2000, timestamp is seconds since same... */
677 result = dateVal * (double) SECS_PER_DAY;
686 * Crosstype comparison functions for dates
690 date_eq_timestamp(PG_FUNCTION_ARGS)
692 DateADT dateVal = PG_GETARG_DATEADT(0);
693 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
696 dt1 = date2timestamp(dateVal);
698 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
702 date_ne_timestamp(PG_FUNCTION_ARGS)
704 DateADT dateVal = PG_GETARG_DATEADT(0);
705 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
708 dt1 = date2timestamp(dateVal);
710 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
714 date_lt_timestamp(PG_FUNCTION_ARGS)
716 DateADT dateVal = PG_GETARG_DATEADT(0);
717 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
720 dt1 = date2timestamp(dateVal);
722 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
726 date_gt_timestamp(PG_FUNCTION_ARGS)
728 DateADT dateVal = PG_GETARG_DATEADT(0);
729 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
732 dt1 = date2timestamp(dateVal);
734 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
738 date_le_timestamp(PG_FUNCTION_ARGS)
740 DateADT dateVal = PG_GETARG_DATEADT(0);
741 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
744 dt1 = date2timestamp(dateVal);
746 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
750 date_ge_timestamp(PG_FUNCTION_ARGS)
752 DateADT dateVal = PG_GETARG_DATEADT(0);
753 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
756 dt1 = date2timestamp(dateVal);
758 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
762 date_cmp_timestamp(PG_FUNCTION_ARGS)
764 DateADT dateVal = PG_GETARG_DATEADT(0);
765 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
768 dt1 = date2timestamp(dateVal);
770 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
774 date_eq_timestamptz(PG_FUNCTION_ARGS)
776 DateADT dateVal = PG_GETARG_DATEADT(0);
777 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
780 dt1 = date2timestamptz(dateVal);
782 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
786 date_ne_timestamptz(PG_FUNCTION_ARGS)
788 DateADT dateVal = PG_GETARG_DATEADT(0);
789 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
792 dt1 = date2timestamptz(dateVal);
794 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
798 date_lt_timestamptz(PG_FUNCTION_ARGS)
800 DateADT dateVal = PG_GETARG_DATEADT(0);
801 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
804 dt1 = date2timestamptz(dateVal);
806 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
810 date_gt_timestamptz(PG_FUNCTION_ARGS)
812 DateADT dateVal = PG_GETARG_DATEADT(0);
813 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
816 dt1 = date2timestamptz(dateVal);
818 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
822 date_le_timestamptz(PG_FUNCTION_ARGS)
824 DateADT dateVal = PG_GETARG_DATEADT(0);
825 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
828 dt1 = date2timestamptz(dateVal);
830 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
834 date_ge_timestamptz(PG_FUNCTION_ARGS)
836 DateADT dateVal = PG_GETARG_DATEADT(0);
837 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
840 dt1 = date2timestamptz(dateVal);
842 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
846 date_cmp_timestamptz(PG_FUNCTION_ARGS)
848 DateADT dateVal = PG_GETARG_DATEADT(0);
849 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
852 dt1 = date2timestamptz(dateVal);
854 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
858 timestamp_eq_date(PG_FUNCTION_ARGS)
860 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
861 DateADT dateVal = PG_GETARG_DATEADT(1);
864 dt2 = date2timestamp(dateVal);
866 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
870 timestamp_ne_date(PG_FUNCTION_ARGS)
872 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
873 DateADT dateVal = PG_GETARG_DATEADT(1);
876 dt2 = date2timestamp(dateVal);
878 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
882 timestamp_lt_date(PG_FUNCTION_ARGS)
884 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
885 DateADT dateVal = PG_GETARG_DATEADT(1);
888 dt2 = date2timestamp(dateVal);
890 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
894 timestamp_gt_date(PG_FUNCTION_ARGS)
896 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
897 DateADT dateVal = PG_GETARG_DATEADT(1);
900 dt2 = date2timestamp(dateVal);
902 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
906 timestamp_le_date(PG_FUNCTION_ARGS)
908 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
909 DateADT dateVal = PG_GETARG_DATEADT(1);
912 dt2 = date2timestamp(dateVal);
914 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
918 timestamp_ge_date(PG_FUNCTION_ARGS)
920 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
921 DateADT dateVal = PG_GETARG_DATEADT(1);
924 dt2 = date2timestamp(dateVal);
926 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
930 timestamp_cmp_date(PG_FUNCTION_ARGS)
932 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
933 DateADT dateVal = PG_GETARG_DATEADT(1);
936 dt2 = date2timestamp(dateVal);
938 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
942 timestamptz_eq_date(PG_FUNCTION_ARGS)
944 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
945 DateADT dateVal = PG_GETARG_DATEADT(1);
948 dt2 = date2timestamptz(dateVal);
950 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
954 timestamptz_ne_date(PG_FUNCTION_ARGS)
956 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
957 DateADT dateVal = PG_GETARG_DATEADT(1);
960 dt2 = date2timestamptz(dateVal);
962 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
966 timestamptz_lt_date(PG_FUNCTION_ARGS)
968 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
969 DateADT dateVal = PG_GETARG_DATEADT(1);
972 dt2 = date2timestamptz(dateVal);
974 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
978 timestamptz_gt_date(PG_FUNCTION_ARGS)
980 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
981 DateADT dateVal = PG_GETARG_DATEADT(1);
984 dt2 = date2timestamptz(dateVal);
986 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
990 timestamptz_le_date(PG_FUNCTION_ARGS)
992 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
993 DateADT dateVal = PG_GETARG_DATEADT(1);
996 dt2 = date2timestamptz(dateVal);
998 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
1002 timestamptz_ge_date(PG_FUNCTION_ARGS)
1004 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1005 DateADT dateVal = PG_GETARG_DATEADT(1);
1008 dt2 = date2timestamptz(dateVal);
1010 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
1014 timestamptz_cmp_date(PG_FUNCTION_ARGS)
1016 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1017 DateADT dateVal = PG_GETARG_DATEADT(1);
1020 dt2 = date2timestamptz(dateVal);
1022 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
1026 /* Add an interval to a date, giving a new date.
1027 * Must handle both positive and negative intervals.
1029 * We implement this by promoting the date to timestamp (without time zone)
1030 * and then using the timestamp plus interval function.
1033 date_pl_interval(PG_FUNCTION_ARGS)
1035 DateADT dateVal = PG_GETARG_DATEADT(0);
1036 Interval *span = PG_GETARG_INTERVAL_P(1);
1037 Timestamp dateStamp;
1039 dateStamp = date2timestamp(dateVal);
1041 return DirectFunctionCall2(timestamp_pl_interval,
1042 TimestampGetDatum(dateStamp),
1043 PointerGetDatum(span));
1046 /* Subtract an interval from a date, giving a new date.
1047 * Must handle both positive and negative intervals.
1049 * We implement this by promoting the date to timestamp (without time zone)
1050 * and then using the timestamp minus interval function.
1053 date_mi_interval(PG_FUNCTION_ARGS)
1055 DateADT dateVal = PG_GETARG_DATEADT(0);
1056 Interval *span = PG_GETARG_INTERVAL_P(1);
1057 Timestamp dateStamp;
1059 dateStamp = date2timestamp(dateVal);
1061 return DirectFunctionCall2(timestamp_mi_interval,
1062 TimestampGetDatum(dateStamp),
1063 PointerGetDatum(span));
1067 * Convert date to timestamp data type.
1070 date_timestamp(PG_FUNCTION_ARGS)
1072 DateADT dateVal = PG_GETARG_DATEADT(0);
1075 result = date2timestamp(dateVal);
1077 PG_RETURN_TIMESTAMP(result);
1081 * Convert timestamp to date data type.
1084 timestamp_date(PG_FUNCTION_ARGS)
1086 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1092 if (TIMESTAMP_IS_NOBEGIN(timestamp))
1093 DATE_NOBEGIN(result);
1094 else if (TIMESTAMP_IS_NOEND(timestamp))
1098 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1100 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1101 errmsg("timestamp out of range")));
1103 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1106 PG_RETURN_DATEADT(result);
1110 /* date_timestamptz()
1111 * Convert date to timestamp with time zone data type.
1114 date_timestamptz(PG_FUNCTION_ARGS)
1116 DateADT dateVal = PG_GETARG_DATEADT(0);
1119 result = date2timestamptz(dateVal);
1121 PG_RETURN_TIMESTAMP(result);
1125 /* timestamptz_date()
1126 * Convert timestamp with time zone to date data type.
1129 timestamptz_date(PG_FUNCTION_ARGS)
1131 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1138 if (TIMESTAMP_IS_NOBEGIN(timestamp))
1139 DATE_NOBEGIN(result);
1140 else if (TIMESTAMP_IS_NOEND(timestamp))
1144 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
1146 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1147 errmsg("timestamp out of range")));
1149 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1152 PG_RETURN_DATEADT(result);
1157 * Convert abstime to date data type.
1160 abstime_date(PG_FUNCTION_ARGS)
1162 AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
1170 case INVALID_ABSTIME:
1172 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1173 errmsg("cannot convert reserved abstime value to date")));
1174 result = 0; /* keep compiler quiet */
1177 case NOSTART_ABSTIME:
1178 DATE_NOBEGIN(result);
1186 abstime2tm(abstime, &tz, tm, NULL);
1187 /* Prevent overflow in Julian-day routines */
1188 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
1190 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1191 errmsg("abstime out of range for date")));
1192 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1193 /* Now check for just-out-of-range dates */
1194 if (!IS_VALID_DATE(result))
1196 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1197 errmsg("abstime out of range for date")));
1201 PG_RETURN_DATEADT(result);
1205 /*****************************************************************************
1207 *****************************************************************************/
1210 time_in(PG_FUNCTION_ARGS)
1212 char *str = PG_GETARG_CSTRING(0);
1215 Oid typelem = PG_GETARG_OID(1);
1217 int32 typmod = PG_GETARG_INT32(2);
1225 char workbuf[MAXDATELEN + 1];
1226 char *field[MAXDATEFIELDS];
1228 int ftype[MAXDATEFIELDS];
1230 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1231 field, ftype, MAXDATEFIELDS, &nf);
1233 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1235 DateTimeParseError(dterr, str, "time");
1237 tm2time(tm, fsec, &result);
1238 AdjustTimeForTypmod(&result, typmod);
1240 PG_RETURN_TIMEADT(result);
1244 * Convert a tm structure to a time data type.
1247 tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
1249 #ifdef HAVE_INT64_TIMESTAMP
1250 *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
1251 * USECS_PER_SEC) + fsec;
1253 *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1259 * Convert time data type to POSIX time structure.
1261 * For dates within the range of pg_time_t, convert to the local time zone.
1262 * If out of this range, leave as UTC (in practice that could only happen
1263 * if pg_time_t is just 32 bits) - thomas 97/05/27
1266 time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
1268 #ifdef HAVE_INT64_TIMESTAMP
1269 tm->tm_hour = time / USECS_PER_HOUR;
1270 time -= tm->tm_hour * USECS_PER_HOUR;
1271 tm->tm_min = time / USECS_PER_MINUTE;
1272 time -= tm->tm_min * USECS_PER_MINUTE;
1273 tm->tm_sec = time / USECS_PER_SEC;
1274 time -= tm->tm_sec * USECS_PER_SEC;
1281 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
1282 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1283 TMODULO(trem, tm->tm_sec, 1.0);
1284 trem = TIMEROUND(trem);
1285 /* roundoff may need to propagate to higher-order fields */
1298 time_out(PG_FUNCTION_ARGS)
1300 TimeADT time = PG_GETARG_TIMEADT(0);
1305 char buf[MAXDATELEN + 1];
1307 time2tm(time, tm, &fsec);
1308 EncodeTimeOnly(tm, fsec, false, 0, DateStyle, buf);
1310 result = pstrdup(buf);
1311 PG_RETURN_CSTRING(result);
1315 * time_recv - converts external binary format to time
1317 * We make no attempt to provide compatibility between int and float
1318 * time representations ...
1321 time_recv(PG_FUNCTION_ARGS)
1323 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1326 Oid typelem = PG_GETARG_OID(1);
1328 int32 typmod = PG_GETARG_INT32(2);
1331 #ifdef HAVE_INT64_TIMESTAMP
1332 result = pq_getmsgint64(buf);
1334 if (result < INT64CONST(0) || result > USECS_PER_DAY)
1336 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1337 errmsg("time out of range")));
1339 result = pq_getmsgfloat8(buf);
1341 if (result < 0 || result > (double) SECS_PER_DAY)
1343 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1344 errmsg("time out of range")));
1347 AdjustTimeForTypmod(&result, typmod);
1349 PG_RETURN_TIMEADT(result);
1353 * time_send - converts time to binary format
1356 time_send(PG_FUNCTION_ARGS)
1358 TimeADT time = PG_GETARG_TIMEADT(0);
1361 pq_begintypsend(&buf);
1362 #ifdef HAVE_INT64_TIMESTAMP
1363 pq_sendint64(&buf, time);
1365 pq_sendfloat8(&buf, time);
1367 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1371 timetypmodin(PG_FUNCTION_ARGS)
1373 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1375 PG_RETURN_INT32(anytime_typmodin(false, ta));
1379 timetypmodout(PG_FUNCTION_ARGS)
1381 int32 typmod = PG_GETARG_INT32(0);
1383 PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
1387 * make_time - time constructor
1390 make_time(PG_FUNCTION_ARGS)
1392 int tm_hour = PG_GETARG_INT32(0);
1393 int tm_min = PG_GETARG_INT32(1);
1394 double sec = PG_GETARG_FLOAT8(2);
1397 /* This should match the checks in DecodeTimeOnly */
1398 if (tm_hour < 0 || tm_min < 0 || tm_min > MINS_PER_HOUR - 1 ||
1399 sec < 0 || sec > SECS_PER_MINUTE ||
1400 tm_hour > HOURS_PER_DAY ||
1401 /* test for > 24:00:00 */
1402 (tm_hour == HOURS_PER_DAY && (tm_min > 0 || sec > 0)))
1404 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
1405 errmsg("time field value out of range: %d:%02d:%02g",
1406 tm_hour, tm_min, sec)));
1408 /* This should match tm2time */
1409 #ifdef HAVE_INT64_TIMESTAMP
1410 time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
1411 * USECS_PER_SEC) + rint(sec * USECS_PER_SEC);
1413 time = ((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE) + sec;
1416 PG_RETURN_TIMEADT(time);
1421 * Flatten calls to time_scale() and timetz_scale() that solely represent
1422 * increases in allowed precision.
1425 time_transform(PG_FUNCTION_ARGS)
1427 PG_RETURN_POINTER(TemporalTransform(MAX_TIME_PRECISION,
1428 (Node *) PG_GETARG_POINTER(0)));
1432 * Adjust time type for specified scale factor.
1433 * Used by PostgreSQL type system to stuff columns.
1436 time_scale(PG_FUNCTION_ARGS)
1438 TimeADT time = PG_GETARG_TIMEADT(0);
1439 int32 typmod = PG_GETARG_INT32(1);
1443 AdjustTimeForTypmod(&result, typmod);
1445 PG_RETURN_TIMEADT(result);
1448 /* AdjustTimeForTypmod()
1449 * Force the precision of the time value to a specified value.
1450 * Uses *exactly* the same code as in AdjustTimestampForTypemod()
1451 * but we make a separate copy because those types do not
1452 * have a fundamental tie together but rather a coincidence of
1453 * implementation. - thomas
1456 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1458 #ifdef HAVE_INT64_TIMESTAMP
1459 static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1460 INT64CONST(1000000),
1469 static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1479 /* note MAX_TIME_PRECISION differs in this case */
1480 static const double TimeScales[MAX_TIME_PRECISION + 1] = {
1495 if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1498 * Note: this round-to-nearest code is not completely consistent about
1499 * rounding values that are exactly halfway between integral values.
1500 * On most platforms, rint() will implement round-to-nearest-even, but
1501 * the integer code always rounds up (away from zero). Is it worth
1502 * trying to be consistent?
1504 #ifdef HAVE_INT64_TIMESTAMP
1505 if (*time >= INT64CONST(0))
1506 *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
1509 *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
1510 TimeScales[typmod]);
1512 *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
1519 time_eq(PG_FUNCTION_ARGS)
1521 TimeADT time1 = PG_GETARG_TIMEADT(0);
1522 TimeADT time2 = PG_GETARG_TIMEADT(1);
1524 PG_RETURN_BOOL(time1 == time2);
1528 time_ne(PG_FUNCTION_ARGS)
1530 TimeADT time1 = PG_GETARG_TIMEADT(0);
1531 TimeADT time2 = PG_GETARG_TIMEADT(1);
1533 PG_RETURN_BOOL(time1 != time2);
1537 time_lt(PG_FUNCTION_ARGS)
1539 TimeADT time1 = PG_GETARG_TIMEADT(0);
1540 TimeADT time2 = PG_GETARG_TIMEADT(1);
1542 PG_RETURN_BOOL(time1 < time2);
1546 time_le(PG_FUNCTION_ARGS)
1548 TimeADT time1 = PG_GETARG_TIMEADT(0);
1549 TimeADT time2 = PG_GETARG_TIMEADT(1);
1551 PG_RETURN_BOOL(time1 <= time2);
1555 time_gt(PG_FUNCTION_ARGS)
1557 TimeADT time1 = PG_GETARG_TIMEADT(0);
1558 TimeADT time2 = PG_GETARG_TIMEADT(1);
1560 PG_RETURN_BOOL(time1 > time2);
1564 time_ge(PG_FUNCTION_ARGS)
1566 TimeADT time1 = PG_GETARG_TIMEADT(0);
1567 TimeADT time2 = PG_GETARG_TIMEADT(1);
1569 PG_RETURN_BOOL(time1 >= time2);
1573 time_cmp(PG_FUNCTION_ARGS)
1575 TimeADT time1 = PG_GETARG_TIMEADT(0);
1576 TimeADT time2 = PG_GETARG_TIMEADT(1);
1579 PG_RETURN_INT32(-1);
1586 time_hash(PG_FUNCTION_ARGS)
1588 /* We can use either hashint8 or hashfloat8 directly */
1589 #ifdef HAVE_INT64_TIMESTAMP
1590 return hashint8(fcinfo);
1592 return hashfloat8(fcinfo);
1597 time_larger(PG_FUNCTION_ARGS)
1599 TimeADT time1 = PG_GETARG_TIMEADT(0);
1600 TimeADT time2 = PG_GETARG_TIMEADT(1);
1602 PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1606 time_smaller(PG_FUNCTION_ARGS)
1608 TimeADT time1 = PG_GETARG_TIMEADT(0);
1609 TimeADT time2 = PG_GETARG_TIMEADT(1);
1611 PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1614 /* overlaps_time() --- implements the SQL OVERLAPS operator.
1616 * Algorithm is per SQL spec. This is much harder than you'd think
1617 * because the spec requires us to deliver a non-null answer in some cases
1618 * where some of the inputs are null.
1621 overlaps_time(PG_FUNCTION_ARGS)
1624 * The arguments are TimeADT, but we leave them as generic Datums to avoid
1625 * dereferencing nulls (TimeADT is pass-by-reference!)
1627 Datum ts1 = PG_GETARG_DATUM(0);
1628 Datum te1 = PG_GETARG_DATUM(1);
1629 Datum ts2 = PG_GETARG_DATUM(2);
1630 Datum te2 = PG_GETARG_DATUM(3);
1631 bool ts1IsNull = PG_ARGISNULL(0);
1632 bool te1IsNull = PG_ARGISNULL(1);
1633 bool ts2IsNull = PG_ARGISNULL(2);
1634 bool te2IsNull = PG_ARGISNULL(3);
1636 #define TIMEADT_GT(t1,t2) \
1637 (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1638 #define TIMEADT_LT(t1,t2) \
1639 (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1642 * If both endpoints of interval 1 are null, the result is null (unknown).
1643 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1644 * take ts1 as the lesser endpoint.
1650 /* swap null for non-null */
1654 else if (!te1IsNull)
1656 if (TIMEADT_GT(ts1, te1))
1665 /* Likewise for interval 2. */
1670 /* swap null for non-null */
1674 else if (!te2IsNull)
1676 if (TIMEADT_GT(ts2, te2))
1686 * At this point neither ts1 nor ts2 is null, so we can consider three
1687 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1689 if (TIMEADT_GT(ts1, ts2))
1692 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
1693 * in the presence of nulls it's not quite completely so.
1697 if (TIMEADT_LT(ts1, te2))
1698 PG_RETURN_BOOL(true);
1703 * If te1 is not null then we had ts1 <= te1 above, and we just found
1704 * ts1 >= te2, hence te1 >= te2.
1706 PG_RETURN_BOOL(false);
1708 else if (TIMEADT_LT(ts1, ts2))
1710 /* This case is ts2 < te1 OR te2 < te1 */
1713 if (TIMEADT_LT(ts2, te1))
1714 PG_RETURN_BOOL(true);
1719 * If te2 is not null then we had ts2 <= te2 above, and we just found
1720 * ts2 >= te1, hence te2 >= te1.
1722 PG_RETURN_BOOL(false);
1727 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1728 * rather silly way of saying "true if both are nonnull, else null".
1730 if (te1IsNull || te2IsNull)
1732 PG_RETURN_BOOL(true);
1740 * Convert timestamp to time data type.
1743 timestamp_time(PG_FUNCTION_ARGS)
1745 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1751 if (TIMESTAMP_NOT_FINITE(timestamp))
1754 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1756 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1757 errmsg("timestamp out of range")));
1759 #ifdef HAVE_INT64_TIMESTAMP
1762 * Could also do this with time = (timestamp / USECS_PER_DAY *
1763 * USECS_PER_DAY) - timestamp;
1765 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1766 USECS_PER_SEC) + fsec;
1768 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1771 PG_RETURN_TIMEADT(result);
1774 /* timestamptz_time()
1775 * Convert timestamptz to time data type.
1778 timestamptz_time(PG_FUNCTION_ARGS)
1780 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1787 if (TIMESTAMP_NOT_FINITE(timestamp))
1790 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
1792 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1793 errmsg("timestamp out of range")));
1795 #ifdef HAVE_INT64_TIMESTAMP
1798 * Could also do this with time = (timestamp / USECS_PER_DAY *
1799 * USECS_PER_DAY) - timestamp;
1801 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1802 USECS_PER_SEC) + fsec;
1804 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1807 PG_RETURN_TIMEADT(result);
1810 /* datetime_timestamp()
1811 * Convert date and time to timestamp data type.
1814 datetime_timestamp(PG_FUNCTION_ARGS)
1816 DateADT date = PG_GETARG_DATEADT(0);
1817 TimeADT time = PG_GETARG_TIMEADT(1);
1820 result = date2timestamp(date);
1821 if (!TIMESTAMP_NOT_FINITE(result))
1824 if (!IS_VALID_TIMESTAMP(result))
1826 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1827 errmsg("timestamp out of range")));
1830 PG_RETURN_TIMESTAMP(result);
1834 * Convert time to interval data type.
1837 time_interval(PG_FUNCTION_ARGS)
1839 TimeADT time = PG_GETARG_TIMEADT(0);
1842 result = (Interval *) palloc(sizeof(Interval));
1844 result->time = time;
1848 PG_RETURN_INTERVAL_P(result);
1852 * Convert interval to time data type.
1854 * This is defined as producing the fractional-day portion of the interval.
1855 * Therefore, we can just ignore the months field. It is not real clear
1856 * what to do with negative intervals, but we choose to subtract the floor,
1857 * so that, say, '-2 hours' becomes '22:00:00'.
1860 interval_time(PG_FUNCTION_ARGS)
1862 Interval *span = PG_GETARG_INTERVAL_P(0);
1865 #ifdef HAVE_INT64_TIMESTAMP
1868 result = span->time;
1869 if (result >= USECS_PER_DAY)
1871 days = result / USECS_PER_DAY;
1872 result -= days * USECS_PER_DAY;
1874 else if (result < 0)
1876 days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
1877 result += days * USECS_PER_DAY;
1880 result = span->time;
1881 if (result >= (double) SECS_PER_DAY || result < 0)
1882 result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
1885 PG_RETURN_TIMEADT(result);
1889 * Subtract two times to produce an interval.
1892 time_mi_time(PG_FUNCTION_ARGS)
1894 TimeADT time1 = PG_GETARG_TIMEADT(0);
1895 TimeADT time2 = PG_GETARG_TIMEADT(1);
1898 result = (Interval *) palloc(sizeof(Interval));
1902 result->time = time1 - time2;
1904 PG_RETURN_INTERVAL_P(result);
1907 /* time_pl_interval()
1908 * Add interval to time.
1911 time_pl_interval(PG_FUNCTION_ARGS)
1913 TimeADT time = PG_GETARG_TIMEADT(0);
1914 Interval *span = PG_GETARG_INTERVAL_P(1);
1917 #ifdef HAVE_INT64_TIMESTAMP
1918 result = time + span->time;
1919 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1920 if (result < INT64CONST(0))
1921 result += USECS_PER_DAY;
1925 result = time + span->time;
1926 TMODULO(result, time1, (double) SECS_PER_DAY);
1928 result += SECS_PER_DAY;
1931 PG_RETURN_TIMEADT(result);
1934 /* time_mi_interval()
1935 * Subtract interval from time.
1938 time_mi_interval(PG_FUNCTION_ARGS)
1940 TimeADT time = PG_GETARG_TIMEADT(0);
1941 Interval *span = PG_GETARG_INTERVAL_P(1);
1944 #ifdef HAVE_INT64_TIMESTAMP
1945 result = time - span->time;
1946 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1947 if (result < INT64CONST(0))
1948 result += USECS_PER_DAY;
1952 result = time - span->time;
1953 TMODULO(result, time1, (double) SECS_PER_DAY);
1955 result += SECS_PER_DAY;
1958 PG_RETURN_TIMEADT(result);
1963 * Extract specified field from time type.
1966 time_part(PG_FUNCTION_ARGS)
1968 text *units = PG_GETARG_TEXT_PP(0);
1969 TimeADT time = PG_GETARG_TIMEADT(1);
1975 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
1976 VARSIZE_ANY_EXHDR(units),
1979 type = DecodeUnits(0, lowunits, &val);
1980 if (type == UNKNOWN_FIELD)
1981 type = DecodeSpecial(0, lowunits, &val);
1989 time2tm(time, tm, &fsec);
1994 #ifdef HAVE_INT64_TIMESTAMP
1995 result = tm->tm_sec * 1000000.0 + fsec;
1997 result = (tm->tm_sec + fsec) * 1000000;
2002 #ifdef HAVE_INT64_TIMESTAMP
2003 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
2005 result = (tm->tm_sec + fsec) * 1000;
2010 #ifdef HAVE_INT64_TIMESTAMP
2011 result = tm->tm_sec + fsec / 1000000.0;
2013 result = tm->tm_sec + fsec;
2018 result = tm->tm_min;
2022 result = tm->tm_hour;
2034 case DTK_MILLENNIUM:
2038 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2039 errmsg("\"time\" units \"%s\" not recognized",
2044 else if (type == RESERV && val == DTK_EPOCH)
2046 #ifdef HAVE_INT64_TIMESTAMP
2047 result = time / 1000000.0;
2055 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2056 errmsg("\"time\" units \"%s\" not recognized",
2061 PG_RETURN_FLOAT8(result);
2065 /*****************************************************************************
2066 * Time With Time Zone ADT
2067 *****************************************************************************/
2070 * Convert a tm structure to a time data type.
2073 tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
2075 #ifdef HAVE_INT64_TIMESTAMP
2076 result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
2077 USECS_PER_SEC) + fsec;
2079 result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
2087 timetz_in(PG_FUNCTION_ARGS)
2089 char *str = PG_GETARG_CSTRING(0);
2092 Oid typelem = PG_GETARG_OID(1);
2094 int32 typmod = PG_GETARG_INT32(2);
2102 char workbuf[MAXDATELEN + 1];
2103 char *field[MAXDATEFIELDS];
2105 int ftype[MAXDATEFIELDS];
2107 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
2108 field, ftype, MAXDATEFIELDS, &nf);
2110 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
2112 DateTimeParseError(dterr, str, "time with time zone");
2114 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2115 tm2timetz(tm, fsec, tz, result);
2116 AdjustTimeForTypmod(&(result->time), typmod);
2118 PG_RETURN_TIMETZADT_P(result);
2122 timetz_out(PG_FUNCTION_ARGS)
2124 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2130 char buf[MAXDATELEN + 1];
2132 timetz2tm(time, tm, &fsec, &tz);
2133 EncodeTimeOnly(tm, fsec, true, tz, DateStyle, buf);
2135 result = pstrdup(buf);
2136 PG_RETURN_CSTRING(result);
2140 * timetz_recv - converts external binary format to timetz
2143 timetz_recv(PG_FUNCTION_ARGS)
2145 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
2148 Oid typelem = PG_GETARG_OID(1);
2150 int32 typmod = PG_GETARG_INT32(2);
2153 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2155 #ifdef HAVE_INT64_TIMESTAMP
2156 result->time = pq_getmsgint64(buf);
2158 if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
2160 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2161 errmsg("time out of range")));
2163 result->time = pq_getmsgfloat8(buf);
2165 if (result->time < 0 || result->time > (double) SECS_PER_DAY)
2167 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2168 errmsg("time out of range")));
2171 result->zone = pq_getmsgint(buf, sizeof(result->zone));
2173 /* Check for sane GMT displacement; see notes in datatype/timestamp.h */
2174 if (result->zone <= -TZDISP_LIMIT || result->zone >= TZDISP_LIMIT)
2176 (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
2177 errmsg("time zone displacement out of range")));
2179 AdjustTimeForTypmod(&(result->time), typmod);
2181 PG_RETURN_TIMETZADT_P(result);
2185 * timetz_send - converts timetz to binary format
2188 timetz_send(PG_FUNCTION_ARGS)
2190 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2193 pq_begintypsend(&buf);
2194 #ifdef HAVE_INT64_TIMESTAMP
2195 pq_sendint64(&buf, time->time);
2197 pq_sendfloat8(&buf, time->time);
2199 pq_sendint(&buf, time->zone, sizeof(time->zone));
2200 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
2204 timetztypmodin(PG_FUNCTION_ARGS)
2206 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
2208 PG_RETURN_INT32(anytime_typmodin(true, ta));
2212 timetztypmodout(PG_FUNCTION_ARGS)
2214 int32 typmod = PG_GETARG_INT32(0);
2216 PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
2221 * Convert TIME WITH TIME ZONE data type to POSIX time structure.
2224 timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
2226 TimeOffset trem = time->time;
2228 #ifdef HAVE_INT64_TIMESTAMP
2229 tm->tm_hour = trem / USECS_PER_HOUR;
2230 trem -= tm->tm_hour * USECS_PER_HOUR;
2231 tm->tm_min = trem / USECS_PER_MINUTE;
2232 trem -= tm->tm_min * USECS_PER_MINUTE;
2233 tm->tm_sec = trem / USECS_PER_SEC;
2234 *fsec = trem - tm->tm_sec * USECS_PER_SEC;
2237 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
2238 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
2239 TMODULO(trem, tm->tm_sec, 1.0);
2240 trem = TIMEROUND(trem);
2241 /* roundoff may need to propagate to higher-order fields */
2244 trem = ceil(time->time);
2257 * Adjust time type for specified scale factor.
2258 * Used by PostgreSQL type system to stuff columns.
2261 timetz_scale(PG_FUNCTION_ARGS)
2263 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2264 int32 typmod = PG_GETARG_INT32(1);
2267 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2269 result->time = time->time;
2270 result->zone = time->zone;
2272 AdjustTimeForTypmod(&(result->time), typmod);
2274 PG_RETURN_TIMETZADT_P(result);
2279 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
2284 /* Primary sort is by true (GMT-equivalent) time */
2285 #ifdef HAVE_INT64_TIMESTAMP
2286 t1 = time1->time + (time1->zone * USECS_PER_SEC);
2287 t2 = time2->time + (time2->zone * USECS_PER_SEC);
2289 t1 = time1->time + time1->zone;
2290 t2 = time2->time + time2->zone;
2299 * If same GMT time, sort by timezone; we only want to say that two
2300 * timetz's are equal if both the time and zone parts are equal.
2302 if (time1->zone > time2->zone)
2304 if (time1->zone < time2->zone)
2311 timetz_eq(PG_FUNCTION_ARGS)
2313 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2314 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2316 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
2320 timetz_ne(PG_FUNCTION_ARGS)
2322 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2323 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2325 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
2329 timetz_lt(PG_FUNCTION_ARGS)
2331 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2332 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2334 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
2338 timetz_le(PG_FUNCTION_ARGS)
2340 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2341 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2343 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
2347 timetz_gt(PG_FUNCTION_ARGS)
2349 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2350 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2352 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
2356 timetz_ge(PG_FUNCTION_ARGS)
2358 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2359 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2361 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
2365 timetz_cmp(PG_FUNCTION_ARGS)
2367 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2368 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2370 PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
2374 timetz_hash(PG_FUNCTION_ARGS)
2376 TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2380 * To avoid any problems with padding bytes in the struct, we figure the
2381 * field hashes separately and XOR them. This also provides a convenient
2382 * framework for dealing with the fact that the time field might be either
2385 #ifdef HAVE_INT64_TIMESTAMP
2386 thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
2387 Int64GetDatumFast(key->time)));
2389 thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8,
2390 Float8GetDatumFast(key->time)));
2392 thash ^= DatumGetUInt32(hash_uint32(key->zone));
2393 PG_RETURN_UINT32(thash);
2397 timetz_larger(PG_FUNCTION_ARGS)
2399 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2400 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2403 if (timetz_cmp_internal(time1, time2) > 0)
2407 PG_RETURN_TIMETZADT_P(result);
2411 timetz_smaller(PG_FUNCTION_ARGS)
2413 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2414 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2417 if (timetz_cmp_internal(time1, time2) < 0)
2421 PG_RETURN_TIMETZADT_P(result);
2424 /* timetz_pl_interval()
2425 * Add interval to timetz.
2428 timetz_pl_interval(PG_FUNCTION_ARGS)
2430 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2431 Interval *span = PG_GETARG_INTERVAL_P(1);
2434 #ifndef HAVE_INT64_TIMESTAMP
2438 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2440 #ifdef HAVE_INT64_TIMESTAMP
2441 result->time = time->time + span->time;
2442 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2443 if (result->time < INT64CONST(0))
2444 result->time += USECS_PER_DAY;
2446 result->time = time->time + span->time;
2447 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2448 if (result->time < 0)
2449 result->time += SECS_PER_DAY;
2452 result->zone = time->zone;
2454 PG_RETURN_TIMETZADT_P(result);
2457 /* timetz_mi_interval()
2458 * Subtract interval from timetz.
2461 timetz_mi_interval(PG_FUNCTION_ARGS)
2463 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2464 Interval *span = PG_GETARG_INTERVAL_P(1);
2467 #ifndef HAVE_INT64_TIMESTAMP
2471 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2473 #ifdef HAVE_INT64_TIMESTAMP
2474 result->time = time->time - span->time;
2475 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2476 if (result->time < INT64CONST(0))
2477 result->time += USECS_PER_DAY;
2479 result->time = time->time - span->time;
2480 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2481 if (result->time < 0)
2482 result->time += SECS_PER_DAY;
2485 result->zone = time->zone;
2487 PG_RETURN_TIMETZADT_P(result);
2490 /* overlaps_timetz() --- implements the SQL OVERLAPS operator.
2492 * Algorithm is per SQL spec. This is much harder than you'd think
2493 * because the spec requires us to deliver a non-null answer in some cases
2494 * where some of the inputs are null.
2497 overlaps_timetz(PG_FUNCTION_ARGS)
2500 * The arguments are TimeTzADT *, but we leave them as generic Datums for
2501 * convenience of notation --- and to avoid dereferencing nulls.
2503 Datum ts1 = PG_GETARG_DATUM(0);
2504 Datum te1 = PG_GETARG_DATUM(1);
2505 Datum ts2 = PG_GETARG_DATUM(2);
2506 Datum te2 = PG_GETARG_DATUM(3);
2507 bool ts1IsNull = PG_ARGISNULL(0);
2508 bool te1IsNull = PG_ARGISNULL(1);
2509 bool ts2IsNull = PG_ARGISNULL(2);
2510 bool te2IsNull = PG_ARGISNULL(3);
2512 #define TIMETZ_GT(t1,t2) \
2513 DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2514 #define TIMETZ_LT(t1,t2) \
2515 DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2518 * If both endpoints of interval 1 are null, the result is null (unknown).
2519 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2520 * take ts1 as the lesser endpoint.
2526 /* swap null for non-null */
2530 else if (!te1IsNull)
2532 if (TIMETZ_GT(ts1, te1))
2541 /* Likewise for interval 2. */
2546 /* swap null for non-null */
2550 else if (!te2IsNull)
2552 if (TIMETZ_GT(ts2, te2))
2562 * At this point neither ts1 nor ts2 is null, so we can consider three
2563 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2565 if (TIMETZ_GT(ts1, ts2))
2568 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2569 * in the presence of nulls it's not quite completely so.
2573 if (TIMETZ_LT(ts1, te2))
2574 PG_RETURN_BOOL(true);
2579 * If te1 is not null then we had ts1 <= te1 above, and we just found
2580 * ts1 >= te2, hence te1 >= te2.
2582 PG_RETURN_BOOL(false);
2584 else if (TIMETZ_LT(ts1, ts2))
2586 /* This case is ts2 < te1 OR te2 < te1 */
2589 if (TIMETZ_LT(ts2, te1))
2590 PG_RETURN_BOOL(true);
2595 * If te2 is not null then we had ts2 <= te2 above, and we just found
2596 * ts2 >= te1, hence te2 >= te1.
2598 PG_RETURN_BOOL(false);
2603 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2604 * rather silly way of saying "true if both are nonnull, else null".
2606 if (te1IsNull || te2IsNull)
2608 PG_RETURN_BOOL(true);
2617 timetz_time(PG_FUNCTION_ARGS)
2619 TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
2622 /* swallow the time zone and just return the time */
2623 result = timetz->time;
2625 PG_RETURN_TIMEADT(result);
2630 time_timetz(PG_FUNCTION_ARGS)
2632 TimeADT time = PG_GETARG_TIMEADT(0);
2639 GetCurrentDateTime(tm);
2640 time2tm(time, tm, &fsec);
2641 tz = DetermineTimeZoneOffset(tm, session_timezone);
2643 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2645 result->time = time;
2648 PG_RETURN_TIMETZADT_P(result);
2652 /* timestamptz_timetz()
2653 * Convert timestamp to timetz data type.
2656 timestamptz_timetz(PG_FUNCTION_ARGS)
2658 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2665 if (TIMESTAMP_NOT_FINITE(timestamp))
2668 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
2670 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2671 errmsg("timestamp out of range")));
2673 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2675 tm2timetz(tm, fsec, tz, result);
2677 PG_RETURN_TIMETZADT_P(result);
2681 /* datetimetz_timestamptz()
2682 * Convert date and timetz to timestamp with time zone data type.
2683 * Timestamp is stored in GMT, so add the time zone
2684 * stored with the timetz to the result.
2685 * - thomas 2000-03-10
2688 datetimetz_timestamptz(PG_FUNCTION_ARGS)
2690 DateADT date = PG_GETARG_DATEADT(0);
2691 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2694 if (DATE_IS_NOBEGIN(date))
2695 TIMESTAMP_NOBEGIN(result);
2696 else if (DATE_IS_NOEND(date))
2697 TIMESTAMP_NOEND(result);
2701 * Date's range is wider than timestamp's, so check for boundaries.
2702 * Since dates have the same minimum values as timestamps, only upper
2703 * boundary need be checked for overflow.
2705 if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
2707 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2708 errmsg("date out of range for timestamp")));
2709 #ifdef HAVE_INT64_TIMESTAMP
2710 result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2712 result = date * (double) SECS_PER_DAY + time->time + time->zone;
2716 * Since it is possible to go beyond allowed timestamptz range because
2717 * of time zone, check for allowed timestamp range after adding tz.
2719 if (!IS_VALID_TIMESTAMP(result))
2721 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2722 errmsg("date out of range for timestamp")));
2725 PG_RETURN_TIMESTAMP(result);
2730 * Extract specified field from time type.
2733 timetz_part(PG_FUNCTION_ARGS)
2735 text *units = PG_GETARG_TEXT_PP(0);
2736 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2742 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
2743 VARSIZE_ANY_EXHDR(units),
2746 type = DecodeUnits(0, lowunits, &val);
2747 if (type == UNKNOWN_FIELD)
2748 type = DecodeSpecial(0, lowunits, &val);
2758 timetz2tm(time, tm, &fsec, &tz);
2768 result /= SECS_PER_MINUTE;
2769 FMODULO(result, dummy, (double) SECS_PER_MINUTE);
2774 FMODULO(dummy, result, (double) SECS_PER_HOUR);
2778 #ifdef HAVE_INT64_TIMESTAMP
2779 result = tm->tm_sec * 1000000.0 + fsec;
2781 result = (tm->tm_sec + fsec) * 1000000;
2786 #ifdef HAVE_INT64_TIMESTAMP
2787 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
2789 result = (tm->tm_sec + fsec) * 1000;
2794 #ifdef HAVE_INT64_TIMESTAMP
2795 result = tm->tm_sec + fsec / 1000000.0;
2797 result = tm->tm_sec + fsec;
2802 result = tm->tm_min;
2806 result = tm->tm_hour;
2815 case DTK_MILLENNIUM:
2818 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2819 errmsg("\"time with time zone\" units \"%s\" not recognized",
2824 else if (type == RESERV && val == DTK_EPOCH)
2826 #ifdef HAVE_INT64_TIMESTAMP
2827 result = time->time / 1000000.0 + time->zone;
2829 result = time->time + time->zone;
2835 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2836 errmsg("\"time with time zone\" units \"%s\" not recognized",
2841 PG_RETURN_FLOAT8(result);
2845 * Encode time with time zone type with specified time zone.
2846 * Applies DST rules as of the current date.
2849 timetz_zone(PG_FUNCTION_ARGS)
2851 text *zone = PG_GETARG_TEXT_PP(0);
2852 TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
2855 char tzname[TZ_STRLEN_MAX + 1];
2862 * Look up the requested timezone. First we look in the timezone
2863 * abbreviation table (to handle cases like "EST"), and if that fails, we
2864 * look in the timezone database (to handle cases like
2865 * "America/New_York"). (This matches the order in which timestamp input
2866 * checks the cases; it's important because the timezone database unwisely
2867 * uses a few zone names that are identical to offset abbreviations.)
2869 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
2871 /* DecodeTimezoneAbbrev requires lowercase input */
2872 lowzone = downcase_truncate_identifier(tzname,
2876 type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
2878 if (type == TZ || type == DTZ)
2880 /* fixed-offset abbreviation */
2883 else if (type == DYNTZ)
2885 /* dynamic-offset abbreviation, resolve using current time */
2886 pg_time_t now = (pg_time_t) time(NULL);
2889 tm = pg_localtime(&now, tzp);
2890 tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
2894 /* try it as a full zone name */
2895 tzp = pg_tzset(tzname);
2898 /* Get the offset-from-GMT that is valid today for the zone */
2899 pg_time_t now = (pg_time_t) time(NULL);
2902 tm = pg_localtime(&now, tzp);
2903 tz = -tm->tm_gmtoff;
2908 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2909 errmsg("time zone \"%s\" not recognized", tzname)));
2910 tz = 0; /* keep compiler quiet */
2914 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2916 #ifdef HAVE_INT64_TIMESTAMP
2917 result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
2918 while (result->time < INT64CONST(0))
2919 result->time += USECS_PER_DAY;
2920 while (result->time >= USECS_PER_DAY)
2921 result->time -= USECS_PER_DAY;
2923 result->time = t->time + (t->zone - tz);
2924 while (result->time < 0)
2925 result->time += SECS_PER_DAY;
2926 while (result->time >= SECS_PER_DAY)
2927 result->time -= SECS_PER_DAY;
2932 PG_RETURN_TIMETZADT_P(result);
2936 * Encode time with time zone type with specified time interval as time zone.
2939 timetz_izone(PG_FUNCTION_ARGS)
2941 Interval *zone = PG_GETARG_INTERVAL_P(0);
2942 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2946 if (zone->month != 0 || zone->day != 0)
2948 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2949 errmsg("interval time zone \"%s\" must not include months or days",
2950 DatumGetCString(DirectFunctionCall1(interval_out,
2951 PointerGetDatum(zone))))));
2953 #ifdef HAVE_INT64_TIMESTAMP
2954 tz = -(zone->time / USECS_PER_SEC);
2959 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2961 #ifdef HAVE_INT64_TIMESTAMP
2962 result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
2963 while (result->time < INT64CONST(0))
2964 result->time += USECS_PER_DAY;
2965 while (result->time >= USECS_PER_DAY)
2966 result->time -= USECS_PER_DAY;
2968 result->time = time->time + (time->zone - tz);
2969 while (result->time < 0)
2970 result->time += SECS_PER_DAY;
2971 while (result->time >= SECS_PER_DAY)
2972 result->time -= SECS_PER_DAY;
2977 PG_RETURN_TIMETZADT_P(result);