1 /*-------------------------------------------------------------------------
4 * implements DATE and TIME data types specified in SQL-92 standard
6 * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994-5, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.137 2007/11/15 21:14:38 momjian Exp $
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 int time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
42 static int timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
43 static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
44 static int tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
45 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
48 /* common code for timetypmodin and timetztypmodin */
50 anytime_typmodin(bool istz, ArrayType *ta)
56 tl = ArrayGetIntegerTypmods(ta, &n);
59 * we're not too tense about good error message here because grammar
60 * shouldn't allow wrong number of modifiers for TIME
64 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
65 errmsg("invalid type modifier")));
69 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
70 errmsg("TIME(%d)%s precision must not be negative",
71 *tl, (istz ? " WITH TIME ZONE" : ""))));
72 if (*tl > MAX_TIME_PRECISION)
75 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
76 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
77 *tl, (istz ? " WITH TIME ZONE" : ""),
78 MAX_TIME_PRECISION)));
79 typmod = MAX_TIME_PRECISION;
87 /* common code for timetypmodout and timetztypmodout */
89 anytime_typmodout(bool istz, int32 typmod)
91 char *res = (char *) palloc(64);
92 const char *tz = istz ? " with time zone" : " without time zone";
95 snprintf(res, 64, "(%d)%s", (int) typmod, tz);
97 snprintf(res, 64, "%s", tz);
102 /*****************************************************************************
104 *****************************************************************************/
108 * Given date text string, convert to internal date format.
111 date_in(PG_FUNCTION_ARGS)
113 char *str = PG_GETARG_CSTRING(0);
122 char *field[MAXDATEFIELDS];
123 int ftype[MAXDATEFIELDS];
124 char workbuf[MAXDATELEN + 1];
126 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
127 field, ftype, MAXDATEFIELDS, &nf);
129 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
131 DateTimeParseError(dterr, str, "date");
140 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
141 errmsg("date/time value \"current\" is no longer supported")));
143 GetCurrentDateTime(tm);
151 DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
155 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
157 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
158 errmsg("date out of range: \"%s\"", str)));
160 date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
162 PG_RETURN_DATEADT(date);
166 * Given internal format date, convert to text string.
169 date_out(PG_FUNCTION_ARGS)
171 DateADT date = PG_GETARG_DATEADT(0);
175 char buf[MAXDATELEN + 1];
177 j2date(date + POSTGRES_EPOCH_JDATE,
178 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
180 EncodeDateOnly(tm, DateStyle, buf);
182 result = pstrdup(buf);
183 PG_RETURN_CSTRING(result);
187 * date_recv - converts external binary format to date
190 date_recv(PG_FUNCTION_ARGS)
192 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
194 PG_RETURN_DATEADT((DateADT) pq_getmsgint(buf, sizeof(DateADT)));
198 * date_send - converts date to binary format
201 date_send(PG_FUNCTION_ARGS)
203 DateADT date = PG_GETARG_DATEADT(0);
206 pq_begintypsend(&buf);
207 pq_sendint(&buf, date, sizeof(date));
208 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
213 * Comparison functions for dates
217 date_eq(PG_FUNCTION_ARGS)
219 DateADT dateVal1 = PG_GETARG_DATEADT(0);
220 DateADT dateVal2 = PG_GETARG_DATEADT(1);
222 PG_RETURN_BOOL(dateVal1 == dateVal2);
226 date_ne(PG_FUNCTION_ARGS)
228 DateADT dateVal1 = PG_GETARG_DATEADT(0);
229 DateADT dateVal2 = PG_GETARG_DATEADT(1);
231 PG_RETURN_BOOL(dateVal1 != dateVal2);
235 date_lt(PG_FUNCTION_ARGS)
237 DateADT dateVal1 = PG_GETARG_DATEADT(0);
238 DateADT dateVal2 = PG_GETARG_DATEADT(1);
240 PG_RETURN_BOOL(dateVal1 < dateVal2);
244 date_le(PG_FUNCTION_ARGS)
246 DateADT dateVal1 = PG_GETARG_DATEADT(0);
247 DateADT dateVal2 = PG_GETARG_DATEADT(1);
249 PG_RETURN_BOOL(dateVal1 <= dateVal2);
253 date_gt(PG_FUNCTION_ARGS)
255 DateADT dateVal1 = PG_GETARG_DATEADT(0);
256 DateADT dateVal2 = PG_GETARG_DATEADT(1);
258 PG_RETURN_BOOL(dateVal1 > dateVal2);
262 date_ge(PG_FUNCTION_ARGS)
264 DateADT dateVal1 = PG_GETARG_DATEADT(0);
265 DateADT dateVal2 = PG_GETARG_DATEADT(1);
267 PG_RETURN_BOOL(dateVal1 >= dateVal2);
271 date_cmp(PG_FUNCTION_ARGS)
273 DateADT dateVal1 = PG_GETARG_DATEADT(0);
274 DateADT dateVal2 = PG_GETARG_DATEADT(1);
276 if (dateVal1 < dateVal2)
278 else if (dateVal1 > dateVal2)
284 date_larger(PG_FUNCTION_ARGS)
286 DateADT dateVal1 = PG_GETARG_DATEADT(0);
287 DateADT dateVal2 = PG_GETARG_DATEADT(1);
289 PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
293 date_smaller(PG_FUNCTION_ARGS)
295 DateADT dateVal1 = PG_GETARG_DATEADT(0);
296 DateADT dateVal2 = PG_GETARG_DATEADT(1);
298 PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
301 /* Compute difference between two dates in days.
304 date_mi(PG_FUNCTION_ARGS)
306 DateADT dateVal1 = PG_GETARG_DATEADT(0);
307 DateADT dateVal2 = PG_GETARG_DATEADT(1);
309 PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
312 /* Add a number of days to a date, giving a new date.
313 * Must handle both positive and negative numbers of days.
316 date_pli(PG_FUNCTION_ARGS)
318 DateADT dateVal = PG_GETARG_DATEADT(0);
319 int32 days = PG_GETARG_INT32(1);
321 PG_RETURN_DATEADT(dateVal + days);
324 /* Subtract a number of days from a date, giving a new date.
327 date_mii(PG_FUNCTION_ARGS)
329 DateADT dateVal = PG_GETARG_DATEADT(0);
330 int32 days = PG_GETARG_INT32(1);
332 PG_RETURN_DATEADT(dateVal - days);
336 * Internal routines for promoting date to timestamp and timestamp with
341 date2timestamp(DateADT dateVal)
345 #ifdef HAVE_INT64_TIMESTAMP
346 /* date is days since 2000, timestamp is microseconds since same... */
347 result = dateVal * USECS_PER_DAY;
348 /* Date's range is wider than timestamp's, so must check for overflow */
349 if (result / USECS_PER_DAY != dateVal)
351 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
352 errmsg("date out of range for timestamp")));
354 /* date is days since 2000, timestamp is seconds since same... */
355 result = dateVal * (double) SECS_PER_DAY;
362 date2timestamptz(DateADT dateVal)
369 j2date(dateVal + POSTGRES_EPOCH_JDATE,
370 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
375 tz = DetermineTimeZoneOffset(tm, session_timezone);
377 #ifdef HAVE_INT64_TIMESTAMP
378 result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
379 /* Date's range is wider than timestamp's, so must check for overflow */
380 if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
382 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
383 errmsg("date out of range for timestamp")));
385 result = dateVal * (double) SECS_PER_DAY + tz;
393 * Crosstype comparison functions for dates
397 date_eq_timestamp(PG_FUNCTION_ARGS)
399 DateADT dateVal = PG_GETARG_DATEADT(0);
400 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
403 dt1 = date2timestamp(dateVal);
405 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
409 date_ne_timestamp(PG_FUNCTION_ARGS)
411 DateADT dateVal = PG_GETARG_DATEADT(0);
412 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
415 dt1 = date2timestamp(dateVal);
417 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
421 date_lt_timestamp(PG_FUNCTION_ARGS)
423 DateADT dateVal = PG_GETARG_DATEADT(0);
424 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
427 dt1 = date2timestamp(dateVal);
429 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
433 date_gt_timestamp(PG_FUNCTION_ARGS)
435 DateADT dateVal = PG_GETARG_DATEADT(0);
436 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
439 dt1 = date2timestamp(dateVal);
441 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
445 date_le_timestamp(PG_FUNCTION_ARGS)
447 DateADT dateVal = PG_GETARG_DATEADT(0);
448 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
451 dt1 = date2timestamp(dateVal);
453 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
457 date_ge_timestamp(PG_FUNCTION_ARGS)
459 DateADT dateVal = PG_GETARG_DATEADT(0);
460 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
463 dt1 = date2timestamp(dateVal);
465 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
469 date_cmp_timestamp(PG_FUNCTION_ARGS)
471 DateADT dateVal = PG_GETARG_DATEADT(0);
472 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
475 dt1 = date2timestamp(dateVal);
477 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
481 date_eq_timestamptz(PG_FUNCTION_ARGS)
483 DateADT dateVal = PG_GETARG_DATEADT(0);
484 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
487 dt1 = date2timestamptz(dateVal);
489 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
493 date_ne_timestamptz(PG_FUNCTION_ARGS)
495 DateADT dateVal = PG_GETARG_DATEADT(0);
496 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
499 dt1 = date2timestamptz(dateVal);
501 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
505 date_lt_timestamptz(PG_FUNCTION_ARGS)
507 DateADT dateVal = PG_GETARG_DATEADT(0);
508 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
511 dt1 = date2timestamptz(dateVal);
513 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
517 date_gt_timestamptz(PG_FUNCTION_ARGS)
519 DateADT dateVal = PG_GETARG_DATEADT(0);
520 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
523 dt1 = date2timestamptz(dateVal);
525 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
529 date_le_timestamptz(PG_FUNCTION_ARGS)
531 DateADT dateVal = PG_GETARG_DATEADT(0);
532 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
535 dt1 = date2timestamptz(dateVal);
537 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
541 date_ge_timestamptz(PG_FUNCTION_ARGS)
543 DateADT dateVal = PG_GETARG_DATEADT(0);
544 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
547 dt1 = date2timestamptz(dateVal);
549 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
553 date_cmp_timestamptz(PG_FUNCTION_ARGS)
555 DateADT dateVal = PG_GETARG_DATEADT(0);
556 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
559 dt1 = date2timestamptz(dateVal);
561 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
565 timestamp_eq_date(PG_FUNCTION_ARGS)
567 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
568 DateADT dateVal = PG_GETARG_DATEADT(1);
571 dt2 = date2timestamp(dateVal);
573 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
577 timestamp_ne_date(PG_FUNCTION_ARGS)
579 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
580 DateADT dateVal = PG_GETARG_DATEADT(1);
583 dt2 = date2timestamp(dateVal);
585 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
589 timestamp_lt_date(PG_FUNCTION_ARGS)
591 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
592 DateADT dateVal = PG_GETARG_DATEADT(1);
595 dt2 = date2timestamp(dateVal);
597 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
601 timestamp_gt_date(PG_FUNCTION_ARGS)
603 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
604 DateADT dateVal = PG_GETARG_DATEADT(1);
607 dt2 = date2timestamp(dateVal);
609 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
613 timestamp_le_date(PG_FUNCTION_ARGS)
615 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
616 DateADT dateVal = PG_GETARG_DATEADT(1);
619 dt2 = date2timestamp(dateVal);
621 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
625 timestamp_ge_date(PG_FUNCTION_ARGS)
627 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
628 DateADT dateVal = PG_GETARG_DATEADT(1);
631 dt2 = date2timestamp(dateVal);
633 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
637 timestamp_cmp_date(PG_FUNCTION_ARGS)
639 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
640 DateADT dateVal = PG_GETARG_DATEADT(1);
643 dt2 = date2timestamp(dateVal);
645 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
649 timestamptz_eq_date(PG_FUNCTION_ARGS)
651 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
652 DateADT dateVal = PG_GETARG_DATEADT(1);
655 dt2 = date2timestamptz(dateVal);
657 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
661 timestamptz_ne_date(PG_FUNCTION_ARGS)
663 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
664 DateADT dateVal = PG_GETARG_DATEADT(1);
667 dt2 = date2timestamptz(dateVal);
669 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
673 timestamptz_lt_date(PG_FUNCTION_ARGS)
675 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
676 DateADT dateVal = PG_GETARG_DATEADT(1);
679 dt2 = date2timestamptz(dateVal);
681 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
685 timestamptz_gt_date(PG_FUNCTION_ARGS)
687 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
688 DateADT dateVal = PG_GETARG_DATEADT(1);
691 dt2 = date2timestamptz(dateVal);
693 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
697 timestamptz_le_date(PG_FUNCTION_ARGS)
699 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
700 DateADT dateVal = PG_GETARG_DATEADT(1);
703 dt2 = date2timestamptz(dateVal);
705 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
709 timestamptz_ge_date(PG_FUNCTION_ARGS)
711 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
712 DateADT dateVal = PG_GETARG_DATEADT(1);
715 dt2 = date2timestamptz(dateVal);
717 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
721 timestamptz_cmp_date(PG_FUNCTION_ARGS)
723 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
724 DateADT dateVal = PG_GETARG_DATEADT(1);
727 dt2 = date2timestamptz(dateVal);
729 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
733 /* Add an interval to a date, giving a new date.
734 * Must handle both positive and negative intervals.
736 * We implement this by promoting the date to timestamp (without time zone)
737 * and then using the timestamp plus interval function.
740 date_pl_interval(PG_FUNCTION_ARGS)
742 DateADT dateVal = PG_GETARG_DATEADT(0);
743 Interval *span = PG_GETARG_INTERVAL_P(1);
746 dateStamp = date2timestamp(dateVal);
748 return DirectFunctionCall2(timestamp_pl_interval,
749 TimestampGetDatum(dateStamp),
750 PointerGetDatum(span));
753 /* Subtract an interval from a date, giving a new date.
754 * Must handle both positive and negative intervals.
756 * We implement this by promoting the date to timestamp (without time zone)
757 * and then using the timestamp minus interval function.
760 date_mi_interval(PG_FUNCTION_ARGS)
762 DateADT dateVal = PG_GETARG_DATEADT(0);
763 Interval *span = PG_GETARG_INTERVAL_P(1);
766 dateStamp = date2timestamp(dateVal);
768 return DirectFunctionCall2(timestamp_mi_interval,
769 TimestampGetDatum(dateStamp),
770 PointerGetDatum(span));
774 * Convert date to timestamp data type.
777 date_timestamp(PG_FUNCTION_ARGS)
779 DateADT dateVal = PG_GETARG_DATEADT(0);
782 result = date2timestamp(dateVal);
784 PG_RETURN_TIMESTAMP(result);
789 * Convert timestamp to date data type.
792 timestamp_date(PG_FUNCTION_ARGS)
794 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
800 if (TIMESTAMP_NOT_FINITE(timestamp))
803 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
805 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
806 errmsg("timestamp out of range")));
808 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
810 PG_RETURN_DATEADT(result);
814 /* date_timestamptz()
815 * Convert date to timestamp with time zone data type.
818 date_timestamptz(PG_FUNCTION_ARGS)
820 DateADT dateVal = PG_GETARG_DATEADT(0);
823 result = date2timestamptz(dateVal);
825 PG_RETURN_TIMESTAMP(result);
829 /* timestamptz_date()
830 * Convert timestamp with time zone to date data type.
833 timestamptz_date(PG_FUNCTION_ARGS)
835 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
843 if (TIMESTAMP_NOT_FINITE(timestamp))
846 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
848 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
849 errmsg("timestamp out of range")));
851 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
853 PG_RETURN_DATEADT(result);
858 * Convert abstime to date data type.
861 abstime_date(PG_FUNCTION_ARGS)
863 AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
871 case INVALID_ABSTIME:
872 case NOSTART_ABSTIME:
875 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
876 errmsg("cannot convert reserved abstime value to date")));
879 * pretend to drop through to make compiler think that result will
884 abstime2tm(abstime, &tz, tm, NULL);
885 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
889 PG_RETURN_DATEADT(result);
893 /*****************************************************************************
895 *****************************************************************************/
898 time_in(PG_FUNCTION_ARGS)
900 char *str = PG_GETARG_CSTRING(0);
903 Oid typelem = PG_GETARG_OID(1);
905 int32 typmod = PG_GETARG_INT32(2);
913 char workbuf[MAXDATELEN + 1];
914 char *field[MAXDATEFIELDS];
916 int ftype[MAXDATEFIELDS];
918 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
919 field, ftype, MAXDATEFIELDS, &nf);
921 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
923 DateTimeParseError(dterr, str, "time");
925 tm2time(tm, fsec, &result);
926 AdjustTimeForTypmod(&result, typmod);
928 PG_RETURN_TIMEADT(result);
932 * Convert a tm structure to a time data type.
935 tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
937 #ifdef HAVE_INT64_TIMESTAMP
938 *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
939 * USECS_PER_SEC) + fsec;
941 *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
947 * Convert time data type to POSIX time structure.
948 * For dates within the system-supported time_t range, convert to the
949 * local time zone. If out of this range, leave as GMT. - tgl 97/05/27
952 time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
954 #ifdef HAVE_INT64_TIMESTAMP
955 tm->tm_hour = time / USECS_PER_HOUR;
956 time -= tm->tm_hour * USECS_PER_HOUR;
957 tm->tm_min = time / USECS_PER_MINUTE;
958 time -= tm->tm_min * USECS_PER_MINUTE;
959 tm->tm_sec = time / USECS_PER_SEC;
960 time -= tm->tm_sec * USECS_PER_SEC;
967 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
968 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
969 TMODULO(trem, tm->tm_sec, 1.0);
970 trem = TIMEROUND(trem);
971 /* roundoff may need to propagate to higher-order fields */
984 time_out(PG_FUNCTION_ARGS)
986 TimeADT time = PG_GETARG_TIMEADT(0);
991 char buf[MAXDATELEN + 1];
993 time2tm(time, tm, &fsec);
994 EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
996 result = pstrdup(buf);
997 PG_RETURN_CSTRING(result);
1001 * time_recv - converts external binary format to time
1003 * We make no attempt to provide compatibility between int and float
1004 * time representations ...
1007 time_recv(PG_FUNCTION_ARGS)
1009 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1012 Oid typelem = PG_GETARG_OID(1);
1014 int32 typmod = PG_GETARG_INT32(2);
1017 #ifdef HAVE_INT64_TIMESTAMP
1018 result = pq_getmsgint64(buf);
1020 result = pq_getmsgfloat8(buf);
1023 AdjustTimeForTypmod(&result, typmod);
1025 PG_RETURN_TIMEADT(result);
1029 * time_send - converts time to binary format
1032 time_send(PG_FUNCTION_ARGS)
1034 TimeADT time = PG_GETARG_TIMEADT(0);
1037 pq_begintypsend(&buf);
1038 #ifdef HAVE_INT64_TIMESTAMP
1039 pq_sendint64(&buf, time);
1041 pq_sendfloat8(&buf, time);
1043 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1047 timetypmodin(PG_FUNCTION_ARGS)
1049 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1051 PG_RETURN_INT32(anytime_typmodin(false, ta));
1055 timetypmodout(PG_FUNCTION_ARGS)
1057 int32 typmod = PG_GETARG_INT32(0);
1059 PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
1064 * Adjust time type for specified scale factor.
1065 * Used by PostgreSQL type system to stuff columns.
1068 time_scale(PG_FUNCTION_ARGS)
1070 TimeADT time = PG_GETARG_TIMEADT(0);
1071 int32 typmod = PG_GETARG_INT32(1);
1075 AdjustTimeForTypmod(&result, typmod);
1077 PG_RETURN_TIMEADT(result);
1080 /* AdjustTimeForTypmod()
1081 * Force the precision of the time value to a specified value.
1082 * Uses *exactly* the same code as in AdjustTimestampForTypemod()
1083 * but we make a separate copy because those types do not
1084 * have a fundamental tie together but rather a coincidence of
1085 * implementation. - thomas
1088 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1090 #ifdef HAVE_INT64_TIMESTAMP
1091 static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1092 INT64CONST(1000000),
1101 static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1111 /* note MAX_TIME_PRECISION differs in this case */
1112 static const double TimeScales[MAX_TIME_PRECISION + 1] = {
1127 if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1130 * Note: this round-to-nearest code is not completely consistent about
1131 * rounding values that are exactly halfway between integral values.
1132 * On most platforms, rint() will implement round-to-nearest-even, but
1133 * the integer code always rounds up (away from zero). Is it worth
1134 * trying to be consistent?
1136 #ifdef HAVE_INT64_TIMESTAMP
1137 if (*time >= INT64CONST(0))
1138 *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
1141 *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
1142 TimeScales[typmod]);
1144 *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
1151 time_eq(PG_FUNCTION_ARGS)
1153 TimeADT time1 = PG_GETARG_TIMEADT(0);
1154 TimeADT time2 = PG_GETARG_TIMEADT(1);
1156 PG_RETURN_BOOL(time1 == time2);
1160 time_ne(PG_FUNCTION_ARGS)
1162 TimeADT time1 = PG_GETARG_TIMEADT(0);
1163 TimeADT time2 = PG_GETARG_TIMEADT(1);
1165 PG_RETURN_BOOL(time1 != time2);
1169 time_lt(PG_FUNCTION_ARGS)
1171 TimeADT time1 = PG_GETARG_TIMEADT(0);
1172 TimeADT time2 = PG_GETARG_TIMEADT(1);
1174 PG_RETURN_BOOL(time1 < time2);
1178 time_le(PG_FUNCTION_ARGS)
1180 TimeADT time1 = PG_GETARG_TIMEADT(0);
1181 TimeADT time2 = PG_GETARG_TIMEADT(1);
1183 PG_RETURN_BOOL(time1 <= time2);
1187 time_gt(PG_FUNCTION_ARGS)
1189 TimeADT time1 = PG_GETARG_TIMEADT(0);
1190 TimeADT time2 = PG_GETARG_TIMEADT(1);
1192 PG_RETURN_BOOL(time1 > time2);
1196 time_ge(PG_FUNCTION_ARGS)
1198 TimeADT time1 = PG_GETARG_TIMEADT(0);
1199 TimeADT time2 = PG_GETARG_TIMEADT(1);
1201 PG_RETURN_BOOL(time1 >= time2);
1205 time_cmp(PG_FUNCTION_ARGS)
1207 TimeADT time1 = PG_GETARG_TIMEADT(0);
1208 TimeADT time2 = PG_GETARG_TIMEADT(1);
1211 PG_RETURN_INT32(-1);
1218 time_hash(PG_FUNCTION_ARGS)
1220 /* We can use either hashint8 or hashfloat8 directly */
1221 #ifdef HAVE_INT64_TIMESTAMP
1222 return hashint8(fcinfo);
1224 return hashfloat8(fcinfo);
1229 time_larger(PG_FUNCTION_ARGS)
1231 TimeADT time1 = PG_GETARG_TIMEADT(0);
1232 TimeADT time2 = PG_GETARG_TIMEADT(1);
1234 PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1238 time_smaller(PG_FUNCTION_ARGS)
1240 TimeADT time1 = PG_GETARG_TIMEADT(0);
1241 TimeADT time2 = PG_GETARG_TIMEADT(1);
1243 PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1246 /* overlaps_time() --- implements the SQL92 OVERLAPS operator.
1248 * Algorithm is per SQL92 spec. This is much harder than you'd think
1249 * because the spec requires us to deliver a non-null answer in some cases
1250 * where some of the inputs are null.
1253 overlaps_time(PG_FUNCTION_ARGS)
1256 * The arguments are TimeADT, but we leave them as generic Datums to avoid
1257 * dereferencing nulls (TimeADT is pass-by-reference!)
1259 Datum ts1 = PG_GETARG_DATUM(0);
1260 Datum te1 = PG_GETARG_DATUM(1);
1261 Datum ts2 = PG_GETARG_DATUM(2);
1262 Datum te2 = PG_GETARG_DATUM(3);
1263 bool ts1IsNull = PG_ARGISNULL(0);
1264 bool te1IsNull = PG_ARGISNULL(1);
1265 bool ts2IsNull = PG_ARGISNULL(2);
1266 bool te2IsNull = PG_ARGISNULL(3);
1268 #define TIMEADT_GT(t1,t2) \
1269 (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1270 #define TIMEADT_LT(t1,t2) \
1271 (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1274 * If both endpoints of interval 1 are null, the result is null (unknown).
1275 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1276 * take ts1 as the lesser endpoint.
1282 /* swap null for non-null */
1286 else if (!te1IsNull)
1288 if (TIMEADT_GT(ts1, te1))
1297 /* Likewise for interval 2. */
1302 /* swap null for non-null */
1306 else if (!te2IsNull)
1308 if (TIMEADT_GT(ts2, te2))
1318 * At this point neither ts1 nor ts2 is null, so we can consider three
1319 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1321 if (TIMEADT_GT(ts1, ts2))
1324 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
1325 * in the presence of nulls it's not quite completely so.
1329 if (TIMEADT_LT(ts1, te2))
1330 PG_RETURN_BOOL(true);
1335 * If te1 is not null then we had ts1 <= te1 above, and we just found
1336 * ts1 >= te2, hence te1 >= te2.
1338 PG_RETURN_BOOL(false);
1340 else if (TIMEADT_LT(ts1, ts2))
1342 /* This case is ts2 < te1 OR te2 < te1 */
1345 if (TIMEADT_LT(ts2, te1))
1346 PG_RETURN_BOOL(true);
1351 * If te2 is not null then we had ts2 <= te2 above, and we just found
1352 * ts2 >= te1, hence te2 >= te1.
1354 PG_RETURN_BOOL(false);
1359 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1360 * rather silly way of saying "true if both are nonnull, else null".
1362 if (te1IsNull || te2IsNull)
1364 PG_RETURN_BOOL(true);
1372 * Convert timestamp to time data type.
1375 timestamp_time(PG_FUNCTION_ARGS)
1377 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1383 if (TIMESTAMP_NOT_FINITE(timestamp))
1386 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1388 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1389 errmsg("timestamp out of range")));
1391 #ifdef HAVE_INT64_TIMESTAMP
1394 * Could also do this with time = (timestamp / USECS_PER_DAY *
1395 * USECS_PER_DAY) - timestamp;
1397 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1398 USECS_PER_SEC) + fsec;
1400 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1403 PG_RETURN_TIMEADT(result);
1406 /* timestamptz_time()
1407 * Convert timestamptz to time data type.
1410 timestamptz_time(PG_FUNCTION_ARGS)
1412 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1420 if (TIMESTAMP_NOT_FINITE(timestamp))
1423 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
1425 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1426 errmsg("timestamp out of range")));
1428 #ifdef HAVE_INT64_TIMESTAMP
1431 * Could also do this with time = (timestamp / USECS_PER_DAY *
1432 * USECS_PER_DAY) - timestamp;
1434 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1435 USECS_PER_SEC) + fsec;
1437 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1440 PG_RETURN_TIMEADT(result);
1443 /* datetime_timestamp()
1444 * Convert date and time to timestamp data type.
1447 datetime_timestamp(PG_FUNCTION_ARGS)
1449 DateADT date = PG_GETARG_DATEADT(0);
1450 TimeADT time = PG_GETARG_TIMEADT(1);
1453 result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
1454 DateADTGetDatum(date)));
1457 PG_RETURN_TIMESTAMP(result);
1461 * Convert time to interval data type.
1464 time_interval(PG_FUNCTION_ARGS)
1466 TimeADT time = PG_GETARG_TIMEADT(0);
1469 result = (Interval *) palloc(sizeof(Interval));
1471 result->time = time;
1475 PG_RETURN_INTERVAL_P(result);
1479 * Convert interval to time data type.
1481 * This is defined as producing the fractional-day portion of the interval.
1482 * Therefore, we can just ignore the months field. It is not real clear
1483 * what to do with negative intervals, but we choose to subtract the floor,
1484 * so that, say, '-2 hours' becomes '22:00:00'.
1487 interval_time(PG_FUNCTION_ARGS)
1489 Interval *span = PG_GETARG_INTERVAL_P(0);
1492 #ifdef HAVE_INT64_TIMESTAMP
1495 result = span->time;
1496 if (result >= USECS_PER_DAY)
1498 days = result / USECS_PER_DAY;
1499 result -= days * USECS_PER_DAY;
1501 else if (result < 0)
1503 days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
1504 result += days * USECS_PER_DAY;
1507 result = span->time;
1508 if (result >= (double) SECS_PER_DAY || result < 0)
1509 result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
1512 PG_RETURN_TIMEADT(result);
1516 * Subtract two times to produce an interval.
1519 time_mi_time(PG_FUNCTION_ARGS)
1521 TimeADT time1 = PG_GETARG_TIMEADT(0);
1522 TimeADT time2 = PG_GETARG_TIMEADT(1);
1525 result = (Interval *) palloc(sizeof(Interval));
1529 result->time = time1 - time2;
1531 PG_RETURN_INTERVAL_P(result);
1534 /* time_pl_interval()
1535 * Add interval to time.
1538 time_pl_interval(PG_FUNCTION_ARGS)
1540 TimeADT time = PG_GETARG_TIMEADT(0);
1541 Interval *span = PG_GETARG_INTERVAL_P(1);
1544 #ifdef HAVE_INT64_TIMESTAMP
1545 result = time + span->time;
1546 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1547 if (result < INT64CONST(0))
1548 result += USECS_PER_DAY;
1552 result = time + span->time;
1553 TMODULO(result, time1, (double) SECS_PER_DAY);
1555 result += SECS_PER_DAY;
1558 PG_RETURN_TIMEADT(result);
1561 /* time_mi_interval()
1562 * Subtract interval from time.
1565 time_mi_interval(PG_FUNCTION_ARGS)
1567 TimeADT time = PG_GETARG_TIMEADT(0);
1568 Interval *span = PG_GETARG_INTERVAL_P(1);
1571 #ifdef HAVE_INT64_TIMESTAMP
1572 result = time - span->time;
1573 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1574 if (result < INT64CONST(0))
1575 result += USECS_PER_DAY;
1579 result = time - span->time;
1580 TMODULO(result, time1, (double) SECS_PER_DAY);
1582 result += SECS_PER_DAY;
1585 PG_RETURN_TIMEADT(result);
1590 * Extract specified field from time type.
1593 time_part(PG_FUNCTION_ARGS)
1595 text *units = PG_GETARG_TEXT_P(0);
1596 TimeADT time = PG_GETARG_TIMEADT(1);
1602 lowunits = downcase_truncate_identifier(VARDATA(units),
1603 VARSIZE(units) - VARHDRSZ,
1606 type = DecodeUnits(0, lowunits, &val);
1607 if (type == UNKNOWN_FIELD)
1608 type = DecodeSpecial(0, lowunits, &val);
1616 time2tm(time, tm, &fsec);
1621 #ifdef HAVE_INT64_TIMESTAMP
1622 result = tm->tm_sec * USECS_PER_SEC + fsec;
1624 result = (tm->tm_sec + fsec) * 1000000;
1629 #ifdef HAVE_INT64_TIMESTAMP
1630 result = tm->tm_sec * INT64CONST(1000) + fsec / INT64CONST(1000);
1632 result = (tm->tm_sec + fsec) * 1000;
1637 #ifdef HAVE_INT64_TIMESTAMP
1638 result = tm->tm_sec + fsec / USECS_PER_SEC;
1640 result = tm->tm_sec + fsec;
1645 result = tm->tm_min;
1649 result = tm->tm_hour;
1661 case DTK_MILLENNIUM:
1665 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1666 errmsg("\"time\" units \"%s\" not recognized",
1667 DatumGetCString(DirectFunctionCall1(textout,
1668 PointerGetDatum(units))))));
1673 else if (type == RESERV && val == DTK_EPOCH)
1675 #ifdef HAVE_INT64_TIMESTAMP
1676 result = time / 1000000.0;
1684 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1685 errmsg("\"time\" units \"%s\" not recognized",
1686 DatumGetCString(DirectFunctionCall1(textout,
1687 PointerGetDatum(units))))));
1691 PG_RETURN_FLOAT8(result);
1695 /*****************************************************************************
1696 * Time With Time Zone ADT
1697 *****************************************************************************/
1700 * Convert a tm structure to a time data type.
1703 tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
1705 #ifdef HAVE_INT64_TIMESTAMP
1706 result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1707 USECS_PER_SEC) + fsec;
1709 result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1717 timetz_in(PG_FUNCTION_ARGS)
1719 char *str = PG_GETARG_CSTRING(0);
1722 Oid typelem = PG_GETARG_OID(1);
1724 int32 typmod = PG_GETARG_INT32(2);
1732 char workbuf[MAXDATELEN + 1];
1733 char *field[MAXDATEFIELDS];
1735 int ftype[MAXDATEFIELDS];
1737 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1738 field, ftype, MAXDATEFIELDS, &nf);
1740 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1742 DateTimeParseError(dterr, str, "time with time zone");
1744 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1745 tm2timetz(tm, fsec, tz, result);
1746 AdjustTimeForTypmod(&(result->time), typmod);
1748 PG_RETURN_TIMETZADT_P(result);
1752 timetz_out(PG_FUNCTION_ARGS)
1754 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1760 char buf[MAXDATELEN + 1];
1762 timetz2tm(time, tm, &fsec, &tz);
1763 EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
1765 result = pstrdup(buf);
1766 PG_RETURN_CSTRING(result);
1770 * timetz_recv - converts external binary format to timetz
1773 timetz_recv(PG_FUNCTION_ARGS)
1775 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1778 Oid typelem = PG_GETARG_OID(1);
1780 int32 typmod = PG_GETARG_INT32(2);
1783 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1785 #ifdef HAVE_INT64_TIMESTAMP
1786 result->time = pq_getmsgint64(buf);
1788 result->time = pq_getmsgfloat8(buf);
1790 result->zone = pq_getmsgint(buf, sizeof(result->zone));
1792 AdjustTimeForTypmod(&(result->time), typmod);
1794 PG_RETURN_TIMETZADT_P(result);
1798 * timetz_send - converts timetz to binary format
1801 timetz_send(PG_FUNCTION_ARGS)
1803 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1806 pq_begintypsend(&buf);
1807 #ifdef HAVE_INT64_TIMESTAMP
1808 pq_sendint64(&buf, time->time);
1810 pq_sendfloat8(&buf, time->time);
1812 pq_sendint(&buf, time->zone, sizeof(time->zone));
1813 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1817 timetztypmodin(PG_FUNCTION_ARGS)
1819 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1821 PG_RETURN_INT32(anytime_typmodin(true, ta));
1825 timetztypmodout(PG_FUNCTION_ARGS)
1827 int32 typmod = PG_GETARG_INT32(0);
1829 PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
1834 * Convert TIME WITH TIME ZONE data type to POSIX time structure.
1837 timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
1839 #ifdef HAVE_INT64_TIMESTAMP
1840 int64 trem = time->time;
1842 tm->tm_hour = trem / USECS_PER_HOUR;
1843 trem -= tm->tm_hour * USECS_PER_HOUR;
1844 tm->tm_min = trem / USECS_PER_MINUTE;
1845 trem -= tm->tm_min * USECS_PER_MINUTE;
1846 tm->tm_sec = trem / USECS_PER_SEC;
1847 *fsec = trem - tm->tm_sec * USECS_PER_SEC;
1849 double trem = time->time;
1852 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
1853 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1854 TMODULO(trem, tm->tm_sec, 1.0);
1855 trem = TIMEROUND(trem);
1856 /* roundoff may need to propagate to higher-order fields */
1859 trem = ceil(time->time);
1872 * Adjust time type for specified scale factor.
1873 * Used by PostgreSQL type system to stuff columns.
1876 timetz_scale(PG_FUNCTION_ARGS)
1878 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1879 int32 typmod = PG_GETARG_INT32(1);
1882 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1884 result->time = time->time;
1885 result->zone = time->zone;
1887 AdjustTimeForTypmod(&(result->time), typmod);
1889 PG_RETURN_TIMETZADT_P(result);
1894 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
1896 /* Primary sort is by true (GMT-equivalent) time */
1897 #ifdef HAVE_INT64_TIMESTAMP
1901 t1 = time1->time + (time1->zone * USECS_PER_SEC);
1902 t2 = time2->time + (time2->zone * USECS_PER_SEC);
1907 t1 = time1->time + time1->zone;
1908 t2 = time2->time + time2->zone;
1917 * If same GMT time, sort by timezone; we only want to say that two
1918 * timetz's are equal if both the time and zone parts are equal.
1920 if (time1->zone > time2->zone)
1922 if (time1->zone < time2->zone)
1929 timetz_eq(PG_FUNCTION_ARGS)
1931 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1932 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1934 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
1938 timetz_ne(PG_FUNCTION_ARGS)
1940 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1941 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1943 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
1947 timetz_lt(PG_FUNCTION_ARGS)
1949 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1950 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1952 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
1956 timetz_le(PG_FUNCTION_ARGS)
1958 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1959 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1961 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
1965 timetz_gt(PG_FUNCTION_ARGS)
1967 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1968 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1970 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
1974 timetz_ge(PG_FUNCTION_ARGS)
1976 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1977 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1979 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
1983 timetz_cmp(PG_FUNCTION_ARGS)
1985 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1986 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1988 PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
1992 timetz_hash(PG_FUNCTION_ARGS)
1994 TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
1998 * To avoid any problems with padding bytes in the struct, we figure the
1999 * field hashes separately and XOR them. This also provides a convenient
2000 * framework for dealing with the fact that the time field might be either
2003 #ifdef HAVE_INT64_TIMESTAMP
2004 thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
2005 Int64GetDatumFast(key->time)));
2007 thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8,
2008 Float8GetDatumFast(key->time)));
2010 thash ^= DatumGetUInt32(hash_uint32(key->zone));
2011 PG_RETURN_UINT32(thash);
2015 timetz_larger(PG_FUNCTION_ARGS)
2017 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2018 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2021 if (timetz_cmp_internal(time1, time2) > 0)
2025 PG_RETURN_TIMETZADT_P(result);
2029 timetz_smaller(PG_FUNCTION_ARGS)
2031 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2032 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2035 if (timetz_cmp_internal(time1, time2) < 0)
2039 PG_RETURN_TIMETZADT_P(result);
2042 /* timetz_pl_interval()
2043 * Add interval to timetz.
2046 timetz_pl_interval(PG_FUNCTION_ARGS)
2048 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2049 Interval *span = PG_GETARG_INTERVAL_P(1);
2052 #ifndef HAVE_INT64_TIMESTAMP
2056 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2058 #ifdef HAVE_INT64_TIMESTAMP
2059 result->time = time->time + span->time;
2060 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2061 if (result->time < INT64CONST(0))
2062 result->time += USECS_PER_DAY;
2064 result->time = time->time + span->time;
2065 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2066 if (result->time < 0)
2067 result->time += SECS_PER_DAY;
2070 result->zone = time->zone;
2072 PG_RETURN_TIMETZADT_P(result);
2075 /* timetz_mi_interval()
2076 * Subtract interval from timetz.
2079 timetz_mi_interval(PG_FUNCTION_ARGS)
2081 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2082 Interval *span = PG_GETARG_INTERVAL_P(1);
2085 #ifndef HAVE_INT64_TIMESTAMP
2089 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2091 #ifdef HAVE_INT64_TIMESTAMP
2092 result->time = time->time - span->time;
2093 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2094 if (result->time < INT64CONST(0))
2095 result->time += USECS_PER_DAY;
2097 result->time = time->time - span->time;
2098 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2099 if (result->time < 0)
2100 result->time += SECS_PER_DAY;
2103 result->zone = time->zone;
2105 PG_RETURN_TIMETZADT_P(result);
2108 /* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
2110 * Algorithm is per SQL92 spec. This is much harder than you'd think
2111 * because the spec requires us to deliver a non-null answer in some cases
2112 * where some of the inputs are null.
2115 overlaps_timetz(PG_FUNCTION_ARGS)
2118 * The arguments are TimeTzADT *, but we leave them as generic Datums for
2119 * convenience of notation --- and to avoid dereferencing nulls.
2121 Datum ts1 = PG_GETARG_DATUM(0);
2122 Datum te1 = PG_GETARG_DATUM(1);
2123 Datum ts2 = PG_GETARG_DATUM(2);
2124 Datum te2 = PG_GETARG_DATUM(3);
2125 bool ts1IsNull = PG_ARGISNULL(0);
2126 bool te1IsNull = PG_ARGISNULL(1);
2127 bool ts2IsNull = PG_ARGISNULL(2);
2128 bool te2IsNull = PG_ARGISNULL(3);
2130 #define TIMETZ_GT(t1,t2) \
2131 DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2132 #define TIMETZ_LT(t1,t2) \
2133 DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2136 * If both endpoints of interval 1 are null, the result is null (unknown).
2137 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2138 * take ts1 as the lesser endpoint.
2144 /* swap null for non-null */
2148 else if (!te1IsNull)
2150 if (TIMETZ_GT(ts1, te1))
2159 /* Likewise for interval 2. */
2164 /* swap null for non-null */
2168 else if (!te2IsNull)
2170 if (TIMETZ_GT(ts2, te2))
2180 * At this point neither ts1 nor ts2 is null, so we can consider three
2181 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2183 if (TIMETZ_GT(ts1, ts2))
2186 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2187 * in the presence of nulls it's not quite completely so.
2191 if (TIMETZ_LT(ts1, te2))
2192 PG_RETURN_BOOL(true);
2197 * If te1 is not null then we had ts1 <= te1 above, and we just found
2198 * ts1 >= te2, hence te1 >= te2.
2200 PG_RETURN_BOOL(false);
2202 else if (TIMETZ_LT(ts1, ts2))
2204 /* This case is ts2 < te1 OR te2 < te1 */
2207 if (TIMETZ_LT(ts2, te1))
2208 PG_RETURN_BOOL(true);
2213 * If te2 is not null then we had ts2 <= te2 above, and we just found
2214 * ts2 >= te1, hence te2 >= te1.
2216 PG_RETURN_BOOL(false);
2221 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2222 * rather silly way of saying "true if both are nonnull, else null".
2224 if (te1IsNull || te2IsNull)
2226 PG_RETURN_BOOL(true);
2235 timetz_time(PG_FUNCTION_ARGS)
2237 TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
2240 /* swallow the time zone and just return the time */
2241 result = timetz->time;
2243 PG_RETURN_TIMEADT(result);
2248 time_timetz(PG_FUNCTION_ARGS)
2250 TimeADT time = PG_GETARG_TIMEADT(0);
2257 GetCurrentDateTime(tm);
2258 time2tm(time, tm, &fsec);
2259 tz = DetermineTimeZoneOffset(tm, session_timezone);
2261 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2263 result->time = time;
2266 PG_RETURN_TIMETZADT_P(result);
2270 /* timestamptz_timetz()
2271 * Convert timestamp to timetz data type.
2274 timestamptz_timetz(PG_FUNCTION_ARGS)
2276 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2284 if (TIMESTAMP_NOT_FINITE(timestamp))
2287 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2289 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2290 errmsg("timestamp out of range")));
2292 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2294 tm2timetz(tm, fsec, tz, result);
2296 PG_RETURN_TIMETZADT_P(result);
2300 /* datetimetz_timestamptz()
2301 * Convert date and timetz to timestamp with time zone data type.
2302 * Timestamp is stored in GMT, so add the time zone
2303 * stored with the timetz to the result.
2304 * - thomas 2000-03-10
2307 datetimetz_timestamptz(PG_FUNCTION_ARGS)
2309 DateADT date = PG_GETARG_DATEADT(0);
2310 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2313 #ifdef HAVE_INT64_TIMESTAMP
2314 result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2316 result = date * (double) SECS_PER_DAY + time->time + time->zone;
2319 PG_RETURN_TIMESTAMP(result);
2324 * Extract specified field from time type.
2327 timetz_part(PG_FUNCTION_ARGS)
2329 text *units = PG_GETARG_TEXT_P(0);
2330 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2336 lowunits = downcase_truncate_identifier(VARDATA(units),
2337 VARSIZE(units) - VARHDRSZ,
2340 type = DecodeUnits(0, lowunits, &val);
2341 if (type == UNKNOWN_FIELD)
2342 type = DecodeSpecial(0, lowunits, &val);
2352 timetz2tm(time, tm, &fsec, &tz);
2362 result /= SECS_PER_MINUTE;
2363 FMODULO(result, dummy, (double) SECS_PER_MINUTE);
2368 FMODULO(dummy, result, (double) SECS_PER_HOUR);
2372 #ifdef HAVE_INT64_TIMESTAMP
2373 result = tm->tm_sec * USECS_PER_SEC + fsec;
2375 result = (tm->tm_sec + fsec) * 1000000;
2380 #ifdef HAVE_INT64_TIMESTAMP
2381 result = tm->tm_sec * INT64CONST(1000) + fsec / INT64CONST(1000);
2383 result = (tm->tm_sec + fsec) * 1000;
2388 #ifdef HAVE_INT64_TIMESTAMP
2389 result = tm->tm_sec + fsec / USECS_PER_SEC;
2391 result = tm->tm_sec + fsec;
2396 result = tm->tm_min;
2400 result = tm->tm_hour;
2409 case DTK_MILLENNIUM:
2412 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2413 errmsg("\"time with time zone\" units \"%s\" not recognized",
2414 DatumGetCString(DirectFunctionCall1(textout,
2415 PointerGetDatum(units))))));
2420 else if (type == RESERV && val == DTK_EPOCH)
2422 #ifdef HAVE_INT64_TIMESTAMP
2423 result = time->time / 1000000.0 + time->zone;
2425 result = time->time + time->zone;
2431 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2432 errmsg("\"time with time zone\" units \"%s\" not recognized",
2433 DatumGetCString(DirectFunctionCall1(textout,
2434 PointerGetDatum(units))))));
2439 PG_RETURN_FLOAT8(result);
2443 * Encode time with time zone type with specified time zone.
2444 * Applies DST rules as of the current date.
2447 timetz_zone(PG_FUNCTION_ARGS)
2449 text *zone = PG_GETARG_TEXT_P(0);
2450 TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
2453 char tzname[TZ_STRLEN_MAX + 1];
2458 * Look up the requested timezone. First we look in the timezone database
2459 * (to handle cases like "America/New_York"), and if that fails, we look
2460 * in the date token table (to handle cases like "EST").
2462 len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
2463 memcpy(tzname, VARDATA(zone), len);
2465 tzp = pg_tzset(tzname);
2468 /* Get the offset-from-GMT that is valid today for the selected zone */
2473 tm = pg_localtime(&now, tzp);
2474 tz = -tm->tm_gmtoff;
2482 lowzone = downcase_truncate_identifier(VARDATA(zone),
2483 VARSIZE(zone) - VARHDRSZ,
2485 type = DecodeSpecial(0, lowzone, &val);
2487 if (type == TZ || type == DTZ)
2492 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2493 errmsg("time zone \"%s\" not recognized", tzname)));
2494 tz = 0; /* keep compiler quiet */
2498 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2500 #ifdef HAVE_INT64_TIMESTAMP
2501 result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
2502 while (result->time < INT64CONST(0))
2503 result->time += USECS_PER_DAY;
2504 while (result->time >= USECS_PER_DAY)
2505 result->time -= USECS_PER_DAY;
2507 result->time = t->time + (t->zone - tz);
2508 while (result->time < 0)
2509 result->time += SECS_PER_DAY;
2510 while (result->time >= SECS_PER_DAY)
2511 result->time -= SECS_PER_DAY;
2516 PG_RETURN_TIMETZADT_P(result);
2520 * Encode time with time zone type with specified time interval as time zone.
2523 timetz_izone(PG_FUNCTION_ARGS)
2525 Interval *zone = PG_GETARG_INTERVAL_P(0);
2526 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2530 if (zone->month != 0)
2532 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2533 errmsg("\"interval\" time zone \"%s\" not valid",
2534 DatumGetCString(DirectFunctionCall1(interval_out,
2535 PointerGetDatum(zone))))));
2537 #ifdef HAVE_INT64_TIMESTAMP
2538 tz = -(zone->time / USECS_PER_SEC);
2543 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2545 #ifdef HAVE_INT64_TIMESTAMP
2546 result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
2547 while (result->time < INT64CONST(0))
2548 result->time += USECS_PER_DAY;
2549 while (result->time >= USECS_PER_DAY)
2550 result->time -= USECS_PER_DAY;
2552 result->time = time->time + (time->zone - tz);
2553 while (result->time < 0)
2554 result->time += SECS_PER_DAY;
2555 while (result->time >= SECS_PER_DAY)
2556 result->time -= SECS_PER_DAY;
2561 PG_RETURN_TIMETZADT_P(result);