1 /*-------------------------------------------------------------------------
4 * implements DATE and TIME data types specified in SQL-92 standard
6 * Portions Copyright (c) 1996-2010, 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/nabstime.h"
33 * gcc's -ffast-math switch breaks routines that expect exact results from
34 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
37 #error -ffast-math is known to break this code
41 static void EncodeSpecialDate(DateADT dt, char *str);
42 static int time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
43 static int timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
44 static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
45 static int tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
46 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
49 /* common code for timetypmodin and timetztypmodin */
51 anytime_typmodin(bool istz, ArrayType *ta)
57 tl = ArrayGetIntegerTypmods(ta, &n);
60 * we're not too tense about good error message here because grammar
61 * shouldn't allow wrong number of modifiers for TIME
65 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
66 errmsg("invalid type modifier")));
70 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
71 errmsg("TIME(%d)%s precision must not be negative",
72 *tl, (istz ? " WITH TIME ZONE" : ""))));
73 if (*tl > MAX_TIME_PRECISION)
76 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
77 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
78 *tl, (istz ? " WITH TIME ZONE" : ""),
79 MAX_TIME_PRECISION)));
80 typmod = MAX_TIME_PRECISION;
88 /* common code for timetypmodout and timetztypmodout */
90 anytime_typmodout(bool istz, int32 typmod)
92 char *res = (char *) palloc(64);
93 const char *tz = istz ? " with time zone" : " without time zone";
96 snprintf(res, 64, "(%d)%s", (int) typmod, tz);
98 snprintf(res, 64, "%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 * Convert reserved date values to string.
240 EncodeSpecialDate(DateADT dt, char *str)
242 if (DATE_IS_NOBEGIN(dt))
244 else if (DATE_IS_NOEND(dt))
246 else /* shouldn't happen */
247 elog(ERROR, "invalid argument for EncodeSpecialDate");
252 * Comparison functions for dates
256 date_eq(PG_FUNCTION_ARGS)
258 DateADT dateVal1 = PG_GETARG_DATEADT(0);
259 DateADT dateVal2 = PG_GETARG_DATEADT(1);
261 PG_RETURN_BOOL(dateVal1 == dateVal2);
265 date_ne(PG_FUNCTION_ARGS)
267 DateADT dateVal1 = PG_GETARG_DATEADT(0);
268 DateADT dateVal2 = PG_GETARG_DATEADT(1);
270 PG_RETURN_BOOL(dateVal1 != dateVal2);
274 date_lt(PG_FUNCTION_ARGS)
276 DateADT dateVal1 = PG_GETARG_DATEADT(0);
277 DateADT dateVal2 = PG_GETARG_DATEADT(1);
279 PG_RETURN_BOOL(dateVal1 < dateVal2);
283 date_le(PG_FUNCTION_ARGS)
285 DateADT dateVal1 = PG_GETARG_DATEADT(0);
286 DateADT dateVal2 = PG_GETARG_DATEADT(1);
288 PG_RETURN_BOOL(dateVal1 <= dateVal2);
292 date_gt(PG_FUNCTION_ARGS)
294 DateADT dateVal1 = PG_GETARG_DATEADT(0);
295 DateADT dateVal2 = PG_GETARG_DATEADT(1);
297 PG_RETURN_BOOL(dateVal1 > dateVal2);
301 date_ge(PG_FUNCTION_ARGS)
303 DateADT dateVal1 = PG_GETARG_DATEADT(0);
304 DateADT dateVal2 = PG_GETARG_DATEADT(1);
306 PG_RETURN_BOOL(dateVal1 >= dateVal2);
310 date_cmp(PG_FUNCTION_ARGS)
312 DateADT dateVal1 = PG_GETARG_DATEADT(0);
313 DateADT dateVal2 = PG_GETARG_DATEADT(1);
315 if (dateVal1 < dateVal2)
317 else if (dateVal1 > dateVal2)
323 date_finite(PG_FUNCTION_ARGS)
325 DateADT date = PG_GETARG_DATEADT(0);
327 PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
331 date_larger(PG_FUNCTION_ARGS)
333 DateADT dateVal1 = PG_GETARG_DATEADT(0);
334 DateADT dateVal2 = PG_GETARG_DATEADT(1);
336 PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
340 date_smaller(PG_FUNCTION_ARGS)
342 DateADT dateVal1 = PG_GETARG_DATEADT(0);
343 DateADT dateVal2 = PG_GETARG_DATEADT(1);
345 PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
348 /* Compute difference between two dates in days.
351 date_mi(PG_FUNCTION_ARGS)
353 DateADT dateVal1 = PG_GETARG_DATEADT(0);
354 DateADT dateVal2 = PG_GETARG_DATEADT(1);
356 if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
358 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
359 errmsg("cannot subtract infinite dates")));
361 PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
364 /* Add a number of days to a date, giving a new date.
365 * Must handle both positive and negative numbers of days.
368 date_pli(PG_FUNCTION_ARGS)
370 DateADT dateVal = PG_GETARG_DATEADT(0);
371 int32 days = PG_GETARG_INT32(1);
373 if (DATE_NOT_FINITE(dateVal))
374 days = 0; /* can't change infinity */
376 PG_RETURN_DATEADT(dateVal + days);
379 /* Subtract a number of days from a date, giving a new date.
382 date_mii(PG_FUNCTION_ARGS)
384 DateADT dateVal = PG_GETARG_DATEADT(0);
385 int32 days = PG_GETARG_INT32(1);
387 if (DATE_NOT_FINITE(dateVal))
388 days = 0; /* can't change infinity */
390 PG_RETURN_DATEADT(dateVal - days);
394 * Internal routines for promoting date to timestamp and timestamp with
399 date2timestamp(DateADT dateVal)
403 if (DATE_IS_NOBEGIN(dateVal))
404 TIMESTAMP_NOBEGIN(result);
405 else if (DATE_IS_NOEND(dateVal))
406 TIMESTAMP_NOEND(result);
409 #ifdef HAVE_INT64_TIMESTAMP
410 /* date is days since 2000, timestamp is microseconds since same... */
411 result = dateVal * USECS_PER_DAY;
412 /* Date's range is wider than timestamp's, so check for overflow */
413 if (result / USECS_PER_DAY != dateVal)
415 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
416 errmsg("date out of range for timestamp")));
418 /* date is days since 2000, timestamp is seconds since same... */
419 result = dateVal * (double) SECS_PER_DAY;
427 date2timestamptz(DateADT dateVal)
434 if (DATE_IS_NOBEGIN(dateVal))
435 TIMESTAMP_NOBEGIN(result);
436 else if (DATE_IS_NOEND(dateVal))
437 TIMESTAMP_NOEND(result);
440 j2date(dateVal + POSTGRES_EPOCH_JDATE,
441 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
445 tz = DetermineTimeZoneOffset(tm, session_timezone);
447 #ifdef HAVE_INT64_TIMESTAMP
448 result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
449 /* Date's range is wider than timestamp's, so check for overflow */
450 if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
452 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
453 errmsg("date out of range for timestamp")));
455 result = dateVal * (double) SECS_PER_DAY + tz;
464 * Crosstype comparison functions for dates
468 date_eq_timestamp(PG_FUNCTION_ARGS)
470 DateADT dateVal = PG_GETARG_DATEADT(0);
471 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
474 dt1 = date2timestamp(dateVal);
476 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
480 date_ne_timestamp(PG_FUNCTION_ARGS)
482 DateADT dateVal = PG_GETARG_DATEADT(0);
483 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
486 dt1 = date2timestamp(dateVal);
488 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
492 date_lt_timestamp(PG_FUNCTION_ARGS)
494 DateADT dateVal = PG_GETARG_DATEADT(0);
495 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
498 dt1 = date2timestamp(dateVal);
500 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
504 date_gt_timestamp(PG_FUNCTION_ARGS)
506 DateADT dateVal = PG_GETARG_DATEADT(0);
507 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
510 dt1 = date2timestamp(dateVal);
512 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
516 date_le_timestamp(PG_FUNCTION_ARGS)
518 DateADT dateVal = PG_GETARG_DATEADT(0);
519 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
522 dt1 = date2timestamp(dateVal);
524 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
528 date_ge_timestamp(PG_FUNCTION_ARGS)
530 DateADT dateVal = PG_GETARG_DATEADT(0);
531 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
534 dt1 = date2timestamp(dateVal);
536 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
540 date_cmp_timestamp(PG_FUNCTION_ARGS)
542 DateADT dateVal = PG_GETARG_DATEADT(0);
543 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
546 dt1 = date2timestamp(dateVal);
548 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
552 date_eq_timestamptz(PG_FUNCTION_ARGS)
554 DateADT dateVal = PG_GETARG_DATEADT(0);
555 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
558 dt1 = date2timestamptz(dateVal);
560 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
564 date_ne_timestamptz(PG_FUNCTION_ARGS)
566 DateADT dateVal = PG_GETARG_DATEADT(0);
567 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
570 dt1 = date2timestamptz(dateVal);
572 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
576 date_lt_timestamptz(PG_FUNCTION_ARGS)
578 DateADT dateVal = PG_GETARG_DATEADT(0);
579 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
582 dt1 = date2timestamptz(dateVal);
584 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
588 date_gt_timestamptz(PG_FUNCTION_ARGS)
590 DateADT dateVal = PG_GETARG_DATEADT(0);
591 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
594 dt1 = date2timestamptz(dateVal);
596 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
600 date_le_timestamptz(PG_FUNCTION_ARGS)
602 DateADT dateVal = PG_GETARG_DATEADT(0);
603 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
606 dt1 = date2timestamptz(dateVal);
608 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
612 date_ge_timestamptz(PG_FUNCTION_ARGS)
614 DateADT dateVal = PG_GETARG_DATEADT(0);
615 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
618 dt1 = date2timestamptz(dateVal);
620 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
624 date_cmp_timestamptz(PG_FUNCTION_ARGS)
626 DateADT dateVal = PG_GETARG_DATEADT(0);
627 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
630 dt1 = date2timestamptz(dateVal);
632 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
636 timestamp_eq_date(PG_FUNCTION_ARGS)
638 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
639 DateADT dateVal = PG_GETARG_DATEADT(1);
642 dt2 = date2timestamp(dateVal);
644 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
648 timestamp_ne_date(PG_FUNCTION_ARGS)
650 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
651 DateADT dateVal = PG_GETARG_DATEADT(1);
654 dt2 = date2timestamp(dateVal);
656 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
660 timestamp_lt_date(PG_FUNCTION_ARGS)
662 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
663 DateADT dateVal = PG_GETARG_DATEADT(1);
666 dt2 = date2timestamp(dateVal);
668 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
672 timestamp_gt_date(PG_FUNCTION_ARGS)
674 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
675 DateADT dateVal = PG_GETARG_DATEADT(1);
678 dt2 = date2timestamp(dateVal);
680 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
684 timestamp_le_date(PG_FUNCTION_ARGS)
686 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
687 DateADT dateVal = PG_GETARG_DATEADT(1);
690 dt2 = date2timestamp(dateVal);
692 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
696 timestamp_ge_date(PG_FUNCTION_ARGS)
698 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
699 DateADT dateVal = PG_GETARG_DATEADT(1);
702 dt2 = date2timestamp(dateVal);
704 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
708 timestamp_cmp_date(PG_FUNCTION_ARGS)
710 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
711 DateADT dateVal = PG_GETARG_DATEADT(1);
714 dt2 = date2timestamp(dateVal);
716 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
720 timestamptz_eq_date(PG_FUNCTION_ARGS)
722 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
723 DateADT dateVal = PG_GETARG_DATEADT(1);
726 dt2 = date2timestamptz(dateVal);
728 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
732 timestamptz_ne_date(PG_FUNCTION_ARGS)
734 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
735 DateADT dateVal = PG_GETARG_DATEADT(1);
738 dt2 = date2timestamptz(dateVal);
740 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
744 timestamptz_lt_date(PG_FUNCTION_ARGS)
746 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
747 DateADT dateVal = PG_GETARG_DATEADT(1);
750 dt2 = date2timestamptz(dateVal);
752 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
756 timestamptz_gt_date(PG_FUNCTION_ARGS)
758 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
759 DateADT dateVal = PG_GETARG_DATEADT(1);
762 dt2 = date2timestamptz(dateVal);
764 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
768 timestamptz_le_date(PG_FUNCTION_ARGS)
770 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
771 DateADT dateVal = PG_GETARG_DATEADT(1);
774 dt2 = date2timestamptz(dateVal);
776 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
780 timestamptz_ge_date(PG_FUNCTION_ARGS)
782 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
783 DateADT dateVal = PG_GETARG_DATEADT(1);
786 dt2 = date2timestamptz(dateVal);
788 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
792 timestamptz_cmp_date(PG_FUNCTION_ARGS)
794 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
795 DateADT dateVal = PG_GETARG_DATEADT(1);
798 dt2 = date2timestamptz(dateVal);
800 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
804 /* Add an interval to a date, giving a new date.
805 * Must handle both positive and negative intervals.
807 * We implement this by promoting the date to timestamp (without time zone)
808 * and then using the timestamp plus interval function.
811 date_pl_interval(PG_FUNCTION_ARGS)
813 DateADT dateVal = PG_GETARG_DATEADT(0);
814 Interval *span = PG_GETARG_INTERVAL_P(1);
817 dateStamp = date2timestamp(dateVal);
819 return DirectFunctionCall2(timestamp_pl_interval,
820 TimestampGetDatum(dateStamp),
821 PointerGetDatum(span));
824 /* Subtract an interval from a date, giving a new date.
825 * Must handle both positive and negative intervals.
827 * We implement this by promoting the date to timestamp (without time zone)
828 * and then using the timestamp minus interval function.
831 date_mi_interval(PG_FUNCTION_ARGS)
833 DateADT dateVal = PG_GETARG_DATEADT(0);
834 Interval *span = PG_GETARG_INTERVAL_P(1);
837 dateStamp = date2timestamp(dateVal);
839 return DirectFunctionCall2(timestamp_mi_interval,
840 TimestampGetDatum(dateStamp),
841 PointerGetDatum(span));
845 * Convert date to timestamp data type.
848 date_timestamp(PG_FUNCTION_ARGS)
850 DateADT dateVal = PG_GETARG_DATEADT(0);
853 result = date2timestamp(dateVal);
855 PG_RETURN_TIMESTAMP(result);
860 * Convert timestamp to date data type.
863 timestamp_date(PG_FUNCTION_ARGS)
865 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
871 if (TIMESTAMP_IS_NOBEGIN(timestamp))
872 DATE_NOBEGIN(result);
873 else if (TIMESTAMP_IS_NOEND(timestamp))
877 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
879 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
880 errmsg("timestamp out of range")));
882 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
885 PG_RETURN_DATEADT(result);
889 /* date_timestamptz()
890 * Convert date to timestamp with time zone data type.
893 date_timestamptz(PG_FUNCTION_ARGS)
895 DateADT dateVal = PG_GETARG_DATEADT(0);
898 result = date2timestamptz(dateVal);
900 PG_RETURN_TIMESTAMP(result);
904 /* timestamptz_date()
905 * Convert timestamp with time zone to date data type.
908 timestamptz_date(PG_FUNCTION_ARGS)
910 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
918 if (TIMESTAMP_IS_NOBEGIN(timestamp))
919 DATE_NOBEGIN(result);
920 else if (TIMESTAMP_IS_NOEND(timestamp))
924 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
926 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
927 errmsg("timestamp out of range")));
929 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
932 PG_RETURN_DATEADT(result);
937 * Convert abstime to date data type.
940 abstime_date(PG_FUNCTION_ARGS)
942 AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
950 case INVALID_ABSTIME:
952 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
953 errmsg("cannot convert reserved abstime value to date")));
954 result = 0; /* keep compiler quiet */
957 case NOSTART_ABSTIME:
958 DATE_NOBEGIN(result);
966 abstime2tm(abstime, &tz, tm, NULL);
967 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
971 PG_RETURN_DATEADT(result);
975 /*****************************************************************************
977 *****************************************************************************/
980 time_in(PG_FUNCTION_ARGS)
982 char *str = PG_GETARG_CSTRING(0);
985 Oid typelem = PG_GETARG_OID(1);
987 int32 typmod = PG_GETARG_INT32(2);
995 char workbuf[MAXDATELEN + 1];
996 char *field[MAXDATEFIELDS];
998 int ftype[MAXDATEFIELDS];
1000 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1001 field, ftype, MAXDATEFIELDS, &nf);
1003 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1005 DateTimeParseError(dterr, str, "time");
1007 tm2time(tm, fsec, &result);
1008 AdjustTimeForTypmod(&result, typmod);
1010 PG_RETURN_TIMEADT(result);
1014 * Convert a tm structure to a time data type.
1017 tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
1019 #ifdef HAVE_INT64_TIMESTAMP
1020 *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
1021 * USECS_PER_SEC) + fsec;
1023 *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1029 * Convert time data type to POSIX time structure.
1031 * For dates within the range of pg_time_t, convert to the local time zone.
1032 * If out of this range, leave as UTC (in practice that could only happen
1033 * if pg_time_t is just 32 bits) - thomas 97/05/27
1036 time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
1038 #ifdef HAVE_INT64_TIMESTAMP
1039 tm->tm_hour = time / USECS_PER_HOUR;
1040 time -= tm->tm_hour * USECS_PER_HOUR;
1041 tm->tm_min = time / USECS_PER_MINUTE;
1042 time -= tm->tm_min * USECS_PER_MINUTE;
1043 tm->tm_sec = time / USECS_PER_SEC;
1044 time -= tm->tm_sec * USECS_PER_SEC;
1051 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
1052 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1053 TMODULO(trem, tm->tm_sec, 1.0);
1054 trem = TIMEROUND(trem);
1055 /* roundoff may need to propagate to higher-order fields */
1068 time_out(PG_FUNCTION_ARGS)
1070 TimeADT time = PG_GETARG_TIMEADT(0);
1075 char buf[MAXDATELEN + 1];
1077 time2tm(time, tm, &fsec);
1078 EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
1080 result = pstrdup(buf);
1081 PG_RETURN_CSTRING(result);
1085 * time_recv - converts external binary format to time
1087 * We make no attempt to provide compatibility between int and float
1088 * time representations ...
1091 time_recv(PG_FUNCTION_ARGS)
1093 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1096 Oid typelem = PG_GETARG_OID(1);
1098 int32 typmod = PG_GETARG_INT32(2);
1101 #ifdef HAVE_INT64_TIMESTAMP
1102 result = pq_getmsgint64(buf);
1104 if (result < INT64CONST(0) || result > USECS_PER_DAY)
1106 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1107 errmsg("time out of range")));
1109 result = pq_getmsgfloat8(buf);
1111 if (result < 0 || result > (double) SECS_PER_DAY)
1113 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1114 errmsg("time out of range")));
1117 AdjustTimeForTypmod(&result, typmod);
1119 PG_RETURN_TIMEADT(result);
1123 * time_send - converts time to binary format
1126 time_send(PG_FUNCTION_ARGS)
1128 TimeADT time = PG_GETARG_TIMEADT(0);
1131 pq_begintypsend(&buf);
1132 #ifdef HAVE_INT64_TIMESTAMP
1133 pq_sendint64(&buf, time);
1135 pq_sendfloat8(&buf, time);
1137 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1141 timetypmodin(PG_FUNCTION_ARGS)
1143 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1145 PG_RETURN_INT32(anytime_typmodin(false, ta));
1149 timetypmodout(PG_FUNCTION_ARGS)
1151 int32 typmod = PG_GETARG_INT32(0);
1153 PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
1158 * Adjust time type for specified scale factor.
1159 * Used by PostgreSQL type system to stuff columns.
1162 time_scale(PG_FUNCTION_ARGS)
1164 TimeADT time = PG_GETARG_TIMEADT(0);
1165 int32 typmod = PG_GETARG_INT32(1);
1169 AdjustTimeForTypmod(&result, typmod);
1171 PG_RETURN_TIMEADT(result);
1174 /* AdjustTimeForTypmod()
1175 * Force the precision of the time value to a specified value.
1176 * Uses *exactly* the same code as in AdjustTimestampForTypemod()
1177 * but we make a separate copy because those types do not
1178 * have a fundamental tie together but rather a coincidence of
1179 * implementation. - thomas
1182 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1184 #ifdef HAVE_INT64_TIMESTAMP
1185 static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1186 INT64CONST(1000000),
1195 static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1205 /* note MAX_TIME_PRECISION differs in this case */
1206 static const double TimeScales[MAX_TIME_PRECISION + 1] = {
1221 if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1224 * Note: this round-to-nearest code is not completely consistent about
1225 * rounding values that are exactly halfway between integral values.
1226 * On most platforms, rint() will implement round-to-nearest-even, but
1227 * the integer code always rounds up (away from zero). Is it worth
1228 * trying to be consistent?
1230 #ifdef HAVE_INT64_TIMESTAMP
1231 if (*time >= INT64CONST(0))
1232 *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
1235 *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
1236 TimeScales[typmod]);
1238 *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
1245 time_eq(PG_FUNCTION_ARGS)
1247 TimeADT time1 = PG_GETARG_TIMEADT(0);
1248 TimeADT time2 = PG_GETARG_TIMEADT(1);
1250 PG_RETURN_BOOL(time1 == time2);
1254 time_ne(PG_FUNCTION_ARGS)
1256 TimeADT time1 = PG_GETARG_TIMEADT(0);
1257 TimeADT time2 = PG_GETARG_TIMEADT(1);
1259 PG_RETURN_BOOL(time1 != time2);
1263 time_lt(PG_FUNCTION_ARGS)
1265 TimeADT time1 = PG_GETARG_TIMEADT(0);
1266 TimeADT time2 = PG_GETARG_TIMEADT(1);
1268 PG_RETURN_BOOL(time1 < time2);
1272 time_le(PG_FUNCTION_ARGS)
1274 TimeADT time1 = PG_GETARG_TIMEADT(0);
1275 TimeADT time2 = PG_GETARG_TIMEADT(1);
1277 PG_RETURN_BOOL(time1 <= time2);
1281 time_gt(PG_FUNCTION_ARGS)
1283 TimeADT time1 = PG_GETARG_TIMEADT(0);
1284 TimeADT time2 = PG_GETARG_TIMEADT(1);
1286 PG_RETURN_BOOL(time1 > time2);
1290 time_ge(PG_FUNCTION_ARGS)
1292 TimeADT time1 = PG_GETARG_TIMEADT(0);
1293 TimeADT time2 = PG_GETARG_TIMEADT(1);
1295 PG_RETURN_BOOL(time1 >= time2);
1299 time_cmp(PG_FUNCTION_ARGS)
1301 TimeADT time1 = PG_GETARG_TIMEADT(0);
1302 TimeADT time2 = PG_GETARG_TIMEADT(1);
1305 PG_RETURN_INT32(-1);
1312 time_hash(PG_FUNCTION_ARGS)
1314 /* We can use either hashint8 or hashfloat8 directly */
1315 #ifdef HAVE_INT64_TIMESTAMP
1316 return hashint8(fcinfo);
1318 return hashfloat8(fcinfo);
1323 time_larger(PG_FUNCTION_ARGS)
1325 TimeADT time1 = PG_GETARG_TIMEADT(0);
1326 TimeADT time2 = PG_GETARG_TIMEADT(1);
1328 PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1332 time_smaller(PG_FUNCTION_ARGS)
1334 TimeADT time1 = PG_GETARG_TIMEADT(0);
1335 TimeADT time2 = PG_GETARG_TIMEADT(1);
1337 PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1340 /* overlaps_time() --- implements the SQL92 OVERLAPS operator.
1342 * Algorithm is per SQL92 spec. This is much harder than you'd think
1343 * because the spec requires us to deliver a non-null answer in some cases
1344 * where some of the inputs are null.
1347 overlaps_time(PG_FUNCTION_ARGS)
1350 * The arguments are TimeADT, but we leave them as generic Datums to avoid
1351 * dereferencing nulls (TimeADT is pass-by-reference!)
1353 Datum ts1 = PG_GETARG_DATUM(0);
1354 Datum te1 = PG_GETARG_DATUM(1);
1355 Datum ts2 = PG_GETARG_DATUM(2);
1356 Datum te2 = PG_GETARG_DATUM(3);
1357 bool ts1IsNull = PG_ARGISNULL(0);
1358 bool te1IsNull = PG_ARGISNULL(1);
1359 bool ts2IsNull = PG_ARGISNULL(2);
1360 bool te2IsNull = PG_ARGISNULL(3);
1362 #define TIMEADT_GT(t1,t2) \
1363 (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1364 #define TIMEADT_LT(t1,t2) \
1365 (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1368 * If both endpoints of interval 1 are null, the result is null (unknown).
1369 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1370 * take ts1 as the lesser endpoint.
1376 /* swap null for non-null */
1380 else if (!te1IsNull)
1382 if (TIMEADT_GT(ts1, te1))
1391 /* Likewise for interval 2. */
1396 /* swap null for non-null */
1400 else if (!te2IsNull)
1402 if (TIMEADT_GT(ts2, te2))
1412 * At this point neither ts1 nor ts2 is null, so we can consider three
1413 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1415 if (TIMEADT_GT(ts1, ts2))
1418 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
1419 * in the presence of nulls it's not quite completely so.
1423 if (TIMEADT_LT(ts1, te2))
1424 PG_RETURN_BOOL(true);
1429 * If te1 is not null then we had ts1 <= te1 above, and we just found
1430 * ts1 >= te2, hence te1 >= te2.
1432 PG_RETURN_BOOL(false);
1434 else if (TIMEADT_LT(ts1, ts2))
1436 /* This case is ts2 < te1 OR te2 < te1 */
1439 if (TIMEADT_LT(ts2, te1))
1440 PG_RETURN_BOOL(true);
1445 * If te2 is not null then we had ts2 <= te2 above, and we just found
1446 * ts2 >= te1, hence te2 >= te1.
1448 PG_RETURN_BOOL(false);
1453 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1454 * rather silly way of saying "true if both are nonnull, else null".
1456 if (te1IsNull || te2IsNull)
1458 PG_RETURN_BOOL(true);
1466 * Convert timestamp to time data type.
1469 timestamp_time(PG_FUNCTION_ARGS)
1471 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1477 if (TIMESTAMP_NOT_FINITE(timestamp))
1480 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1482 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1483 errmsg("timestamp out of range")));
1485 #ifdef HAVE_INT64_TIMESTAMP
1488 * Could also do this with time = (timestamp / USECS_PER_DAY *
1489 * USECS_PER_DAY) - timestamp;
1491 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1492 USECS_PER_SEC) + fsec;
1494 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1497 PG_RETURN_TIMEADT(result);
1500 /* timestamptz_time()
1501 * Convert timestamptz to time data type.
1504 timestamptz_time(PG_FUNCTION_ARGS)
1506 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1514 if (TIMESTAMP_NOT_FINITE(timestamp))
1517 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
1519 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1520 errmsg("timestamp out of range")));
1522 #ifdef HAVE_INT64_TIMESTAMP
1525 * Could also do this with time = (timestamp / USECS_PER_DAY *
1526 * USECS_PER_DAY) - timestamp;
1528 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1529 USECS_PER_SEC) + fsec;
1531 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1534 PG_RETURN_TIMEADT(result);
1537 /* datetime_timestamp()
1538 * Convert date and time to timestamp data type.
1541 datetime_timestamp(PG_FUNCTION_ARGS)
1543 DateADT date = PG_GETARG_DATEADT(0);
1544 TimeADT time = PG_GETARG_TIMEADT(1);
1547 result = date2timestamp(date);
1548 if (!TIMESTAMP_NOT_FINITE(result))
1551 PG_RETURN_TIMESTAMP(result);
1555 * Convert time to interval data type.
1558 time_interval(PG_FUNCTION_ARGS)
1560 TimeADT time = PG_GETARG_TIMEADT(0);
1563 result = (Interval *) palloc(sizeof(Interval));
1565 result->time = time;
1569 PG_RETURN_INTERVAL_P(result);
1573 * Convert interval to time data type.
1575 * This is defined as producing the fractional-day portion of the interval.
1576 * Therefore, we can just ignore the months field. It is not real clear
1577 * what to do with negative intervals, but we choose to subtract the floor,
1578 * so that, say, '-2 hours' becomes '22:00:00'.
1581 interval_time(PG_FUNCTION_ARGS)
1583 Interval *span = PG_GETARG_INTERVAL_P(0);
1586 #ifdef HAVE_INT64_TIMESTAMP
1589 result = span->time;
1590 if (result >= USECS_PER_DAY)
1592 days = result / USECS_PER_DAY;
1593 result -= days * USECS_PER_DAY;
1595 else if (result < 0)
1597 days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
1598 result += days * USECS_PER_DAY;
1601 result = span->time;
1602 if (result >= (double) SECS_PER_DAY || result < 0)
1603 result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
1606 PG_RETURN_TIMEADT(result);
1610 * Subtract two times to produce an interval.
1613 time_mi_time(PG_FUNCTION_ARGS)
1615 TimeADT time1 = PG_GETARG_TIMEADT(0);
1616 TimeADT time2 = PG_GETARG_TIMEADT(1);
1619 result = (Interval *) palloc(sizeof(Interval));
1623 result->time = time1 - time2;
1625 PG_RETURN_INTERVAL_P(result);
1628 /* time_pl_interval()
1629 * Add interval to time.
1632 time_pl_interval(PG_FUNCTION_ARGS)
1634 TimeADT time = PG_GETARG_TIMEADT(0);
1635 Interval *span = PG_GETARG_INTERVAL_P(1);
1638 #ifdef HAVE_INT64_TIMESTAMP
1639 result = time + span->time;
1640 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1641 if (result < INT64CONST(0))
1642 result += USECS_PER_DAY;
1646 result = time + span->time;
1647 TMODULO(result, time1, (double) SECS_PER_DAY);
1649 result += SECS_PER_DAY;
1652 PG_RETURN_TIMEADT(result);
1655 /* time_mi_interval()
1656 * Subtract interval from time.
1659 time_mi_interval(PG_FUNCTION_ARGS)
1661 TimeADT time = PG_GETARG_TIMEADT(0);
1662 Interval *span = PG_GETARG_INTERVAL_P(1);
1665 #ifdef HAVE_INT64_TIMESTAMP
1666 result = time - span->time;
1667 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1668 if (result < INT64CONST(0))
1669 result += USECS_PER_DAY;
1673 result = time - span->time;
1674 TMODULO(result, time1, (double) SECS_PER_DAY);
1676 result += SECS_PER_DAY;
1679 PG_RETURN_TIMEADT(result);
1684 * Extract specified field from time type.
1687 time_part(PG_FUNCTION_ARGS)
1689 text *units = PG_GETARG_TEXT_PP(0);
1690 TimeADT time = PG_GETARG_TIMEADT(1);
1696 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
1697 VARSIZE_ANY_EXHDR(units),
1700 type = DecodeUnits(0, lowunits, &val);
1701 if (type == UNKNOWN_FIELD)
1702 type = DecodeSpecial(0, lowunits, &val);
1710 time2tm(time, tm, &fsec);
1715 #ifdef HAVE_INT64_TIMESTAMP
1716 result = tm->tm_sec * 1000000.0 + fsec;
1718 result = (tm->tm_sec + fsec) * 1000000;
1723 #ifdef HAVE_INT64_TIMESTAMP
1724 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
1726 result = (tm->tm_sec + fsec) * 1000;
1731 #ifdef HAVE_INT64_TIMESTAMP
1732 result = tm->tm_sec + fsec / 1000000.0;
1734 result = tm->tm_sec + fsec;
1739 result = tm->tm_min;
1743 result = tm->tm_hour;
1755 case DTK_MILLENNIUM:
1759 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1760 errmsg("\"time\" units \"%s\" not recognized",
1765 else if (type == RESERV && val == DTK_EPOCH)
1767 #ifdef HAVE_INT64_TIMESTAMP
1768 result = time / 1000000.0;
1776 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1777 errmsg("\"time\" units \"%s\" not recognized",
1782 PG_RETURN_FLOAT8(result);
1786 /*****************************************************************************
1787 * Time With Time Zone ADT
1788 *****************************************************************************/
1791 * Convert a tm structure to a time data type.
1794 tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
1796 #ifdef HAVE_INT64_TIMESTAMP
1797 result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1798 USECS_PER_SEC) + fsec;
1800 result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1808 timetz_in(PG_FUNCTION_ARGS)
1810 char *str = PG_GETARG_CSTRING(0);
1813 Oid typelem = PG_GETARG_OID(1);
1815 int32 typmod = PG_GETARG_INT32(2);
1823 char workbuf[MAXDATELEN + 1];
1824 char *field[MAXDATEFIELDS];
1826 int ftype[MAXDATEFIELDS];
1828 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1829 field, ftype, MAXDATEFIELDS, &nf);
1831 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1833 DateTimeParseError(dterr, str, "time with time zone");
1835 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1836 tm2timetz(tm, fsec, tz, result);
1837 AdjustTimeForTypmod(&(result->time), typmod);
1839 PG_RETURN_TIMETZADT_P(result);
1843 timetz_out(PG_FUNCTION_ARGS)
1845 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1851 char buf[MAXDATELEN + 1];
1853 timetz2tm(time, tm, &fsec, &tz);
1854 EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
1856 result = pstrdup(buf);
1857 PG_RETURN_CSTRING(result);
1861 * timetz_recv - converts external binary format to timetz
1864 timetz_recv(PG_FUNCTION_ARGS)
1866 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1869 Oid typelem = PG_GETARG_OID(1);
1871 int32 typmod = PG_GETARG_INT32(2);
1874 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1876 #ifdef HAVE_INT64_TIMESTAMP
1877 result->time = pq_getmsgint64(buf);
1879 if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
1881 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1882 errmsg("time out of range")));
1884 result->time = pq_getmsgfloat8(buf);
1886 if (result->time < 0 || result->time > (double) SECS_PER_DAY)
1888 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1889 errmsg("time out of range")));
1892 result->zone = pq_getmsgint(buf, sizeof(result->zone));
1894 /* we allow GMT displacements up to 14:59:59, cf DecodeTimezone() */
1895 if (result->zone <= -15 * SECS_PER_HOUR ||
1896 result->zone >= 15 * SECS_PER_HOUR)
1898 (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
1899 errmsg("time zone displacement out of range")));
1901 AdjustTimeForTypmod(&(result->time), typmod);
1903 PG_RETURN_TIMETZADT_P(result);
1907 * timetz_send - converts timetz to binary format
1910 timetz_send(PG_FUNCTION_ARGS)
1912 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1915 pq_begintypsend(&buf);
1916 #ifdef HAVE_INT64_TIMESTAMP
1917 pq_sendint64(&buf, time->time);
1919 pq_sendfloat8(&buf, time->time);
1921 pq_sendint(&buf, time->zone, sizeof(time->zone));
1922 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1926 timetztypmodin(PG_FUNCTION_ARGS)
1928 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1930 PG_RETURN_INT32(anytime_typmodin(true, ta));
1934 timetztypmodout(PG_FUNCTION_ARGS)
1936 int32 typmod = PG_GETARG_INT32(0);
1938 PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
1943 * Convert TIME WITH TIME ZONE data type to POSIX time structure.
1946 timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
1948 TimeOffset trem = time->time;
1950 #ifdef HAVE_INT64_TIMESTAMP
1951 tm->tm_hour = trem / USECS_PER_HOUR;
1952 trem -= tm->tm_hour * USECS_PER_HOUR;
1953 tm->tm_min = trem / USECS_PER_MINUTE;
1954 trem -= tm->tm_min * USECS_PER_MINUTE;
1955 tm->tm_sec = trem / USECS_PER_SEC;
1956 *fsec = trem - tm->tm_sec * USECS_PER_SEC;
1959 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
1960 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1961 TMODULO(trem, tm->tm_sec, 1.0);
1962 trem = TIMEROUND(trem);
1963 /* roundoff may need to propagate to higher-order fields */
1966 trem = ceil(time->time);
1979 * Adjust time type for specified scale factor.
1980 * Used by PostgreSQL type system to stuff columns.
1983 timetz_scale(PG_FUNCTION_ARGS)
1985 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1986 int32 typmod = PG_GETARG_INT32(1);
1989 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1991 result->time = time->time;
1992 result->zone = time->zone;
1994 AdjustTimeForTypmod(&(result->time), typmod);
1996 PG_RETURN_TIMETZADT_P(result);
2001 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
2006 /* Primary sort is by true (GMT-equivalent) time */
2007 #ifdef HAVE_INT64_TIMESTAMP
2008 t1 = time1->time + (time1->zone * USECS_PER_SEC);
2009 t2 = time2->time + (time2->zone * USECS_PER_SEC);
2011 t1 = time1->time + time1->zone;
2012 t2 = time2->time + time2->zone;
2021 * If same GMT time, sort by timezone; we only want to say that two
2022 * timetz's are equal if both the time and zone parts are equal.
2024 if (time1->zone > time2->zone)
2026 if (time1->zone < time2->zone)
2033 timetz_eq(PG_FUNCTION_ARGS)
2035 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2036 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2038 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
2042 timetz_ne(PG_FUNCTION_ARGS)
2044 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2045 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2047 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
2051 timetz_lt(PG_FUNCTION_ARGS)
2053 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2054 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2056 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
2060 timetz_le(PG_FUNCTION_ARGS)
2062 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2063 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2065 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
2069 timetz_gt(PG_FUNCTION_ARGS)
2071 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2072 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2074 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
2078 timetz_ge(PG_FUNCTION_ARGS)
2080 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2081 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2083 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
2087 timetz_cmp(PG_FUNCTION_ARGS)
2089 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2090 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2092 PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
2096 timetz_hash(PG_FUNCTION_ARGS)
2098 TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2102 * To avoid any problems with padding bytes in the struct, we figure the
2103 * field hashes separately and XOR them. This also provides a convenient
2104 * framework for dealing with the fact that the time field might be either
2107 #ifdef HAVE_INT64_TIMESTAMP
2108 thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
2109 Int64GetDatumFast(key->time)));
2111 thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8,
2112 Float8GetDatumFast(key->time)));
2114 thash ^= DatumGetUInt32(hash_uint32(key->zone));
2115 PG_RETURN_UINT32(thash);
2119 timetz_larger(PG_FUNCTION_ARGS)
2121 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2122 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2125 if (timetz_cmp_internal(time1, time2) > 0)
2129 PG_RETURN_TIMETZADT_P(result);
2133 timetz_smaller(PG_FUNCTION_ARGS)
2135 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2136 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2139 if (timetz_cmp_internal(time1, time2) < 0)
2143 PG_RETURN_TIMETZADT_P(result);
2146 /* timetz_pl_interval()
2147 * Add interval to timetz.
2150 timetz_pl_interval(PG_FUNCTION_ARGS)
2152 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2153 Interval *span = PG_GETARG_INTERVAL_P(1);
2156 #ifndef HAVE_INT64_TIMESTAMP
2160 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2162 #ifdef HAVE_INT64_TIMESTAMP
2163 result->time = time->time + span->time;
2164 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2165 if (result->time < INT64CONST(0))
2166 result->time += USECS_PER_DAY;
2168 result->time = time->time + span->time;
2169 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2170 if (result->time < 0)
2171 result->time += SECS_PER_DAY;
2174 result->zone = time->zone;
2176 PG_RETURN_TIMETZADT_P(result);
2179 /* timetz_mi_interval()
2180 * Subtract interval from timetz.
2183 timetz_mi_interval(PG_FUNCTION_ARGS)
2185 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2186 Interval *span = PG_GETARG_INTERVAL_P(1);
2189 #ifndef HAVE_INT64_TIMESTAMP
2193 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2195 #ifdef HAVE_INT64_TIMESTAMP
2196 result->time = time->time - span->time;
2197 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2198 if (result->time < INT64CONST(0))
2199 result->time += USECS_PER_DAY;
2201 result->time = time->time - span->time;
2202 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2203 if (result->time < 0)
2204 result->time += SECS_PER_DAY;
2207 result->zone = time->zone;
2209 PG_RETURN_TIMETZADT_P(result);
2212 /* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
2214 * Algorithm is per SQL92 spec. This is much harder than you'd think
2215 * because the spec requires us to deliver a non-null answer in some cases
2216 * where some of the inputs are null.
2219 overlaps_timetz(PG_FUNCTION_ARGS)
2222 * The arguments are TimeTzADT *, but we leave them as generic Datums for
2223 * convenience of notation --- and to avoid dereferencing nulls.
2225 Datum ts1 = PG_GETARG_DATUM(0);
2226 Datum te1 = PG_GETARG_DATUM(1);
2227 Datum ts2 = PG_GETARG_DATUM(2);
2228 Datum te2 = PG_GETARG_DATUM(3);
2229 bool ts1IsNull = PG_ARGISNULL(0);
2230 bool te1IsNull = PG_ARGISNULL(1);
2231 bool ts2IsNull = PG_ARGISNULL(2);
2232 bool te2IsNull = PG_ARGISNULL(3);
2234 #define TIMETZ_GT(t1,t2) \
2235 DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2236 #define TIMETZ_LT(t1,t2) \
2237 DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2240 * If both endpoints of interval 1 are null, the result is null (unknown).
2241 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2242 * take ts1 as the lesser endpoint.
2248 /* swap null for non-null */
2252 else if (!te1IsNull)
2254 if (TIMETZ_GT(ts1, te1))
2263 /* Likewise for interval 2. */
2268 /* swap null for non-null */
2272 else if (!te2IsNull)
2274 if (TIMETZ_GT(ts2, te2))
2284 * At this point neither ts1 nor ts2 is null, so we can consider three
2285 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2287 if (TIMETZ_GT(ts1, ts2))
2290 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2291 * in the presence of nulls it's not quite completely so.
2295 if (TIMETZ_LT(ts1, te2))
2296 PG_RETURN_BOOL(true);
2301 * If te1 is not null then we had ts1 <= te1 above, and we just found
2302 * ts1 >= te2, hence te1 >= te2.
2304 PG_RETURN_BOOL(false);
2306 else if (TIMETZ_LT(ts1, ts2))
2308 /* This case is ts2 < te1 OR te2 < te1 */
2311 if (TIMETZ_LT(ts2, te1))
2312 PG_RETURN_BOOL(true);
2317 * If te2 is not null then we had ts2 <= te2 above, and we just found
2318 * ts2 >= te1, hence te2 >= te1.
2320 PG_RETURN_BOOL(false);
2325 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2326 * rather silly way of saying "true if both are nonnull, else null".
2328 if (te1IsNull || te2IsNull)
2330 PG_RETURN_BOOL(true);
2339 timetz_time(PG_FUNCTION_ARGS)
2341 TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
2344 /* swallow the time zone and just return the time */
2345 result = timetz->time;
2347 PG_RETURN_TIMEADT(result);
2352 time_timetz(PG_FUNCTION_ARGS)
2354 TimeADT time = PG_GETARG_TIMEADT(0);
2361 GetCurrentDateTime(tm);
2362 time2tm(time, tm, &fsec);
2363 tz = DetermineTimeZoneOffset(tm, session_timezone);
2365 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2367 result->time = time;
2370 PG_RETURN_TIMETZADT_P(result);
2374 /* timestamptz_timetz()
2375 * Convert timestamp to timetz data type.
2378 timestamptz_timetz(PG_FUNCTION_ARGS)
2380 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2388 if (TIMESTAMP_NOT_FINITE(timestamp))
2391 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2393 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2394 errmsg("timestamp out of range")));
2396 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2398 tm2timetz(tm, fsec, tz, result);
2400 PG_RETURN_TIMETZADT_P(result);
2404 /* datetimetz_timestamptz()
2405 * Convert date and timetz to timestamp with time zone data type.
2406 * Timestamp is stored in GMT, so add the time zone
2407 * stored with the timetz to the result.
2408 * - thomas 2000-03-10
2411 datetimetz_timestamptz(PG_FUNCTION_ARGS)
2413 DateADT date = PG_GETARG_DATEADT(0);
2414 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2417 if (DATE_IS_NOBEGIN(date))
2418 TIMESTAMP_NOBEGIN(result);
2419 else if (DATE_IS_NOEND(date))
2420 TIMESTAMP_NOEND(result);
2423 #ifdef HAVE_INT64_TIMESTAMP
2424 result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2426 result = date * (double) SECS_PER_DAY + time->time + time->zone;
2430 PG_RETURN_TIMESTAMP(result);
2435 * Extract specified field from time type.
2438 timetz_part(PG_FUNCTION_ARGS)
2440 text *units = PG_GETARG_TEXT_PP(0);
2441 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2447 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
2448 VARSIZE_ANY_EXHDR(units),
2451 type = DecodeUnits(0, lowunits, &val);
2452 if (type == UNKNOWN_FIELD)
2453 type = DecodeSpecial(0, lowunits, &val);
2463 timetz2tm(time, tm, &fsec, &tz);
2473 result /= SECS_PER_MINUTE;
2474 FMODULO(result, dummy, (double) SECS_PER_MINUTE);
2479 FMODULO(dummy, result, (double) SECS_PER_HOUR);
2483 #ifdef HAVE_INT64_TIMESTAMP
2484 result = tm->tm_sec * 1000000.0 + fsec;
2486 result = (tm->tm_sec + fsec) * 1000000;
2491 #ifdef HAVE_INT64_TIMESTAMP
2492 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
2494 result = (tm->tm_sec + fsec) * 1000;
2499 #ifdef HAVE_INT64_TIMESTAMP
2500 result = tm->tm_sec + fsec / 1000000.0;
2502 result = tm->tm_sec + fsec;
2507 result = tm->tm_min;
2511 result = tm->tm_hour;
2520 case DTK_MILLENNIUM:
2523 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2524 errmsg("\"time with time zone\" units \"%s\" not recognized",
2529 else if (type == RESERV && val == DTK_EPOCH)
2531 #ifdef HAVE_INT64_TIMESTAMP
2532 result = time->time / 1000000.0 + time->zone;
2534 result = time->time + time->zone;
2540 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2541 errmsg("\"time with time zone\" units \"%s\" not recognized",
2546 PG_RETURN_FLOAT8(result);
2550 * Encode time with time zone type with specified time zone.
2551 * Applies DST rules as of the current date.
2554 timetz_zone(PG_FUNCTION_ARGS)
2556 text *zone = PG_GETARG_TEXT_PP(0);
2557 TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
2560 char tzname[TZ_STRLEN_MAX + 1];
2567 * Look up the requested timezone. First we look in the date token table
2568 * (to handle cases like "EST"), and if that fails, we look in the
2569 * timezone database (to handle cases like "America/New_York"). (This
2570 * matches the order in which timestamp input checks the cases; it's
2571 * important because the timezone database unwisely uses a few zone names
2572 * that are identical to offset abbreviations.)
2574 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
2575 lowzone = downcase_truncate_identifier(tzname,
2579 type = DecodeSpecial(0, lowzone, &val);
2581 if (type == TZ || type == DTZ)
2585 tzp = pg_tzset(tzname);
2588 /* Get the offset-from-GMT that is valid today for the zone */
2589 pg_time_t now = (pg_time_t) time(NULL);
2592 tm = pg_localtime(&now, tzp);
2593 tz = -tm->tm_gmtoff;
2598 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2599 errmsg("time zone \"%s\" not recognized", tzname)));
2600 tz = 0; /* keep compiler quiet */
2604 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2606 #ifdef HAVE_INT64_TIMESTAMP
2607 result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
2608 while (result->time < INT64CONST(0))
2609 result->time += USECS_PER_DAY;
2610 while (result->time >= USECS_PER_DAY)
2611 result->time -= USECS_PER_DAY;
2613 result->time = t->time + (t->zone - tz);
2614 while (result->time < 0)
2615 result->time += SECS_PER_DAY;
2616 while (result->time >= SECS_PER_DAY)
2617 result->time -= SECS_PER_DAY;
2622 PG_RETURN_TIMETZADT_P(result);
2626 * Encode time with time zone type with specified time interval as time zone.
2629 timetz_izone(PG_FUNCTION_ARGS)
2631 Interval *zone = PG_GETARG_INTERVAL_P(0);
2632 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2636 if (zone->month != 0)
2638 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2639 errmsg("\"interval\" time zone \"%s\" not valid",
2640 DatumGetCString(DirectFunctionCall1(interval_out,
2641 PointerGetDatum(zone))))));
2643 #ifdef HAVE_INT64_TIMESTAMP
2644 tz = -(zone->time / USECS_PER_SEC);
2649 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2651 #ifdef HAVE_INT64_TIMESTAMP
2652 result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
2653 while (result->time < INT64CONST(0))
2654 result->time += USECS_PER_DAY;
2655 while (result->time >= USECS_PER_DAY)
2656 result->time -= USECS_PER_DAY;
2658 result->time = time->time + (time->zone - tz);
2659 while (result->time < 0)
2660 result->time += SECS_PER_DAY;
2661 while (result->time >= SECS_PER_DAY)
2662 result->time -= SECS_PER_DAY;
2667 PG_RETURN_TIMETZADT_P(result);