1 /*-------------------------------------------------------------------------
4 * implements DATE and TIME data types specified in SQL-92 standard
6 * Portions Copyright (c) 1996-2006, 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.124 2006/03/05 15:58:41 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/builtins.h"
28 #include "utils/date.h"
29 #include "utils/nabstime.h"
30 #include "utils/timestamp.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);
47 /*****************************************************************************
49 *****************************************************************************/
53 * Given date text string, convert to internal date format.
56 date_in(PG_FUNCTION_ARGS)
58 char *str = PG_GETARG_CSTRING(0);
67 char *field[MAXDATEFIELDS];
68 int ftype[MAXDATEFIELDS];
69 char workbuf[MAXDATELEN + 1];
71 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
72 field, ftype, MAXDATEFIELDS, &nf);
74 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
76 DateTimeParseError(dterr, str, "date");
85 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
86 errmsg("date/time value \"current\" is no longer supported")));
88 GetCurrentDateTime(tm);
96 DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
100 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
102 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
103 errmsg("date out of range: \"%s\"", str)));
105 date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
107 PG_RETURN_DATEADT(date);
111 * Given internal format date, convert to text string.
114 date_out(PG_FUNCTION_ARGS)
116 DateADT date = PG_GETARG_DATEADT(0);
120 char buf[MAXDATELEN + 1];
122 j2date(date + POSTGRES_EPOCH_JDATE,
123 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
125 EncodeDateOnly(tm, DateStyle, buf);
127 result = pstrdup(buf);
128 PG_RETURN_CSTRING(result);
132 * date_recv - converts external binary format to date
135 date_recv(PG_FUNCTION_ARGS)
137 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
139 PG_RETURN_DATEADT((DateADT) pq_getmsgint(buf, sizeof(DateADT)));
143 * date_send - converts date to binary format
146 date_send(PG_FUNCTION_ARGS)
148 DateADT date = PG_GETARG_DATEADT(0);
151 pq_begintypsend(&buf);
152 pq_sendint(&buf, date, sizeof(date));
153 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
158 * Comparison functions for dates
162 date_eq(PG_FUNCTION_ARGS)
164 DateADT dateVal1 = PG_GETARG_DATEADT(0);
165 DateADT dateVal2 = PG_GETARG_DATEADT(1);
167 PG_RETURN_BOOL(dateVal1 == dateVal2);
171 date_ne(PG_FUNCTION_ARGS)
173 DateADT dateVal1 = PG_GETARG_DATEADT(0);
174 DateADT dateVal2 = PG_GETARG_DATEADT(1);
176 PG_RETURN_BOOL(dateVal1 != dateVal2);
180 date_lt(PG_FUNCTION_ARGS)
182 DateADT dateVal1 = PG_GETARG_DATEADT(0);
183 DateADT dateVal2 = PG_GETARG_DATEADT(1);
185 PG_RETURN_BOOL(dateVal1 < dateVal2);
189 date_le(PG_FUNCTION_ARGS)
191 DateADT dateVal1 = PG_GETARG_DATEADT(0);
192 DateADT dateVal2 = PG_GETARG_DATEADT(1);
194 PG_RETURN_BOOL(dateVal1 <= dateVal2);
198 date_gt(PG_FUNCTION_ARGS)
200 DateADT dateVal1 = PG_GETARG_DATEADT(0);
201 DateADT dateVal2 = PG_GETARG_DATEADT(1);
203 PG_RETURN_BOOL(dateVal1 > dateVal2);
207 date_ge(PG_FUNCTION_ARGS)
209 DateADT dateVal1 = PG_GETARG_DATEADT(0);
210 DateADT dateVal2 = PG_GETARG_DATEADT(1);
212 PG_RETURN_BOOL(dateVal1 >= dateVal2);
216 date_cmp(PG_FUNCTION_ARGS)
218 DateADT dateVal1 = PG_GETARG_DATEADT(0);
219 DateADT dateVal2 = PG_GETARG_DATEADT(1);
221 if (dateVal1 < dateVal2)
223 else if (dateVal1 > dateVal2)
229 date_larger(PG_FUNCTION_ARGS)
231 DateADT dateVal1 = PG_GETARG_DATEADT(0);
232 DateADT dateVal2 = PG_GETARG_DATEADT(1);
234 PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
238 date_smaller(PG_FUNCTION_ARGS)
240 DateADT dateVal1 = PG_GETARG_DATEADT(0);
241 DateADT dateVal2 = PG_GETARG_DATEADT(1);
243 PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
246 /* Compute difference between two dates in days.
249 date_mi(PG_FUNCTION_ARGS)
251 DateADT dateVal1 = PG_GETARG_DATEADT(0);
252 DateADT dateVal2 = PG_GETARG_DATEADT(1);
254 PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
257 /* Add a number of days to a date, giving a new date.
258 * Must handle both positive and negative numbers of days.
261 date_pli(PG_FUNCTION_ARGS)
263 DateADT dateVal = PG_GETARG_DATEADT(0);
264 int32 days = PG_GETARG_INT32(1);
266 PG_RETURN_DATEADT(dateVal + days);
269 /* Subtract a number of days from a date, giving a new date.
272 date_mii(PG_FUNCTION_ARGS)
274 DateADT dateVal = PG_GETARG_DATEADT(0);
275 int32 days = PG_GETARG_INT32(1);
277 PG_RETURN_DATEADT(dateVal - days);
281 * Internal routines for promoting date to timestamp and timestamp with
285 #ifdef HAVE_INT64_TIMESTAMP
286 /* date is days since 2000, timestamp is microseconds since same... */
287 #define date2timestamp(dateVal) \
288 ((Timestamp) ((dateVal) * USECS_PER_DAY))
290 /* date is days since 2000, timestamp is seconds since same... */
291 #define date2timestamp(dateVal) \
292 ((Timestamp) ((dateVal) * (double)SECS_PER_DAY))
296 date2timestamptz(DateADT dateVal)
303 j2date(dateVal + POSTGRES_EPOCH_JDATE,
304 &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
309 tz = DetermineTimeZoneOffset(tm, global_timezone);
311 #ifdef HAVE_INT64_TIMESTAMP
312 result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
314 result = dateVal * (double) SECS_PER_DAY + tz;
322 * Crosstype comparison functions for dates
326 date_eq_timestamp(PG_FUNCTION_ARGS)
328 DateADT dateVal = PG_GETARG_DATEADT(0);
329 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
332 dt1 = date2timestamp(dateVal);
334 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
338 date_ne_timestamp(PG_FUNCTION_ARGS)
340 DateADT dateVal = PG_GETARG_DATEADT(0);
341 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
344 dt1 = date2timestamp(dateVal);
346 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
350 date_lt_timestamp(PG_FUNCTION_ARGS)
352 DateADT dateVal = PG_GETARG_DATEADT(0);
353 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
356 dt1 = date2timestamp(dateVal);
358 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
362 date_gt_timestamp(PG_FUNCTION_ARGS)
364 DateADT dateVal = PG_GETARG_DATEADT(0);
365 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
368 dt1 = date2timestamp(dateVal);
370 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
374 date_le_timestamp(PG_FUNCTION_ARGS)
376 DateADT dateVal = PG_GETARG_DATEADT(0);
377 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
380 dt1 = date2timestamp(dateVal);
382 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
386 date_ge_timestamp(PG_FUNCTION_ARGS)
388 DateADT dateVal = PG_GETARG_DATEADT(0);
389 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
392 dt1 = date2timestamp(dateVal);
394 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
398 date_cmp_timestamp(PG_FUNCTION_ARGS)
400 DateADT dateVal = PG_GETARG_DATEADT(0);
401 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
404 dt1 = date2timestamp(dateVal);
406 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
410 date_eq_timestamptz(PG_FUNCTION_ARGS)
412 DateADT dateVal = PG_GETARG_DATEADT(0);
413 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
416 dt1 = date2timestamptz(dateVal);
418 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
422 date_ne_timestamptz(PG_FUNCTION_ARGS)
424 DateADT dateVal = PG_GETARG_DATEADT(0);
425 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
428 dt1 = date2timestamptz(dateVal);
430 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
434 date_lt_timestamptz(PG_FUNCTION_ARGS)
436 DateADT dateVal = PG_GETARG_DATEADT(0);
437 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
440 dt1 = date2timestamptz(dateVal);
442 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
446 date_gt_timestamptz(PG_FUNCTION_ARGS)
448 DateADT dateVal = PG_GETARG_DATEADT(0);
449 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
452 dt1 = date2timestamptz(dateVal);
454 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
458 date_le_timestamptz(PG_FUNCTION_ARGS)
460 DateADT dateVal = PG_GETARG_DATEADT(0);
461 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
464 dt1 = date2timestamptz(dateVal);
466 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
470 date_ge_timestamptz(PG_FUNCTION_ARGS)
472 DateADT dateVal = PG_GETARG_DATEADT(0);
473 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
476 dt1 = date2timestamptz(dateVal);
478 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
482 date_cmp_timestamptz(PG_FUNCTION_ARGS)
484 DateADT dateVal = PG_GETARG_DATEADT(0);
485 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
488 dt1 = date2timestamptz(dateVal);
490 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
494 timestamp_eq_date(PG_FUNCTION_ARGS)
496 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
497 DateADT dateVal = PG_GETARG_DATEADT(1);
500 dt2 = date2timestamp(dateVal);
502 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
506 timestamp_ne_date(PG_FUNCTION_ARGS)
508 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
509 DateADT dateVal = PG_GETARG_DATEADT(1);
512 dt2 = date2timestamp(dateVal);
514 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
518 timestamp_lt_date(PG_FUNCTION_ARGS)
520 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
521 DateADT dateVal = PG_GETARG_DATEADT(1);
524 dt2 = date2timestamp(dateVal);
526 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
530 timestamp_gt_date(PG_FUNCTION_ARGS)
532 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
533 DateADT dateVal = PG_GETARG_DATEADT(1);
536 dt2 = date2timestamp(dateVal);
538 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
542 timestamp_le_date(PG_FUNCTION_ARGS)
544 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
545 DateADT dateVal = PG_GETARG_DATEADT(1);
548 dt2 = date2timestamp(dateVal);
550 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
554 timestamp_ge_date(PG_FUNCTION_ARGS)
556 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
557 DateADT dateVal = PG_GETARG_DATEADT(1);
560 dt2 = date2timestamp(dateVal);
562 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
566 timestamp_cmp_date(PG_FUNCTION_ARGS)
568 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
569 DateADT dateVal = PG_GETARG_DATEADT(1);
572 dt2 = date2timestamp(dateVal);
574 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
578 timestamptz_eq_date(PG_FUNCTION_ARGS)
580 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
581 DateADT dateVal = PG_GETARG_DATEADT(1);
584 dt2 = date2timestamptz(dateVal);
586 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
590 timestamptz_ne_date(PG_FUNCTION_ARGS)
592 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
593 DateADT dateVal = PG_GETARG_DATEADT(1);
596 dt2 = date2timestamptz(dateVal);
598 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
602 timestamptz_lt_date(PG_FUNCTION_ARGS)
604 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
605 DateADT dateVal = PG_GETARG_DATEADT(1);
608 dt2 = date2timestamptz(dateVal);
610 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
614 timestamptz_gt_date(PG_FUNCTION_ARGS)
616 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
617 DateADT dateVal = PG_GETARG_DATEADT(1);
620 dt2 = date2timestamptz(dateVal);
622 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
626 timestamptz_le_date(PG_FUNCTION_ARGS)
628 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
629 DateADT dateVal = PG_GETARG_DATEADT(1);
632 dt2 = date2timestamptz(dateVal);
634 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
638 timestamptz_ge_date(PG_FUNCTION_ARGS)
640 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
641 DateADT dateVal = PG_GETARG_DATEADT(1);
644 dt2 = date2timestamptz(dateVal);
646 PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
650 timestamptz_cmp_date(PG_FUNCTION_ARGS)
652 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
653 DateADT dateVal = PG_GETARG_DATEADT(1);
656 dt2 = date2timestamptz(dateVal);
658 PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
662 /* Add an interval to a date, giving a new date.
663 * Must handle both positive and negative intervals.
665 * We implement this by promoting the date to timestamp (without time zone)
666 * and then using the timestamp plus interval function.
669 date_pl_interval(PG_FUNCTION_ARGS)
671 DateADT dateVal = PG_GETARG_DATEADT(0);
672 Interval *span = PG_GETARG_INTERVAL_P(1);
675 dateStamp = date2timestamp(dateVal);
677 return DirectFunctionCall2(timestamp_pl_interval,
678 TimestampGetDatum(dateStamp),
679 PointerGetDatum(span));
682 /* Subtract an interval from a date, giving a new date.
683 * Must handle both positive and negative intervals.
685 * We implement this by promoting the date to timestamp (without time zone)
686 * and then using the timestamp minus interval function.
689 date_mi_interval(PG_FUNCTION_ARGS)
691 DateADT dateVal = PG_GETARG_DATEADT(0);
692 Interval *span = PG_GETARG_INTERVAL_P(1);
695 dateStamp = date2timestamp(dateVal);
697 return DirectFunctionCall2(timestamp_mi_interval,
698 TimestampGetDatum(dateStamp),
699 PointerGetDatum(span));
703 * Convert date to timestamp data type.
706 date_timestamp(PG_FUNCTION_ARGS)
708 DateADT dateVal = PG_GETARG_DATEADT(0);
711 result = date2timestamp(dateVal);
713 PG_RETURN_TIMESTAMP(result);
718 * Convert timestamp to date data type.
721 timestamp_date(PG_FUNCTION_ARGS)
723 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
729 if (TIMESTAMP_NOT_FINITE(timestamp))
732 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
734 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
735 errmsg("timestamp out of range")));
737 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
739 PG_RETURN_DATEADT(result);
743 /* date_timestamptz()
744 * Convert date to timestamp with time zone data type.
747 date_timestamptz(PG_FUNCTION_ARGS)
749 DateADT dateVal = PG_GETARG_DATEADT(0);
752 result = date2timestamptz(dateVal);
754 PG_RETURN_TIMESTAMP(result);
758 /* timestamptz_date()
759 * Convert timestamp with time zone to date data type.
762 timestamptz_date(PG_FUNCTION_ARGS)
764 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
772 if (TIMESTAMP_NOT_FINITE(timestamp))
775 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
777 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
778 errmsg("timestamp out of range")));
780 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
782 PG_RETURN_DATEADT(result);
787 * Convert abstime to date data type.
790 abstime_date(PG_FUNCTION_ARGS)
792 AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
800 case INVALID_ABSTIME:
801 case NOSTART_ABSTIME:
804 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
805 errmsg("cannot convert reserved abstime value to date")));
808 * pretend to drop through to make compiler think that result will
813 abstime2tm(abstime, &tz, tm, NULL);
814 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
818 PG_RETURN_DATEADT(result);
823 * Convert date to text data type.
826 date_text(PG_FUNCTION_ARGS)
828 /* Input is a Date, but may as well leave it in Datum form */
829 Datum date = PG_GETARG_DATUM(0);
834 str = DatumGetCString(DirectFunctionCall1(date_out, date));
836 len = strlen(str) + VARHDRSZ;
838 result = palloc(len);
840 VARATT_SIZEP(result) = len;
841 memmove(VARDATA(result), str, (len - VARHDRSZ));
845 PG_RETURN_TEXT_P(result);
850 * Convert text string to date.
851 * Text type is not null terminated, so use temporary string
852 * then call the standard input routine.
855 text_date(PG_FUNCTION_ARGS)
857 text *str = PG_GETARG_TEXT_P(0);
861 dstr[MAXDATELEN + 1];
863 if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
865 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
866 errmsg("invalid input syntax for type date: \"%s\"",
871 for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
875 return DirectFunctionCall1(date_in,
876 CStringGetDatum(dstr));
880 /*****************************************************************************
882 *****************************************************************************/
885 time_in(PG_FUNCTION_ARGS)
887 char *str = PG_GETARG_CSTRING(0);
890 Oid typelem = PG_GETARG_OID(1);
892 int32 typmod = PG_GETARG_INT32(2);
900 char workbuf[MAXDATELEN + 1];
901 char *field[MAXDATEFIELDS];
903 int ftype[MAXDATEFIELDS];
905 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
906 field, ftype, MAXDATEFIELDS, &nf);
908 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
910 DateTimeParseError(dterr, str, "time");
912 tm2time(tm, fsec, &result);
913 AdjustTimeForTypmod(&result, typmod);
915 PG_RETURN_TIMEADT(result);
919 * Convert a tm structure to a time data type.
922 tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
924 #ifdef HAVE_INT64_TIMESTAMP
925 *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
926 * USECS_PER_SEC) + fsec;
928 *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
934 * Convert time data type to POSIX time structure.
935 * For dates within the system-supported time_t range, convert to the
936 * local time zone. If out of this range, leave as GMT. - tgl 97/05/27
939 time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
941 #ifdef HAVE_INT64_TIMESTAMP
942 tm->tm_hour = time / USECS_PER_HOUR;
943 time -= tm->tm_hour * USECS_PER_HOUR;
944 tm->tm_min = time / USECS_PER_MINUTE;
945 time -= tm->tm_min * USECS_PER_MINUTE;
946 tm->tm_sec = time / USECS_PER_SEC;
947 time -= tm->tm_sec * USECS_PER_SEC;
954 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
955 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
956 TMODULO(trem, tm->tm_sec, 1.0);
957 trem = TIMEROUND(trem);
958 /* roundoff may need to propagate to higher-order fields */
971 time_out(PG_FUNCTION_ARGS)
973 TimeADT time = PG_GETARG_TIMEADT(0);
978 char buf[MAXDATELEN + 1];
980 time2tm(time, tm, &fsec);
981 EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
983 result = pstrdup(buf);
984 PG_RETURN_CSTRING(result);
988 * time_recv - converts external binary format to time
990 * We make no attempt to provide compatibility between int and float
991 * time representations ...
994 time_recv(PG_FUNCTION_ARGS)
996 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
999 Oid typelem = PG_GETARG_OID(1);
1001 int32 typmod = PG_GETARG_INT32(2);
1004 #ifdef HAVE_INT64_TIMESTAMP
1005 result = pq_getmsgint64(buf);
1007 result = pq_getmsgfloat8(buf);
1010 AdjustTimeForTypmod(&result, typmod);
1012 PG_RETURN_TIMEADT(result);
1016 * time_send - converts time to binary format
1019 time_send(PG_FUNCTION_ARGS)
1021 TimeADT time = PG_GETARG_TIMEADT(0);
1024 pq_begintypsend(&buf);
1025 #ifdef HAVE_INT64_TIMESTAMP
1026 pq_sendint64(&buf, time);
1028 pq_sendfloat8(&buf, time);
1030 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1035 * Adjust time type for specified scale factor.
1036 * Used by PostgreSQL type system to stuff columns.
1039 time_scale(PG_FUNCTION_ARGS)
1041 TimeADT time = PG_GETARG_TIMEADT(0);
1042 int32 typmod = PG_GETARG_INT32(1);
1046 AdjustTimeForTypmod(&result, typmod);
1048 PG_RETURN_TIMEADT(result);
1051 /* AdjustTimeForTypmod()
1052 * Force the precision of the time value to a specified value.
1053 * Uses *exactly* the same code as in AdjustTimestampForTypemod()
1054 * but we make a separate copy because those types do not
1055 * have a fundamental tie together but rather a coincidence of
1056 * implementation. - thomas
1059 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1061 #ifdef HAVE_INT64_TIMESTAMP
1062 static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1063 INT64CONST(1000000),
1072 static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1082 /* note MAX_TIME_PRECISION differs in this case */
1083 static const double TimeScales[MAX_TIME_PRECISION + 1] = {
1098 if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1101 * Note: this round-to-nearest code is not completely consistent about
1102 * rounding values that are exactly halfway between integral values.
1103 * On most platforms, rint() will implement round-to-nearest-even, but
1104 * the integer code always rounds up (away from zero). Is it worth
1105 * trying to be consistent?
1107 #ifdef HAVE_INT64_TIMESTAMP
1108 if (*time >= INT64CONST(0))
1109 *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
1112 *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
1113 TimeScales[typmod]);
1115 *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
1122 time_eq(PG_FUNCTION_ARGS)
1124 TimeADT time1 = PG_GETARG_TIMEADT(0);
1125 TimeADT time2 = PG_GETARG_TIMEADT(1);
1127 PG_RETURN_BOOL(time1 == time2);
1131 time_ne(PG_FUNCTION_ARGS)
1133 TimeADT time1 = PG_GETARG_TIMEADT(0);
1134 TimeADT time2 = PG_GETARG_TIMEADT(1);
1136 PG_RETURN_BOOL(time1 != time2);
1140 time_lt(PG_FUNCTION_ARGS)
1142 TimeADT time1 = PG_GETARG_TIMEADT(0);
1143 TimeADT time2 = PG_GETARG_TIMEADT(1);
1145 PG_RETURN_BOOL(time1 < time2);
1149 time_le(PG_FUNCTION_ARGS)
1151 TimeADT time1 = PG_GETARG_TIMEADT(0);
1152 TimeADT time2 = PG_GETARG_TIMEADT(1);
1154 PG_RETURN_BOOL(time1 <= time2);
1158 time_gt(PG_FUNCTION_ARGS)
1160 TimeADT time1 = PG_GETARG_TIMEADT(0);
1161 TimeADT time2 = PG_GETARG_TIMEADT(1);
1163 PG_RETURN_BOOL(time1 > time2);
1167 time_ge(PG_FUNCTION_ARGS)
1169 TimeADT time1 = PG_GETARG_TIMEADT(0);
1170 TimeADT time2 = PG_GETARG_TIMEADT(1);
1172 PG_RETURN_BOOL(time1 >= time2);
1176 time_cmp(PG_FUNCTION_ARGS)
1178 TimeADT time1 = PG_GETARG_TIMEADT(0);
1179 TimeADT time2 = PG_GETARG_TIMEADT(1);
1182 PG_RETURN_INT32(-1);
1189 time_larger(PG_FUNCTION_ARGS)
1191 TimeADT time1 = PG_GETARG_TIMEADT(0);
1192 TimeADT time2 = PG_GETARG_TIMEADT(1);
1194 PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1198 time_smaller(PG_FUNCTION_ARGS)
1200 TimeADT time1 = PG_GETARG_TIMEADT(0);
1201 TimeADT time2 = PG_GETARG_TIMEADT(1);
1203 PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1206 /* overlaps_time() --- implements the SQL92 OVERLAPS operator.
1208 * Algorithm is per SQL92 spec. This is much harder than you'd think
1209 * because the spec requires us to deliver a non-null answer in some cases
1210 * where some of the inputs are null.
1213 overlaps_time(PG_FUNCTION_ARGS)
1216 * The arguments are TimeADT, but we leave them as generic Datums to avoid
1217 * dereferencing nulls (TimeADT is pass-by-reference!)
1219 Datum ts1 = PG_GETARG_DATUM(0);
1220 Datum te1 = PG_GETARG_DATUM(1);
1221 Datum ts2 = PG_GETARG_DATUM(2);
1222 Datum te2 = PG_GETARG_DATUM(3);
1223 bool ts1IsNull = PG_ARGISNULL(0);
1224 bool te1IsNull = PG_ARGISNULL(1);
1225 bool ts2IsNull = PG_ARGISNULL(2);
1226 bool te2IsNull = PG_ARGISNULL(3);
1228 #define TIMEADT_GT(t1,t2) \
1229 (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1230 #define TIMEADT_LT(t1,t2) \
1231 (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1234 * If both endpoints of interval 1 are null, the result is null (unknown).
1235 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1236 * take ts1 as the lesser endpoint.
1242 /* swap null for non-null */
1246 else if (!te1IsNull)
1248 if (TIMEADT_GT(ts1, te1))
1257 /* Likewise for interval 2. */
1262 /* swap null for non-null */
1266 else if (!te2IsNull)
1268 if (TIMEADT_GT(ts2, te2))
1278 * At this point neither ts1 nor ts2 is null, so we can consider three
1279 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1281 if (TIMEADT_GT(ts1, ts2))
1284 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
1285 * in the presence of nulls it's not quite completely so.
1289 if (TIMEADT_LT(ts1, te2))
1290 PG_RETURN_BOOL(true);
1295 * If te1 is not null then we had ts1 <= te1 above, and we just found
1296 * ts1 >= te2, hence te1 >= te2.
1298 PG_RETURN_BOOL(false);
1300 else if (TIMEADT_LT(ts1, ts2))
1302 /* This case is ts2 < te1 OR te2 < te1 */
1305 if (TIMEADT_LT(ts2, te1))
1306 PG_RETURN_BOOL(true);
1311 * If te2 is not null then we had ts2 <= te2 above, and we just found
1312 * ts2 >= te1, hence te2 >= te1.
1314 PG_RETURN_BOOL(false);
1319 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1320 * rather silly way of saying "true if both are nonnull, else null".
1322 if (te1IsNull || te2IsNull)
1324 PG_RETURN_BOOL(true);
1332 * Convert timestamp to time data type.
1335 timestamp_time(PG_FUNCTION_ARGS)
1337 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1343 if (TIMESTAMP_NOT_FINITE(timestamp))
1346 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1348 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1349 errmsg("timestamp out of range")));
1351 #ifdef HAVE_INT64_TIMESTAMP
1354 * Could also do this with time = (timestamp / USECS_PER_DAY *
1355 * USECS_PER_DAY) - timestamp;
1357 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1358 USECS_PER_SEC) + fsec;
1360 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1363 PG_RETURN_TIMEADT(result);
1366 /* timestamptz_time()
1367 * Convert timestamptz to time data type.
1370 timestamptz_time(PG_FUNCTION_ARGS)
1372 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1380 if (TIMESTAMP_NOT_FINITE(timestamp))
1383 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
1385 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1386 errmsg("timestamp out of range")));
1388 #ifdef HAVE_INT64_TIMESTAMP
1391 * Could also do this with time = (timestamp / USECS_PER_DAY *
1392 * USECS_PER_DAY) - timestamp;
1394 result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1395 USECS_PER_SEC) + fsec;
1397 result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1400 PG_RETURN_TIMEADT(result);
1403 /* datetime_timestamp()
1404 * Convert date and time to timestamp data type.
1407 datetime_timestamp(PG_FUNCTION_ARGS)
1409 DateADT date = PG_GETARG_DATEADT(0);
1410 TimeADT time = PG_GETARG_TIMEADT(1);
1413 result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
1414 DateADTGetDatum(date)));
1417 PG_RETURN_TIMESTAMP(result);
1421 * Convert time to interval data type.
1424 time_interval(PG_FUNCTION_ARGS)
1426 TimeADT time = PG_GETARG_TIMEADT(0);
1429 result = (Interval *) palloc(sizeof(Interval));
1431 result->time = time;
1435 PG_RETURN_INTERVAL_P(result);
1439 * Convert interval to time data type.
1441 * This is defined as producing the fractional-day portion of the interval.
1442 * Therefore, we can just ignore the months field. It is not real clear
1443 * what to do with negative intervals, but we choose to subtract the floor,
1444 * so that, say, '-2 hours' becomes '22:00:00'.
1447 interval_time(PG_FUNCTION_ARGS)
1449 Interval *span = PG_GETARG_INTERVAL_P(0);
1452 #ifdef HAVE_INT64_TIMESTAMP
1455 result = span->time;
1456 if (result >= USECS_PER_DAY)
1458 days = result / USECS_PER_DAY;
1459 result -= days * USECS_PER_DAY;
1461 else if (result < 0)
1463 days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
1464 result += days * USECS_PER_DAY;
1467 result = span->time;
1468 if (result >= (double) SECS_PER_DAY || result < 0)
1469 result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
1472 PG_RETURN_TIMEADT(result);
1476 * Subtract two times to produce an interval.
1479 time_mi_time(PG_FUNCTION_ARGS)
1481 TimeADT time1 = PG_GETARG_TIMEADT(0);
1482 TimeADT time2 = PG_GETARG_TIMEADT(1);
1485 result = (Interval *) palloc(sizeof(Interval));
1489 result->time = time1 - time2;
1491 PG_RETURN_INTERVAL_P(result);
1494 /* time_pl_interval()
1495 * Add interval to time.
1498 time_pl_interval(PG_FUNCTION_ARGS)
1500 TimeADT time = PG_GETARG_TIMEADT(0);
1501 Interval *span = PG_GETARG_INTERVAL_P(1);
1504 #ifdef HAVE_INT64_TIMESTAMP
1505 result = time + span->time;
1506 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1507 if (result < INT64CONST(0))
1508 result += USECS_PER_DAY;
1512 result = time + span->time;
1513 TMODULO(result, time1, (double) SECS_PER_DAY);
1515 result += SECS_PER_DAY;
1518 PG_RETURN_TIMEADT(result);
1521 /* time_mi_interval()
1522 * Subtract interval from time.
1525 time_mi_interval(PG_FUNCTION_ARGS)
1527 TimeADT time = PG_GETARG_TIMEADT(0);
1528 Interval *span = PG_GETARG_INTERVAL_P(1);
1531 #ifdef HAVE_INT64_TIMESTAMP
1532 result = time - span->time;
1533 result -= result / USECS_PER_DAY * USECS_PER_DAY;
1534 if (result < INT64CONST(0))
1535 result += USECS_PER_DAY;
1539 result = time - span->time;
1540 TMODULO(result, time1, (double) SECS_PER_DAY);
1542 result += SECS_PER_DAY;
1545 PG_RETURN_TIMEADT(result);
1550 * Convert time to text data type.
1553 time_text(PG_FUNCTION_ARGS)
1555 /* Input is a Time, but may as well leave it in Datum form */
1556 Datum time = PG_GETARG_DATUM(0);
1561 str = DatumGetCString(DirectFunctionCall1(time_out, time));
1563 len = strlen(str) + VARHDRSZ;
1565 result = palloc(len);
1567 VARATT_SIZEP(result) = len;
1568 memmove(VARDATA(result), str, (len - VARHDRSZ));
1572 PG_RETURN_TEXT_P(result);
1577 * Convert text string to time.
1578 * Text type is not null terminated, so use temporary string
1579 * then call the standard input routine.
1582 text_time(PG_FUNCTION_ARGS)
1584 text *str = PG_GETARG_TEXT_P(0);
1588 dstr[MAXDATELEN + 1];
1590 if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
1592 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
1593 errmsg("invalid input syntax for type time: \"%s\"",
1598 for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
1602 return DirectFunctionCall3(time_in,
1603 CStringGetDatum(dstr),
1604 ObjectIdGetDatum(InvalidOid),
1609 * Extract specified field from time type.
1612 time_part(PG_FUNCTION_ARGS)
1614 text *units = PG_GETARG_TEXT_P(0);
1615 TimeADT time = PG_GETARG_TIMEADT(1);
1621 lowunits = downcase_truncate_identifier(VARDATA(units),
1622 VARSIZE(units) - VARHDRSZ,
1625 type = DecodeUnits(0, lowunits, &val);
1626 if (type == UNKNOWN_FIELD)
1627 type = DecodeSpecial(0, lowunits, &val);
1635 time2tm(time, tm, &fsec);
1640 #ifdef HAVE_INT64_TIMESTAMP
1641 result = tm->tm_sec * USECS_PER_SEC + fsec;
1643 result = (tm->tm_sec + fsec) * 1000000;
1648 #ifdef HAVE_INT64_TIMESTAMP
1649 result = tm->tm_sec * INT64CONST(1000) + fsec / INT64CONST(1000);
1651 result = (tm->tm_sec + fsec) * 1000;
1656 #ifdef HAVE_INT64_TIMESTAMP
1657 result = tm->tm_sec + fsec / USECS_PER_SEC;
1659 result = tm->tm_sec + fsec;
1664 result = tm->tm_min;
1668 result = tm->tm_hour;
1680 case DTK_MILLENNIUM:
1683 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1684 errmsg("\"time\" units \"%s\" not recognized",
1685 DatumGetCString(DirectFunctionCall1(textout,
1686 PointerGetDatum(units))))));
1691 else if (type == RESERV && val == DTK_EPOCH)
1693 #ifdef HAVE_INT64_TIMESTAMP
1694 result = time / 1000000.0;
1702 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1703 errmsg("\"time\" units \"%s\" not recognized",
1704 DatumGetCString(DirectFunctionCall1(textout,
1705 PointerGetDatum(units))))));
1709 PG_RETURN_FLOAT8(result);
1713 /*****************************************************************************
1714 * Time With Time Zone ADT
1715 *****************************************************************************/
1718 * Convert a tm structure to a time data type.
1721 tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
1723 #ifdef HAVE_INT64_TIMESTAMP
1724 result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1725 USECS_PER_SEC) + fsec;
1727 result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1735 timetz_in(PG_FUNCTION_ARGS)
1737 char *str = PG_GETARG_CSTRING(0);
1740 Oid typelem = PG_GETARG_OID(1);
1742 int32 typmod = PG_GETARG_INT32(2);
1750 char workbuf[MAXDATELEN + 1];
1751 char *field[MAXDATEFIELDS];
1753 int ftype[MAXDATEFIELDS];
1755 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1756 field, ftype, MAXDATEFIELDS, &nf);
1758 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1760 DateTimeParseError(dterr, str, "time with time zone");
1762 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1763 tm2timetz(tm, fsec, tz, result);
1764 AdjustTimeForTypmod(&(result->time), typmod);
1766 PG_RETURN_TIMETZADT_P(result);
1770 timetz_out(PG_FUNCTION_ARGS)
1772 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1778 char buf[MAXDATELEN + 1];
1780 timetz2tm(time, tm, &fsec, &tz);
1781 EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
1783 result = pstrdup(buf);
1784 PG_RETURN_CSTRING(result);
1788 * timetz_recv - converts external binary format to timetz
1791 timetz_recv(PG_FUNCTION_ARGS)
1793 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1796 Oid typelem = PG_GETARG_OID(1);
1798 int32 typmod = PG_GETARG_INT32(2);
1801 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1803 #ifdef HAVE_INT64_TIMESTAMP
1804 result->time = pq_getmsgint64(buf);
1806 result->time = pq_getmsgfloat8(buf);
1808 result->zone = pq_getmsgint(buf, sizeof(result->zone));
1810 AdjustTimeForTypmod(&(result->time), typmod);
1812 PG_RETURN_TIMETZADT_P(result);
1816 * timetz_send - converts timetz to binary format
1819 timetz_send(PG_FUNCTION_ARGS)
1821 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1824 pq_begintypsend(&buf);
1825 #ifdef HAVE_INT64_TIMESTAMP
1826 pq_sendint64(&buf, time->time);
1828 pq_sendfloat8(&buf, time->time);
1830 pq_sendint(&buf, time->zone, sizeof(time->zone));
1831 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1836 * Convert TIME WITH TIME ZONE data type to POSIX time structure.
1839 timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
1841 #ifdef HAVE_INT64_TIMESTAMP
1842 int64 trem = time->time;
1844 tm->tm_hour = trem / USECS_PER_HOUR;
1845 trem -= tm->tm_hour * USECS_PER_HOUR;
1846 tm->tm_min = trem / USECS_PER_MINUTE;
1847 trem -= tm->tm_min * USECS_PER_MINUTE;
1848 tm->tm_sec = trem / USECS_PER_SEC;
1849 *fsec = trem - tm->tm_sec * USECS_PER_SEC;
1851 double trem = time->time;
1854 TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
1855 TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1856 TMODULO(trem, tm->tm_sec, 1.0);
1857 trem = TIMEROUND(trem);
1858 /* roundoff may need to propagate to higher-order fields */
1861 trem = ceil(time->time);
1874 * Adjust time type for specified scale factor.
1875 * Used by PostgreSQL type system to stuff columns.
1878 timetz_scale(PG_FUNCTION_ARGS)
1880 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
1881 int32 typmod = PG_GETARG_INT32(1);
1884 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1886 result->time = time->time;
1887 result->zone = time->zone;
1889 AdjustTimeForTypmod(&(result->time), typmod);
1891 PG_RETURN_TIMETZADT_P(result);
1896 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
1898 /* Primary sort is by true (GMT-equivalent) time */
1899 #ifdef HAVE_INT64_TIMESTAMP
1903 t1 = time1->time + (time1->zone * USECS_PER_SEC);
1904 t2 = time2->time + (time2->zone * USECS_PER_SEC);
1909 t1 = time1->time + time1->zone;
1910 t2 = time2->time + time2->zone;
1919 * If same GMT time, sort by timezone; we only want to say that two
1920 * timetz's are equal if both the time and zone parts are equal.
1922 if (time1->zone > time2->zone)
1924 if (time1->zone < time2->zone)
1931 timetz_eq(PG_FUNCTION_ARGS)
1933 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1934 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1936 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
1940 timetz_ne(PG_FUNCTION_ARGS)
1942 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1943 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1945 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
1949 timetz_lt(PG_FUNCTION_ARGS)
1951 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1952 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1954 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
1958 timetz_le(PG_FUNCTION_ARGS)
1960 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1961 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1963 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
1967 timetz_gt(PG_FUNCTION_ARGS)
1969 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1970 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1972 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
1976 timetz_ge(PG_FUNCTION_ARGS)
1978 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1979 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1981 PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
1985 timetz_cmp(PG_FUNCTION_ARGS)
1987 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
1988 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
1990 PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
1994 * timetz, being an unusual size, needs a specialized hash function.
1997 timetz_hash(PG_FUNCTION_ARGS)
1999 TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2002 * Specify hash length as sizeof(double) + sizeof(int4), not as
2003 * sizeof(TimeTzADT), so that any garbage pad bytes in the structure won't
2004 * be included in the hash!
2006 return hash_any((unsigned char *) key, sizeof(key->time) + sizeof(key->zone));
2010 timetz_larger(PG_FUNCTION_ARGS)
2012 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2013 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2016 if (timetz_cmp_internal(time1, time2) > 0)
2020 PG_RETURN_TIMETZADT_P(result);
2024 timetz_smaller(PG_FUNCTION_ARGS)
2026 TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2027 TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2030 if (timetz_cmp_internal(time1, time2) < 0)
2034 PG_RETURN_TIMETZADT_P(result);
2037 /* timetz_pl_interval()
2038 * Add interval to timetz.
2041 timetz_pl_interval(PG_FUNCTION_ARGS)
2043 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2044 Interval *span = PG_GETARG_INTERVAL_P(1);
2047 #ifndef HAVE_INT64_TIMESTAMP
2051 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2053 #ifdef HAVE_INT64_TIMESTAMP
2054 result->time = time->time + span->time;
2055 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2056 if (result->time < INT64CONST(0))
2057 result->time += USECS_PER_DAY;
2059 result->time = time->time + span->time;
2060 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2061 if (result->time < 0)
2062 result->time += SECS_PER_DAY;
2065 result->zone = time->zone;
2067 PG_RETURN_TIMETZADT_P(result);
2070 /* timetz_mi_interval()
2071 * Subtract interval from timetz.
2074 timetz_mi_interval(PG_FUNCTION_ARGS)
2076 TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2077 Interval *span = PG_GETARG_INTERVAL_P(1);
2080 #ifndef HAVE_INT64_TIMESTAMP
2084 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2086 #ifdef HAVE_INT64_TIMESTAMP
2087 result->time = time->time - span->time;
2088 result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2089 if (result->time < INT64CONST(0))
2090 result->time += USECS_PER_DAY;
2092 result->time = time->time - span->time;
2093 TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2094 if (result->time < 0)
2095 result->time += SECS_PER_DAY;
2098 result->zone = time->zone;
2100 PG_RETURN_TIMETZADT_P(result);
2103 /* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
2105 * Algorithm is per SQL92 spec. This is much harder than you'd think
2106 * because the spec requires us to deliver a non-null answer in some cases
2107 * where some of the inputs are null.
2110 overlaps_timetz(PG_FUNCTION_ARGS)
2113 * The arguments are TimeTzADT *, but we leave them as generic Datums for
2114 * convenience of notation --- and to avoid dereferencing nulls.
2116 Datum ts1 = PG_GETARG_DATUM(0);
2117 Datum te1 = PG_GETARG_DATUM(1);
2118 Datum ts2 = PG_GETARG_DATUM(2);
2119 Datum te2 = PG_GETARG_DATUM(3);
2120 bool ts1IsNull = PG_ARGISNULL(0);
2121 bool te1IsNull = PG_ARGISNULL(1);
2122 bool ts2IsNull = PG_ARGISNULL(2);
2123 bool te2IsNull = PG_ARGISNULL(3);
2125 #define TIMETZ_GT(t1,t2) \
2126 DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2127 #define TIMETZ_LT(t1,t2) \
2128 DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2131 * If both endpoints of interval 1 are null, the result is null (unknown).
2132 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2133 * take ts1 as the lesser endpoint.
2139 /* swap null for non-null */
2143 else if (!te1IsNull)
2145 if (TIMETZ_GT(ts1, te1))
2154 /* Likewise for interval 2. */
2159 /* swap null for non-null */
2163 else if (!te2IsNull)
2165 if (TIMETZ_GT(ts2, te2))
2175 * At this point neither ts1 nor ts2 is null, so we can consider three
2176 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2178 if (TIMETZ_GT(ts1, ts2))
2181 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2182 * in the presence of nulls it's not quite completely so.
2186 if (TIMETZ_LT(ts1, te2))
2187 PG_RETURN_BOOL(true);
2192 * If te1 is not null then we had ts1 <= te1 above, and we just found
2193 * ts1 >= te2, hence te1 >= te2.
2195 PG_RETURN_BOOL(false);
2197 else if (TIMETZ_LT(ts1, ts2))
2199 /* This case is ts2 < te1 OR te2 < te1 */
2202 if (TIMETZ_LT(ts2, te1))
2203 PG_RETURN_BOOL(true);
2208 * If te2 is not null then we had ts2 <= te2 above, and we just found
2209 * ts2 >= te1, hence te2 >= te1.
2211 PG_RETURN_BOOL(false);
2216 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2217 * rather silly way of saying "true if both are nonnull, else null".
2219 if (te1IsNull || te2IsNull)
2221 PG_RETURN_BOOL(true);
2230 timetz_time(PG_FUNCTION_ARGS)
2232 TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
2235 /* swallow the time zone and just return the time */
2236 result = timetz->time;
2238 PG_RETURN_TIMEADT(result);
2243 time_timetz(PG_FUNCTION_ARGS)
2245 TimeADT time = PG_GETARG_TIMEADT(0);
2252 GetCurrentDateTime(tm);
2253 time2tm(time, tm, &fsec);
2254 tz = DetermineTimeZoneOffset(tm, global_timezone);
2256 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2258 result->time = time;
2261 PG_RETURN_TIMETZADT_P(result);
2265 /* timestamptz_timetz()
2266 * Convert timestamp to timetz data type.
2269 timestamptz_timetz(PG_FUNCTION_ARGS)
2271 TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2279 if (TIMESTAMP_NOT_FINITE(timestamp))
2282 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2284 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2285 errmsg("timestamp out of range")));
2287 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2289 tm2timetz(tm, fsec, tz, result);
2291 PG_RETURN_TIMETZADT_P(result);
2295 /* datetimetz_timestamptz()
2296 * Convert date and timetz to timestamp with time zone data type.
2297 * Timestamp is stored in GMT, so add the time zone
2298 * stored with the timetz to the result.
2299 * - thomas 2000-03-10
2302 datetimetz_timestamptz(PG_FUNCTION_ARGS)
2304 DateADT date = PG_GETARG_DATEADT(0);
2305 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2308 #ifdef HAVE_INT64_TIMESTAMP
2309 result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2311 result = date * (double) SECS_PER_DAY + time->time + time->zone;
2314 PG_RETURN_TIMESTAMP(result);
2319 * Convert timetz to text data type.
2322 timetz_text(PG_FUNCTION_ARGS)
2324 /* Input is a Timetz, but may as well leave it in Datum form */
2325 Datum timetz = PG_GETARG_DATUM(0);
2330 str = DatumGetCString(DirectFunctionCall1(timetz_out, timetz));
2332 len = strlen(str) + VARHDRSZ;
2334 result = palloc(len);
2336 VARATT_SIZEP(result) = len;
2337 memmove(VARDATA(result), str, (len - VARHDRSZ));
2341 PG_RETURN_TEXT_P(result);
2346 * Convert text string to timetz.
2347 * Text type is not null terminated, so use temporary string
2348 * then call the standard input routine.
2351 text_timetz(PG_FUNCTION_ARGS)
2353 text *str = PG_GETARG_TEXT_P(0);
2357 dstr[MAXDATELEN + 1];
2359 if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2361 (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2362 errmsg("invalid input syntax for type time with time zone: \"%s\"",
2367 for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
2371 return DirectFunctionCall3(timetz_in,
2372 CStringGetDatum(dstr),
2373 ObjectIdGetDatum(InvalidOid),
2378 * Extract specified field from time type.
2381 timetz_part(PG_FUNCTION_ARGS)
2383 text *units = PG_GETARG_TEXT_P(0);
2384 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2390 lowunits = downcase_truncate_identifier(VARDATA(units),
2391 VARSIZE(units) - VARHDRSZ,
2394 type = DecodeUnits(0, lowunits, &val);
2395 if (type == UNKNOWN_FIELD)
2396 type = DecodeSpecial(0, lowunits, &val);
2406 timetz2tm(time, tm, &fsec, &tz);
2416 result /= SECS_PER_MINUTE;
2417 FMODULO(result, dummy, (double) SECS_PER_MINUTE);
2422 FMODULO(dummy, result, (double) SECS_PER_HOUR);
2426 #ifdef HAVE_INT64_TIMESTAMP
2427 result = tm->tm_sec * USECS_PER_SEC + fsec;
2429 result = (tm->tm_sec + fsec) * 1000000;
2434 #ifdef HAVE_INT64_TIMESTAMP
2435 result = tm->tm_sec * INT64CONST(1000) + fsec / INT64CONST(1000);
2437 result = (tm->tm_sec + fsec) * 1000;
2442 #ifdef HAVE_INT64_TIMESTAMP
2443 result = tm->tm_sec + fsec / USECS_PER_SEC;
2445 result = tm->tm_sec + fsec;
2450 result = tm->tm_min;
2454 result = tm->tm_hour;
2463 case DTK_MILLENNIUM:
2466 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2467 errmsg("\"time with time zone\" units \"%s\" not recognized",
2468 DatumGetCString(DirectFunctionCall1(textout,
2469 PointerGetDatum(units))))));
2474 else if (type == RESERV && val == DTK_EPOCH)
2476 #ifdef HAVE_INT64_TIMESTAMP
2477 result = time->time / 1000000.0 + time->zone;
2479 result = time->time + time->zone;
2485 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2486 errmsg("\"time with time zone\" units \"%s\" not recognized",
2487 DatumGetCString(DirectFunctionCall1(textout,
2488 PointerGetDatum(units))))));
2493 PG_RETURN_FLOAT8(result);
2497 * Encode time with time zone type with specified time zone.
2498 * Applies DST rules as of the current date.
2501 timetz_zone(PG_FUNCTION_ARGS)
2503 text *zone = PG_GETARG_TEXT_P(0);
2504 TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
2507 char tzname[TZ_STRLEN_MAX + 1];
2512 * Look up the requested timezone. First we look in the timezone database
2513 * (to handle cases like "America/New_York"), and if that fails, we look
2514 * in the date token table (to handle cases like "EST").
2516 len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
2517 memcpy(tzname, VARDATA(zone), len);
2519 tzp = pg_tzset(tzname);
2522 /* Get the offset-from-GMT that is valid today for the selected zone */
2527 tm = pg_localtime(&now, tzp);
2528 tz = -tm->tm_gmtoff;
2536 lowzone = downcase_truncate_identifier(VARDATA(zone),
2537 VARSIZE(zone) - VARHDRSZ,
2539 type = DecodeSpecial(0, lowzone, &val);
2541 if (type == TZ || type == DTZ)
2546 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2547 errmsg("time zone \"%s\" not recognized", tzname)));
2548 tz = 0; /* keep compiler quiet */
2552 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2554 #ifdef HAVE_INT64_TIMESTAMP
2555 result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
2556 while (result->time < INT64CONST(0))
2557 result->time += USECS_PER_DAY;
2558 while (result->time >= USECS_PER_DAY)
2559 result->time -= USECS_PER_DAY;
2561 result->time = t->time + (t->zone - tz);
2562 while (result->time < 0)
2563 result->time += SECS_PER_DAY;
2564 while (result->time >= SECS_PER_DAY)
2565 result->time -= SECS_PER_DAY;
2570 PG_RETURN_TIMETZADT_P(result);
2574 * Encode time with time zone type with specified time interval as time zone.
2577 timetz_izone(PG_FUNCTION_ARGS)
2579 Interval *zone = PG_GETARG_INTERVAL_P(0);
2580 TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
2584 if (zone->month != 0)
2586 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2587 errmsg("\"interval\" time zone \"%s\" not valid",
2588 DatumGetCString(DirectFunctionCall1(interval_out,
2589 PointerGetDatum(zone))))));
2591 #ifdef HAVE_INT64_TIMESTAMP
2592 tz = -(zone->time / USECS_PER_SEC);
2597 result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2599 #ifdef HAVE_INT64_TIMESTAMP
2600 result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
2601 while (result->time < INT64CONST(0))
2602 result->time += USECS_PER_DAY;
2603 while (result->time >= USECS_PER_DAY)
2604 result->time -= USECS_PER_DAY;
2606 result->time = time->time + (time->zone - tz);
2607 while (result->time < 0)
2608 result->time += SECS_PER_DAY;
2609 while (result->time >= SECS_PER_DAY)
2610 result->time -= SECS_PER_DAY;
2615 PG_RETURN_TIMETZADT_P(result);