]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/date.c
271b57a83027c7f11e5d2f771e183829ffd35a94
[postgresql] / src / backend / utils / adt / date.c
1 /*-------------------------------------------------------------------------
2  *
3  * date.c
4  *        implements DATE and TIME data types specified in SQL-92 standard
5  *
6  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994-5, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        src/backend/utils/adt/date.c
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include <ctype.h>
19 #include <limits.h>
20 #include <float.h>
21 #include <time.h>
22
23 #include "access/hash.h"
24 #include "libpq/pqformat.h"
25 #include "miscadmin.h"
26 #include "parser/scansup.h"
27 #include "utils/array.h"
28 #include "utils/builtins.h"
29 #include "utils/date.h"
30 #include "utils/datetime.h"
31 #include "utils/nabstime.h"
32
33 /*
34  * gcc's -ffast-math switch breaks routines that expect exact results from
35  * expressions like timeval / SECS_PER_HOUR, where timeval is double.
36  */
37 #ifdef __FAST_MATH__
38 #error -ffast-math is known to break this code
39 #endif
40
41
42 static void EncodeSpecialDate(DateADT dt, char *str);
43 static int      time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
44 static int      timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
45 static int      tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
46 static int      tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
47 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
48
49
50 /* common code for timetypmodin and timetztypmodin */
51 static int32
52 anytime_typmodin(bool istz, ArrayType *ta)
53 {
54         int32           typmod;
55         int32      *tl;
56         int                     n;
57
58         tl = ArrayGetIntegerTypmods(ta, &n);
59
60         /*
61          * we're not too tense about good error message here because grammar
62          * shouldn't allow wrong number of modifiers for TIME
63          */
64         if (n != 1)
65                 ereport(ERROR,
66                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
67                                  errmsg("invalid type modifier")));
68
69         if (*tl < 0)
70                 ereport(ERROR,
71                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
72                                  errmsg("TIME(%d)%s precision must not be negative",
73                                                 *tl, (istz ? " WITH TIME ZONE" : ""))));
74         if (*tl > MAX_TIME_PRECISION)
75         {
76                 ereport(WARNING,
77                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
78                                  errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
79                                                 *tl, (istz ? " WITH TIME ZONE" : ""),
80                                                 MAX_TIME_PRECISION)));
81                 typmod = MAX_TIME_PRECISION;
82         }
83         else
84                 typmod = *tl;
85
86         return typmod;
87 }
88
89 /* common code for timetypmodout and timetztypmodout */
90 static char *
91 anytime_typmodout(bool istz, int32 typmod)
92 {
93         char       *res = (char *) palloc(64);
94         const char *tz = istz ? " with time zone" : " without time zone";
95
96         if (typmod >= 0)
97                 snprintf(res, 64, "(%d)%s", (int) typmod, tz);
98         else
99                 snprintf(res, 64, "%s", tz);
100         return res;
101 }
102
103
104 /*****************************************************************************
105  *       Date ADT
106  *****************************************************************************/
107
108
109 /* date_in()
110  * Given date text string, convert to internal date format.
111  */
112 Datum
113 date_in(PG_FUNCTION_ARGS)
114 {
115         char       *str = PG_GETARG_CSTRING(0);
116         DateADT         date;
117         fsec_t          fsec;
118         struct pg_tm tt,
119                            *tm = &tt;
120         int                     tzp;
121         int                     dtype;
122         int                     nf;
123         int                     dterr;
124         char       *field[MAXDATEFIELDS];
125         int                     ftype[MAXDATEFIELDS];
126         char            workbuf[MAXDATELEN + 1];
127
128         dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
129                                                   field, ftype, MAXDATEFIELDS, &nf);
130         if (dterr == 0)
131                 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
132         if (dterr != 0)
133                 DateTimeParseError(dterr, str, "date");
134
135         switch (dtype)
136         {
137                 case DTK_DATE:
138                         break;
139
140                 case DTK_CURRENT:
141                         ereport(ERROR,
142                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
143                           errmsg("date/time value \"current\" is no longer supported")));
144
145                         GetCurrentDateTime(tm);
146                         break;
147
148                 case DTK_EPOCH:
149                         GetEpochTime(tm);
150                         break;
151
152                 case DTK_LATE:
153                         DATE_NOEND(date);
154                         PG_RETURN_DATEADT(date);
155
156                 case DTK_EARLY:
157                         DATE_NOBEGIN(date);
158                         PG_RETURN_DATEADT(date);
159
160                 default:
161                         DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
162                         break;
163         }
164
165         if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
166                 ereport(ERROR,
167                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
168                                  errmsg("date out of range: \"%s\"", str)));
169
170         date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
171
172         PG_RETURN_DATEADT(date);
173 }
174
175 /* date_out()
176  * Given internal format date, convert to text string.
177  */
178 Datum
179 date_out(PG_FUNCTION_ARGS)
180 {
181         DateADT         date = PG_GETARG_DATEADT(0);
182         char       *result;
183         struct pg_tm tt,
184                            *tm = &tt;
185         char            buf[MAXDATELEN + 1];
186
187         if (DATE_NOT_FINITE(date))
188                 EncodeSpecialDate(date, buf);
189         else
190         {
191                 j2date(date + POSTGRES_EPOCH_JDATE,
192                            &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
193                 EncodeDateOnly(tm, DateStyle, buf);
194         }
195
196         result = pstrdup(buf);
197         PG_RETURN_CSTRING(result);
198 }
199
200 /*
201  *              date_recv                       - converts external binary format to date
202  */
203 Datum
204 date_recv(PG_FUNCTION_ARGS)
205 {
206         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
207         DateADT         result;
208
209         result = (DateADT) pq_getmsgint(buf, sizeof(DateADT));
210
211         /* Limit to the same range that date_in() accepts. */
212         if (DATE_NOT_FINITE(result))
213                  /* ok */ ;
214         else if (result < -POSTGRES_EPOCH_JDATE ||
215                          result >= JULIAN_MAX - POSTGRES_EPOCH_JDATE)
216                 ereport(ERROR,
217                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
218                                  errmsg("date out of range")));
219
220         PG_RETURN_DATEADT(result);
221 }
222
223 /*
224  *              date_send                       - converts date to binary format
225  */
226 Datum
227 date_send(PG_FUNCTION_ARGS)
228 {
229         DateADT         date = PG_GETARG_DATEADT(0);
230         StringInfoData buf;
231
232         pq_begintypsend(&buf);
233         pq_sendint(&buf, date, sizeof(date));
234         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
235 }
236
237 /*
238  * Convert reserved date values to string.
239  */
240 static void
241 EncodeSpecialDate(DateADT dt, char *str)
242 {
243         if (DATE_IS_NOBEGIN(dt))
244                 strcpy(str, EARLY);
245         else if (DATE_IS_NOEND(dt))
246                 strcpy(str, LATE);
247         else    /* shouldn't happen */
248                 elog(ERROR, "invalid argument for EncodeSpecialDate");
249 }
250
251
252 /*
253  * Comparison functions for dates
254  */
255
256 Datum
257 date_eq(PG_FUNCTION_ARGS)
258 {
259         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
260         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
261
262         PG_RETURN_BOOL(dateVal1 == dateVal2);
263 }
264
265 Datum
266 date_ne(PG_FUNCTION_ARGS)
267 {
268         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
269         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
270
271         PG_RETURN_BOOL(dateVal1 != dateVal2);
272 }
273
274 Datum
275 date_lt(PG_FUNCTION_ARGS)
276 {
277         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
278         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
279
280         PG_RETURN_BOOL(dateVal1 < dateVal2);
281 }
282
283 Datum
284 date_le(PG_FUNCTION_ARGS)
285 {
286         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
287         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
288
289         PG_RETURN_BOOL(dateVal1 <= dateVal2);
290 }
291
292 Datum
293 date_gt(PG_FUNCTION_ARGS)
294 {
295         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
296         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
297
298         PG_RETURN_BOOL(dateVal1 > dateVal2);
299 }
300
301 Datum
302 date_ge(PG_FUNCTION_ARGS)
303 {
304         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
305         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
306
307         PG_RETURN_BOOL(dateVal1 >= dateVal2);
308 }
309
310 Datum
311 date_cmp(PG_FUNCTION_ARGS)
312 {
313         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
314         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
315
316         if (dateVal1 < dateVal2)
317                 PG_RETURN_INT32(-1);
318         else if (dateVal1 > dateVal2)
319                 PG_RETURN_INT32(1);
320         PG_RETURN_INT32(0);
321 }
322
323 Datum
324 date_finite(PG_FUNCTION_ARGS)
325 {
326         DateADT         date = PG_GETARG_DATEADT(0);
327
328         PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
329 }
330
331 Datum
332 date_larger(PG_FUNCTION_ARGS)
333 {
334         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
335         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
336
337         PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
338 }
339
340 Datum
341 date_smaller(PG_FUNCTION_ARGS)
342 {
343         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
344         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
345
346         PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
347 }
348
349 /* Compute difference between two dates in days.
350  */
351 Datum
352 date_mi(PG_FUNCTION_ARGS)
353 {
354         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
355         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
356
357         if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
358                 ereport(ERROR,
359                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
360                                  errmsg("cannot subtract infinite dates")));
361
362         PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
363 }
364
365 /* Add a number of days to a date, giving a new date.
366  * Must handle both positive and negative numbers of days.
367  */
368 Datum
369 date_pli(PG_FUNCTION_ARGS)
370 {
371         DateADT         dateVal = PG_GETARG_DATEADT(0);
372         int32           days = PG_GETARG_INT32(1);
373
374         if (DATE_NOT_FINITE(dateVal))
375                 days = 0;                               /* can't change infinity */
376
377         PG_RETURN_DATEADT(dateVal + days);
378 }
379
380 /* Subtract a number of days from a date, giving a new date.
381  */
382 Datum
383 date_mii(PG_FUNCTION_ARGS)
384 {
385         DateADT         dateVal = PG_GETARG_DATEADT(0);
386         int32           days = PG_GETARG_INT32(1);
387
388         if (DATE_NOT_FINITE(dateVal))
389                 days = 0;                               /* can't change infinity */
390
391         PG_RETURN_DATEADT(dateVal - days);
392 }
393
394 /*
395  * Internal routines for promoting date to timestamp and timestamp with
396  * time zone
397  */
398
399 static Timestamp
400 date2timestamp(DateADT dateVal)
401 {
402         Timestamp       result;
403
404         if (DATE_IS_NOBEGIN(dateVal))
405                 TIMESTAMP_NOBEGIN(result);
406         else if (DATE_IS_NOEND(dateVal))
407                 TIMESTAMP_NOEND(result);
408         else
409         {
410 #ifdef HAVE_INT64_TIMESTAMP
411                 /* date is days since 2000, timestamp is microseconds since same... */
412                 result = dateVal * USECS_PER_DAY;
413                 /* Date's range is wider than timestamp's, so check for overflow */
414                 if (result / USECS_PER_DAY != dateVal)
415                         ereport(ERROR,
416                                         (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
417                                          errmsg("date out of range for timestamp")));
418 #else
419                 /* date is days since 2000, timestamp is seconds since same... */
420                 result = dateVal * (double) SECS_PER_DAY;
421 #endif
422         }
423
424         return result;
425 }
426
427 static TimestampTz
428 date2timestamptz(DateADT dateVal)
429 {
430         TimestampTz result;
431         struct pg_tm tt,
432                            *tm = &tt;
433         int                     tz;
434
435         if (DATE_IS_NOBEGIN(dateVal))
436                 TIMESTAMP_NOBEGIN(result);
437         else if (DATE_IS_NOEND(dateVal))
438                 TIMESTAMP_NOEND(result);
439         else
440         {
441                 j2date(dateVal + POSTGRES_EPOCH_JDATE,
442                            &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
443                 tm->tm_hour = 0;
444                 tm->tm_min = 0;
445                 tm->tm_sec = 0;
446                 tz = DetermineTimeZoneOffset(tm, session_timezone);
447
448 #ifdef HAVE_INT64_TIMESTAMP
449                 result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
450                 /* Date's range is wider than timestamp's, so check for overflow */
451                 if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
452                         ereport(ERROR,
453                                         (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
454                                          errmsg("date out of range for timestamp")));
455 #else
456                 result = dateVal * (double) SECS_PER_DAY + tz;
457 #endif
458         }
459
460         return result;
461 }
462
463 /*
464  * date2timestamp_no_overflow
465  *
466  * This is chartered to produce a double value that is numerically
467  * equivalent to the corresponding Timestamp value, if the date is in the
468  * valid range of Timestamps, but in any case not throw an overflow error.
469  * We can do this since the numerical range of double is greater than
470  * that of non-erroneous timestamps.  The results are currently only
471  * used for statistical estimation purposes.
472  */
473 double
474 date2timestamp_no_overflow(DateADT dateVal)
475 {
476         double          result;
477
478         if (DATE_IS_NOBEGIN(dateVal))
479                 result = -DBL_MAX;
480         else if (DATE_IS_NOEND(dateVal))
481                 result = DBL_MAX;
482         else
483         {
484 #ifdef HAVE_INT64_TIMESTAMP
485                 /* date is days since 2000, timestamp is microseconds since same... */
486                 result = dateVal * (double) USECS_PER_DAY;
487 #else
488                 /* date is days since 2000, timestamp is seconds since same... */
489                 result = dateVal * (double) SECS_PER_DAY;
490 #endif
491         }
492
493         return result;
494 }
495
496
497 /*
498  * Crosstype comparison functions for dates
499  */
500
501 Datum
502 date_eq_timestamp(PG_FUNCTION_ARGS)
503 {
504         DateADT         dateVal = PG_GETARG_DATEADT(0);
505         Timestamp       dt2 = PG_GETARG_TIMESTAMP(1);
506         Timestamp       dt1;
507
508         dt1 = date2timestamp(dateVal);
509
510         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
511 }
512
513 Datum
514 date_ne_timestamp(PG_FUNCTION_ARGS)
515 {
516         DateADT         dateVal = PG_GETARG_DATEADT(0);
517         Timestamp       dt2 = PG_GETARG_TIMESTAMP(1);
518         Timestamp       dt1;
519
520         dt1 = date2timestamp(dateVal);
521
522         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
523 }
524
525 Datum
526 date_lt_timestamp(PG_FUNCTION_ARGS)
527 {
528         DateADT         dateVal = PG_GETARG_DATEADT(0);
529         Timestamp       dt2 = PG_GETARG_TIMESTAMP(1);
530         Timestamp       dt1;
531
532         dt1 = date2timestamp(dateVal);
533
534         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
535 }
536
537 Datum
538 date_gt_timestamp(PG_FUNCTION_ARGS)
539 {
540         DateADT         dateVal = PG_GETARG_DATEADT(0);
541         Timestamp       dt2 = PG_GETARG_TIMESTAMP(1);
542         Timestamp       dt1;
543
544         dt1 = date2timestamp(dateVal);
545
546         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
547 }
548
549 Datum
550 date_le_timestamp(PG_FUNCTION_ARGS)
551 {
552         DateADT         dateVal = PG_GETARG_DATEADT(0);
553         Timestamp       dt2 = PG_GETARG_TIMESTAMP(1);
554         Timestamp       dt1;
555
556         dt1 = date2timestamp(dateVal);
557
558         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
559 }
560
561 Datum
562 date_ge_timestamp(PG_FUNCTION_ARGS)
563 {
564         DateADT         dateVal = PG_GETARG_DATEADT(0);
565         Timestamp       dt2 = PG_GETARG_TIMESTAMP(1);
566         Timestamp       dt1;
567
568         dt1 = date2timestamp(dateVal);
569
570         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
571 }
572
573 Datum
574 date_cmp_timestamp(PG_FUNCTION_ARGS)
575 {
576         DateADT         dateVal = PG_GETARG_DATEADT(0);
577         Timestamp       dt2 = PG_GETARG_TIMESTAMP(1);
578         Timestamp       dt1;
579
580         dt1 = date2timestamp(dateVal);
581
582         PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
583 }
584
585 Datum
586 date_eq_timestamptz(PG_FUNCTION_ARGS)
587 {
588         DateADT         dateVal = PG_GETARG_DATEADT(0);
589         TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
590         TimestampTz dt1;
591
592         dt1 = date2timestamptz(dateVal);
593
594         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
595 }
596
597 Datum
598 date_ne_timestamptz(PG_FUNCTION_ARGS)
599 {
600         DateADT         dateVal = PG_GETARG_DATEADT(0);
601         TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
602         TimestampTz dt1;
603
604         dt1 = date2timestamptz(dateVal);
605
606         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
607 }
608
609 Datum
610 date_lt_timestamptz(PG_FUNCTION_ARGS)
611 {
612         DateADT         dateVal = PG_GETARG_DATEADT(0);
613         TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
614         TimestampTz dt1;
615
616         dt1 = date2timestamptz(dateVal);
617
618         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
619 }
620
621 Datum
622 date_gt_timestamptz(PG_FUNCTION_ARGS)
623 {
624         DateADT         dateVal = PG_GETARG_DATEADT(0);
625         TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
626         TimestampTz dt1;
627
628         dt1 = date2timestamptz(dateVal);
629
630         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
631 }
632
633 Datum
634 date_le_timestamptz(PG_FUNCTION_ARGS)
635 {
636         DateADT         dateVal = PG_GETARG_DATEADT(0);
637         TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
638         TimestampTz dt1;
639
640         dt1 = date2timestamptz(dateVal);
641
642         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
643 }
644
645 Datum
646 date_ge_timestamptz(PG_FUNCTION_ARGS)
647 {
648         DateADT         dateVal = PG_GETARG_DATEADT(0);
649         TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
650         TimestampTz dt1;
651
652         dt1 = date2timestamptz(dateVal);
653
654         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
655 }
656
657 Datum
658 date_cmp_timestamptz(PG_FUNCTION_ARGS)
659 {
660         DateADT         dateVal = PG_GETARG_DATEADT(0);
661         TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
662         TimestampTz dt1;
663
664         dt1 = date2timestamptz(dateVal);
665
666         PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
667 }
668
669 Datum
670 timestamp_eq_date(PG_FUNCTION_ARGS)
671 {
672         Timestamp       dt1 = PG_GETARG_TIMESTAMP(0);
673         DateADT         dateVal = PG_GETARG_DATEADT(1);
674         Timestamp       dt2;
675
676         dt2 = date2timestamp(dateVal);
677
678         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
679 }
680
681 Datum
682 timestamp_ne_date(PG_FUNCTION_ARGS)
683 {
684         Timestamp       dt1 = PG_GETARG_TIMESTAMP(0);
685         DateADT         dateVal = PG_GETARG_DATEADT(1);
686         Timestamp       dt2;
687
688         dt2 = date2timestamp(dateVal);
689
690         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
691 }
692
693 Datum
694 timestamp_lt_date(PG_FUNCTION_ARGS)
695 {
696         Timestamp       dt1 = PG_GETARG_TIMESTAMP(0);
697         DateADT         dateVal = PG_GETARG_DATEADT(1);
698         Timestamp       dt2;
699
700         dt2 = date2timestamp(dateVal);
701
702         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
703 }
704
705 Datum
706 timestamp_gt_date(PG_FUNCTION_ARGS)
707 {
708         Timestamp       dt1 = PG_GETARG_TIMESTAMP(0);
709         DateADT         dateVal = PG_GETARG_DATEADT(1);
710         Timestamp       dt2;
711
712         dt2 = date2timestamp(dateVal);
713
714         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
715 }
716
717 Datum
718 timestamp_le_date(PG_FUNCTION_ARGS)
719 {
720         Timestamp       dt1 = PG_GETARG_TIMESTAMP(0);
721         DateADT         dateVal = PG_GETARG_DATEADT(1);
722         Timestamp       dt2;
723
724         dt2 = date2timestamp(dateVal);
725
726         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
727 }
728
729 Datum
730 timestamp_ge_date(PG_FUNCTION_ARGS)
731 {
732         Timestamp       dt1 = PG_GETARG_TIMESTAMP(0);
733         DateADT         dateVal = PG_GETARG_DATEADT(1);
734         Timestamp       dt2;
735
736         dt2 = date2timestamp(dateVal);
737
738         PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
739 }
740
741 Datum
742 timestamp_cmp_date(PG_FUNCTION_ARGS)
743 {
744         Timestamp       dt1 = PG_GETARG_TIMESTAMP(0);
745         DateADT         dateVal = PG_GETARG_DATEADT(1);
746         Timestamp       dt2;
747
748         dt2 = date2timestamp(dateVal);
749
750         PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
751 }
752
753 Datum
754 timestamptz_eq_date(PG_FUNCTION_ARGS)
755 {
756         TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
757         DateADT         dateVal = PG_GETARG_DATEADT(1);
758         TimestampTz dt2;
759
760         dt2 = date2timestamptz(dateVal);
761
762         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
763 }
764
765 Datum
766 timestamptz_ne_date(PG_FUNCTION_ARGS)
767 {
768         TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
769         DateADT         dateVal = PG_GETARG_DATEADT(1);
770         TimestampTz dt2;
771
772         dt2 = date2timestamptz(dateVal);
773
774         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
775 }
776
777 Datum
778 timestamptz_lt_date(PG_FUNCTION_ARGS)
779 {
780         TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
781         DateADT         dateVal = PG_GETARG_DATEADT(1);
782         TimestampTz dt2;
783
784         dt2 = date2timestamptz(dateVal);
785
786         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
787 }
788
789 Datum
790 timestamptz_gt_date(PG_FUNCTION_ARGS)
791 {
792         TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
793         DateADT         dateVal = PG_GETARG_DATEADT(1);
794         TimestampTz dt2;
795
796         dt2 = date2timestamptz(dateVal);
797
798         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
799 }
800
801 Datum
802 timestamptz_le_date(PG_FUNCTION_ARGS)
803 {
804         TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
805         DateADT         dateVal = PG_GETARG_DATEADT(1);
806         TimestampTz dt2;
807
808         dt2 = date2timestamptz(dateVal);
809
810         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
811 }
812
813 Datum
814 timestamptz_ge_date(PG_FUNCTION_ARGS)
815 {
816         TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
817         DateADT         dateVal = PG_GETARG_DATEADT(1);
818         TimestampTz dt2;
819
820         dt2 = date2timestamptz(dateVal);
821
822         PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
823 }
824
825 Datum
826 timestamptz_cmp_date(PG_FUNCTION_ARGS)
827 {
828         TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
829         DateADT         dateVal = PG_GETARG_DATEADT(1);
830         TimestampTz dt2;
831
832         dt2 = date2timestamptz(dateVal);
833
834         PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
835 }
836
837
838 /* Add an interval to a date, giving a new date.
839  * Must handle both positive and negative intervals.
840  *
841  * We implement this by promoting the date to timestamp (without time zone)
842  * and then using the timestamp plus interval function.
843  */
844 Datum
845 date_pl_interval(PG_FUNCTION_ARGS)
846 {
847         DateADT         dateVal = PG_GETARG_DATEADT(0);
848         Interval   *span = PG_GETARG_INTERVAL_P(1);
849         Timestamp       dateStamp;
850
851         dateStamp = date2timestamp(dateVal);
852
853         return DirectFunctionCall2(timestamp_pl_interval,
854                                                            TimestampGetDatum(dateStamp),
855                                                            PointerGetDatum(span));
856 }
857
858 /* Subtract an interval from a date, giving a new date.
859  * Must handle both positive and negative intervals.
860  *
861  * We implement this by promoting the date to timestamp (without time zone)
862  * and then using the timestamp minus interval function.
863  */
864 Datum
865 date_mi_interval(PG_FUNCTION_ARGS)
866 {
867         DateADT         dateVal = PG_GETARG_DATEADT(0);
868         Interval   *span = PG_GETARG_INTERVAL_P(1);
869         Timestamp       dateStamp;
870
871         dateStamp = date2timestamp(dateVal);
872
873         return DirectFunctionCall2(timestamp_mi_interval,
874                                                            TimestampGetDatum(dateStamp),
875                                                            PointerGetDatum(span));
876 }
877
878 /* date_timestamp()
879  * Convert date to timestamp data type.
880  */
881 Datum
882 date_timestamp(PG_FUNCTION_ARGS)
883 {
884         DateADT         dateVal = PG_GETARG_DATEADT(0);
885         Timestamp       result;
886
887         result = date2timestamp(dateVal);
888
889         PG_RETURN_TIMESTAMP(result);
890 }
891
892 /* timestamp_date()
893  * Convert timestamp to date data type.
894  */
895 Datum
896 timestamp_date(PG_FUNCTION_ARGS)
897 {
898         Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
899         DateADT         result;
900         struct pg_tm tt,
901                            *tm = &tt;
902         fsec_t          fsec;
903
904         if (TIMESTAMP_IS_NOBEGIN(timestamp))
905                 DATE_NOBEGIN(result);
906         else if (TIMESTAMP_IS_NOEND(timestamp))
907                 DATE_NOEND(result);
908         else
909         {
910                 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
911                         ereport(ERROR,
912                                         (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
913                                          errmsg("timestamp out of range")));
914
915                 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
916         }
917
918         PG_RETURN_DATEADT(result);
919 }
920
921
922 /* date_timestamptz()
923  * Convert date to timestamp with time zone data type.
924  */
925 Datum
926 date_timestamptz(PG_FUNCTION_ARGS)
927 {
928         DateADT         dateVal = PG_GETARG_DATEADT(0);
929         TimestampTz result;
930
931         result = date2timestamptz(dateVal);
932
933         PG_RETURN_TIMESTAMP(result);
934 }
935
936
937 /* timestamptz_date()
938  * Convert timestamp with time zone to date data type.
939  */
940 Datum
941 timestamptz_date(PG_FUNCTION_ARGS)
942 {
943         TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
944         DateADT         result;
945         struct pg_tm tt,
946                            *tm = &tt;
947         fsec_t          fsec;
948         int                     tz;
949         char       *tzn;
950
951         if (TIMESTAMP_IS_NOBEGIN(timestamp))
952                 DATE_NOBEGIN(result);
953         else if (TIMESTAMP_IS_NOEND(timestamp))
954                 DATE_NOEND(result);
955         else
956         {
957                 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
958                         ereport(ERROR,
959                                         (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
960                                          errmsg("timestamp out of range")));
961
962                 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
963         }
964
965         PG_RETURN_DATEADT(result);
966 }
967
968
969 /* abstime_date()
970  * Convert abstime to date data type.
971  */
972 Datum
973 abstime_date(PG_FUNCTION_ARGS)
974 {
975         AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
976         DateADT         result;
977         struct pg_tm tt,
978                            *tm = &tt;
979         int                     tz;
980
981         switch (abstime)
982         {
983                 case INVALID_ABSTIME:
984                         ereport(ERROR,
985                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
986                                    errmsg("cannot convert reserved abstime value to date")));
987                         result = 0;                     /* keep compiler quiet */
988                         break;
989
990                 case NOSTART_ABSTIME:
991                         DATE_NOBEGIN(result);
992                         break;
993
994                 case NOEND_ABSTIME:
995                         DATE_NOEND(result);
996                         break;
997
998                 default:
999                         abstime2tm(abstime, &tz, tm, NULL);
1000                         result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1001                         break;
1002         }
1003
1004         PG_RETURN_DATEADT(result);
1005 }
1006
1007
1008 /*****************************************************************************
1009  *       Time ADT
1010  *****************************************************************************/
1011
1012 Datum
1013 time_in(PG_FUNCTION_ARGS)
1014 {
1015         char       *str = PG_GETARG_CSTRING(0);
1016
1017 #ifdef NOT_USED
1018         Oid                     typelem = PG_GETARG_OID(1);
1019 #endif
1020         int32           typmod = PG_GETARG_INT32(2);
1021         TimeADT         result;
1022         fsec_t          fsec;
1023         struct pg_tm tt,
1024                            *tm = &tt;
1025         int                     tz;
1026         int                     nf;
1027         int                     dterr;
1028         char            workbuf[MAXDATELEN + 1];
1029         char       *field[MAXDATEFIELDS];
1030         int                     dtype;
1031         int                     ftype[MAXDATEFIELDS];
1032
1033         dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1034                                                   field, ftype, MAXDATEFIELDS, &nf);
1035         if (dterr == 0)
1036                 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1037         if (dterr != 0)
1038                 DateTimeParseError(dterr, str, "time");
1039
1040         tm2time(tm, fsec, &result);
1041         AdjustTimeForTypmod(&result, typmod);
1042
1043         PG_RETURN_TIMEADT(result);
1044 }
1045
1046 /* tm2time()
1047  * Convert a tm structure to a time data type.
1048  */
1049 static int
1050 tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
1051 {
1052 #ifdef HAVE_INT64_TIMESTAMP
1053         *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
1054                            * USECS_PER_SEC) + fsec;
1055 #else
1056         *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1057 #endif
1058         return 0;
1059 }
1060
1061 /* time2tm()
1062  * Convert time data type to POSIX time structure.
1063  *
1064  * For dates within the range of pg_time_t, convert to the local time zone.
1065  * If out of this range, leave as UTC (in practice that could only happen
1066  * if pg_time_t is just 32 bits) - thomas 97/05/27
1067  */
1068 static int
1069 time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
1070 {
1071 #ifdef HAVE_INT64_TIMESTAMP
1072         tm->tm_hour = time / USECS_PER_HOUR;
1073         time -= tm->tm_hour * USECS_PER_HOUR;
1074         tm->tm_min = time / USECS_PER_MINUTE;
1075         time -= tm->tm_min * USECS_PER_MINUTE;
1076         tm->tm_sec = time / USECS_PER_SEC;
1077         time -= tm->tm_sec * USECS_PER_SEC;
1078         *fsec = time;
1079 #else
1080         double          trem;
1081
1082 recalc:
1083         trem = time;
1084         TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
1085         TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1086         TMODULO(trem, tm->tm_sec, 1.0);
1087         trem = TIMEROUND(trem);
1088         /* roundoff may need to propagate to higher-order fields */
1089         if (trem >= 1.0)
1090         {
1091                 time = ceil(time);
1092                 goto recalc;
1093         }
1094         *fsec = trem;
1095 #endif
1096
1097         return 0;
1098 }
1099
1100 Datum
1101 time_out(PG_FUNCTION_ARGS)
1102 {
1103         TimeADT         time = PG_GETARG_TIMEADT(0);
1104         char       *result;
1105         struct pg_tm tt,
1106                            *tm = &tt;
1107         fsec_t          fsec;
1108         char            buf[MAXDATELEN + 1];
1109
1110         time2tm(time, tm, &fsec);
1111         EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
1112
1113         result = pstrdup(buf);
1114         PG_RETURN_CSTRING(result);
1115 }
1116
1117 /*
1118  *              time_recv                       - converts external binary format to time
1119  *
1120  * We make no attempt to provide compatibility between int and float
1121  * time representations ...
1122  */
1123 Datum
1124 time_recv(PG_FUNCTION_ARGS)
1125 {
1126         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
1127
1128 #ifdef NOT_USED
1129         Oid                     typelem = PG_GETARG_OID(1);
1130 #endif
1131         int32           typmod = PG_GETARG_INT32(2);
1132         TimeADT         result;
1133
1134 #ifdef HAVE_INT64_TIMESTAMP
1135         result = pq_getmsgint64(buf);
1136
1137         if (result < INT64CONST(0) || result > USECS_PER_DAY)
1138                 ereport(ERROR,
1139                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1140                                  errmsg("time out of range")));
1141 #else
1142         result = pq_getmsgfloat8(buf);
1143
1144         if (result < 0 || result > (double) SECS_PER_DAY)
1145                 ereport(ERROR,
1146                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1147                                  errmsg("time out of range")));
1148 #endif
1149
1150         AdjustTimeForTypmod(&result, typmod);
1151
1152         PG_RETURN_TIMEADT(result);
1153 }
1154
1155 /*
1156  *              time_send                       - converts time to binary format
1157  */
1158 Datum
1159 time_send(PG_FUNCTION_ARGS)
1160 {
1161         TimeADT         time = PG_GETARG_TIMEADT(0);
1162         StringInfoData buf;
1163
1164         pq_begintypsend(&buf);
1165 #ifdef HAVE_INT64_TIMESTAMP
1166         pq_sendint64(&buf, time);
1167 #else
1168         pq_sendfloat8(&buf, time);
1169 #endif
1170         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1171 }
1172
1173 Datum
1174 timetypmodin(PG_FUNCTION_ARGS)
1175 {
1176         ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
1177
1178         PG_RETURN_INT32(anytime_typmodin(false, ta));
1179 }
1180
1181 Datum
1182 timetypmodout(PG_FUNCTION_ARGS)
1183 {
1184         int32           typmod = PG_GETARG_INT32(0);
1185
1186         PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
1187 }
1188
1189
1190 /* time_scale()
1191  * Adjust time type for specified scale factor.
1192  * Used by PostgreSQL type system to stuff columns.
1193  */
1194 Datum
1195 time_scale(PG_FUNCTION_ARGS)
1196 {
1197         TimeADT         time = PG_GETARG_TIMEADT(0);
1198         int32           typmod = PG_GETARG_INT32(1);
1199         TimeADT         result;
1200
1201         result = time;
1202         AdjustTimeForTypmod(&result, typmod);
1203
1204         PG_RETURN_TIMEADT(result);
1205 }
1206
1207 /* AdjustTimeForTypmod()
1208  * Force the precision of the time value to a specified value.
1209  * Uses *exactly* the same code as in AdjustTimestampForTypemod()
1210  * but we make a separate copy because those types do not
1211  * have a fundamental tie together but rather a coincidence of
1212  * implementation. - thomas
1213  */
1214 static void
1215 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1216 {
1217 #ifdef HAVE_INT64_TIMESTAMP
1218         static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1219                 INT64CONST(1000000),
1220                 INT64CONST(100000),
1221                 INT64CONST(10000),
1222                 INT64CONST(1000),
1223                 INT64CONST(100),
1224                 INT64CONST(10),
1225                 INT64CONST(1)
1226         };
1227
1228         static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1229                 INT64CONST(500000),
1230                 INT64CONST(50000),
1231                 INT64CONST(5000),
1232                 INT64CONST(500),
1233                 INT64CONST(50),
1234                 INT64CONST(5),
1235                 INT64CONST(0)
1236         };
1237 #else
1238         /* note MAX_TIME_PRECISION differs in this case */
1239         static const double TimeScales[MAX_TIME_PRECISION + 1] = {
1240                 1.0,
1241                 10.0,
1242                 100.0,
1243                 1000.0,
1244                 10000.0,
1245                 100000.0,
1246                 1000000.0,
1247                 10000000.0,
1248                 100000000.0,
1249                 1000000000.0,
1250                 10000000000.0
1251         };
1252 #endif
1253
1254         if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1255         {
1256                 /*
1257                  * Note: this round-to-nearest code is not completely consistent about
1258                  * rounding values that are exactly halfway between integral values.
1259                  * On most platforms, rint() will implement round-to-nearest-even, but
1260                  * the integer code always rounds up (away from zero).  Is it worth
1261                  * trying to be consistent?
1262                  */
1263 #ifdef HAVE_INT64_TIMESTAMP
1264                 if (*time >= INT64CONST(0))
1265                         *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
1266                                 TimeScales[typmod];
1267                 else
1268                         *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
1269                                           TimeScales[typmod]);
1270 #else
1271                 *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
1272 #endif
1273         }
1274 }
1275
1276
1277 Datum
1278 time_eq(PG_FUNCTION_ARGS)
1279 {
1280         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1281         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1282
1283         PG_RETURN_BOOL(time1 == time2);
1284 }
1285
1286 Datum
1287 time_ne(PG_FUNCTION_ARGS)
1288 {
1289         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1290         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1291
1292         PG_RETURN_BOOL(time1 != time2);
1293 }
1294
1295 Datum
1296 time_lt(PG_FUNCTION_ARGS)
1297 {
1298         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1299         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1300
1301         PG_RETURN_BOOL(time1 < time2);
1302 }
1303
1304 Datum
1305 time_le(PG_FUNCTION_ARGS)
1306 {
1307         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1308         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1309
1310         PG_RETURN_BOOL(time1 <= time2);
1311 }
1312
1313 Datum
1314 time_gt(PG_FUNCTION_ARGS)
1315 {
1316         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1317         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1318
1319         PG_RETURN_BOOL(time1 > time2);
1320 }
1321
1322 Datum
1323 time_ge(PG_FUNCTION_ARGS)
1324 {
1325         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1326         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1327
1328         PG_RETURN_BOOL(time1 >= time2);
1329 }
1330
1331 Datum
1332 time_cmp(PG_FUNCTION_ARGS)
1333 {
1334         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1335         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1336
1337         if (time1 < time2)
1338                 PG_RETURN_INT32(-1);
1339         if (time1 > time2)
1340                 PG_RETURN_INT32(1);
1341         PG_RETURN_INT32(0);
1342 }
1343
1344 Datum
1345 time_hash(PG_FUNCTION_ARGS)
1346 {
1347         /* We can use either hashint8 or hashfloat8 directly */
1348 #ifdef HAVE_INT64_TIMESTAMP
1349         return hashint8(fcinfo);
1350 #else
1351         return hashfloat8(fcinfo);
1352 #endif
1353 }
1354
1355 Datum
1356 time_larger(PG_FUNCTION_ARGS)
1357 {
1358         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1359         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1360
1361         PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1362 }
1363
1364 Datum
1365 time_smaller(PG_FUNCTION_ARGS)
1366 {
1367         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1368         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1369
1370         PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1371 }
1372
1373 /* overlaps_time() --- implements the SQL92 OVERLAPS operator.
1374  *
1375  * Algorithm is per SQL92 spec.  This is much harder than you'd think
1376  * because the spec requires us to deliver a non-null answer in some cases
1377  * where some of the inputs are null.
1378  */
1379 Datum
1380 overlaps_time(PG_FUNCTION_ARGS)
1381 {
1382         /*
1383          * The arguments are TimeADT, but we leave them as generic Datums to avoid
1384          * dereferencing nulls (TimeADT is pass-by-reference!)
1385          */
1386         Datum           ts1 = PG_GETARG_DATUM(0);
1387         Datum           te1 = PG_GETARG_DATUM(1);
1388         Datum           ts2 = PG_GETARG_DATUM(2);
1389         Datum           te2 = PG_GETARG_DATUM(3);
1390         bool            ts1IsNull = PG_ARGISNULL(0);
1391         bool            te1IsNull = PG_ARGISNULL(1);
1392         bool            ts2IsNull = PG_ARGISNULL(2);
1393         bool            te2IsNull = PG_ARGISNULL(3);
1394
1395 #define TIMEADT_GT(t1,t2) \
1396         (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1397 #define TIMEADT_LT(t1,t2) \
1398         (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1399
1400         /*
1401          * If both endpoints of interval 1 are null, the result is null (unknown).
1402          * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1403          * take ts1 as the lesser endpoint.
1404          */
1405         if (ts1IsNull)
1406         {
1407                 if (te1IsNull)
1408                         PG_RETURN_NULL();
1409                 /* swap null for non-null */
1410                 ts1 = te1;
1411                 te1IsNull = true;
1412         }
1413         else if (!te1IsNull)
1414         {
1415                 if (TIMEADT_GT(ts1, te1))
1416                 {
1417                         Datum           tt = ts1;
1418
1419                         ts1 = te1;
1420                         te1 = tt;
1421                 }
1422         }
1423
1424         /* Likewise for interval 2. */
1425         if (ts2IsNull)
1426         {
1427                 if (te2IsNull)
1428                         PG_RETURN_NULL();
1429                 /* swap null for non-null */
1430                 ts2 = te2;
1431                 te2IsNull = true;
1432         }
1433         else if (!te2IsNull)
1434         {
1435                 if (TIMEADT_GT(ts2, te2))
1436                 {
1437                         Datum           tt = ts2;
1438
1439                         ts2 = te2;
1440                         te2 = tt;
1441                 }
1442         }
1443
1444         /*
1445          * At this point neither ts1 nor ts2 is null, so we can consider three
1446          * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1447          */
1448         if (TIMEADT_GT(ts1, ts2))
1449         {
1450                 /*
1451                  * This case is ts1 < te2 OR te1 < te2, which may look redundant but
1452                  * in the presence of nulls it's not quite completely so.
1453                  */
1454                 if (te2IsNull)
1455                         PG_RETURN_NULL();
1456                 if (TIMEADT_LT(ts1, te2))
1457                         PG_RETURN_BOOL(true);
1458                 if (te1IsNull)
1459                         PG_RETURN_NULL();
1460
1461                 /*
1462                  * If te1 is not null then we had ts1 <= te1 above, and we just found
1463                  * ts1 >= te2, hence te1 >= te2.
1464                  */
1465                 PG_RETURN_BOOL(false);
1466         }
1467         else if (TIMEADT_LT(ts1, ts2))
1468         {
1469                 /* This case is ts2 < te1 OR te2 < te1 */
1470                 if (te1IsNull)
1471                         PG_RETURN_NULL();
1472                 if (TIMEADT_LT(ts2, te1))
1473                         PG_RETURN_BOOL(true);
1474                 if (te2IsNull)
1475                         PG_RETURN_NULL();
1476
1477                 /*
1478                  * If te2 is not null then we had ts2 <= te2 above, and we just found
1479                  * ts2 >= te1, hence te2 >= te1.
1480                  */
1481                 PG_RETURN_BOOL(false);
1482         }
1483         else
1484         {
1485                 /*
1486                  * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1487                  * rather silly way of saying "true if both are nonnull, else null".
1488                  */
1489                 if (te1IsNull || te2IsNull)
1490                         PG_RETURN_NULL();
1491                 PG_RETURN_BOOL(true);
1492         }
1493
1494 #undef TIMEADT_GT
1495 #undef TIMEADT_LT
1496 }
1497
1498 /* timestamp_time()
1499  * Convert timestamp to time data type.
1500  */
1501 Datum
1502 timestamp_time(PG_FUNCTION_ARGS)
1503 {
1504         Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
1505         TimeADT         result;
1506         struct pg_tm tt,
1507                            *tm = &tt;
1508         fsec_t          fsec;
1509
1510         if (TIMESTAMP_NOT_FINITE(timestamp))
1511                 PG_RETURN_NULL();
1512
1513         if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1514                 ereport(ERROR,
1515                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1516                                  errmsg("timestamp out of range")));
1517
1518 #ifdef HAVE_INT64_TIMESTAMP
1519
1520         /*
1521          * Could also do this with time = (timestamp / USECS_PER_DAY *
1522          * USECS_PER_DAY) - timestamp;
1523          */
1524         result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1525                           USECS_PER_SEC) + fsec;
1526 #else
1527         result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1528 #endif
1529
1530         PG_RETURN_TIMEADT(result);
1531 }
1532
1533 /* timestamptz_time()
1534  * Convert timestamptz to time data type.
1535  */
1536 Datum
1537 timestamptz_time(PG_FUNCTION_ARGS)
1538 {
1539         TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1540         TimeADT         result;
1541         struct pg_tm tt,
1542                            *tm = &tt;
1543         int                     tz;
1544         fsec_t          fsec;
1545         char       *tzn;
1546
1547         if (TIMESTAMP_NOT_FINITE(timestamp))
1548                 PG_RETURN_NULL();
1549
1550         if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
1551                 ereport(ERROR,
1552                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1553                                  errmsg("timestamp out of range")));
1554
1555 #ifdef HAVE_INT64_TIMESTAMP
1556
1557         /*
1558          * Could also do this with time = (timestamp / USECS_PER_DAY *
1559          * USECS_PER_DAY) - timestamp;
1560          */
1561         result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1562                           USECS_PER_SEC) + fsec;
1563 #else
1564         result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1565 #endif
1566
1567         PG_RETURN_TIMEADT(result);
1568 }
1569
1570 /* datetime_timestamp()
1571  * Convert date and time to timestamp data type.
1572  */
1573 Datum
1574 datetime_timestamp(PG_FUNCTION_ARGS)
1575 {
1576         DateADT         date = PG_GETARG_DATEADT(0);
1577         TimeADT         time = PG_GETARG_TIMEADT(1);
1578         Timestamp       result;
1579
1580         result = date2timestamp(date);
1581         if (!TIMESTAMP_NOT_FINITE(result))
1582                 result += time;
1583
1584         PG_RETURN_TIMESTAMP(result);
1585 }
1586
1587 /* time_interval()
1588  * Convert time to interval data type.
1589  */
1590 Datum
1591 time_interval(PG_FUNCTION_ARGS)
1592 {
1593         TimeADT         time = PG_GETARG_TIMEADT(0);
1594         Interval   *result;
1595
1596         result = (Interval *) palloc(sizeof(Interval));
1597
1598         result->time = time;
1599         result->day = 0;
1600         result->month = 0;
1601
1602         PG_RETURN_INTERVAL_P(result);
1603 }
1604
1605 /* interval_time()
1606  * Convert interval to time data type.
1607  *
1608  * This is defined as producing the fractional-day portion of the interval.
1609  * Therefore, we can just ignore the months field.      It is not real clear
1610  * what to do with negative intervals, but we choose to subtract the floor,
1611  * so that, say, '-2 hours' becomes '22:00:00'.
1612  */
1613 Datum
1614 interval_time(PG_FUNCTION_ARGS)
1615 {
1616         Interval   *span = PG_GETARG_INTERVAL_P(0);
1617         TimeADT         result;
1618
1619 #ifdef HAVE_INT64_TIMESTAMP
1620         int64           days;
1621
1622         result = span->time;
1623         if (result >= USECS_PER_DAY)
1624         {
1625                 days = result / USECS_PER_DAY;
1626                 result -= days * USECS_PER_DAY;
1627         }
1628         else if (result < 0)
1629         {
1630                 days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
1631                 result += days * USECS_PER_DAY;
1632         }
1633 #else
1634         result = span->time;
1635         if (result >= (double) SECS_PER_DAY || result < 0)
1636                 result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
1637 #endif
1638
1639         PG_RETURN_TIMEADT(result);
1640 }
1641
1642 /* time_mi_time()
1643  * Subtract two times to produce an interval.
1644  */
1645 Datum
1646 time_mi_time(PG_FUNCTION_ARGS)
1647 {
1648         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1649         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1650         Interval   *result;
1651
1652         result = (Interval *) palloc(sizeof(Interval));
1653
1654         result->month = 0;
1655         result->day = 0;
1656         result->time = time1 - time2;
1657
1658         PG_RETURN_INTERVAL_P(result);
1659 }
1660
1661 /* time_pl_interval()
1662  * Add interval to time.
1663  */
1664 Datum
1665 time_pl_interval(PG_FUNCTION_ARGS)
1666 {
1667         TimeADT         time = PG_GETARG_TIMEADT(0);
1668         Interval   *span = PG_GETARG_INTERVAL_P(1);
1669         TimeADT         result;
1670
1671 #ifdef HAVE_INT64_TIMESTAMP
1672         result = time + span->time;
1673         result -= result / USECS_PER_DAY * USECS_PER_DAY;
1674         if (result < INT64CONST(0))
1675                 result += USECS_PER_DAY;
1676 #else
1677         TimeADT         time1;
1678
1679         result = time + span->time;
1680         TMODULO(result, time1, (double) SECS_PER_DAY);
1681         if (result < 0)
1682                 result += SECS_PER_DAY;
1683 #endif
1684
1685         PG_RETURN_TIMEADT(result);
1686 }
1687
1688 /* time_mi_interval()
1689  * Subtract interval from time.
1690  */
1691 Datum
1692 time_mi_interval(PG_FUNCTION_ARGS)
1693 {
1694         TimeADT         time = PG_GETARG_TIMEADT(0);
1695         Interval   *span = PG_GETARG_INTERVAL_P(1);
1696         TimeADT         result;
1697
1698 #ifdef HAVE_INT64_TIMESTAMP
1699         result = time - span->time;
1700         result -= result / USECS_PER_DAY * USECS_PER_DAY;
1701         if (result < INT64CONST(0))
1702                 result += USECS_PER_DAY;
1703 #else
1704         TimeADT         time1;
1705
1706         result = time - span->time;
1707         TMODULO(result, time1, (double) SECS_PER_DAY);
1708         if (result < 0)
1709                 result += SECS_PER_DAY;
1710 #endif
1711
1712         PG_RETURN_TIMEADT(result);
1713 }
1714
1715
1716 /* time_part()
1717  * Extract specified field from time type.
1718  */
1719 Datum
1720 time_part(PG_FUNCTION_ARGS)
1721 {
1722         text       *units = PG_GETARG_TEXT_PP(0);
1723         TimeADT         time = PG_GETARG_TIMEADT(1);
1724         float8          result;
1725         int                     type,
1726                                 val;
1727         char       *lowunits;
1728
1729         lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
1730                                                                                         VARSIZE_ANY_EXHDR(units),
1731                                                                                         false);
1732
1733         type = DecodeUnits(0, lowunits, &val);
1734         if (type == UNKNOWN_FIELD)
1735                 type = DecodeSpecial(0, lowunits, &val);
1736
1737         if (type == UNITS)
1738         {
1739                 fsec_t          fsec;
1740                 struct pg_tm tt,
1741                                    *tm = &tt;
1742
1743                 time2tm(time, tm, &fsec);
1744
1745                 switch (val)
1746                 {
1747                         case DTK_MICROSEC:
1748 #ifdef HAVE_INT64_TIMESTAMP
1749                                 result = tm->tm_sec * 1000000.0 + fsec;
1750 #else
1751                                 result = (tm->tm_sec + fsec) * 1000000;
1752 #endif
1753                                 break;
1754
1755                         case DTK_MILLISEC:
1756 #ifdef HAVE_INT64_TIMESTAMP
1757                                 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
1758 #else
1759                                 result = (tm->tm_sec + fsec) * 1000;
1760 #endif
1761                                 break;
1762
1763                         case DTK_SECOND:
1764 #ifdef HAVE_INT64_TIMESTAMP
1765                                 result = tm->tm_sec + fsec / 1000000.0;
1766 #else
1767                                 result = tm->tm_sec + fsec;
1768 #endif
1769                                 break;
1770
1771                         case DTK_MINUTE:
1772                                 result = tm->tm_min;
1773                                 break;
1774
1775                         case DTK_HOUR:
1776                                 result = tm->tm_hour;
1777                                 break;
1778
1779                         case DTK_TZ:
1780                         case DTK_TZ_MINUTE:
1781                         case DTK_TZ_HOUR:
1782                         case DTK_DAY:
1783                         case DTK_MONTH:
1784                         case DTK_QUARTER:
1785                         case DTK_YEAR:
1786                         case DTK_DECADE:
1787                         case DTK_CENTURY:
1788                         case DTK_MILLENNIUM:
1789                         case DTK_ISOYEAR:
1790                         default:
1791                                 ereport(ERROR,
1792                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1793                                                  errmsg("\"time\" units \"%s\" not recognized",
1794                                                                 lowunits)));
1795                                 result = 0;
1796                 }
1797         }
1798         else if (type == RESERV && val == DTK_EPOCH)
1799         {
1800 #ifdef HAVE_INT64_TIMESTAMP
1801                 result = time / 1000000.0;
1802 #else
1803                 result = time;
1804 #endif
1805         }
1806         else
1807         {
1808                 ereport(ERROR,
1809                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1810                                  errmsg("\"time\" units \"%s\" not recognized",
1811                                                 lowunits)));
1812                 result = 0;
1813         }
1814
1815         PG_RETURN_FLOAT8(result);
1816 }
1817
1818
1819 /*****************************************************************************
1820  *       Time With Time Zone ADT
1821  *****************************************************************************/
1822
1823 /* tm2timetz()
1824  * Convert a tm structure to a time data type.
1825  */
1826 static int
1827 tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
1828 {
1829 #ifdef HAVE_INT64_TIMESTAMP
1830         result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1831                                         USECS_PER_SEC) + fsec;
1832 #else
1833         result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1834 #endif
1835         result->zone = tz;
1836
1837         return 0;
1838 }
1839
1840 Datum
1841 timetz_in(PG_FUNCTION_ARGS)
1842 {
1843         char       *str = PG_GETARG_CSTRING(0);
1844
1845 #ifdef NOT_USED
1846         Oid                     typelem = PG_GETARG_OID(1);
1847 #endif
1848         int32           typmod = PG_GETARG_INT32(2);
1849         TimeTzADT  *result;
1850         fsec_t          fsec;
1851         struct pg_tm tt,
1852                            *tm = &tt;
1853         int                     tz;
1854         int                     nf;
1855         int                     dterr;
1856         char            workbuf[MAXDATELEN + 1];
1857         char       *field[MAXDATEFIELDS];
1858         int                     dtype;
1859         int                     ftype[MAXDATEFIELDS];
1860
1861         dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1862                                                   field, ftype, MAXDATEFIELDS, &nf);
1863         if (dterr == 0)
1864                 dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
1865         if (dterr != 0)
1866                 DateTimeParseError(dterr, str, "time with time zone");
1867
1868         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1869         tm2timetz(tm, fsec, tz, result);
1870         AdjustTimeForTypmod(&(result->time), typmod);
1871
1872         PG_RETURN_TIMETZADT_P(result);
1873 }
1874
1875 Datum
1876 timetz_out(PG_FUNCTION_ARGS)
1877 {
1878         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1879         char       *result;
1880         struct pg_tm tt,
1881                            *tm = &tt;
1882         fsec_t          fsec;
1883         int                     tz;
1884         char            buf[MAXDATELEN + 1];
1885
1886         timetz2tm(time, tm, &fsec, &tz);
1887         EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
1888
1889         result = pstrdup(buf);
1890         PG_RETURN_CSTRING(result);
1891 }
1892
1893 /*
1894  *              timetz_recv                     - converts external binary format to timetz
1895  */
1896 Datum
1897 timetz_recv(PG_FUNCTION_ARGS)
1898 {
1899         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
1900
1901 #ifdef NOT_USED
1902         Oid                     typelem = PG_GETARG_OID(1);
1903 #endif
1904         int32           typmod = PG_GETARG_INT32(2);
1905         TimeTzADT  *result;
1906
1907         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1908
1909 #ifdef HAVE_INT64_TIMESTAMP
1910         result->time = pq_getmsgint64(buf);
1911
1912         if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
1913                 ereport(ERROR,
1914                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1915                                  errmsg("time out of range")));
1916 #else
1917         result->time = pq_getmsgfloat8(buf);
1918
1919         if (result->time < 0 || result->time > (double) SECS_PER_DAY)
1920                 ereport(ERROR,
1921                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1922                                  errmsg("time out of range")));
1923 #endif
1924
1925         result->zone = pq_getmsgint(buf, sizeof(result->zone));
1926
1927         /* we allow GMT displacements up to 14:59:59, cf DecodeTimezone() */
1928         if (result->zone <= -15 * SECS_PER_HOUR ||
1929                 result->zone >= 15 * SECS_PER_HOUR)
1930                 ereport(ERROR,
1931                                 (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
1932                                  errmsg("time zone displacement out of range")));
1933
1934         AdjustTimeForTypmod(&(result->time), typmod);
1935
1936         PG_RETURN_TIMETZADT_P(result);
1937 }
1938
1939 /*
1940  *              timetz_send                     - converts timetz to binary format
1941  */
1942 Datum
1943 timetz_send(PG_FUNCTION_ARGS)
1944 {
1945         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1946         StringInfoData buf;
1947
1948         pq_begintypsend(&buf);
1949 #ifdef HAVE_INT64_TIMESTAMP
1950         pq_sendint64(&buf, time->time);
1951 #else
1952         pq_sendfloat8(&buf, time->time);
1953 #endif
1954         pq_sendint(&buf, time->zone, sizeof(time->zone));
1955         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1956 }
1957
1958 Datum
1959 timetztypmodin(PG_FUNCTION_ARGS)
1960 {
1961         ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
1962
1963         PG_RETURN_INT32(anytime_typmodin(true, ta));
1964 }
1965
1966 Datum
1967 timetztypmodout(PG_FUNCTION_ARGS)
1968 {
1969         int32           typmod = PG_GETARG_INT32(0);
1970
1971         PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
1972 }
1973
1974
1975 /* timetz2tm()
1976  * Convert TIME WITH TIME ZONE data type to POSIX time structure.
1977  */
1978 static int
1979 timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
1980 {
1981         TimeOffset      trem = time->time;
1982
1983 #ifdef HAVE_INT64_TIMESTAMP
1984         tm->tm_hour = trem / USECS_PER_HOUR;
1985         trem -= tm->tm_hour * USECS_PER_HOUR;
1986         tm->tm_min = trem / USECS_PER_MINUTE;
1987         trem -= tm->tm_min * USECS_PER_MINUTE;
1988         tm->tm_sec = trem / USECS_PER_SEC;
1989         *fsec = trem - tm->tm_sec * USECS_PER_SEC;
1990 #else
1991 recalc:
1992         TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
1993         TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1994         TMODULO(trem, tm->tm_sec, 1.0);
1995         trem = TIMEROUND(trem);
1996         /* roundoff may need to propagate to higher-order fields */
1997         if (trem >= 1.0)
1998         {
1999                 trem = ceil(time->time);
2000                 goto recalc;
2001         }
2002         *fsec = trem;
2003 #endif
2004
2005         if (tzp != NULL)
2006                 *tzp = time->zone;
2007
2008         return 0;
2009 }
2010
2011 /* timetz_scale()
2012  * Adjust time type for specified scale factor.
2013  * Used by PostgreSQL type system to stuff columns.
2014  */
2015 Datum
2016 timetz_scale(PG_FUNCTION_ARGS)
2017 {
2018         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
2019         int32           typmod = PG_GETARG_INT32(1);
2020         TimeTzADT  *result;
2021
2022         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2023
2024         result->time = time->time;
2025         result->zone = time->zone;
2026
2027         AdjustTimeForTypmod(&(result->time), typmod);
2028
2029         PG_RETURN_TIMETZADT_P(result);
2030 }
2031
2032
2033 static int
2034 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
2035 {
2036         TimeOffset      t1,
2037                                 t2;
2038
2039         /* Primary sort is by true (GMT-equivalent) time */
2040 #ifdef HAVE_INT64_TIMESTAMP
2041         t1 = time1->time + (time1->zone * USECS_PER_SEC);
2042         t2 = time2->time + (time2->zone * USECS_PER_SEC);
2043 #else
2044         t1 = time1->time + time1->zone;
2045         t2 = time2->time + time2->zone;
2046 #endif
2047
2048         if (t1 > t2)
2049                 return 1;
2050         if (t1 < t2)
2051                 return -1;
2052
2053         /*
2054          * If same GMT time, sort by timezone; we only want to say that two
2055          * timetz's are equal if both the time and zone parts are equal.
2056          */
2057         if (time1->zone > time2->zone)
2058                 return 1;
2059         if (time1->zone < time2->zone)
2060                 return -1;
2061
2062         return 0;
2063 }
2064
2065 Datum
2066 timetz_eq(PG_FUNCTION_ARGS)
2067 {
2068         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2069         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2070
2071         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
2072 }
2073
2074 Datum
2075 timetz_ne(PG_FUNCTION_ARGS)
2076 {
2077         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2078         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2079
2080         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
2081 }
2082
2083 Datum
2084 timetz_lt(PG_FUNCTION_ARGS)
2085 {
2086         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2087         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2088
2089         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
2090 }
2091
2092 Datum
2093 timetz_le(PG_FUNCTION_ARGS)
2094 {
2095         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2096         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2097
2098         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
2099 }
2100
2101 Datum
2102 timetz_gt(PG_FUNCTION_ARGS)
2103 {
2104         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2105         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2106
2107         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
2108 }
2109
2110 Datum
2111 timetz_ge(PG_FUNCTION_ARGS)
2112 {
2113         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2114         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2115
2116         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
2117 }
2118
2119 Datum
2120 timetz_cmp(PG_FUNCTION_ARGS)
2121 {
2122         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2123         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2124
2125         PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
2126 }
2127
2128 Datum
2129 timetz_hash(PG_FUNCTION_ARGS)
2130 {
2131         TimeTzADT  *key = PG_GETARG_TIMETZADT_P(0);
2132         uint32          thash;
2133
2134         /*
2135          * To avoid any problems with padding bytes in the struct, we figure the
2136          * field hashes separately and XOR them.  This also provides a convenient
2137          * framework for dealing with the fact that the time field might be either
2138          * double or int64.
2139          */
2140 #ifdef HAVE_INT64_TIMESTAMP
2141         thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
2142                                                                                            Int64GetDatumFast(key->time)));
2143 #else
2144         thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8,
2145                                                                                          Float8GetDatumFast(key->time)));
2146 #endif
2147         thash ^= DatumGetUInt32(hash_uint32(key->zone));
2148         PG_RETURN_UINT32(thash);
2149 }
2150
2151 Datum
2152 timetz_larger(PG_FUNCTION_ARGS)
2153 {
2154         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2155         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2156         TimeTzADT  *result;
2157
2158         if (timetz_cmp_internal(time1, time2) > 0)
2159                 result = time1;
2160         else
2161                 result = time2;
2162         PG_RETURN_TIMETZADT_P(result);
2163 }
2164
2165 Datum
2166 timetz_smaller(PG_FUNCTION_ARGS)
2167 {
2168         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
2169         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2170         TimeTzADT  *result;
2171
2172         if (timetz_cmp_internal(time1, time2) < 0)
2173                 result = time1;
2174         else
2175                 result = time2;
2176         PG_RETURN_TIMETZADT_P(result);
2177 }
2178
2179 /* timetz_pl_interval()
2180  * Add interval to timetz.
2181  */
2182 Datum
2183 timetz_pl_interval(PG_FUNCTION_ARGS)
2184 {
2185         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
2186         Interval   *span = PG_GETARG_INTERVAL_P(1);
2187         TimeTzADT  *result;
2188
2189 #ifndef HAVE_INT64_TIMESTAMP
2190         TimeTzADT       time1;
2191 #endif
2192
2193         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2194
2195 #ifdef HAVE_INT64_TIMESTAMP
2196         result->time = time->time + span->time;
2197         result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2198         if (result->time < INT64CONST(0))
2199                 result->time += USECS_PER_DAY;
2200 #else
2201         result->time = time->time + span->time;
2202         TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2203         if (result->time < 0)
2204                 result->time += SECS_PER_DAY;
2205 #endif
2206
2207         result->zone = time->zone;
2208
2209         PG_RETURN_TIMETZADT_P(result);
2210 }
2211
2212 /* timetz_mi_interval()
2213  * Subtract interval from timetz.
2214  */
2215 Datum
2216 timetz_mi_interval(PG_FUNCTION_ARGS)
2217 {
2218         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
2219         Interval   *span = PG_GETARG_INTERVAL_P(1);
2220         TimeTzADT  *result;
2221
2222 #ifndef HAVE_INT64_TIMESTAMP
2223         TimeTzADT       time1;
2224 #endif
2225
2226         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2227
2228 #ifdef HAVE_INT64_TIMESTAMP
2229         result->time = time->time - span->time;
2230         result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2231         if (result->time < INT64CONST(0))
2232                 result->time += USECS_PER_DAY;
2233 #else
2234         result->time = time->time - span->time;
2235         TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2236         if (result->time < 0)
2237                 result->time += SECS_PER_DAY;
2238 #endif
2239
2240         result->zone = time->zone;
2241
2242         PG_RETURN_TIMETZADT_P(result);
2243 }
2244
2245 /* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
2246  *
2247  * Algorithm is per SQL92 spec.  This is much harder than you'd think
2248  * because the spec requires us to deliver a non-null answer in some cases
2249  * where some of the inputs are null.
2250  */
2251 Datum
2252 overlaps_timetz(PG_FUNCTION_ARGS)
2253 {
2254         /*
2255          * The arguments are TimeTzADT *, but we leave them as generic Datums for
2256          * convenience of notation --- and to avoid dereferencing nulls.
2257          */
2258         Datum           ts1 = PG_GETARG_DATUM(0);
2259         Datum           te1 = PG_GETARG_DATUM(1);
2260         Datum           ts2 = PG_GETARG_DATUM(2);
2261         Datum           te2 = PG_GETARG_DATUM(3);
2262         bool            ts1IsNull = PG_ARGISNULL(0);
2263         bool            te1IsNull = PG_ARGISNULL(1);
2264         bool            ts2IsNull = PG_ARGISNULL(2);
2265         bool            te2IsNull = PG_ARGISNULL(3);
2266
2267 #define TIMETZ_GT(t1,t2) \
2268         DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2269 #define TIMETZ_LT(t1,t2) \
2270         DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2271
2272         /*
2273          * If both endpoints of interval 1 are null, the result is null (unknown).
2274          * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2275          * take ts1 as the lesser endpoint.
2276          */
2277         if (ts1IsNull)
2278         {
2279                 if (te1IsNull)
2280                         PG_RETURN_NULL();
2281                 /* swap null for non-null */
2282                 ts1 = te1;
2283                 te1IsNull = true;
2284         }
2285         else if (!te1IsNull)
2286         {
2287                 if (TIMETZ_GT(ts1, te1))
2288                 {
2289                         Datum           tt = ts1;
2290
2291                         ts1 = te1;
2292                         te1 = tt;
2293                 }
2294         }
2295
2296         /* Likewise for interval 2. */
2297         if (ts2IsNull)
2298         {
2299                 if (te2IsNull)
2300                         PG_RETURN_NULL();
2301                 /* swap null for non-null */
2302                 ts2 = te2;
2303                 te2IsNull = true;
2304         }
2305         else if (!te2IsNull)
2306         {
2307                 if (TIMETZ_GT(ts2, te2))
2308                 {
2309                         Datum           tt = ts2;
2310
2311                         ts2 = te2;
2312                         te2 = tt;
2313                 }
2314         }
2315
2316         /*
2317          * At this point neither ts1 nor ts2 is null, so we can consider three
2318          * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2319          */
2320         if (TIMETZ_GT(ts1, ts2))
2321         {
2322                 /*
2323                  * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2324                  * in the presence of nulls it's not quite completely so.
2325                  */
2326                 if (te2IsNull)
2327                         PG_RETURN_NULL();
2328                 if (TIMETZ_LT(ts1, te2))
2329                         PG_RETURN_BOOL(true);
2330                 if (te1IsNull)
2331                         PG_RETURN_NULL();
2332
2333                 /*
2334                  * If te1 is not null then we had ts1 <= te1 above, and we just found
2335                  * ts1 >= te2, hence te1 >= te2.
2336                  */
2337                 PG_RETURN_BOOL(false);
2338         }
2339         else if (TIMETZ_LT(ts1, ts2))
2340         {
2341                 /* This case is ts2 < te1 OR te2 < te1 */
2342                 if (te1IsNull)
2343                         PG_RETURN_NULL();
2344                 if (TIMETZ_LT(ts2, te1))
2345                         PG_RETURN_BOOL(true);
2346                 if (te2IsNull)
2347                         PG_RETURN_NULL();
2348
2349                 /*
2350                  * If te2 is not null then we had ts2 <= te2 above, and we just found
2351                  * ts2 >= te1, hence te2 >= te1.
2352                  */
2353                 PG_RETURN_BOOL(false);
2354         }
2355         else
2356         {
2357                 /*
2358                  * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2359                  * rather silly way of saying "true if both are nonnull, else null".
2360                  */
2361                 if (te1IsNull || te2IsNull)
2362                         PG_RETURN_NULL();
2363                 PG_RETURN_BOOL(true);
2364         }
2365
2366 #undef TIMETZ_GT
2367 #undef TIMETZ_LT
2368 }
2369
2370
2371 Datum
2372 timetz_time(PG_FUNCTION_ARGS)
2373 {
2374         TimeTzADT  *timetz = PG_GETARG_TIMETZADT_P(0);
2375         TimeADT         result;
2376
2377         /* swallow the time zone and just return the time */
2378         result = timetz->time;
2379
2380         PG_RETURN_TIMEADT(result);
2381 }
2382
2383
2384 Datum
2385 time_timetz(PG_FUNCTION_ARGS)
2386 {
2387         TimeADT         time = PG_GETARG_TIMEADT(0);
2388         TimeTzADT  *result;
2389         struct pg_tm tt,
2390                            *tm = &tt;
2391         fsec_t          fsec;
2392         int                     tz;
2393
2394         GetCurrentDateTime(tm);
2395         time2tm(time, tm, &fsec);
2396         tz = DetermineTimeZoneOffset(tm, session_timezone);
2397
2398         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2399
2400         result->time = time;
2401         result->zone = tz;
2402
2403         PG_RETURN_TIMETZADT_P(result);
2404 }
2405
2406
2407 /* timestamptz_timetz()
2408  * Convert timestamp to timetz data type.
2409  */
2410 Datum
2411 timestamptz_timetz(PG_FUNCTION_ARGS)
2412 {
2413         TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2414         TimeTzADT  *result;
2415         struct pg_tm tt,
2416                            *tm = &tt;
2417         int                     tz;
2418         fsec_t          fsec;
2419         char       *tzn;
2420
2421         if (TIMESTAMP_NOT_FINITE(timestamp))
2422                 PG_RETURN_NULL();
2423
2424         if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2425                 ereport(ERROR,
2426                                 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2427                                  errmsg("timestamp out of range")));
2428
2429         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2430
2431         tm2timetz(tm, fsec, tz, result);
2432
2433         PG_RETURN_TIMETZADT_P(result);
2434 }
2435
2436
2437 /* datetimetz_timestamptz()
2438  * Convert date and timetz to timestamp with time zone data type.
2439  * Timestamp is stored in GMT, so add the time zone
2440  * stored with the timetz to the result.
2441  * - thomas 2000-03-10
2442  */
2443 Datum
2444 datetimetz_timestamptz(PG_FUNCTION_ARGS)
2445 {
2446         DateADT         date = PG_GETARG_DATEADT(0);
2447         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
2448         TimestampTz result;
2449
2450         if (DATE_IS_NOBEGIN(date))
2451                 TIMESTAMP_NOBEGIN(result);
2452         else if (DATE_IS_NOEND(date))
2453                 TIMESTAMP_NOEND(result);
2454         else
2455         {
2456 #ifdef HAVE_INT64_TIMESTAMP
2457                 result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2458 #else
2459                 result = date * (double) SECS_PER_DAY + time->time + time->zone;
2460 #endif
2461         }
2462
2463         PG_RETURN_TIMESTAMP(result);
2464 }
2465
2466
2467 /* timetz_part()
2468  * Extract specified field from time type.
2469  */
2470 Datum
2471 timetz_part(PG_FUNCTION_ARGS)
2472 {
2473         text       *units = PG_GETARG_TEXT_PP(0);
2474         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
2475         float8          result;
2476         int                     type,
2477                                 val;
2478         char       *lowunits;
2479
2480         lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
2481                                                                                         VARSIZE_ANY_EXHDR(units),
2482                                                                                         false);
2483
2484         type = DecodeUnits(0, lowunits, &val);
2485         if (type == UNKNOWN_FIELD)
2486                 type = DecodeSpecial(0, lowunits, &val);
2487
2488         if (type == UNITS)
2489         {
2490                 double          dummy;
2491                 int                     tz;
2492                 fsec_t          fsec;
2493                 struct pg_tm tt,
2494                                    *tm = &tt;
2495
2496                 timetz2tm(time, tm, &fsec, &tz);
2497
2498                 switch (val)
2499                 {
2500                         case DTK_TZ:
2501                                 result = -tz;
2502                                 break;
2503
2504                         case DTK_TZ_MINUTE:
2505                                 result = -tz;
2506                                 result /= SECS_PER_MINUTE;
2507                                 FMODULO(result, dummy, (double) SECS_PER_MINUTE);
2508                                 break;
2509
2510                         case DTK_TZ_HOUR:
2511                                 dummy = -tz;
2512                                 FMODULO(dummy, result, (double) SECS_PER_HOUR);
2513                                 break;
2514
2515                         case DTK_MICROSEC:
2516 #ifdef HAVE_INT64_TIMESTAMP
2517                                 result = tm->tm_sec * 1000000.0 + fsec;
2518 #else
2519                                 result = (tm->tm_sec + fsec) * 1000000;
2520 #endif
2521                                 break;
2522
2523                         case DTK_MILLISEC:
2524 #ifdef HAVE_INT64_TIMESTAMP
2525                                 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
2526 #else
2527                                 result = (tm->tm_sec + fsec) * 1000;
2528 #endif
2529                                 break;
2530
2531                         case DTK_SECOND:
2532 #ifdef HAVE_INT64_TIMESTAMP
2533                                 result = tm->tm_sec + fsec / 1000000.0;
2534 #else
2535                                 result = tm->tm_sec + fsec;
2536 #endif
2537                                 break;
2538
2539                         case DTK_MINUTE:
2540                                 result = tm->tm_min;
2541                                 break;
2542
2543                         case DTK_HOUR:
2544                                 result = tm->tm_hour;
2545                                 break;
2546
2547                         case DTK_DAY:
2548                         case DTK_MONTH:
2549                         case DTK_QUARTER:
2550                         case DTK_YEAR:
2551                         case DTK_DECADE:
2552                         case DTK_CENTURY:
2553                         case DTK_MILLENNIUM:
2554                         default:
2555                                 ereport(ERROR,
2556                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2557                                 errmsg("\"time with time zone\" units \"%s\" not recognized",
2558                                            lowunits)));
2559                                 result = 0;
2560                 }
2561         }
2562         else if (type == RESERV && val == DTK_EPOCH)
2563         {
2564 #ifdef HAVE_INT64_TIMESTAMP
2565                 result = time->time / 1000000.0 + time->zone;
2566 #else
2567                 result = time->time + time->zone;
2568 #endif
2569         }
2570         else
2571         {
2572                 ereport(ERROR,
2573                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2574                                  errmsg("\"time with time zone\" units \"%s\" not recognized",
2575                                                 lowunits)));
2576                 result = 0;
2577         }
2578
2579         PG_RETURN_FLOAT8(result);
2580 }
2581
2582 /* timetz_zone()
2583  * Encode time with time zone type with specified time zone.
2584  * Applies DST rules as of the current date.
2585  */
2586 Datum
2587 timetz_zone(PG_FUNCTION_ARGS)
2588 {
2589         text       *zone = PG_GETARG_TEXT_PP(0);
2590         TimeTzADT  *t = PG_GETARG_TIMETZADT_P(1);
2591         TimeTzADT  *result;
2592         int                     tz;
2593         char            tzname[TZ_STRLEN_MAX + 1];
2594         char       *lowzone;
2595         int                     type,
2596                                 val;
2597         pg_tz      *tzp;
2598
2599         /*
2600          * Look up the requested timezone.      First we look in the date token table
2601          * (to handle cases like "EST"), and if that fails, we look in the
2602          * timezone database (to handle cases like "America/New_York").  (This
2603          * matches the order in which timestamp input checks the cases; it's
2604          * important because the timezone database unwisely uses a few zone names
2605          * that are identical to offset abbreviations.)
2606          */
2607         text_to_cstring_buffer(zone, tzname, sizeof(tzname));
2608         lowzone = downcase_truncate_identifier(tzname,
2609                                                                                    strlen(tzname),
2610                                                                                    false);
2611
2612         type = DecodeSpecial(0, lowzone, &val);
2613
2614         if (type == TZ || type == DTZ)
2615                 tz = val * MINS_PER_HOUR;
2616         else
2617         {
2618                 tzp = pg_tzset(tzname);
2619                 if (tzp)
2620                 {
2621                         /* Get the offset-from-GMT that is valid today for the zone */
2622                         pg_time_t       now = (pg_time_t) time(NULL);
2623                         struct pg_tm *tm;
2624
2625                         tm = pg_localtime(&now, tzp);
2626                         tz = -tm->tm_gmtoff;
2627                 }
2628                 else
2629                 {
2630                         ereport(ERROR,
2631                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2632                                          errmsg("time zone \"%s\" not recognized", tzname)));
2633                         tz = 0;                         /* keep compiler quiet */
2634                 }
2635         }
2636
2637         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2638
2639 #ifdef HAVE_INT64_TIMESTAMP
2640         result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
2641         while (result->time < INT64CONST(0))
2642                 result->time += USECS_PER_DAY;
2643         while (result->time >= USECS_PER_DAY)
2644                 result->time -= USECS_PER_DAY;
2645 #else
2646         result->time = t->time + (t->zone - tz);
2647         while (result->time < 0)
2648                 result->time += SECS_PER_DAY;
2649         while (result->time >= SECS_PER_DAY)
2650                 result->time -= SECS_PER_DAY;
2651 #endif
2652
2653         result->zone = tz;
2654
2655         PG_RETURN_TIMETZADT_P(result);
2656 }
2657
2658 /* timetz_izone()
2659  * Encode time with time zone type with specified time interval as time zone.
2660  */
2661 Datum
2662 timetz_izone(PG_FUNCTION_ARGS)
2663 {
2664         Interval   *zone = PG_GETARG_INTERVAL_P(0);
2665         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
2666         TimeTzADT  *result;
2667         int                     tz;
2668
2669         if (zone->month != 0)
2670                 ereport(ERROR,
2671                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2672                                  errmsg("\"interval\" time zone \"%s\" not valid",
2673                                                 DatumGetCString(DirectFunctionCall1(interval_out,
2674                                                                                                   PointerGetDatum(zone))))));
2675
2676 #ifdef HAVE_INT64_TIMESTAMP
2677         tz = -(zone->time / USECS_PER_SEC);
2678 #else
2679         tz = -(zone->time);
2680 #endif
2681
2682         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2683
2684 #ifdef HAVE_INT64_TIMESTAMP
2685         result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
2686         while (result->time < INT64CONST(0))
2687                 result->time += USECS_PER_DAY;
2688         while (result->time >= USECS_PER_DAY)
2689                 result->time -= USECS_PER_DAY;
2690 #else
2691         result->time = time->time + (time->zone - tz);
2692         while (result->time < 0)
2693                 result->time += SECS_PER_DAY;
2694         while (result->time >= SECS_PER_DAY)
2695                 result->time -= SECS_PER_DAY;
2696 #endif
2697
2698         result->zone = tz;
2699
2700         PG_RETURN_TIMETZADT_P(result);
2701 }