1 /*-------------------------------------------------------------------------
4 * implements DATE and TIME data types specified in SQL standard
6 * Portions Copyright (c) 1996-2014, 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 "libpq/pqformat.h"
25 #include "miscadmin.h"
26 #include "parser/scansup.h"
27 #include "utils/array.h"
28 #include "utils/builtins.h"
29 #include "utils/date.h"
30 #include "utils/datetime.h"
31 #include "utils/nabstime.h"
32 #include "utils/sortsupport.h"
35 * gcc's -ffast-math switch breaks routines that expect exact results from
36 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
39 #error -ffast-math is known to break this code
43 static void EncodeSpecialDate(DateADT dt, char *str);
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)
59 tl = ArrayGetIntegerTypmods(ta, &n);
62 * we're not too tense about good error message here because grammar
63 * shouldn't allow wrong number of modifiers for TIME
67 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
68 errmsg("invalid type modifier")));
72 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
73 errmsg("TIME(%d)%s precision must not be negative",
74 *tl, (istz ? " WITH TIME ZONE" : ""))));
75 if (*tl > MAX_TIME_PRECISION)
78 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
79 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
80 *tl, (istz ? " WITH TIME ZONE" : ""),
81 MAX_TIME_PRECISION)));
82 typmod = MAX_TIME_PRECISION;
90 /* common code for timetypmodout and timetztypmodout */
92 anytime_typmodout(bool istz, int32 typmod)
94 const char *tz = istz ? " with time zone" : " without time zone";
97 return psprintf("(%d)%s", (int) typmod, tz);
99 return psprintf("%s", tz);
103 /*****************************************************************************
105 *****************************************************************************/
109 * Given date text string, convert to internal date format.
112 date_in(PG_FUNCTION_ARGS)
114 char *str = PG_GETARG_CSTRING(0);
123 char *field[MAXDATEFIELDS];
124 int ftype[MAXDATEFIELDS];
125 char workbuf[MAXDATELEN + 1];
127 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
128 field, ftype, MAXDATEFIELDS, &nf);
130 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
132 DateTimeParseError(dterr, str, "date");
141 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
142 errmsg("date/time value \"current\" is no longer supported")));
144 GetCurrentDateTime(tm);
153 PG_RETURN_DATEADT(date);
157 PG_RETURN_DATEADT(date);
160 DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
164 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
166 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
167 errmsg("date out of range: \"%s\"", str)));
169 date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
171 PG_RETURN_DATEADT(date);
175 * Given internal format date, convert to text string.
178 date_out(PG_FUNCTION_ARGS)
180 DateADT date = PG_GETARG_DATEADT(0);
184 char buf[MAXDATELEN + 1];
186 if (DATE_NOT_FINITE(date))
187 EncodeSpecialDate(date, buf);
190 j2date(date + POSTGRES_EPOCH_JDATE,
191 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
192 EncodeDateOnly(tm, DateStyle, buf);
195 result = pstrdup(buf);
196 PG_RETURN_CSTRING(result);
200 * date_recv - converts external binary format to date
203 date_recv(PG_FUNCTION_ARGS)
205 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
208 result = (DateADT) pq_getmsgint(buf, sizeof(DateADT));
210 /* Limit to the same range that date_in() accepts. */
211 if (DATE_NOT_FINITE(result))
213 else if (result < -POSTGRES_EPOCH_JDATE ||
214 result >= JULIAN_MAX - POSTGRES_EPOCH_JDATE)
216 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
217 errmsg("date out of range")));
219 PG_RETURN_DATEADT(result);
223 * date_send - converts date to binary format
226 date_send(PG_FUNCTION_ARGS)
228 DateADT date = PG_GETARG_DATEADT(0);
231 pq_begintypsend(&buf);
232 pq_sendint(&buf, date, sizeof(date));
233 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
237 * make_date - date constructor
240 make_date(PG_FUNCTION_ARGS)
246 tm.tm_year = PG_GETARG_INT32(0);
247 tm.tm_mon = PG_GETARG_INT32(1);
248 tm.tm_mday = PG_GETARG_INT32(2);
251 * Note: we'll reject zero or negative year values. Perhaps negatives
252 * should be allowed to represent BC years?
254 dterr = ValidateDate(DTK_DATE_M, false, false, false, &tm);
258 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
259 errmsg("date field value out of range: %d-%02d-%02d",
260 tm.tm_year, tm.tm_mon, tm.tm_mday)));
262 if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
264 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
265 errmsg("date out of range: %d-%02d-%02d",
266 tm.tm_year, tm.tm_mon, tm.tm_mday)));
268 date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
270 PG_RETURN_DATEADT(date);
274 * Convert reserved date values to string.
277 EncodeSpecialDate(DateADT dt, char *str)
279 if (DATE_IS_NOBEGIN(dt))
281 else if (DATE_IS_NOEND(dt))
283 else /* shouldn't happen */
284 elog(ERROR, "invalid argument for EncodeSpecialDate");
289 * Comparison functions for dates
293 date_eq(PG_FUNCTION_ARGS)
295 DateADT dateVal1 = PG_GETARG_DATEADT(0);
296 DateADT dateVal2 = PG_GETARG_DATEADT(1);
298 PG_RETURN_BOOL(dateVal1 == dateVal2);
302 date_ne(PG_FUNCTION_ARGS)
304 DateADT dateVal1 = PG_GETARG_DATEADT(0);
305 DateADT dateVal2 = PG_GETARG_DATEADT(1);
307 PG_RETURN_BOOL(dateVal1 != dateVal2);
311 date_lt(PG_FUNCTION_ARGS)
313 DateADT dateVal1 = PG_GETARG_DATEADT(0);
314 DateADT dateVal2 = PG_GETARG_DATEADT(1);
316 PG_RETURN_BOOL(dateVal1 < dateVal2);
320 date_le(PG_FUNCTION_ARGS)
322 DateADT dateVal1 = PG_GETARG_DATEADT(0);
323 DateADT dateVal2 = PG_GETARG_DATEADT(1);
325 PG_RETURN_BOOL(dateVal1 <= dateVal2);
329 date_gt(PG_FUNCTION_ARGS)
331 DateADT dateVal1 = PG_GETARG_DATEADT(0);
332 DateADT dateVal2 = PG_GETARG_DATEADT(1);
334 PG_RETURN_BOOL(dateVal1 > dateVal2);
338 date_ge(PG_FUNCTION_ARGS)
340 DateADT dateVal1 = PG_GETARG_DATEADT(0);
341 DateADT dateVal2 = PG_GETARG_DATEADT(1);
343 PG_RETURN_BOOL(dateVal1 >= dateVal2);
347 date_cmp(PG_FUNCTION_ARGS)
349 DateADT dateVal1 = PG_GETARG_DATEADT(0);
350 DateADT dateVal2 = PG_GETARG_DATEADT(1);
352 if (dateVal1 < dateVal2)
354 else if (dateVal1 > dateVal2)
360 date_fastcmp(Datum x, Datum y, SortSupport ssup)
362 DateADT a = DatumGetDateADT(x);
363 DateADT b = DatumGetDateADT(y);
373 date_sortsupport(PG_FUNCTION_ARGS)
375 SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
377 ssup->comparator = date_fastcmp;
382 date_finite(PG_FUNCTION_ARGS)
384 DateADT date = PG_GETARG_DATEADT(0);
386 PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
390 date_larger(PG_FUNCTION_ARGS)
392 DateADT dateVal1 = PG_GETARG_DATEADT(0);
393 DateADT dateVal2 = PG_GETARG_DATEADT(1);
395 PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
399 date_smaller(PG_FUNCTION_ARGS)
401 DateADT dateVal1 = PG_GETARG_DATEADT(0);
402 DateADT dateVal2 = PG_GETARG_DATEADT(1);
404 PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
407 /* Compute difference between two dates in days.
410 date_mi(PG_FUNCTION_ARGS)
412 DateADT dateVal1 = PG_GETARG_DATEADT(0);
413 DateADT dateVal2 = PG_GETARG_DATEADT(1);
415 if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
417 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
418 errmsg("cannot subtract infinite dates")));
420 PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
423 /* Add a number of days to a date, giving a new date.
424 * Must handle both positive and negative numbers of days.
427 date_pli(PG_FUNCTION_ARGS)
429 DateADT dateVal = PG_GETARG_DATEADT(0);
430 int32 days = PG_GETARG_INT32(1);
432 if (DATE_NOT_FINITE(dateVal))
433 days = 0; /* can't change infinity */
435 PG_RETURN_DATEADT(dateVal + days);
438 /* Subtract a number of days from a date, giving a new date.
441 date_mii(PG_FUNCTION_ARGS)
443 DateADT dateVal = PG_GETARG_DATEADT(0);
444 int32 days = PG_GETARG_INT32(1);
446 if (DATE_NOT_FINITE(dateVal))
447 days = 0; /* can't change infinity */
449 PG_RETURN_DATEADT(dateVal - days);
453 * Internal routines for promoting date to timestamp and timestamp with
458 date2timestamp(DateADT dateVal)
462 if (DATE_IS_NOBEGIN(dateVal))
463 TIMESTAMP_NOBEGIN(result);
464 else if (DATE_IS_NOEND(dateVal))
465 TIMESTAMP_NOEND(result);
468 #ifdef HAVE_INT64_TIMESTAMP
469 /* date is days since 2000, timestamp is microseconds since same... */
470 result = dateVal * USECS_PER_DAY;
471 /* Date's range is wider than timestamp's, so check for overflow */
472 if (result / USECS_PER_DAY != dateVal)
474 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
475 errmsg("date out of range for timestamp")));
477 /* date is days since 2000, timestamp is seconds since same... */
478 result = dateVal * (double) SECS_PER_DAY;
486 date2timestamptz(DateADT dateVal)
493 if (DATE_IS_NOBEGIN(dateVal))
494 TIMESTAMP_NOBEGIN(result);
495 else if (DATE_IS_NOEND(dateVal))
496 TIMESTAMP_NOEND(result);
499 j2date(dateVal + POSTGRES_EPOCH_JDATE,
500 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
504 tz = DetermineTimeZoneOffset(tm, session_timezone);
506 #ifdef HAVE_INT64_TIMESTAMP
507 result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
508 /* Date's range is wider than timestamp's, so check for overflow */
509 if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
511 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
512 errmsg("date out of range for timestamp")));
514 result = dateVal * (double) SECS_PER_DAY + tz;
522 * date2timestamp_no_overflow
524 * This is chartered to produce a double value that is numerically
525 * equivalent to the corresponding Timestamp value, if the date is in the
526 * valid range of Timestamps, but in any case not throw an overflow error.
527 * We can do this since the numerical range of double is greater than
528 * that of non-erroneous timestamps. The results are currently only
529 * used for statistical estimation purposes.
532 date2timestamp_no_overflow(DateADT dateVal)
536 if (DATE_IS_NOBEGIN(dateVal))
538 else if (DATE_IS_NOEND(dateVal))
542 #ifdef HAVE_INT64_TIMESTAMP
543 /* date is days since 2000, timestamp is microseconds since same... */
544 result = dateVal * (double) USECS_PER_DAY;
546 /* date is days since 2000, timestamp is seconds since same... */
547 result = dateVal * (double) SECS_PER_DAY;
556 * Crosstype comparison functions for dates
560 date_eq_timestamp(PG_FUNCTION_ARGS)
562 DateADT dateVal = PG_GETARG_DATEADT(0);
563 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
566 dt1 = date2timestamp(dateVal);
568 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
572 date_ne_timestamp(PG_FUNCTION_ARGS)
574 DateADT dateVal = PG_GETARG_DATEADT(0);
575 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
578 dt1 = date2timestamp(dateVal);
580 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
584 date_lt_timestamp(PG_FUNCTION_ARGS)
586 DateADT dateVal = PG_GETARG_DATEADT(0);
587 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
590 dt1 = date2timestamp(dateVal);
592 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
596 date_gt_timestamp(PG_FUNCTION_ARGS)
598 DateADT dateVal = PG_GETARG_DATEADT(0);
599 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
602 dt1 = date2timestamp(dateVal);
604 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
608 date_le_timestamp(PG_FUNCTION_ARGS)
610 DateADT dateVal = PG_GETARG_DATEADT(0);
611 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
614 dt1 = date2timestamp(dateVal);
616 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
620 date_ge_timestamp(PG_FUNCTION_ARGS)
622 DateADT dateVal = PG_GETARG_DATEADT(0);
623 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
626 dt1 = date2timestamp(dateVal);
628 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
632 date_cmp_timestamp(PG_FUNCTION_ARGS)
634 DateADT dateVal = PG_GETARG_DATEADT(0);
635 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
638 dt1 = date2timestamp(dateVal);
640 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
644 date_eq_timestamptz(PG_FUNCTION_ARGS)
646 DateADT dateVal = PG_GETARG_DATEADT(0);
647 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
650 dt1 = date2timestamptz(dateVal);
652 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
656 date_ne_timestamptz(PG_FUNCTION_ARGS)
658 DateADT dateVal = PG_GETARG_DATEADT(0);
659 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
662 dt1 = date2timestamptz(dateVal);
664 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
668 date_lt_timestamptz(PG_FUNCTION_ARGS)
670 DateADT dateVal = PG_GETARG_DATEADT(0);
671 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
674 dt1 = date2timestamptz(dateVal);
676 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
680 date_gt_timestamptz(PG_FUNCTION_ARGS)
682 DateADT dateVal = PG_GETARG_DATEADT(0);
683 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
686 dt1 = date2timestamptz(dateVal);
688 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
692 date_le_timestamptz(PG_FUNCTION_ARGS)
694 DateADT dateVal = PG_GETARG_DATEADT(0);
695 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
698 dt1 = date2timestamptz(dateVal);
700 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
704 date_ge_timestamptz(PG_FUNCTION_ARGS)
706 DateADT dateVal = PG_GETARG_DATEADT(0);
707 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
710 dt1 = date2timestamptz(dateVal);
712 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
716 date_cmp_timestamptz(PG_FUNCTION_ARGS)
718 DateADT dateVal = PG_GETARG_DATEADT(0);
719 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
722 dt1 = date2timestamptz(dateVal);
724 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
728 timestamp_eq_date(PG_FUNCTION_ARGS)
730 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
731 DateADT dateVal = PG_GETARG_DATEADT(1);
734 dt2 = date2timestamp(dateVal);
736 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
740 timestamp_ne_date(PG_FUNCTION_ARGS)
742 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
743 DateADT dateVal = PG_GETARG_DATEADT(1);
746 dt2 = date2timestamp(dateVal);
748 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
752 timestamp_lt_date(PG_FUNCTION_ARGS)
754 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
755 DateADT dateVal = PG_GETARG_DATEADT(1);
758 dt2 = date2timestamp(dateVal);
760 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
764 timestamp_gt_date(PG_FUNCTION_ARGS)
766 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
767 DateADT dateVal = PG_GETARG_DATEADT(1);
770 dt2 = date2timestamp(dateVal);
772 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
776 timestamp_le_date(PG_FUNCTION_ARGS)
778 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
779 DateADT dateVal = PG_GETARG_DATEADT(1);
782 dt2 = date2timestamp(dateVal);
784 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
788 timestamp_ge_date(PG_FUNCTION_ARGS)
790 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
791 DateADT dateVal = PG_GETARG_DATEADT(1);
794 dt2 = date2timestamp(dateVal);
796 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
800 timestamp_cmp_date(PG_FUNCTION_ARGS)
802 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
803 DateADT dateVal = PG_GETARG_DATEADT(1);
806 dt2 = date2timestamp(dateVal);
808 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
812 timestamptz_eq_date(PG_FUNCTION_ARGS)
814 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
815 DateADT dateVal = PG_GETARG_DATEADT(1);
818 dt2 = date2timestamptz(dateVal);
820 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
824 timestamptz_ne_date(PG_FUNCTION_ARGS)
826 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
827 DateADT dateVal = PG_GETARG_DATEADT(1);
830 dt2 = date2timestamptz(dateVal);
832 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
836 timestamptz_lt_date(PG_FUNCTION_ARGS)
838 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
839 DateADT dateVal = PG_GETARG_DATEADT(1);
842 dt2 = date2timestamptz(dateVal);
844 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
848 timestamptz_gt_date(PG_FUNCTION_ARGS)
850 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
851 DateADT dateVal = PG_GETARG_DATEADT(1);
854 dt2 = date2timestamptz(dateVal);
856 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
860 timestamptz_le_date(PG_FUNCTION_ARGS)
862 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
863 DateADT dateVal = PG_GETARG_DATEADT(1);
866 dt2 = date2timestamptz(dateVal);
868 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
872 timestamptz_ge_date(PG_FUNCTION_ARGS)
874 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
875 DateADT dateVal = PG_GETARG_DATEADT(1);
878 dt2 = date2timestamptz(dateVal);
880 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
884 timestamptz_cmp_date(PG_FUNCTION_ARGS)
886 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
887 DateADT dateVal = PG_GETARG_DATEADT(1);
890 dt2 = date2timestamptz(dateVal);
892 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
896 /* Add an interval to a date, giving a new date.
897 * Must handle both positive and negative intervals.
899 * We implement this by promoting the date to timestamp (without time zone)
900 * and then using the timestamp plus interval function.
903 date_pl_interval(PG_FUNCTION_ARGS)
905 DateADT dateVal = PG_GETARG_DATEADT(0);
906 Interval *span = PG_GETARG_INTERVAL_P(1);
909 dateStamp = date2timestamp(dateVal);
911 return DirectFunctionCall2(timestamp_pl_interval,
912 TimestampGetDatum(dateStamp),
913 PointerGetDatum(span));
916 /* Subtract an interval from a date, giving a new date.
917 * Must handle both positive and negative intervals.
919 * We implement this by promoting the date to timestamp (without time zone)
920 * and then using the timestamp minus interval function.
923 date_mi_interval(PG_FUNCTION_ARGS)
925 DateADT dateVal = PG_GETARG_DATEADT(0);
926 Interval *span = PG_GETARG_INTERVAL_P(1);
929 dateStamp = date2timestamp(dateVal);
931 return DirectFunctionCall2(timestamp_mi_interval,
932 TimestampGetDatum(dateStamp),
933 PointerGetDatum(span));
937 * Convert date to timestamp data type.
940 date_timestamp(PG_FUNCTION_ARGS)
942 DateADT dateVal = PG_GETARG_DATEADT(0);
945 result = date2timestamp(dateVal);
947 PG_RETURN_TIMESTAMP(result);
951 * Convert timestamp to date data type.
954 timestamp_date(PG_FUNCTION_ARGS)
956 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
962 if (TIMESTAMP_IS_NOBEGIN(timestamp))
963 DATE_NOBEGIN(result);
964 else if (TIMESTAMP_IS_NOEND(timestamp))
968 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
970 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
971 errmsg("timestamp out of range")));
973 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
976 PG_RETURN_DATEADT(result);
980 /* date_timestamptz()
981 * Convert date to timestamp with time zone data type.
984 date_timestamptz(PG_FUNCTION_ARGS)
986 DateADT dateVal = PG_GETARG_DATEADT(0);
989 result = date2timestamptz(dateVal);
991 PG_RETURN_TIMESTAMP(result);
995 /* timestamptz_date()
996 * Convert timestamp with time zone to date data type.
999 timestamptz_date(PG_FUNCTION_ARGS)
1001 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1008 if (TIMESTAMP_IS_NOBEGIN(timestamp))
1009 DATE_NOBEGIN(result);
1010 else if (TIMESTAMP_IS_NOEND(timestamp))
1014 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
1016 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1017 errmsg("timestamp out of range")));
1019 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1022 PG_RETURN_DATEADT(result);
1027 * Convert abstime to date data type.
1030 abstime_date(PG_FUNCTION_ARGS)
1032 AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
1040 case INVALID_ABSTIME:
1042 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1043 errmsg("cannot convert reserved abstime value to date")));
1044 result = 0; /* keep compiler quiet */
1047 case NOSTART_ABSTIME:
1048 DATE_NOBEGIN(result);
1056 abstime2tm(abstime, &tz, tm, NULL);
1057 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1061 PG_RETURN_DATEADT(result);
1065 /*****************************************************************************
1067 *****************************************************************************/
1070 time_in(PG_FUNCTION_ARGS)
1072 char *str = PG_GETARG_CSTRING(0);
1075 Oid typelem = PG_GETARG_OID(1);
1077 int32 typmod = PG_GETARG_INT32(2);
1085 char workbuf[MAXDATELEN + 1];
1086 char *field[MAXDATEFIELDS];
1088 int ftype[MAXDATEFIELDS];
1090 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1091 field, ftype, MAXDATEFIELDS, &nf);
1093 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1095 DateTimeParseError(dterr, str, "time");
1097 tm2time(tm, fsec, &result);
1098 AdjustTimeForTypmod(&result, typmod);
1100 PG_RETURN_TIMEADT(result);
1104 * Convert a tm structure to a time data type.
1107 tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
1109 #ifdef HAVE_INT64_TIMESTAMP
1110 *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
1111 * USECS_PER_SEC) + fsec;
1113 *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1119 * Convert time data type to POSIX time structure.
1121 * For dates within the range of pg_time_t, convert to the local time zone.
1122 * If out of this range, leave as UTC (in practice that could only happen
1123 * if pg_time_t is just 32 bits) - thomas 97/05/27
1126 time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
1128 #ifdef HAVE_INT64_TIMESTAMP
1129 tm->tm_hour = time / USECS_PER_HOUR;
1130 time -= tm->tm_hour * USECS_PER_HOUR;
1131 tm->tm_min = time / USECS_PER_MINUTE;
1132 time -= tm->tm_min * USECS_PER_MINUTE;
1133 tm->tm_sec = time / USECS_PER_SEC;
1134 time -= tm->tm_sec * USECS_PER_SEC;
1141 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
1142 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1143 TMODULO(trem, tm->tm_sec, 1.0);
1144 trem = TIMEROUND(trem);
1145 /* roundoff may need to propagate to higher-order fields */
1158 time_out(PG_FUNCTION_ARGS)
1160 TimeADT time = PG_GETARG_TIMEADT(0);
1165 char buf[MAXDATELEN + 1];
1167 time2tm(time, tm, &fsec);
1168 EncodeTimeOnly(tm, fsec, false, 0, DateStyle, buf);
1170 result = pstrdup(buf);
1171 PG_RETURN_CSTRING(result);
1175 * time_recv - converts external binary format to time
1177 * We make no attempt to provide compatibility between int and float
1178 * time representations ...
1181 time_recv(PG_FUNCTION_ARGS)
1183 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1186 Oid typelem = PG_GETARG_OID(1);
1188 int32 typmod = PG_GETARG_INT32(2);
1191 #ifdef HAVE_INT64_TIMESTAMP
1192 result = pq_getmsgint64(buf);
1194 if (result < INT64CONST(0) || result > USECS_PER_DAY)
1196 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1197 errmsg("time out of range")));
1199 result = pq_getmsgfloat8(buf);
1201 if (result < 0 || result > (double) SECS_PER_DAY)
1203 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1204 errmsg("time out of range")));
1207 AdjustTimeForTypmod(&result, typmod);
1209 PG_RETURN_TIMEADT(result);
1213 * time_send - converts time to binary format
1216 time_send(PG_FUNCTION_ARGS)
1218 TimeADT time = PG_GETARG_TIMEADT(0);
1221 pq_begintypsend(&buf);
1222 #ifdef HAVE_INT64_TIMESTAMP
1223 pq_sendint64(&buf, time);
1225 pq_sendfloat8(&buf, time);
1227 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1231 timetypmodin(PG_FUNCTION_ARGS)
1233 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1235 PG_RETURN_INT32(anytime_typmodin(false, ta));
1239 timetypmodout(PG_FUNCTION_ARGS)
1241 int32 typmod = PG_GETARG_INT32(0);
1243 PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
1247 * make_time - time constructor
1250 make_time(PG_FUNCTION_ARGS)
1252 int tm_hour = PG_GETARG_INT32(0);
1253 int tm_min = PG_GETARG_INT32(1);
1254 double sec = PG_GETARG_FLOAT8(2);
1257 /* This should match the checks in DecodeTimeOnly */
1258 if (tm_hour < 0 || tm_min < 0 || tm_min > MINS_PER_HOUR - 1 ||
1259 sec < 0 || sec > SECS_PER_MINUTE ||
1260 tm_hour > HOURS_PER_DAY ||
1261 /* test for > 24:00:00 */
1262 (tm_hour == HOURS_PER_DAY && (tm_min > 0 || sec > 0)))
1264 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
1265 errmsg("time field value out of range: %d:%02d:%02g",
1266 tm_hour, tm_min, sec)));
1268 /* This should match tm2time */
1269 #ifdef HAVE_INT64_TIMESTAMP
1270 time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
1271 * USECS_PER_SEC) + rint(sec * USECS_PER_SEC);
1273 time = ((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE) + sec;
1276 PG_RETURN_TIMEADT(time);
1281 * Flatten calls to time_scale() and timetz_scale() that solely represent
1282 * increases in allowed precision.
1285 time_transform(PG_FUNCTION_ARGS)
1287 PG_RETURN_POINTER(TemporalTransform(MAX_TIME_PRECISION,
1288 (Node *) PG_GETARG_POINTER(0)));
1292 * Adjust time type for specified scale factor.
1293 * Used by PostgreSQL type system to stuff columns.
1296 time_scale(PG_FUNCTION_ARGS)
1298 TimeADT time = PG_GETARG_TIMEADT(0);
1299 int32 typmod = PG_GETARG_INT32(1);
1303 AdjustTimeForTypmod(&result, typmod);
1305 PG_RETURN_TIMEADT(result);
1308 /* AdjustTimeForTypmod()
1309 * Force the precision of the time value to a specified value.
1310 * Uses *exactly* the same code as in AdjustTimestampForTypemod()
1311 * but we make a separate copy because those types do not
1312 * have a fundamental tie together but rather a coincidence of
1313 * implementation. - thomas
1316 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1318 #ifdef HAVE_INT64_TIMESTAMP
1319 static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1320 INT64CONST(1000000),
1329 static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1339 /* note MAX_TIME_PRECISION differs in this case */
1340 static const double TimeScales[MAX_TIME_PRECISION + 1] = {
1355 if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1358 * Note: this round-to-nearest code is not completely consistent about
1359 * rounding values that are exactly halfway between integral values.
1360 * On most platforms, rint() will implement round-to-nearest-even, but
1361 * the integer code always rounds up (away from zero). Is it worth
1362 * trying to be consistent?
1364 #ifdef HAVE_INT64_TIMESTAMP
1365 if (*time >= INT64CONST(0))
1366 *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
1369 *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
1370 TimeScales[typmod]);
1372 *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
1379 time_eq(PG_FUNCTION_ARGS)
1381 TimeADT time1 = PG_GETARG_TIMEADT(0);
1382 TimeADT time2 = PG_GETARG_TIMEADT(1);
1384 PG_RETURN_BOOL(time1 == time2);
1388 time_ne(PG_FUNCTION_ARGS)
1390 TimeADT time1 = PG_GETARG_TIMEADT(0);
1391 TimeADT time2 = PG_GETARG_TIMEADT(1);
1393 PG_RETURN_BOOL(time1 != time2);
1397 time_lt(PG_FUNCTION_ARGS)
1399 TimeADT time1 = PG_GETARG_TIMEADT(0);
1400 TimeADT time2 = PG_GETARG_TIMEADT(1);
1402 PG_RETURN_BOOL(time1 < time2);
1406 time_le(PG_FUNCTION_ARGS)
1408 TimeADT time1 = PG_GETARG_TIMEADT(0);
1409 TimeADT time2 = PG_GETARG_TIMEADT(1);
1411 PG_RETURN_BOOL(time1 <= time2);
1415 time_gt(PG_FUNCTION_ARGS)
1417 TimeADT time1 = PG_GETARG_TIMEADT(0);
1418 TimeADT time2 = PG_GETARG_TIMEADT(1);
1420 PG_RETURN_BOOL(time1 > time2);
1424 time_ge(PG_FUNCTION_ARGS)
1426 TimeADT time1 = PG_GETARG_TIMEADT(0);
1427 TimeADT time2 = PG_GETARG_TIMEADT(1);
1429 PG_RETURN_BOOL(time1 >= time2);
1433 time_cmp(PG_FUNCTION_ARGS)
1435 TimeADT time1 = PG_GETARG_TIMEADT(0);
1436 TimeADT time2 = PG_GETARG_TIMEADT(1);
1439 PG_RETURN_INT32(-1);
1446 time_hash(PG_FUNCTION_ARGS)
1448 /* We can use either hashint8 or hashfloat8 directly */
1449 #ifdef HAVE_INT64_TIMESTAMP
1450 return hashint8(fcinfo);
1452 return hashfloat8(fcinfo);
1457 time_larger(PG_FUNCTION_ARGS)
1459 TimeADT time1 = PG_GETARG_TIMEADT(0);
1460 TimeADT time2 = PG_GETARG_TIMEADT(1);
1462 PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1466 time_smaller(PG_FUNCTION_ARGS)
1468 TimeADT time1 = PG_GETARG_TIMEADT(0);
1469 TimeADT time2 = PG_GETARG_TIMEADT(1);
1471 PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1474 /* overlaps_time() --- implements the SQL OVERLAPS operator.
1476 * Algorithm is per SQL spec. This is much harder than you'd think
1477 * because the spec requires us to deliver a non-null answer in some cases
1478 * where some of the inputs are null.
1481 overlaps_time(PG_FUNCTION_ARGS)
1484 * The arguments are TimeADT, but we leave them as generic Datums to avoid
1485 * dereferencing nulls (TimeADT is pass-by-reference!)
1487 Datum ts1 = PG_GETARG_DATUM(0);
1488 Datum te1 = PG_GETARG_DATUM(1);
1489 Datum ts2 = PG_GETARG_DATUM(2);
1490 Datum te2 = PG_GETARG_DATUM(3);
1491 bool ts1IsNull = PG_ARGISNULL(0);
1492 bool te1IsNull = PG_ARGISNULL(1);
1493 bool ts2IsNull = PG_ARGISNULL(2);
1494 bool te2IsNull = PG_ARGISNULL(3);
1496 #define TIMEADT_GT(t1,t2) \
1497 (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1498 #define TIMEADT_LT(t1,t2) \
1499 (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1502 * If both endpoints of interval 1 are null, the result is null (unknown).
1503 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1504 * take ts1 as the lesser endpoint.
1510 /* swap null for non-null */
1514 else if (!te1IsNull)
1516 if (TIMEADT_GT(ts1, te1))
1525 /* Likewise for interval 2. */
1530 /* swap null for non-null */
1534 else if (!te2IsNull)
1536 if (TIMEADT_GT(ts2, te2))
1546 * At this point neither ts1 nor ts2 is null, so we can consider three
1547 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1549 if (TIMEADT_GT(ts1, ts2))
1552 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
1553 * in the presence of nulls it's not quite completely so.
1557 if (TIMEADT_LT(ts1, te2))
1558 PG_RETURN_BOOL(true);
1563 * If te1 is not null then we had ts1 <= te1 above, and we just found
1564 * ts1 >= te2, hence te1 >= te2.
1566 PG_RETURN_BOOL(false);
1568 else if (TIMEADT_LT(ts1, ts2))
1570 /* This case is ts2 < te1 OR te2 < te1 */
1573 if (TIMEADT_LT(ts2, te1))
1574 PG_RETURN_BOOL(true);
1579 * If te2 is not null then we had ts2 <= te2 above, and we just found
1580 * ts2 >= te1, hence te2 >= te1.
1582 PG_RETURN_BOOL(false);
1587 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1588 * rather silly way of saying "true if both are nonnull, else null".
1590 if (te1IsNull || te2IsNull)
1592 PG_RETURN_BOOL(true);
1600 * Convert timestamp to time data type.
1603 timestamp_time(PG_FUNCTION_ARGS)
1605 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1611 if (TIMESTAMP_NOT_FINITE(timestamp))
1614 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1616 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1617 errmsg("timestamp out of range")));
1619 #ifdef HAVE_INT64_TIMESTAMP
1622 * Could also do this with time = (timestamp / USECS_PER_DAY *
1623 * USECS_PER_DAY) - timestamp;
1625 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1626 USECS_PER_SEC) + fsec;
1628 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1631 PG_RETURN_TIMEADT(result);
1634 /* timestamptz_time()
1635 * Convert timestamptz to time data type.
1638 timestamptz_time(PG_FUNCTION_ARGS)
1640 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1647 if (TIMESTAMP_NOT_FINITE(timestamp))
1650 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
1652 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1653 errmsg("timestamp out of range")));
1655 #ifdef HAVE_INT64_TIMESTAMP
1658 * Could also do this with time = (timestamp / USECS_PER_DAY *
1659 * USECS_PER_DAY) - timestamp;
1661 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1662 USECS_PER_SEC) + fsec;
1664 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1667 PG_RETURN_TIMEADT(result);
1670 /* datetime_timestamp()
1671 * Convert date and time to timestamp data type.
1674 datetime_timestamp(PG_FUNCTION_ARGS)
1676 DateADT date = PG_GETARG_DATEADT(0);
1677 TimeADT time = PG_GETARG_TIMEADT(1);
1680 result = date2timestamp(date);
1681 if (!TIMESTAMP_NOT_FINITE(result))
1684 PG_RETURN_TIMESTAMP(result);
1688 * Convert time to interval data type.
1691 time_interval(PG_FUNCTION_ARGS)
1693 TimeADT time = PG_GETARG_TIMEADT(0);
1696 result = (Interval *) palloc(sizeof(Interval));
1698 result->time = time;
1702 PG_RETURN_INTERVAL_P(result);
1706 * Convert interval to time data type.
1708 * This is defined as producing the fractional-day portion of the interval.
1709 * Therefore, we can just ignore the months field. It is not real clear
1710 * what to do with negative intervals, but we choose to subtract the floor,
1711 * so that, say, '-2 hours' becomes '22:00:00'.
1714 interval_time(PG_FUNCTION_ARGS)
1716 Interval *span = PG_GETARG_INTERVAL_P(0);
1719 #ifdef HAVE_INT64_TIMESTAMP
1722 result = span->time;
1723 if (result >= USECS_PER_DAY)
1725 days = result / USECS_PER_DAY;
1726 result -= days * USECS_PER_DAY;
1728 else if (result < 0)
1730 days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
1731 result += days * USECS_PER_DAY;
1734 result = span->time;
1735 if (result >= (double) SECS_PER_DAY || result < 0)
1736 result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
1739 PG_RETURN_TIMEADT(result);
1743 * Subtract two times to produce an interval.
1746 time_mi_time(PG_FUNCTION_ARGS)
1748 TimeADT time1 = PG_GETARG_TIMEADT(0);
1749 TimeADT time2 = PG_GETARG_TIMEADT(1);
1752 result = (Interval *) palloc(sizeof(Interval));
1756 result->time = time1 - time2;
1758 PG_RETURN_INTERVAL_P(result);
1761 /* time_pl_interval()
1762 * Add interval to time.
1765 time_pl_interval(PG_FUNCTION_ARGS)
1767 TimeADT time = PG_GETARG_TIMEADT(0);
1768 Interval *span = PG_GETARG_INTERVAL_P(1);
1771 #ifdef HAVE_INT64_TIMESTAMP
1772 result = time + span->time;
1773 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1774 if (result < INT64CONST(0))
1775 result += USECS_PER_DAY;
1779 result = time + span->time;
1780 TMODULO(result, time1, (double) SECS_PER_DAY);
1782 result += SECS_PER_DAY;
1785 PG_RETURN_TIMEADT(result);
1788 /* time_mi_interval()
1789 * Subtract interval from time.
1792 time_mi_interval(PG_FUNCTION_ARGS)
1794 TimeADT time = PG_GETARG_TIMEADT(0);
1795 Interval *span = PG_GETARG_INTERVAL_P(1);
1798 #ifdef HAVE_INT64_TIMESTAMP
1799 result = time - span->time;
1800 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1801 if (result < INT64CONST(0))
1802 result += USECS_PER_DAY;
1806 result = time - span->time;
1807 TMODULO(result, time1, (double) SECS_PER_DAY);
1809 result += SECS_PER_DAY;
1812 PG_RETURN_TIMEADT(result);
1817 * Extract specified field from time type.
1820 time_part(PG_FUNCTION_ARGS)
1822 text *units = PG_GETARG_TEXT_PP(0);
1823 TimeADT time = PG_GETARG_TIMEADT(1);
1829 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
1830 VARSIZE_ANY_EXHDR(units),
1833 type = DecodeUnits(0, lowunits, &val);
1834 if (type == UNKNOWN_FIELD)
1835 type = DecodeSpecial(0, lowunits, &val);
1843 time2tm(time, tm, &fsec);
1848 #ifdef HAVE_INT64_TIMESTAMP
1849 result = tm->tm_sec * 1000000.0 + fsec;
1851 result = (tm->tm_sec + fsec) * 1000000;
1856 #ifdef HAVE_INT64_TIMESTAMP
1857 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
1859 result = (tm->tm_sec + fsec) * 1000;
1864 #ifdef HAVE_INT64_TIMESTAMP
1865 result = tm->tm_sec + fsec / 1000000.0;
1867 result = tm->tm_sec + fsec;
1872 result = tm->tm_min;
1876 result = tm->tm_hour;
1888 case DTK_MILLENNIUM:
1892 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1893 errmsg("\"time\" units \"%s\" not recognized",
1898 else if (type == RESERV && val == DTK_EPOCH)
1900 #ifdef HAVE_INT64_TIMESTAMP
1901 result = time / 1000000.0;
1909 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1910 errmsg("\"time\" units \"%s\" not recognized",
1915 PG_RETURN_FLOAT8(result);
1919 /*****************************************************************************
1920 * Time With Time Zone ADT
1921 *****************************************************************************/
1924 * Convert a tm structure to a time data type.
1927 tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
1929 #ifdef HAVE_INT64_TIMESTAMP
1930 result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1931 USECS_PER_SEC) + fsec;
1933 result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1941 timetz_in(PG_FUNCTION_ARGS)
1943 char *str = PG_GETARG_CSTRING(0);
1946 Oid typelem = PG_GETARG_OID(1);
1948 int32 typmod = PG_GETARG_INT32(2);
1956 char workbuf[MAXDATELEN + 1];
1957 char *field[MAXDATEFIELDS];
1959 int ftype[MAXDATEFIELDS];
1961 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1962 field, ftype, MAXDATEFIELDS, &nf);
1964 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1966 DateTimeParseError(dterr, str, "time with time zone");
1968 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1969 tm2timetz(tm, fsec, tz, result);
1970 AdjustTimeForTypmod(&(result->time), typmod);
1972 PG_RETURN_TIMETZADT_P(result);
1976 timetz_out(PG_FUNCTION_ARGS)
1978 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1984 char buf[MAXDATELEN + 1];
1986 timetz2tm(time, tm, &fsec, &tz);
1987 EncodeTimeOnly(tm, fsec, true, tz, DateStyle, buf);
1989 result = pstrdup(buf);
1990 PG_RETURN_CSTRING(result);
1994 * timetz_recv - converts external binary format to timetz
1997 timetz_recv(PG_FUNCTION_ARGS)
1999 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
2002 Oid typelem = PG_GETARG_OID(1);
2004 int32 typmod = PG_GETARG_INT32(2);
2007 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2009 #ifdef HAVE_INT64_TIMESTAMP
2010 result->time = pq_getmsgint64(buf);
2012 if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
2014 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2015 errmsg("time out of range")));
2017 result->time = pq_getmsgfloat8(buf);
2019 if (result->time < 0 || result->time > (double) SECS_PER_DAY)
2021 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2022 errmsg("time out of range")));
2025 result->zone = pq_getmsgint(buf, sizeof(result->zone));
2027 /* Check for sane GMT displacement; see notes in datatype/timestamp.h */
2028 if (result->zone <= -TZDISP_LIMIT || result->zone >= TZDISP_LIMIT)
2030 (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
2031 errmsg("time zone displacement out of range")));
2033 AdjustTimeForTypmod(&(result->time), typmod);
2035 PG_RETURN_TIMETZADT_P(result);
2039 * timetz_send - converts timetz to binary format
2042 timetz_send(PG_FUNCTION_ARGS)
2044 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2047 pq_begintypsend(&buf);
2048 #ifdef HAVE_INT64_TIMESTAMP
2049 pq_sendint64(&buf, time->time);
2051 pq_sendfloat8(&buf, time->time);
2053 pq_sendint(&buf, time->zone, sizeof(time->zone));
2054 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
2058 timetztypmodin(PG_FUNCTION_ARGS)
2060 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
2062 PG_RETURN_INT32(anytime_typmodin(true, ta));
2066 timetztypmodout(PG_FUNCTION_ARGS)
2068 int32 typmod = PG_GETARG_INT32(0);
2070 PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
2075 * Convert TIME WITH TIME ZONE data type to POSIX time structure.
2078 timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
2080 TimeOffset trem = time->time;
2082 #ifdef HAVE_INT64_TIMESTAMP
2083 tm->tm_hour = trem / USECS_PER_HOUR;
2084 trem -= tm->tm_hour * USECS_PER_HOUR;
2085 tm->tm_min = trem / USECS_PER_MINUTE;
2086 trem -= tm->tm_min * USECS_PER_MINUTE;
2087 tm->tm_sec = trem / USECS_PER_SEC;
2088 *fsec = trem - tm->tm_sec * USECS_PER_SEC;
2091 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
2092 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
2093 TMODULO(trem, tm->tm_sec, 1.0);
2094 trem = TIMEROUND(trem);
2095 /* roundoff may need to propagate to higher-order fields */
2098 trem = ceil(time->time);
2111 * Adjust time type for specified scale factor.
2112 * Used by PostgreSQL type system to stuff columns.
2115 timetz_scale(PG_FUNCTION_ARGS)
2117 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2118 int32 typmod = PG_GETARG_INT32(1);
2121 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2123 result->time = time->time;
2124 result->zone = time->zone;
2126 AdjustTimeForTypmod(&(result->time), typmod);
2128 PG_RETURN_TIMETZADT_P(result);
2133 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
2138 /* Primary sort is by true (GMT-equivalent) time */
2139 #ifdef HAVE_INT64_TIMESTAMP
2140 t1 = time1->time + (time1->zone * USECS_PER_SEC);
2141 t2 = time2->time + (time2->zone * USECS_PER_SEC);
2143 t1 = time1->time + time1->zone;
2144 t2 = time2->time + time2->zone;
2153 * If same GMT time, sort by timezone; we only want to say that two
2154 * timetz's are equal if both the time and zone parts are equal.
2156 if (time1->zone > time2->zone)
2158 if (time1->zone < time2->zone)
2165 timetz_eq(PG_FUNCTION_ARGS)
2167 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2168 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2170 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
2174 timetz_ne(PG_FUNCTION_ARGS)
2176 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2177 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2179 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
2183 timetz_lt(PG_FUNCTION_ARGS)
2185 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2186 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2188 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
2192 timetz_le(PG_FUNCTION_ARGS)
2194 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2195 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2197 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
2201 timetz_gt(PG_FUNCTION_ARGS)
2203 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2204 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2206 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
2210 timetz_ge(PG_FUNCTION_ARGS)
2212 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2213 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2215 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
2219 timetz_cmp(PG_FUNCTION_ARGS)
2221 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2222 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2224 PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
2228 timetz_hash(PG_FUNCTION_ARGS)
2230 TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2234 * To avoid any problems with padding bytes in the struct, we figure the
2235 * field hashes separately and XOR them. This also provides a convenient
2236 * framework for dealing with the fact that the time field might be either
2239 #ifdef HAVE_INT64_TIMESTAMP
2240 thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
2241 Int64GetDatumFast(key->time)));
2243 thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8,
2244 Float8GetDatumFast(key->time)));
2246 thash ^= DatumGetUInt32(hash_uint32(key->zone));
2247 PG_RETURN_UINT32(thash);
2251 timetz_larger(PG_FUNCTION_ARGS)
2253 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2254 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2257 if (timetz_cmp_internal(time1, time2) > 0)
2261 PG_RETURN_TIMETZADT_P(result);
2265 timetz_smaller(PG_FUNCTION_ARGS)
2267 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2268 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2271 if (timetz_cmp_internal(time1, time2) < 0)
2275 PG_RETURN_TIMETZADT_P(result);
2278 /* timetz_pl_interval()
2279 * Add interval to timetz.
2282 timetz_pl_interval(PG_FUNCTION_ARGS)
2284 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2285 Interval *span = PG_GETARG_INTERVAL_P(1);
2288 #ifndef HAVE_INT64_TIMESTAMP
2292 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2294 #ifdef HAVE_INT64_TIMESTAMP
2295 result->time = time->time + span->time;
2296 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2297 if (result->time < INT64CONST(0))
2298 result->time += USECS_PER_DAY;
2300 result->time = time->time + span->time;
2301 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2302 if (result->time < 0)
2303 result->time += SECS_PER_DAY;
2306 result->zone = time->zone;
2308 PG_RETURN_TIMETZADT_P(result);
2311 /* timetz_mi_interval()
2312 * Subtract interval from timetz.
2315 timetz_mi_interval(PG_FUNCTION_ARGS)
2317 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2318 Interval *span = PG_GETARG_INTERVAL_P(1);
2321 #ifndef HAVE_INT64_TIMESTAMP
2325 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2327 #ifdef HAVE_INT64_TIMESTAMP
2328 result->time = time->time - span->time;
2329 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2330 if (result->time < INT64CONST(0))
2331 result->time += USECS_PER_DAY;
2333 result->time = time->time - span->time;
2334 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2335 if (result->time < 0)
2336 result->time += SECS_PER_DAY;
2339 result->zone = time->zone;
2341 PG_RETURN_TIMETZADT_P(result);
2344 /* overlaps_timetz() --- implements the SQL OVERLAPS operator.
2346 * Algorithm is per SQL spec. This is much harder than you'd think
2347 * because the spec requires us to deliver a non-null answer in some cases
2348 * where some of the inputs are null.
2351 overlaps_timetz(PG_FUNCTION_ARGS)
2354 * The arguments are TimeTzADT *, but we leave them as generic Datums for
2355 * convenience of notation --- and to avoid dereferencing nulls.
2357 Datum ts1 = PG_GETARG_DATUM(0);
2358 Datum te1 = PG_GETARG_DATUM(1);
2359 Datum ts2 = PG_GETARG_DATUM(2);
2360 Datum te2 = PG_GETARG_DATUM(3);
2361 bool ts1IsNull = PG_ARGISNULL(0);
2362 bool te1IsNull = PG_ARGISNULL(1);
2363 bool ts2IsNull = PG_ARGISNULL(2);
2364 bool te2IsNull = PG_ARGISNULL(3);
2366 #define TIMETZ_GT(t1,t2) \
2367 DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2368 #define TIMETZ_LT(t1,t2) \
2369 DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2372 * If both endpoints of interval 1 are null, the result is null (unknown).
2373 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2374 * take ts1 as the lesser endpoint.
2380 /* swap null for non-null */
2384 else if (!te1IsNull)
2386 if (TIMETZ_GT(ts1, te1))
2395 /* Likewise for interval 2. */
2400 /* swap null for non-null */
2404 else if (!te2IsNull)
2406 if (TIMETZ_GT(ts2, te2))
2416 * At this point neither ts1 nor ts2 is null, so we can consider three
2417 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2419 if (TIMETZ_GT(ts1, ts2))
2422 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2423 * in the presence of nulls it's not quite completely so.
2427 if (TIMETZ_LT(ts1, te2))
2428 PG_RETURN_BOOL(true);
2433 * If te1 is not null then we had ts1 <= te1 above, and we just found
2434 * ts1 >= te2, hence te1 >= te2.
2436 PG_RETURN_BOOL(false);
2438 else if (TIMETZ_LT(ts1, ts2))
2440 /* This case is ts2 < te1 OR te2 < te1 */
2443 if (TIMETZ_LT(ts2, te1))
2444 PG_RETURN_BOOL(true);
2449 * If te2 is not null then we had ts2 <= te2 above, and we just found
2450 * ts2 >= te1, hence te2 >= te1.
2452 PG_RETURN_BOOL(false);
2457 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2458 * rather silly way of saying "true if both are nonnull, else null".
2460 if (te1IsNull || te2IsNull)
2462 PG_RETURN_BOOL(true);
2471 timetz_time(PG_FUNCTION_ARGS)
2473 TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
2476 /* swallow the time zone and just return the time */
2477 result = timetz->time;
2479 PG_RETURN_TIMEADT(result);
2484 time_timetz(PG_FUNCTION_ARGS)
2486 TimeADT time = PG_GETARG_TIMEADT(0);
2493 GetCurrentDateTime(tm);
2494 time2tm(time, tm, &fsec);
2495 tz = DetermineTimeZoneOffset(tm, session_timezone);
2497 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2499 result->time = time;
2502 PG_RETURN_TIMETZADT_P(result);
2506 /* timestamptz_timetz()
2507 * Convert timestamp to timetz data type.
2510 timestamptz_timetz(PG_FUNCTION_ARGS)
2512 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2519 if (TIMESTAMP_NOT_FINITE(timestamp))
2522 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
2524 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2525 errmsg("timestamp out of range")));
2527 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2529 tm2timetz(tm, fsec, tz, result);
2531 PG_RETURN_TIMETZADT_P(result);
2535 /* datetimetz_timestamptz()
2536 * Convert date and timetz to timestamp with time zone data type.
2537 * Timestamp is stored in GMT, so add the time zone
2538 * stored with the timetz to the result.
2539 * - thomas 2000-03-10
2542 datetimetz_timestamptz(PG_FUNCTION_ARGS)
2544 DateADT date = PG_GETARG_DATEADT(0);
2545 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2548 if (DATE_IS_NOBEGIN(date))
2549 TIMESTAMP_NOBEGIN(result);
2550 else if (DATE_IS_NOEND(date))
2551 TIMESTAMP_NOEND(result);
2554 #ifdef HAVE_INT64_TIMESTAMP
2555 result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2557 result = date * (double) SECS_PER_DAY + time->time + time->zone;
2561 PG_RETURN_TIMESTAMP(result);
2566 * Extract specified field from time type.
2569 timetz_part(PG_FUNCTION_ARGS)
2571 text *units = PG_GETARG_TEXT_PP(0);
2572 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2578 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
2579 VARSIZE_ANY_EXHDR(units),
2582 type = DecodeUnits(0, lowunits, &val);
2583 if (type == UNKNOWN_FIELD)
2584 type = DecodeSpecial(0, lowunits, &val);
2594 timetz2tm(time, tm, &fsec, &tz);
2604 result /= SECS_PER_MINUTE;
2605 FMODULO(result, dummy, (double) SECS_PER_MINUTE);
2610 FMODULO(dummy, result, (double) SECS_PER_HOUR);
2614 #ifdef HAVE_INT64_TIMESTAMP
2615 result = tm->tm_sec * 1000000.0 + fsec;
2617 result = (tm->tm_sec + fsec) * 1000000;
2622 #ifdef HAVE_INT64_TIMESTAMP
2623 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
2625 result = (tm->tm_sec + fsec) * 1000;
2630 #ifdef HAVE_INT64_TIMESTAMP
2631 result = tm->tm_sec + fsec / 1000000.0;
2633 result = tm->tm_sec + fsec;
2638 result = tm->tm_min;
2642 result = tm->tm_hour;
2651 case DTK_MILLENNIUM:
2654 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2655 errmsg("\"time with time zone\" units \"%s\" not recognized",
2660 else if (type == RESERV && val == DTK_EPOCH)
2662 #ifdef HAVE_INT64_TIMESTAMP
2663 result = time->time / 1000000.0 + time->zone;
2665 result = time->time + time->zone;
2671 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2672 errmsg("\"time with time zone\" units \"%s\" not recognized",
2677 PG_RETURN_FLOAT8(result);
2681 * Encode time with time zone type with specified time zone.
2682 * Applies DST rules as of the current date.
2685 timetz_zone(PG_FUNCTION_ARGS)
2687 text *zone = PG_GETARG_TEXT_PP(0);
2688 TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
2691 char tzname[TZ_STRLEN_MAX + 1];
2698 * Look up the requested timezone. First we look in the timezone
2699 * abbreviation table (to handle cases like "EST"), and if that fails, we
2700 * look in the timezone database (to handle cases like
2701 * "America/New_York"). (This matches the order in which timestamp input
2702 * checks the cases; it's important because the timezone database unwisely
2703 * uses a few zone names that are identical to offset abbreviations.)
2705 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
2707 /* DecodeTimezoneAbbrev requires lowercase input */
2708 lowzone = downcase_truncate_identifier(tzname,
2712 type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
2714 if (type == TZ || type == DTZ)
2716 /* fixed-offset abbreviation */
2719 else if (type == DYNTZ)
2721 /* dynamic-offset abbreviation, resolve using current time */
2722 pg_time_t now = (pg_time_t) time(NULL);
2725 tm = pg_localtime(&now, tzp);
2726 tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
2730 /* try it as a full zone name */
2731 tzp = pg_tzset(tzname);
2734 /* Get the offset-from-GMT that is valid today for the zone */
2735 pg_time_t now = (pg_time_t) time(NULL);
2738 tm = pg_localtime(&now, tzp);
2739 tz = -tm->tm_gmtoff;
2744 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2745 errmsg("time zone \"%s\" not recognized", tzname)));
2746 tz = 0; /* keep compiler quiet */
2750 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2752 #ifdef HAVE_INT64_TIMESTAMP
2753 result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
2754 while (result->time < INT64CONST(0))
2755 result->time += USECS_PER_DAY;
2756 while (result->time >= USECS_PER_DAY)
2757 result->time -= USECS_PER_DAY;
2759 result->time = t->time + (t->zone - tz);
2760 while (result->time < 0)
2761 result->time += SECS_PER_DAY;
2762 while (result->time >= SECS_PER_DAY)
2763 result->time -= SECS_PER_DAY;
2768 PG_RETURN_TIMETZADT_P(result);
2772 * Encode time with time zone type with specified time interval as time zone.
2775 timetz_izone(PG_FUNCTION_ARGS)
2777 Interval *zone = PG_GETARG_INTERVAL_P(0);
2778 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2782 if (zone->month != 0 || zone->day != 0)
2784 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2785 errmsg("interval time zone \"%s\" must not include months or days",
2786 DatumGetCString(DirectFunctionCall1(interval_out,
2787 PointerGetDatum(zone))))));
2789 #ifdef HAVE_INT64_TIMESTAMP
2790 tz = -(zone->time / USECS_PER_SEC);
2795 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2797 #ifdef HAVE_INT64_TIMESTAMP
2798 result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
2799 while (result->time < INT64CONST(0))
2800 result->time += USECS_PER_DAY;
2801 while (result->time >= USECS_PER_DAY)
2802 result->time -= USECS_PER_DAY;
2804 result->time = time->time + (time->zone - tz);
2805 while (result->time < 0)
2806 result->time += SECS_PER_DAY;
2807 while (result->time >= SECS_PER_DAY)
2808 result->time -= SECS_PER_DAY;
2813 PG_RETURN_TIMETZADT_P(result);