1 /*-------------------------------------------------------------------------
4 * implements DATE and TIME data types specified in SQL-92 standard
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994-5, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.62 2001/10/18 17:30:15 thomas Exp $
13 *-------------------------------------------------------------------------
23 #include "access/hash.h"
24 #include "miscadmin.h"
25 #include "utils/builtins.h"
26 #include "utils/date.h"
27 #include "utils/nabstime.h"
28 #include "utils/timestamp.h"
31 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
33 /*****************************************************************************
35 *****************************************************************************/
39 * Given date text string, convert to internal date format.
42 date_in(PG_FUNCTION_ARGS)
44 char *str = PG_GETARG_CSTRING(0);
52 char *field[MAXDATEFIELDS];
53 int ftype[MAXDATEFIELDS];
54 char lowstr[MAXDATELEN + 1];
56 if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
57 || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
58 elog(ERROR, "Bad date external representation '%s'", str);
66 elog(ERROR, "Date CURRENT no longer supported"
67 "\n\tdate_in() internal coding error");
76 elog(ERROR, "Unrecognized date external representation '%s'", str);
79 date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
81 PG_RETURN_DATEADT(date);
85 * Given internal format date, convert to text string.
88 date_out(PG_FUNCTION_ARGS)
90 DateADT date = PG_GETARG_DATEADT(0);
94 char buf[MAXDATELEN + 1];
96 j2date((date + date2j(2000, 1, 1)),
97 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
99 EncodeDateOnly(tm, DateStyle, buf);
101 result = pstrdup(buf);
102 PG_RETURN_CSTRING(result);
106 date_eq(PG_FUNCTION_ARGS)
108 DateADT dateVal1 = PG_GETARG_DATEADT(0);
109 DateADT dateVal2 = PG_GETARG_DATEADT(1);
111 PG_RETURN_BOOL(dateVal1 == dateVal2);
115 date_ne(PG_FUNCTION_ARGS)
117 DateADT dateVal1 = PG_GETARG_DATEADT(0);
118 DateADT dateVal2 = PG_GETARG_DATEADT(1);
120 PG_RETURN_BOOL(dateVal1 != dateVal2);
124 date_lt(PG_FUNCTION_ARGS)
126 DateADT dateVal1 = PG_GETARG_DATEADT(0);
127 DateADT dateVal2 = PG_GETARG_DATEADT(1);
129 PG_RETURN_BOOL(dateVal1 < dateVal2);
133 date_le(PG_FUNCTION_ARGS)
135 DateADT dateVal1 = PG_GETARG_DATEADT(0);
136 DateADT dateVal2 = PG_GETARG_DATEADT(1);
138 PG_RETURN_BOOL(dateVal1 <= dateVal2);
142 date_gt(PG_FUNCTION_ARGS)
144 DateADT dateVal1 = PG_GETARG_DATEADT(0);
145 DateADT dateVal2 = PG_GETARG_DATEADT(1);
147 PG_RETURN_BOOL(dateVal1 > dateVal2);
151 date_ge(PG_FUNCTION_ARGS)
153 DateADT dateVal1 = PG_GETARG_DATEADT(0);
154 DateADT dateVal2 = PG_GETARG_DATEADT(1);
156 PG_RETURN_BOOL(dateVal1 >= dateVal2);
160 date_cmp(PG_FUNCTION_ARGS)
162 DateADT dateVal1 = PG_GETARG_DATEADT(0);
163 DateADT dateVal2 = PG_GETARG_DATEADT(1);
165 if (dateVal1 < dateVal2)
167 else if (dateVal1 > dateVal2)
173 date_larger(PG_FUNCTION_ARGS)
175 DateADT dateVal1 = PG_GETARG_DATEADT(0);
176 DateADT dateVal2 = PG_GETARG_DATEADT(1);
178 PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
182 date_smaller(PG_FUNCTION_ARGS)
184 DateADT dateVal1 = PG_GETARG_DATEADT(0);
185 DateADT dateVal2 = PG_GETARG_DATEADT(1);
187 PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
190 /* Compute difference between two dates in days.
193 date_mi(PG_FUNCTION_ARGS)
195 DateADT dateVal1 = PG_GETARG_DATEADT(0);
196 DateADT dateVal2 = PG_GETARG_DATEADT(1);
198 PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
201 /* Add a number of days to a date, giving a new date.
202 * Must handle both positive and negative numbers of days.
205 date_pli(PG_FUNCTION_ARGS)
207 DateADT dateVal = PG_GETARG_DATEADT(0);
208 int32 days = PG_GETARG_INT32(1);
210 PG_RETURN_DATEADT(dateVal + days);
213 /* Subtract a number of days from a date, giving a new date.
216 date_mii(PG_FUNCTION_ARGS)
218 DateADT dateVal = PG_GETARG_DATEADT(0);
219 int32 days = PG_GETARG_INT32(1);
221 PG_RETURN_DATEADT(dateVal - days);
225 * Convert date to timestamp data type.
228 date_timestamp(PG_FUNCTION_ARGS)
230 DateADT dateVal = PG_GETARG_DATEADT(0);
233 /* date is days since 2000, timestamp is seconds since same... */
234 result = dateVal * 86400.0;
236 PG_RETURN_TIMESTAMP(result);
241 * Convert timestamp to date data type.
244 timestamp_date(PG_FUNCTION_ARGS)
246 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
252 if (TIMESTAMP_NOT_FINITE(timestamp))
255 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
256 elog(ERROR, "Unable to convert timestamp to date");
258 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
260 PG_RETURN_DATEADT(result);
264 /* date_timestamptz()
265 * Convert date to timestamp with time zone data type.
268 date_timestamptz(PG_FUNCTION_ARGS)
270 DateADT dateVal = PG_GETARG_DATEADT(0);
276 j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
278 if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
280 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
290 elog(ERROR, "Unable to convert date to tm");
292 result = utime + ((date2j(1970, 1, 1) - date2j(2000, 1, 1)) * 86400.0);
294 result = dateVal * 86400.0 + CTimeZone;
299 /* Outside of range for timezone support, so assume UTC */
300 result = dateVal * 86400.0;
303 PG_RETURN_TIMESTAMP(result);
307 /* timestamptz_date()
308 * Convert timestamp with time zone to date data type.
311 timestamptz_date(PG_FUNCTION_ARGS)
313 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
321 if (TIMESTAMP_NOT_FINITE(timestamp))
324 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
325 elog(ERROR, "Unable to convert timestamp to date");
327 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
329 PG_RETURN_DATEADT(result);
334 * Convert abstime to date data type.
337 abstime_date(PG_FUNCTION_ARGS)
339 AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
347 case INVALID_ABSTIME:
348 case NOSTART_ABSTIME:
350 elog(ERROR, "Unable to convert reserved abstime value to date");
353 * pretend to drop through to make compiler think that result
358 abstime2tm(abstime, &tz, tm, NULL);
359 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
363 PG_RETURN_DATEADT(result);
368 * Convert date to text data type.
371 date_text(PG_FUNCTION_ARGS)
373 /* Input is a Date, but may as well leave it in Datum form */
374 Datum date = PG_GETARG_DATUM(0);
379 str = DatumGetCString(DirectFunctionCall1(date_out, date));
381 len = (strlen(str) + VARHDRSZ);
383 result = palloc(len);
385 VARATT_SIZEP(result) = len;
386 memmove(VARDATA(result), str, (len - VARHDRSZ));
390 PG_RETURN_TEXT_P(result);
395 * Convert text string to date.
396 * Text type is not null terminated, so use temporary string
397 * then call the standard input routine.
400 text_date(PG_FUNCTION_ARGS)
402 text *str = PG_GETARG_TEXT_P(0);
406 dstr[MAXDATELEN + 1];
408 if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
409 elog(ERROR, "Bad date external representation (too long)");
413 for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
417 return DirectFunctionCall1(date_in,
418 CStringGetDatum(dstr));
422 /*****************************************************************************
424 *****************************************************************************/
427 time_in(PG_FUNCTION_ARGS)
429 char *str = PG_GETARG_CSTRING(0);
431 Oid typelem = PG_GETARG_OID(1);
433 int32 typmod = PG_GETARG_INT32(2);
439 char lowstr[MAXDATELEN + 1];
440 char *field[MAXDATEFIELDS];
442 int ftype[MAXDATEFIELDS];
444 if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
445 || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, NULL) != 0))
446 elog(ERROR, "Bad time external representation '%s'", str);
448 result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
450 AdjustTimeForTypmod(&result, typmod);
452 PG_RETURN_TIMEADT(result);
456 time_out(PG_FUNCTION_ARGS)
458 TimeADT time = PG_GETARG_TIMEADT(0);
464 char buf[MAXDATELEN + 1];
467 TMODULO(trem, tm->tm_hour, 3600e0);
468 TMODULO(trem, tm->tm_min, 60e0);
469 TMODULO(trem, tm->tm_sec, 1e0);
472 EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
474 result = pstrdup(buf);
475 PG_RETURN_CSTRING(result);
479 * Adjust time type for specified scale factor.
480 * Used by PostgreSQL type system to stuff columns.
483 time_scale(PG_FUNCTION_ARGS)
485 TimeADT time = PG_GETARG_TIMEADT(0);
486 int32 typmod = PG_GETARG_INT32(1);
490 AdjustTimeForTypmod(&result, typmod);
492 PG_RETURN_TIMEADT(result);
496 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
498 if ((typmod >= 0) && (typmod <= 13))
500 static double TimeScale = 1;
501 static int32 TimeTypmod = 0;
503 if (typmod != TimeTypmod)
505 TimeScale = pow(10.0, typmod);
509 *time = (rint(((double) *time)*TimeScale)/TimeScale);
520 time_eq(PG_FUNCTION_ARGS)
522 TimeADT time1 = PG_GETARG_TIMEADT(0);
523 TimeADT time2 = PG_GETARG_TIMEADT(1);
525 PG_RETURN_BOOL(time1 == time2);
529 time_ne(PG_FUNCTION_ARGS)
531 TimeADT time1 = PG_GETARG_TIMEADT(0);
532 TimeADT time2 = PG_GETARG_TIMEADT(1);
534 PG_RETURN_BOOL(time1 != time2);
538 time_lt(PG_FUNCTION_ARGS)
540 TimeADT time1 = PG_GETARG_TIMEADT(0);
541 TimeADT time2 = PG_GETARG_TIMEADT(1);
543 PG_RETURN_BOOL(time1 < time2);
547 time_le(PG_FUNCTION_ARGS)
549 TimeADT time1 = PG_GETARG_TIMEADT(0);
550 TimeADT time2 = PG_GETARG_TIMEADT(1);
552 PG_RETURN_BOOL(time1 <= time2);
556 time_gt(PG_FUNCTION_ARGS)
558 TimeADT time1 = PG_GETARG_TIMEADT(0);
559 TimeADT time2 = PG_GETARG_TIMEADT(1);
561 PG_RETURN_BOOL(time1 > time2);
565 time_ge(PG_FUNCTION_ARGS)
567 TimeADT time1 = PG_GETARG_TIMEADT(0);
568 TimeADT time2 = PG_GETARG_TIMEADT(1);
570 PG_RETURN_BOOL(time1 >= time2);
574 time_cmp(PG_FUNCTION_ARGS)
576 TimeADT time1 = PG_GETARG_TIMEADT(0);
577 TimeADT time2 = PG_GETARG_TIMEADT(1);
587 time_larger(PG_FUNCTION_ARGS)
589 TimeADT time1 = PG_GETARG_TIMEADT(0);
590 TimeADT time2 = PG_GETARG_TIMEADT(1);
592 PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
596 time_smaller(PG_FUNCTION_ARGS)
598 TimeADT time1 = PG_GETARG_TIMEADT(0);
599 TimeADT time2 = PG_GETARG_TIMEADT(1);
601 PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
604 /* overlaps_time() --- implements the SQL92 OVERLAPS operator.
606 * Algorithm is per SQL92 spec. This is much harder than you'd think
607 * because the spec requires us to deliver a non-null answer in some cases
608 * where some of the inputs are null.
611 overlaps_time(PG_FUNCTION_ARGS)
615 * The arguments are TimeADT, but we leave them as generic Datums to
616 * avoid dereferencing nulls (TimeADT is pass-by-reference!)
618 Datum ts1 = PG_GETARG_DATUM(0);
619 Datum te1 = PG_GETARG_DATUM(1);
620 Datum ts2 = PG_GETARG_DATUM(2);
621 Datum te2 = PG_GETARG_DATUM(3);
622 bool ts1IsNull = PG_ARGISNULL(0);
623 bool te1IsNull = PG_ARGISNULL(1);
624 bool ts2IsNull = PG_ARGISNULL(2);
625 bool te2IsNull = PG_ARGISNULL(3);
627 #define TIMEADT_GT(t1,t2) \
628 (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
629 #define TIMEADT_LT(t1,t2) \
630 (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
633 * If both endpoints of interval 1 are null, the result is null
634 * (unknown). If just one endpoint is null, take ts1 as the non-null
635 * one. Otherwise, take ts1 as the lesser endpoint.
641 /* swap null for non-null */
647 if (TIMEADT_GT(ts1, te1))
656 /* Likewise for interval 2. */
661 /* swap null for non-null */
667 if (TIMEADT_GT(ts2, te2))
677 * At this point neither ts1 nor ts2 is null, so we can consider three
678 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
680 if (TIMEADT_GT(ts1, ts2))
684 * This case is ts1 < te2 OR te1 < te2, which may look redundant
685 * but in the presence of nulls it's not quite completely so.
689 if (TIMEADT_LT(ts1, te2))
690 PG_RETURN_BOOL(true);
695 * If te1 is not null then we had ts1 <= te1 above, and we just
696 * found ts1 >= te2, hence te1 >= te2.
698 PG_RETURN_BOOL(false);
700 else if (TIMEADT_LT(ts1, ts2))
702 /* This case is ts2 < te1 OR te2 < te1 */
705 if (TIMEADT_LT(ts2, te1))
706 PG_RETURN_BOOL(true);
711 * If te2 is not null then we had ts2 <= te2 above, and we just
712 * found ts2 >= te1, hence te2 >= te1.
714 PG_RETURN_BOOL(false);
720 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
721 * rather silly way of saying "true if both are nonnull, else
724 if (te1IsNull || te2IsNull)
726 PG_RETURN_BOOL(true);
734 * Convert timestamp to time data type.
737 timestamp_time(PG_FUNCTION_ARGS)
739 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
745 if (TIMESTAMP_NOT_FINITE(timestamp))
748 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
749 elog(ERROR, "Unable to convert timestamp to date");
751 result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
753 PG_RETURN_TIMEADT(result);
756 /* datetime_timestamp()
757 * Convert date and time to timestamp data type.
760 datetime_timestamp(PG_FUNCTION_ARGS)
762 DateADT date = PG_GETARG_DATEADT(0);
763 TimeADT time = PG_GETARG_TIMEADT(1);
766 result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
767 DateADTGetDatum(date)));
770 PG_RETURN_TIMESTAMP(result);
774 * Convert time to interval data type.
777 time_interval(PG_FUNCTION_ARGS)
779 TimeADT time = PG_GETARG_TIMEADT(0);
782 result = (Interval *) palloc(sizeof(Interval));
787 PG_RETURN_INTERVAL_P(result);
791 * Convert interval to time data type.
794 interval_time(PG_FUNCTION_ARGS)
796 Interval *span = PG_GETARG_INTERVAL_P(0);
801 TMODULO(result, span1.time, 86400e0);
803 PG_RETURN_TIMEADT(result);
807 * Subtract two times to produce an interval.
810 time_mi_time(PG_FUNCTION_ARGS)
812 TimeADT time1 = PG_GETARG_TIMEADT(0);
813 TimeADT time2 = PG_GETARG_TIMEADT(1);
816 result = (Interval *) palloc(sizeof(Interval));
818 result->time = time2 - time1;
821 PG_RETURN_INTERVAL_P(result);
824 /* time_pl_interval()
825 * Add interval to time.
828 time_pl_interval(PG_FUNCTION_ARGS)
830 TimeADT time = PG_GETARG_TIMEADT(0);
831 Interval *span = PG_GETARG_INTERVAL_P(1);
835 result = (time + span->time);
836 TMODULO(result, time1, 86400e0);
840 PG_RETURN_TIMEADT(result);
843 /* time_mi_interval()
844 * Subtract interval from time.
847 time_mi_interval(PG_FUNCTION_ARGS)
849 TimeADT time = PG_GETARG_TIMEADT(0);
850 Interval *span = PG_GETARG_INTERVAL_P(1);
854 result = (time - span->time);
855 TMODULO(result, time1, 86400e0);
859 PG_RETURN_TIMEADT(result);
862 /* interval_pl_time()
863 * Add time to interval.
866 interval_pl_time(PG_FUNCTION_ARGS)
868 Datum span = PG_GETARG_DATUM(0);
869 Datum time = PG_GETARG_DATUM(1);
871 return DirectFunctionCall2(time_pl_interval, time, span);
876 * Convert time to text data type.
879 time_text(PG_FUNCTION_ARGS)
881 /* Input is a Time, but may as well leave it in Datum form */
882 Datum time = PG_GETARG_DATUM(0);
887 str = DatumGetCString(DirectFunctionCall1(time_out, time));
889 len = (strlen(str) + VARHDRSZ);
891 result = palloc(len);
893 VARATT_SIZEP(result) = len;
894 memmove(VARDATA(result), str, (len - VARHDRSZ));
898 PG_RETURN_TEXT_P(result);
903 * Convert text string to time.
904 * Text type is not null terminated, so use temporary string
905 * then call the standard input routine.
908 text_time(PG_FUNCTION_ARGS)
910 text *str = PG_GETARG_TEXT_P(0);
914 dstr[MAXDATELEN + 1];
916 if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
917 elog(ERROR, "Bad time external representation (too long)");
921 for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
925 return DirectFunctionCall3(time_in,
926 CStringGetDatum(dstr),
927 ObjectIdGetDatum(InvalidOid),
932 /*****************************************************************************
933 * Time With Time Zone ADT
934 *****************************************************************************/
937 timetz_in(PG_FUNCTION_ARGS)
939 char *str = PG_GETARG_CSTRING(0);
941 Oid typelem = PG_GETARG_OID(1);
943 int32 typmod = PG_GETARG_INT32(2);
950 char lowstr[MAXDATELEN + 1];
951 char *field[MAXDATEFIELDS];
953 int ftype[MAXDATEFIELDS];
955 if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
956 || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
957 elog(ERROR, "Bad time external representation '%s'", str);
959 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
961 result->time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
964 AdjustTimeForTypmod(&(result->time), typmod);
966 PG_RETURN_TIMETZADT_P(result);
970 timetz_out(PG_FUNCTION_ARGS)
972 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
979 char buf[MAXDATELEN + 1];
982 TMODULO(trem, tm->tm_hour, 3600e0);
983 TMODULO(trem, tm->tm_min, 60e0);
984 TMODULO(trem, tm->tm_sec, 1e0);
989 EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
991 result = pstrdup(buf);
992 PG_RETURN_CSTRING(result);
996 * Adjust time type for specified scale factor.
997 * Used by PostgreSQL type system to stuff columns.
1000 timetz_scale(PG_FUNCTION_ARGS)
1002 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1003 int32 typmod = PG_GETARG_INT32(1);
1006 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1008 result->time = time->time;
1009 result->zone = time->zone;
1011 AdjustTimeForTypmod(&(result->time), typmod);
1013 PG_RETURN_TIMETZADT_P(result);
1018 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
1023 /* Primary sort is by true (GMT-equivalent) time */
1024 t1 = time1->time + time1->zone;
1025 t2 = time2->time + time2->zone;
1033 * If same GMT time, sort by timezone; we only want to say that two
1034 * timetz's are equal if both the time and zone parts are equal.
1036 if (time1->zone > time2->zone)
1038 if (time1->zone < time2->zone)
1045 timetz_eq(PG_FUNCTION_ARGS)
1047 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1048 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1050 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
1054 timetz_ne(PG_FUNCTION_ARGS)
1056 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1057 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1059 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
1063 timetz_lt(PG_FUNCTION_ARGS)
1065 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1066 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1068 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
1072 timetz_le(PG_FUNCTION_ARGS)
1074 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1075 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1077 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
1081 timetz_gt(PG_FUNCTION_ARGS)
1083 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1084 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1086 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
1090 timetz_ge(PG_FUNCTION_ARGS)
1092 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1093 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1095 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
1099 timetz_cmp(PG_FUNCTION_ARGS)
1101 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1102 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1104 PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
1108 * timetz, being an unusual size, needs a specialized hash function.
1111 timetz_hash(PG_FUNCTION_ARGS)
1113 TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
1116 * Specify hash length as sizeof(double) + sizeof(int4), not as
1117 * sizeof(TimeTzADT), so that any garbage pad bytes in the structure
1118 * won't be included in the hash!
1120 return hash_any((char *) key, sizeof(double) + sizeof(int4));
1124 timetz_larger(PG_FUNCTION_ARGS)
1126 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1127 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1129 if (DatumGetBool(DirectFunctionCall2(timetz_gt,
1130 TimeTzADTPGetDatum(time1),
1131 TimeTzADTPGetDatum(time2))))
1132 PG_RETURN_TIMETZADT_P(time1);
1133 PG_RETURN_TIMETZADT_P(time2);
1137 timetz_smaller(PG_FUNCTION_ARGS)
1139 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1140 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1142 if (DatumGetBool(DirectFunctionCall2(timetz_lt,
1143 TimeTzADTPGetDatum(time1),
1144 TimeTzADTPGetDatum(time2))))
1145 PG_RETURN_TIMETZADT_P(time1);
1146 PG_RETURN_TIMETZADT_P(time2);
1149 /* timetz_pl_interval()
1150 * Add interval to timetz.
1153 timetz_pl_interval(PG_FUNCTION_ARGS)
1155 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1156 Interval *span = PG_GETARG_INTERVAL_P(1);
1160 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1162 result->time = (time->time + span->time);
1163 TMODULO(result->time, time1.time, 86400e0);
1164 if (result->time < 0)
1165 result->time += 86400;
1166 result->zone = time->zone;
1168 PG_RETURN_TIMETZADT_P(result);
1171 /* timetz_mi_interval()
1172 * Subtract interval from timetz.
1175 timetz_mi_interval(PG_FUNCTION_ARGS)
1177 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1178 Interval *span = PG_GETARG_INTERVAL_P(1);
1182 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1184 result->time = (time->time - span->time);
1185 TMODULO(result->time, time1.time, 86400e0);
1186 if (result->time < 0)
1187 result->time += 86400;
1188 result->zone = time->zone;
1190 PG_RETURN_TIMETZADT_P(result);
1193 /* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
1195 * Algorithm is per SQL92 spec. This is much harder than you'd think
1196 * because the spec requires us to deliver a non-null answer in some cases
1197 * where some of the inputs are null.
1200 overlaps_timetz(PG_FUNCTION_ARGS)
1204 * The arguments are TimeTzADT *, but we leave them as generic Datums
1205 * for convenience of notation --- and to avoid dereferencing nulls.
1207 Datum ts1 = PG_GETARG_DATUM(0);
1208 Datum te1 = PG_GETARG_DATUM(1);
1209 Datum ts2 = PG_GETARG_DATUM(2);
1210 Datum te2 = PG_GETARG_DATUM(3);
1211 bool ts1IsNull = PG_ARGISNULL(0);
1212 bool te1IsNull = PG_ARGISNULL(1);
1213 bool ts2IsNull = PG_ARGISNULL(2);
1214 bool te2IsNull = PG_ARGISNULL(3);
1216 #define TIMETZ_GT(t1,t2) \
1217 DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
1218 #define TIMETZ_LT(t1,t2) \
1219 DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
1222 * If both endpoints of interval 1 are null, the result is null
1223 * (unknown). If just one endpoint is null, take ts1 as the non-null
1224 * one. Otherwise, take ts1 as the lesser endpoint.
1230 /* swap null for non-null */
1234 else if (!te1IsNull)
1236 if (TIMETZ_GT(ts1, te1))
1245 /* Likewise for interval 2. */
1250 /* swap null for non-null */
1254 else if (!te2IsNull)
1256 if (TIMETZ_GT(ts2, te2))
1266 * At this point neither ts1 nor ts2 is null, so we can consider three
1267 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1269 if (TIMETZ_GT(ts1, ts2))
1273 * This case is ts1 < te2 OR te1 < te2, which may look redundant
1274 * but in the presence of nulls it's not quite completely so.
1278 if (TIMETZ_LT(ts1, te2))
1279 PG_RETURN_BOOL(true);
1284 * If te1 is not null then we had ts1 <= te1 above, and we just
1285 * found ts1 >= te2, hence te1 >= te2.
1287 PG_RETURN_BOOL(false);
1289 else if (TIMETZ_LT(ts1, ts2))
1291 /* This case is ts2 < te1 OR te2 < te1 */
1294 if (TIMETZ_LT(ts2, te1))
1295 PG_RETURN_BOOL(true);
1300 * If te2 is not null then we had ts2 <= te2 above, and we just
1301 * found ts2 >= te1, hence te2 >= te1.
1303 PG_RETURN_BOOL(false);
1309 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1310 * rather silly way of saying "true if both are nonnull, else
1313 if (te1IsNull || te2IsNull)
1315 PG_RETURN_BOOL(true);
1324 timetz_time(PG_FUNCTION_ARGS)
1326 TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
1329 /* swallow the time zone and just return the time */
1330 result = timetz->time;
1332 PG_RETURN_TIMEADT(result);
1337 time_timetz(PG_FUNCTION_ARGS)
1339 TimeADT time = PG_GETARG_TIMEADT(0);
1346 tz = DetermineLocalTimeZone(tm);
1348 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1350 result->time = time;
1353 PG_RETURN_TIMETZADT_P(result);
1357 /* timestamptz_timetz()
1358 * Convert timestamp to timetz data type.
1361 timestamptz_timetz(PG_FUNCTION_ARGS)
1363 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1371 if (TIMESTAMP_NOT_FINITE(timestamp))
1374 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
1375 elog(ERROR, "Unable to convert timestamp to date");
1377 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1379 result->time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
1382 PG_RETURN_TIMETZADT_P(result);
1386 /* datetimetz_timestamptz()
1387 * Convert date and timetz to timestamp with time zone data type.
1388 * Timestamp is stored in GMT, so add the time zone
1389 * stored with the timetz to the result.
1390 * - thomas 2000-03-10
1393 datetimetz_timestamptz(PG_FUNCTION_ARGS)
1395 DateADT date = PG_GETARG_DATEADT(0);
1396 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
1399 result = date * 86400.0 + time->time + time->zone;
1401 PG_RETURN_TIMESTAMP(result);
1406 * Convert timetz to text data type.
1409 timetz_text(PG_FUNCTION_ARGS)
1411 /* Input is a Timetz, but may as well leave it in Datum form */
1412 Datum timetz = PG_GETARG_DATUM(0);
1417 str = DatumGetCString(DirectFunctionCall1(timetz_out, timetz));
1419 len = (strlen(str) + VARHDRSZ);
1421 result = palloc(len);
1423 VARATT_SIZEP(result) = len;
1424 memmove(VARDATA(result), str, (len - VARHDRSZ));
1428 PG_RETURN_TEXT_P(result);
1433 * Convert text string to timetz.
1434 * Text type is not null terminated, so use temporary string
1435 * then call the standard input routine.
1438 text_timetz(PG_FUNCTION_ARGS)
1440 text *str = PG_GETARG_TEXT_P(0);
1444 dstr[MAXDATELEN + 1];
1446 if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
1447 elog(ERROR, "Bad timetz external representation (too long)");
1451 for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
1455 return DirectFunctionCall3(timetz_in,
1456 CStringGetDatum(dstr),
1457 ObjectIdGetDatum(InvalidOid),
1462 * Extract specified field from time type.
1465 timetz_part(PG_FUNCTION_ARGS)
1467 text *units = PG_GETARG_TEXT_P(0);
1468 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
1475 lowunits[MAXDATELEN + 1];
1477 if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
1478 elog(ERROR, "TIMETZ units '%s' not recognized",
1479 DatumGetCString(DirectFunctionCall1(textout,
1480 PointerGetDatum(units))));
1481 up = VARDATA(units);
1483 for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
1484 *lp++ = tolower((unsigned char) *up++);
1487 type = DecodeUnits(0, lowunits, &val);
1488 if (type == UNKNOWN_FIELD)
1489 type = DecodeSpecial(0, lowunits, &val);
1501 TMODULO(trem, tm->tm_hour, 3600e0);
1502 TMODULO(trem, tm->tm_min, 60e0);
1503 TMODULO(trem, tm->tm_sec, 1e0);
1515 TMODULO(result, dummy, 60e0);
1520 TMODULO(dummy, result, 3600e0);
1524 result = ((tm->tm_sec + fsec) * 1000000);
1528 result = ((tm->tm_sec + fsec) * 1000);
1532 result = (tm->tm_sec + fsec);
1536 result = tm->tm_min;
1540 result = tm->tm_hour;
1549 case DTK_MILLENNIUM:
1551 elog(ERROR, "TIMETZ units '%s' not supported",
1552 DatumGetCString(DirectFunctionCall1(textout,
1553 PointerGetDatum(units))));
1557 else if ((type == RESERV) && (val == DTK_EPOCH))
1559 result = time->time - time->zone;
1563 elog(ERROR, "TIMETZ units '%s' not recognized",
1564 DatumGetCString(DirectFunctionCall1(textout,
1565 PointerGetDatum(units))));
1569 PG_RETURN_FLOAT8(result);
1573 * Encode time with time zone type with specified time zone.
1576 timetz_zone(PG_FUNCTION_ARGS)
1578 text *zone = PG_GETARG_TEXT_P(0);
1579 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
1588 lowzone[MAXDATELEN + 1];
1590 if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN)
1591 elog(ERROR, "Time zone '%s' not recognized",
1592 DatumGetCString(DirectFunctionCall1(textout,
1593 PointerGetDatum(zone))));
1596 for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++)
1597 *lp++ = tolower((unsigned char) *up++);
1600 type = DecodeSpecial(0, lowzone, &val);
1602 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1604 if ((type == TZ) || (type == DTZ))
1607 time1 = time->time - time->zone + tz;
1608 TMODULO(result->time, time1, 86400e0);
1609 if (result->time < 0)
1610 result->time += 86400;
1615 elog(ERROR, "Time zone '%s' not recognized", lowzone);
1619 PG_RETURN_TIMETZADT_P(result);
1620 } /* timetz_zone() */
1623 * Encode time with time zone type with specified time interval as time zone.
1626 timetz_izone(PG_FUNCTION_ARGS)
1628 Interval *zone = PG_GETARG_INTERVAL_P(0);
1629 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
1634 if (zone->month != 0)
1635 elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)",
1636 DatumGetCString(DirectFunctionCall1(interval_out,
1637 PointerGetDatum(zone))));
1641 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1643 time1 = time->time - time->zone + tz;
1644 TMODULO(result->time, time1, 86400e0);
1645 if (result->time < 0)
1646 result->time += 86400;
1649 PG_RETURN_TIMETZADT_P(result);
1650 } /* timetz_izone() */