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