]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/date.c
Eliminate unnecessary dependency on mktime(), and consequent 'Unable to
[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-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994-5, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.71 2002/09/03 19:41:28 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include <ctype.h>
19 #include <limits.h>
20 #include <time.h>
21 #include <float.h>
22
23 #include "access/hash.h"
24 #include "miscadmin.h"
25 #include "utils/builtins.h"
26 #include "utils/date.h"
27 #include "utils/nabstime.h"
28 #include "utils/timestamp.h"
29
30
31 int time2tm(TimeADT time, struct tm * tm, fsec_t *fsec);
32 int timetz2tm(TimeTzADT *time, struct tm * tm, fsec_t *fsec, int *tzp);
33 int tm2time(struct tm * tm, fsec_t fsec, TimeADT *result);
34 int tm2timetz(struct tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
35 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
36
37 /*****************************************************************************
38  *       Date ADT
39  *****************************************************************************/
40
41
42 /* date_in()
43  * Given date text string, convert to internal date format.
44  */
45 Datum
46 date_in(PG_FUNCTION_ARGS)
47 {
48         char       *str = PG_GETARG_CSTRING(0);
49         DateADT         date;
50         fsec_t          fsec;
51         struct tm       tt,
52                            *tm = &tt;
53         int                     tzp;
54         int                     dtype;
55         int                     nf;
56         char       *field[MAXDATEFIELDS];
57         int                     ftype[MAXDATEFIELDS];
58         char            lowstr[MAXDATELEN + 1];
59
60         if (strlen(str) >= sizeof(lowstr))
61                 elog(ERROR, "Bad date external representation (too long) '%s'", str);
62
63         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
64          || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
65                 elog(ERROR, "Bad date external representation '%s'", str);
66
67         switch (dtype)
68         {
69                 case DTK_DATE:
70                         break;
71
72                 case DTK_CURRENT:
73                         elog(ERROR, "Date CURRENT no longer supported"
74                                  "\n\tdate_in() internal coding error");
75                         GetCurrentDateTime(tm);
76                         break;
77
78                 case DTK_EPOCH:
79                         GetEpochTime(tm);
80                         break;
81
82                 default:
83                         elog(ERROR, "Unrecognized date external representation '%s'", str);
84         }
85
86         date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
87
88         PG_RETURN_DATEADT(date);
89 }
90
91 /* date_out()
92  * Given internal format date, convert to text string.
93  */
94 Datum
95 date_out(PG_FUNCTION_ARGS)
96 {
97         DateADT         date = PG_GETARG_DATEADT(0);
98         char       *result;
99         struct tm       tt,
100                            *tm = &tt;
101         char            buf[MAXDATELEN + 1];
102
103         j2date((date + date2j(2000, 1, 1)),
104                    &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
105
106         EncodeDateOnly(tm, DateStyle, buf);
107
108         result = pstrdup(buf);
109         PG_RETURN_CSTRING(result);
110 }
111
112 Datum
113 date_eq(PG_FUNCTION_ARGS)
114 {
115         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
116         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
117
118         PG_RETURN_BOOL(dateVal1 == dateVal2);
119 }
120
121 Datum
122 date_ne(PG_FUNCTION_ARGS)
123 {
124         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
125         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
126
127         PG_RETURN_BOOL(dateVal1 != dateVal2);
128 }
129
130 Datum
131 date_lt(PG_FUNCTION_ARGS)
132 {
133         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
134         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
135
136         PG_RETURN_BOOL(dateVal1 < dateVal2);
137 }
138
139 Datum
140 date_le(PG_FUNCTION_ARGS)
141 {
142         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
143         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
144
145         PG_RETURN_BOOL(dateVal1 <= dateVal2);
146 }
147
148 Datum
149 date_gt(PG_FUNCTION_ARGS)
150 {
151         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
152         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
153
154         PG_RETURN_BOOL(dateVal1 > dateVal2);
155 }
156
157 Datum
158 date_ge(PG_FUNCTION_ARGS)
159 {
160         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
161         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
162
163         PG_RETURN_BOOL(dateVal1 >= dateVal2);
164 }
165
166 Datum
167 date_cmp(PG_FUNCTION_ARGS)
168 {
169         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
170         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
171
172         if (dateVal1 < dateVal2)
173                 PG_RETURN_INT32(-1);
174         else if (dateVal1 > dateVal2)
175                 PG_RETURN_INT32(1);
176         PG_RETURN_INT32(0);
177 }
178
179 Datum
180 date_larger(PG_FUNCTION_ARGS)
181 {
182         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
183         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
184
185         PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
186 }
187
188 Datum
189 date_smaller(PG_FUNCTION_ARGS)
190 {
191         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
192         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
193
194         PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
195 }
196
197 /* Compute difference between two dates in days.
198  */
199 Datum
200 date_mi(PG_FUNCTION_ARGS)
201 {
202         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
203         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
204
205         PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
206 }
207
208 /* Add a number of days to a date, giving a new date.
209  * Must handle both positive and negative numbers of days.
210  */
211 Datum
212 date_pli(PG_FUNCTION_ARGS)
213 {
214         DateADT         dateVal = PG_GETARG_DATEADT(0);
215         int32           days = PG_GETARG_INT32(1);
216
217         PG_RETURN_DATEADT(dateVal + days);
218 }
219
220 /* Subtract a number of days from a date, giving a new date.
221  */
222 Datum
223 date_mii(PG_FUNCTION_ARGS)
224 {
225         DateADT         dateVal = PG_GETARG_DATEADT(0);
226         int32           days = PG_GETARG_INT32(1);
227
228         PG_RETURN_DATEADT(dateVal - days);
229 }
230
231 #if NOT_USED
232 /* date_pl_interval() and date_mi_interval() are probably
233  * better implmented by converting the input date
234  * to timestamp without time zone. So that is what we do
235  * in pg_proc.h - thomas 2002-03-11
236  */
237
238 /* Add an interval to a date, giving a new date.
239  * Must handle both positive and negative intervals.
240  */
241 Datum
242 date_pl_interval(PG_FUNCTION_ARGS)
243 {
244         DateADT         dateVal = PG_GETARG_DATEADT(0);
245         Interval   *span = PG_GETARG_INTERVAL_P(1);
246         struct tm       tt,
247                            *tm = &tt;
248
249         if (span->month != 0)
250         {
251                 j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
252                 tm->tm_mon += span->month;
253                 dateVal = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
254         }
255         if (span->time != 0)
256                 dateVal += (span->time / 86400e0);
257
258         PG_RETURN_DATEADT(dateVal);
259 }
260
261 /* Subtract an interval from a date, giving a new date.
262  * Must handle both positive and negative intervals.
263  */
264 Datum
265 date_mi_interval(PG_FUNCTION_ARGS)
266 {
267         DateADT         dateVal = PG_GETARG_DATEADT(0);
268         Interval   *span = PG_GETARG_INTERVAL_P(1);
269         struct tm       tt,
270                            *tm = &tt;
271
272         if (span->month != 0)
273         {
274                 j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
275                 tm->tm_mon -= span->month;
276                 dateVal = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
277         }
278         if (span->time != 0)
279                 dateVal -= (span->time / 86400e0);
280
281         PG_RETURN_DATEADT(dateVal);
282 }
283 #endif
284
285 /* date_timestamp()
286  * Convert date to timestamp data type.
287  */
288 Datum
289 date_timestamp(PG_FUNCTION_ARGS)
290 {
291         DateADT         dateVal = PG_GETARG_DATEADT(0);
292         Timestamp       result;
293
294 #ifdef HAVE_INT64_TIMESTAMP
295         /* date is days since 2000, timestamp is microseconds since same... */
296         result = dateVal * INT64CONST(86400000000);
297 #else
298         /* date is days since 2000, timestamp is seconds since same... */
299         result = dateVal * 86400.0;
300 #endif
301
302         PG_RETURN_TIMESTAMP(result);
303 }
304
305
306 /* timestamp_date()
307  * Convert timestamp to date data type.
308  */
309 Datum
310 timestamp_date(PG_FUNCTION_ARGS)
311 {
312         Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
313         DateADT         result;
314
315         if (TIMESTAMP_NOT_FINITE(timestamp))
316                 PG_RETURN_NULL();
317
318 #ifdef HAVE_INT64_TIMESTAMP
319         /* Microseconds to days */
320         result = (timestamp / INT64CONST(86400000000));
321 #else
322         /* Seconds to days */
323         result = (timestamp / 86400.0);
324 #endif
325
326         PG_RETURN_DATEADT(result);
327 }
328
329
330 /* date_timestamptz()
331  * Convert date to timestamp with time zone data type.
332  */
333 Datum
334 date_timestamptz(PG_FUNCTION_ARGS)
335 {
336         DateADT         dateVal = PG_GETARG_DATEADT(0);
337         TimestampTz result;
338         struct tm       tt,
339                            *tm = &tt;
340
341         j2date((dateVal + date2j(2000, 1, 1)),
342                    &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
343
344         if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
345         {
346                 int             tz;
347
348                 tm->tm_hour = 0;
349                 tm->tm_min = 0;
350                 tm->tm_sec = 0;
351                 tz = DetermineLocalTimeZone(tm);
352
353 #ifdef HAVE_INT64_TIMESTAMP
354                 result = (dateVal * INT64CONST(86400000000))
355                         + (tz * INT64CONST(1000000));
356 #else
357                 result = dateVal * 86400.0 + tz;
358 #endif
359         }
360         else
361         {
362                 /* Outside of range for timezone support, so assume UTC */
363 #ifdef HAVE_INT64_TIMESTAMP
364                 result = (dateVal * INT64CONST(86400000000));
365 #else
366                 result = dateVal * 86400.0;
367 #endif
368         }
369
370         PG_RETURN_TIMESTAMP(result);
371 }
372
373
374 /* timestamptz_date()
375  * Convert timestamp with time zone to date data type.
376  */
377 Datum
378 timestamptz_date(PG_FUNCTION_ARGS)
379 {
380         TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
381         DateADT         result;
382         struct tm       tt,
383                            *tm = &tt;
384         fsec_t          fsec;
385         int                     tz;
386         char       *tzn;
387
388         if (TIMESTAMP_NOT_FINITE(timestamp))
389                 PG_RETURN_NULL();
390
391         if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
392                 elog(ERROR, "Unable to convert timestamp to date");
393
394         result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
395
396         PG_RETURN_DATEADT(result);
397 }
398
399
400 /* abstime_date()
401  * Convert abstime to date data type.
402  */
403 Datum
404 abstime_date(PG_FUNCTION_ARGS)
405 {
406         AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
407         DateADT         result;
408         struct tm       tt,
409                            *tm = &tt;
410         int                     tz;
411
412         switch (abstime)
413         {
414                 case INVALID_ABSTIME:
415                 case NOSTART_ABSTIME:
416                 case NOEND_ABSTIME:
417                         elog(ERROR, "Unable to convert reserved abstime value to date");
418
419                         /*
420                          * pretend to drop through to make compiler think that result
421                          * will be set
422                          */
423
424                 default:
425                         abstime2tm(abstime, &tz, tm, NULL);
426                         result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
427                         break;
428         }
429
430         PG_RETURN_DATEADT(result);
431 }
432
433
434 /* date_text()
435  * Convert date to text data type.
436  */
437 Datum
438 date_text(PG_FUNCTION_ARGS)
439 {
440         /* Input is a Date, but may as well leave it in Datum form */
441         Datum           date = PG_GETARG_DATUM(0);
442         text       *result;
443         char       *str;
444         int                     len;
445
446         str = DatumGetCString(DirectFunctionCall1(date_out, date));
447
448         len = (strlen(str) + VARHDRSZ);
449
450         result = palloc(len);
451
452         VARATT_SIZEP(result) = len;
453         memmove(VARDATA(result), str, (len - VARHDRSZ));
454
455         pfree(str);
456
457         PG_RETURN_TEXT_P(result);
458 }
459
460
461 /* text_date()
462  * Convert text string to date.
463  * Text type is not null terminated, so use temporary string
464  *      then call the standard input routine.
465  */
466 Datum
467 text_date(PG_FUNCTION_ARGS)
468 {
469         text       *str = PG_GETARG_TEXT_P(0);
470         int                     i;
471         char       *sp,
472                            *dp,
473                                 dstr[MAXDATELEN + 1];
474
475         if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
476                 elog(ERROR, "Bad date external representation (too long)");
477
478         sp = VARDATA(str);
479         dp = dstr;
480         for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
481                 *dp++ = *sp++;
482         *dp = '\0';
483
484         return DirectFunctionCall1(date_in,
485                                                            CStringGetDatum(dstr));
486 }
487
488
489 /*****************************************************************************
490  *       Time ADT
491  *****************************************************************************/
492
493 Datum
494 time_in(PG_FUNCTION_ARGS)
495 {
496         char       *str = PG_GETARG_CSTRING(0);
497 #ifdef NOT_USED
498         Oid                     typelem = PG_GETARG_OID(1);
499 #endif
500         int32           typmod = PG_GETARG_INT32(2);
501         TimeADT         result;
502         fsec_t          fsec;
503         struct tm       tt,
504                            *tm = &tt;
505         int                     nf;
506         char            lowstr[MAXDATELEN + 1];
507         char       *field[MAXDATEFIELDS];
508         int                     dtype;
509         int                     ftype[MAXDATEFIELDS];
510
511         if (strlen(str) >= sizeof(lowstr))
512                 elog(ERROR, "Bad time external representation (too long) '%s'", str);
513
514         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
515          || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, NULL) != 0))
516                 elog(ERROR, "Bad time external representation '%s'", str);
517
518         tm2time(tm, fsec, &result);
519         AdjustTimeForTypmod(&result, typmod);
520
521         PG_RETURN_TIMEADT(result);
522 }
523
524 /* tm2time()
525  * Convert a tm structure to a time data type.
526  */
527 int
528 tm2time(struct tm * tm, fsec_t fsec, TimeADT *result)
529 {
530 #ifdef HAVE_INT64_TIMESTAMP
531         *result = ((((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec)
532                            * INT64CONST(1000000)) + fsec);
533 #else
534         *result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
535 #endif
536         return 0;
537 }
538
539 /* time2tm()
540  * Convert time data type to POSIX time structure.
541  * For dates within the system-supported time_t range, convert to the
542  *      local time zone. If out of this range, leave as GMT. - tgl 97/05/27
543  */
544 int
545 time2tm(TimeADT time, struct tm *tm, fsec_t *fsec)
546 {
547 #ifdef HAVE_INT64_TIMESTAMP
548         tm->tm_hour = (time / INT64CONST(3600000000));
549         time -= (tm->tm_hour * INT64CONST(3600000000));
550         tm->tm_min = (time / INT64CONST(60000000));
551         time -= (tm->tm_min * INT64CONST(60000000));
552         tm->tm_sec = (time / INT64CONST(1000000));
553         time -= (tm->tm_sec * INT64CONST(1000000));
554         *fsec = time;
555 #else
556         double          trem;
557
558         trem = time;
559         TMODULO(trem, tm->tm_hour, 3600e0);
560         TMODULO(trem, tm->tm_min, 60e0);
561         TMODULO(trem, tm->tm_sec, 1e0);
562         *fsec = trem;
563 #endif
564
565         return 0;
566 }
567
568 Datum
569 time_out(PG_FUNCTION_ARGS)
570 {
571         TimeADT         time = PG_GETARG_TIMEADT(0);
572         char       *result;
573         struct tm       tt,
574                            *tm = &tt;
575         fsec_t          fsec;
576         char            buf[MAXDATELEN + 1];
577
578         time2tm(time, tm, &fsec);
579         EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
580
581         result = pstrdup(buf);
582         PG_RETURN_CSTRING(result);
583 }
584
585 /* time_scale()
586  * Adjust time type for specified scale factor.
587  * Used by PostgreSQL type system to stuff columns.
588  */
589 Datum
590 time_scale(PG_FUNCTION_ARGS)
591 {
592         TimeADT         time = PG_GETARG_TIMEADT(0);
593         int32           typmod = PG_GETARG_INT32(1);
594         TimeADT         result;
595
596         result = time;
597         AdjustTimeForTypmod(&result, typmod);
598
599         PG_RETURN_TIMEADT(result);
600 }
601
602 /* AdjustTimeForTypmod()
603  * Force the precision of the time value to a specified value.
604  * Uses *exactly* the same code as in AdjustTimestampForTypemod()
605  * but we make a separate copy because those types do not
606  * have a fundamental tie together but rather a coincidence of
607  * implementation. - thomas
608  */
609 static void
610 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
611 {
612 #ifdef HAVE_INT64_TIMESTAMP
613         static const int64 TimeScales[MAX_TIMESTAMP_PRECISION+1] = {
614                 INT64CONST(1000000),
615                 INT64CONST(100000),
616                 INT64CONST(10000),
617                 INT64CONST(1000),
618                 INT64CONST(100),
619                 INT64CONST(10),
620                 INT64CONST(1)
621         };
622
623         static const int64 TimeOffsets[MAX_TIMESTAMP_PRECISION+1] = {
624                 INT64CONST(-500000),
625                 INT64CONST(-50000),
626                 INT64CONST(-5000),
627                 INT64CONST(-500),
628                 INT64CONST(-50),
629                 INT64CONST(-5),
630                 INT64CONST(0)
631         };
632 #else
633         static const double TimeScales[MAX_TIMESTAMP_PRECISION+1] = {
634                 1,
635                 10,
636                 100,
637                 1000,
638                 10000,
639                 100000,
640                 1000000
641         };
642
643         static const double TimeOffsets[MAX_TIMESTAMP_PRECISION+1] = {
644                 0.5,
645                 0.05,
646                 0.005,
647                 0.0005,
648                 0.00005,
649                 0.000005,
650                 0.0000005
651         };
652 #endif
653
654         if ((typmod >= 0) && (typmod <= MAX_TIME_PRECISION))
655         {
656 #ifdef HAVE_INT64_TIMESTAMP
657                 /* we have different truncation behavior depending on sign */
658                 if (*time >= INT64CONST(0))
659                 {
660                         *time = ((*time / TimeScales[typmod])
661                                          * TimeScales[typmod]);
662                 }
663                 else
664                 {
665                         *time = (((*time + TimeOffsets[typmod]) / TimeScales[typmod])
666                                          * TimeScales[typmod]);
667                 }
668 #else
669                 /* we have different truncation behavior depending on sign */
670                 if (*time >= 0)
671                 {
672                         *time = (rint(((double) *time) * TimeScales[typmod])
673                                          / TimeScales[typmod]);
674                 }
675                 else
676                 {
677                         /* Scale and truncate first, then add to help the rounding behavior */
678                         *time = (rint((((double) *time) * TimeScales[typmod]) + TimeOffsets[typmod])
679                                          / TimeScales[typmod]);
680                 }
681 #endif
682         }
683
684         return;
685 }
686
687
688 Datum
689 time_eq(PG_FUNCTION_ARGS)
690 {
691         TimeADT         time1 = PG_GETARG_TIMEADT(0);
692         TimeADT         time2 = PG_GETARG_TIMEADT(1);
693
694         PG_RETURN_BOOL(time1 == time2);
695 }
696
697 Datum
698 time_ne(PG_FUNCTION_ARGS)
699 {
700         TimeADT         time1 = PG_GETARG_TIMEADT(0);
701         TimeADT         time2 = PG_GETARG_TIMEADT(1);
702
703         PG_RETURN_BOOL(time1 != time2);
704 }
705
706 Datum
707 time_lt(PG_FUNCTION_ARGS)
708 {
709         TimeADT         time1 = PG_GETARG_TIMEADT(0);
710         TimeADT         time2 = PG_GETARG_TIMEADT(1);
711
712         PG_RETURN_BOOL(time1 < time2);
713 }
714
715 Datum
716 time_le(PG_FUNCTION_ARGS)
717 {
718         TimeADT         time1 = PG_GETARG_TIMEADT(0);
719         TimeADT         time2 = PG_GETARG_TIMEADT(1);
720
721         PG_RETURN_BOOL(time1 <= time2);
722 }
723
724 Datum
725 time_gt(PG_FUNCTION_ARGS)
726 {
727         TimeADT         time1 = PG_GETARG_TIMEADT(0);
728         TimeADT         time2 = PG_GETARG_TIMEADT(1);
729
730         PG_RETURN_BOOL(time1 > time2);
731 }
732
733 Datum
734 time_ge(PG_FUNCTION_ARGS)
735 {
736         TimeADT         time1 = PG_GETARG_TIMEADT(0);
737         TimeADT         time2 = PG_GETARG_TIMEADT(1);
738
739         PG_RETURN_BOOL(time1 >= time2);
740 }
741
742 Datum
743 time_cmp(PG_FUNCTION_ARGS)
744 {
745         TimeADT         time1 = PG_GETARG_TIMEADT(0);
746         TimeADT         time2 = PG_GETARG_TIMEADT(1);
747
748         if (time1 < time2)
749                 PG_RETURN_INT32(-1);
750         if (time1 > time2)
751                 PG_RETURN_INT32(1);
752         PG_RETURN_INT32(0);
753 }
754
755 Datum
756 time_larger(PG_FUNCTION_ARGS)
757 {
758         TimeADT         time1 = PG_GETARG_TIMEADT(0);
759         TimeADT         time2 = PG_GETARG_TIMEADT(1);
760
761         PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
762 }
763
764 Datum
765 time_smaller(PG_FUNCTION_ARGS)
766 {
767         TimeADT         time1 = PG_GETARG_TIMEADT(0);
768         TimeADT         time2 = PG_GETARG_TIMEADT(1);
769
770         PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
771 }
772
773 /* overlaps_time() --- implements the SQL92 OVERLAPS operator.
774  *
775  * Algorithm is per SQL92 spec.  This is much harder than you'd think
776  * because the spec requires us to deliver a non-null answer in some cases
777  * where some of the inputs are null.
778  */
779 Datum
780 overlaps_time(PG_FUNCTION_ARGS)
781 {
782         /*
783          * The arguments are TimeADT, but we leave them as generic Datums to
784          * avoid dereferencing nulls (TimeADT is pass-by-reference!)
785          */
786         Datum           ts1 = PG_GETARG_DATUM(0);
787         Datum           te1 = PG_GETARG_DATUM(1);
788         Datum           ts2 = PG_GETARG_DATUM(2);
789         Datum           te2 = PG_GETARG_DATUM(3);
790         bool            ts1IsNull = PG_ARGISNULL(0);
791         bool            te1IsNull = PG_ARGISNULL(1);
792         bool            ts2IsNull = PG_ARGISNULL(2);
793         bool            te2IsNull = PG_ARGISNULL(3);
794
795 #define TIMEADT_GT(t1,t2) \
796         (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
797 #define TIMEADT_LT(t1,t2) \
798         (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
799
800         /*
801          * If both endpoints of interval 1 are null, the result is null
802          * (unknown). If just one endpoint is null, take ts1 as the non-null
803          * one. Otherwise, take ts1 as the lesser endpoint.
804          */
805         if (ts1IsNull)
806         {
807                 if (te1IsNull)
808                         PG_RETURN_NULL();
809                 /* swap null for non-null */
810                 ts1 = te1;
811                 te1IsNull = true;
812         }
813         else if (!te1IsNull)
814         {
815                 if (TIMEADT_GT(ts1, te1))
816                 {
817                         Datum           tt = ts1;
818
819                         ts1 = te1;
820                         te1 = tt;
821                 }
822         }
823
824         /* Likewise for interval 2. */
825         if (ts2IsNull)
826         {
827                 if (te2IsNull)
828                         PG_RETURN_NULL();
829                 /* swap null for non-null */
830                 ts2 = te2;
831                 te2IsNull = true;
832         }
833         else if (!te2IsNull)
834         {
835                 if (TIMEADT_GT(ts2, te2))
836                 {
837                         Datum           tt = ts2;
838
839                         ts2 = te2;
840                         te2 = tt;
841                 }
842         }
843
844         /*
845          * At this point neither ts1 nor ts2 is null, so we can consider three
846          * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
847          */
848         if (TIMEADT_GT(ts1, ts2))
849         {
850                 /*
851                  * This case is ts1 < te2 OR te1 < te2, which may look redundant
852                  * but in the presence of nulls it's not quite completely so.
853                  */
854                 if (te2IsNull)
855                         PG_RETURN_NULL();
856                 if (TIMEADT_LT(ts1, te2))
857                         PG_RETURN_BOOL(true);
858                 if (te1IsNull)
859                         PG_RETURN_NULL();
860
861                 /*
862                  * If te1 is not null then we had ts1 <= te1 above, and we just
863                  * found ts1 >= te2, hence te1 >= te2.
864                  */
865                 PG_RETURN_BOOL(false);
866         }
867         else if (TIMEADT_LT(ts1, ts2))
868         {
869                 /* This case is ts2 < te1 OR te2 < te1 */
870                 if (te1IsNull)
871                         PG_RETURN_NULL();
872                 if (TIMEADT_LT(ts2, te1))
873                         PG_RETURN_BOOL(true);
874                 if (te2IsNull)
875                         PG_RETURN_NULL();
876
877                 /*
878                  * If te2 is not null then we had ts2 <= te2 above, and we just
879                  * found ts2 >= te1, hence te2 >= te1.
880                  */
881                 PG_RETURN_BOOL(false);
882         }
883         else
884         {
885                 /*
886                  * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
887                  * rather silly way of saying "true if both are nonnull, else
888                  * null".
889                  */
890                 if (te1IsNull || te2IsNull)
891                         PG_RETURN_NULL();
892                 PG_RETURN_BOOL(true);
893         }
894
895 #undef TIMEADT_GT
896 #undef TIMEADT_LT
897 }
898
899 /* timestamp_time()
900  * Convert timestamp to time data type.
901  */
902 Datum
903 timestamp_time(PG_FUNCTION_ARGS)
904 {
905         Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
906         TimeADT         result;
907         struct tm       tt,
908                            *tm = &tt;
909         fsec_t          fsec;
910
911         if (TIMESTAMP_NOT_FINITE(timestamp))
912                 PG_RETURN_NULL();
913
914         if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
915                 elog(ERROR, "Unable to convert timestamp to time");
916
917 #ifdef HAVE_INT64_TIMESTAMP
918         /* Could also do this with
919          * time = (timestamp / 86400000000 * 86400000000) - timestamp;
920          */
921         result = ((((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec)
922                            * INT64CONST(1000000)) + fsec);
923 #else
924         result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
925 #endif
926
927         PG_RETURN_TIMEADT(result);
928 }
929
930 /* timestamptz_time()
931  * Convert timestamptz to time data type.
932  */
933 Datum
934 timestamptz_time(PG_FUNCTION_ARGS)
935 {
936         TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
937         TimeADT         result;
938         struct tm       tt,
939                            *tm = &tt;
940         int                     tz;
941         fsec_t          fsec;
942         char       *tzn;
943
944         if (TIMESTAMP_NOT_FINITE(timestamp))
945                 PG_RETURN_NULL();
946
947         if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
948                 elog(ERROR, "Unable to convert timestamptz to time");
949
950 #ifdef HAVE_INT64_TIMESTAMP
951         /* Could also do this with
952          * time = (timestamp / 86400000000 * 86400000000) - timestamp;
953          */
954         result = ((((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec)
955                            * INT64CONST(1000000)) + fsec);
956 #else
957         result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
958 #endif
959
960         PG_RETURN_TIMEADT(result);
961 }
962
963 /* datetime_timestamp()
964  * Convert date and time to timestamp data type.
965  */
966 Datum
967 datetime_timestamp(PG_FUNCTION_ARGS)
968 {
969         DateADT         date = PG_GETARG_DATEADT(0);
970         TimeADT         time = PG_GETARG_TIMEADT(1);
971         Timestamp       result;
972
973         result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
974                                                                                                  DateADTGetDatum(date)));
975         result += time;
976
977         PG_RETURN_TIMESTAMP(result);
978 }
979
980 /* time_interval()
981  * Convert time to interval data type.
982  */
983 Datum
984 time_interval(PG_FUNCTION_ARGS)
985 {
986         TimeADT         time = PG_GETARG_TIMEADT(0);
987         Interval   *result;
988
989         result = (Interval *) palloc(sizeof(Interval));
990
991         result->time = time;
992         result->month = 0;
993
994         PG_RETURN_INTERVAL_P(result);
995 }
996
997 /* interval_time()
998  * Convert interval to time data type.
999  */
1000 Datum
1001 interval_time(PG_FUNCTION_ARGS)
1002 {
1003         Interval   *span = PG_GETARG_INTERVAL_P(0);
1004         TimeADT         result;
1005
1006 #ifdef HAVE_INT64_TIMESTAMP
1007         result = span->time;
1008         if ((result >= INT64CONST(86400000000))
1009                 || (result <= INT64CONST(-86400000000)))
1010                 result -= (result / INT64CONST(1000000) * INT64CONST(1000000));
1011 #else
1012         Interval        span1;
1013
1014         result = span->time;
1015         TMODULO(result, span1.time, 86400e0);
1016 #endif
1017
1018         PG_RETURN_TIMEADT(result);
1019 }
1020
1021 /* time_mi_time()
1022  * Subtract two times to produce an interval.
1023  */
1024 Datum
1025 time_mi_time(PG_FUNCTION_ARGS)
1026 {
1027         TimeADT         time1 = PG_GETARG_TIMEADT(0);
1028         TimeADT         time2 = PG_GETARG_TIMEADT(1);
1029         Interval   *result;
1030
1031         result = (Interval *) palloc(sizeof(Interval));
1032
1033         result->time = (time1 - time2);
1034         result->month = 0;
1035
1036         PG_RETURN_INTERVAL_P(result);
1037 }
1038
1039 /* time_pl_interval()
1040  * Add interval to time.
1041  */
1042 Datum
1043 time_pl_interval(PG_FUNCTION_ARGS)
1044 {
1045         TimeADT         time = PG_GETARG_TIMEADT(0);
1046         Interval   *span = PG_GETARG_INTERVAL_P(1);
1047         TimeADT         result;
1048
1049 #ifdef HAVE_INT64_TIMESTAMP
1050         result = (time + span->time);
1051         result -= (result / INT64CONST(86400000000) * INT64CONST(86400000000));
1052         if (result < INT64CONST(0))
1053                 result += INT64CONST(86400000000);
1054 #else
1055         TimeADT         time1;
1056
1057         result = (time + span->time);
1058         TMODULO(result, time1, 86400e0);
1059         if (result < 0)
1060                 result += 86400;
1061 #endif
1062
1063         PG_RETURN_TIMEADT(result);
1064 }
1065
1066 /* time_mi_interval()
1067  * Subtract interval from time.
1068  */
1069 Datum
1070 time_mi_interval(PG_FUNCTION_ARGS)
1071 {
1072         TimeADT         time = PG_GETARG_TIMEADT(0);
1073         Interval   *span = PG_GETARG_INTERVAL_P(1);
1074         TimeADT         result;
1075
1076 #ifdef HAVE_INT64_TIMESTAMP
1077         result = (time - span->time);
1078         result -= (result / INT64CONST(86400000000) * INT64CONST(86400000000));
1079         if (result < INT64CONST(0))
1080                 result += INT64CONST(86400000000);
1081 #else
1082         TimeADT         time1;
1083
1084         result = (time - span->time);
1085         TMODULO(result, time1, 86400e0);
1086         if (result < 0)
1087                 result += 86400;
1088 #endif
1089
1090         PG_RETURN_TIMEADT(result);
1091 }
1092
1093 /* interval_pl_time()
1094  * Add time to interval.
1095  */
1096 Datum
1097 interval_pl_time(PG_FUNCTION_ARGS)
1098 {
1099         Datum           span = PG_GETARG_DATUM(0);
1100         Datum           time = PG_GETARG_DATUM(1);
1101
1102         return DirectFunctionCall2(time_pl_interval, time, span);
1103 }
1104
1105
1106 /* time_text()
1107  * Convert time to text data type.
1108  */
1109 Datum
1110 time_text(PG_FUNCTION_ARGS)
1111 {
1112         /* Input is a Time, but may as well leave it in Datum form */
1113         Datum           time = PG_GETARG_DATUM(0);
1114         text       *result;
1115         char       *str;
1116         int                     len;
1117
1118         str = DatumGetCString(DirectFunctionCall1(time_out, time));
1119
1120         len = (strlen(str) + VARHDRSZ);
1121
1122         result = palloc(len);
1123
1124         VARATT_SIZEP(result) = len;
1125         memmove(VARDATA(result), str, (len - VARHDRSZ));
1126
1127         pfree(str);
1128
1129         PG_RETURN_TEXT_P(result);
1130 }
1131
1132
1133 /* text_time()
1134  * Convert text string to time.
1135  * Text type is not null terminated, so use temporary string
1136  *      then call the standard input routine.
1137  */
1138 Datum
1139 text_time(PG_FUNCTION_ARGS)
1140 {
1141         text       *str = PG_GETARG_TEXT_P(0);
1142         int                     i;
1143         char       *sp,
1144                            *dp,
1145                                 dstr[MAXDATELEN + 1];
1146
1147         if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
1148                 elog(ERROR, "Bad time external representation (too long)");
1149
1150         sp = VARDATA(str);
1151         dp = dstr;
1152         for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
1153                 *dp++ = *sp++;
1154         *dp = '\0';
1155
1156         return DirectFunctionCall3(time_in,
1157                                                            CStringGetDatum(dstr),
1158                                                            ObjectIdGetDatum(InvalidOid),
1159                                                            Int32GetDatum(-1));
1160 }
1161
1162 /* time_part()
1163  * Extract specified field from time type.
1164  */
1165 Datum
1166 time_part(PG_FUNCTION_ARGS)
1167 {
1168         text       *units = PG_GETARG_TEXT_P(0);
1169         TimeADT         time = PG_GETARG_TIMEADT(1);
1170         float8          result;
1171         int                     type,
1172                                 val;
1173         int                     i;
1174         char       *up,
1175                            *lp,
1176                                 lowunits[MAXDATELEN + 1];
1177
1178         if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
1179                 elog(ERROR, "TIME units '%s' not recognized",
1180                          DatumGetCString(DirectFunctionCall1(textout,
1181                                                                                            PointerGetDatum(units))));
1182         up = VARDATA(units);
1183         lp = lowunits;
1184         for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
1185                 *lp++ = tolower((unsigned char) *up++);
1186         *lp = '\0';
1187
1188         type = DecodeUnits(0, lowunits, &val);
1189         if (type == UNKNOWN_FIELD)
1190                 type = DecodeSpecial(0, lowunits, &val);
1191
1192         if (type == UNITS)
1193         {
1194                 fsec_t          fsec;
1195                 struct tm       tt,
1196                                    *tm = &tt;
1197
1198                 time2tm(time, tm, &fsec);
1199
1200                 switch (val)
1201                 {
1202                         case DTK_MICROSEC:
1203 #ifdef HAVE_INT64_TIMESTAMP
1204                                 result = ((tm->tm_sec * INT64CONST(1000000)) + fsec);
1205 #else
1206                                 result = ((tm->tm_sec + fsec) * 1000000);
1207 #endif
1208                                 break;
1209
1210                         case DTK_MILLISEC:
1211 #ifdef HAVE_INT64_TIMESTAMP
1212                                 result = ((tm->tm_sec * INT64CONST(1000))
1213                                                   + (fsec / INT64CONST(1000)));
1214 #else
1215                                 result = ((tm->tm_sec + fsec) * 1000);
1216 #endif
1217                                 break;
1218
1219                         case DTK_SECOND:
1220 #ifdef HAVE_INT64_TIMESTAMP
1221                                 result = (tm->tm_sec + (fsec / INT64CONST(1000000)));
1222 #else
1223                                 result = (tm->tm_sec + fsec);
1224 #endif
1225                                 break;
1226
1227                         case DTK_MINUTE:
1228                                 result = tm->tm_min;
1229                                 break;
1230
1231                         case DTK_HOUR:
1232                                 result = tm->tm_hour;
1233                                 break;
1234
1235                         case DTK_TZ:
1236                         case DTK_TZ_MINUTE:
1237                         case DTK_TZ_HOUR:
1238                         case DTK_DAY:
1239                         case DTK_MONTH:
1240                         case DTK_QUARTER:
1241                         case DTK_YEAR:
1242                         case DTK_DECADE:
1243                         case DTK_CENTURY:
1244                         case DTK_MILLENNIUM:
1245                         default:
1246                                 elog(ERROR, "TIME units '%s' not supported",
1247                                          DatumGetCString(DirectFunctionCall1(textout,
1248                                                                                            PointerGetDatum(units))));
1249                                 result = 0;
1250                 }
1251         }
1252         else if ((type == RESERV) && (val == DTK_EPOCH))
1253         {
1254 #ifdef HAVE_INT64_TIMESTAMP
1255                 result = (time / 1000000e0);
1256 #else
1257                 result = time;
1258 #endif
1259         }
1260         else
1261         {
1262                 elog(ERROR, "TIME units '%s' not recognized",
1263                          DatumGetCString(DirectFunctionCall1(textout,
1264                                                                                            PointerGetDatum(units))));
1265                 result = 0;
1266         }
1267
1268         PG_RETURN_FLOAT8(result);
1269 }
1270
1271
1272 /*****************************************************************************
1273  *       Time With Time Zone ADT
1274  *****************************************************************************/
1275
1276 /* tm2timetz()
1277  * Convert a tm structure to a time data type.
1278  */
1279 int
1280 tm2timetz(struct tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
1281 {
1282 #ifdef HAVE_INT64_TIMESTAMP
1283         result->time = ((((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec)
1284                                          * INT64CONST(1000000)) + fsec);
1285 #else
1286         result->time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
1287 #endif
1288         result->zone = tz;
1289
1290         return 0;
1291 }
1292
1293 Datum
1294 timetz_in(PG_FUNCTION_ARGS)
1295 {
1296         char       *str = PG_GETARG_CSTRING(0);
1297
1298 #ifdef NOT_USED
1299         Oid                     typelem = PG_GETARG_OID(1);
1300 #endif
1301         int32           typmod = PG_GETARG_INT32(2);
1302         TimeTzADT  *result;
1303         fsec_t          fsec;
1304         struct tm       tt,
1305                            *tm = &tt;
1306         int                     tz;
1307         int                     nf;
1308         char            lowstr[MAXDATELEN + 1];
1309         char       *field[MAXDATEFIELDS];
1310         int                     dtype;
1311         int                     ftype[MAXDATEFIELDS];
1312
1313         if (strlen(str) >= sizeof(lowstr))
1314                 elog(ERROR, "Bad time with time zone"
1315                          " external representation (too long) '%s'", str);
1316
1317         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
1318           || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
1319                 elog(ERROR, "Bad time external representation '%s'", str);
1320
1321         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1322         tm2timetz(tm, fsec, tz, result);
1323         AdjustTimeForTypmod(&(result->time), typmod);
1324
1325         PG_RETURN_TIMETZADT_P(result);
1326 }
1327
1328 Datum
1329 timetz_out(PG_FUNCTION_ARGS)
1330 {
1331         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1332         char       *result;
1333         struct tm       tt,
1334                            *tm = &tt;
1335         fsec_t          fsec;
1336         int                     tz;
1337         char            buf[MAXDATELEN + 1];
1338
1339         timetz2tm(time, tm, &fsec, &tz);
1340         EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
1341
1342         result = pstrdup(buf);
1343         PG_RETURN_CSTRING(result);
1344 }
1345
1346 /* timetz2tm()
1347  * Convert TIME WITH TIME ZONE data type to POSIX time structure.
1348  * For dates within the system-supported time_t range, convert to the
1349  *      local time zone. If out of this range, leave as GMT. - tgl 97/05/27
1350  */
1351 int
1352 timetz2tm(TimeTzADT *time, struct tm *tm, fsec_t *fsec, int *tzp)
1353 {
1354 #ifdef HAVE_INT64_TIMESTAMP
1355         tm->tm_hour = (time->time / INT64CONST(3600000000));
1356         time->time -= (tm->tm_hour * INT64CONST(3600000000));
1357         tm->tm_min = (time->time / INT64CONST(60000000));
1358         time->time -= (tm->tm_min * INT64CONST(60000000));
1359         tm->tm_sec = (time->time / INT64CONST(1000000));
1360         *fsec = (time->time - (tm->tm_sec * INT64CONST(1000000)));
1361 #else
1362         double          trem;
1363
1364         trem = time->time;
1365         TMODULO(trem, tm->tm_hour, 3600e0);
1366         TMODULO(trem, tm->tm_min, 60e0);
1367         TMODULO(trem, tm->tm_sec, 1e0);
1368         *fsec = trem;
1369 #endif
1370
1371         if (tzp != NULL)
1372                 *tzp = time->zone;
1373
1374         return 0;
1375 }
1376
1377 /* timetz_scale()
1378  * Adjust time type for specified scale factor.
1379  * Used by PostgreSQL type system to stuff columns.
1380  */
1381 Datum
1382 timetz_scale(PG_FUNCTION_ARGS)
1383 {
1384         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1385         int32           typmod = PG_GETARG_INT32(1);
1386         TimeTzADT  *result;
1387
1388         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1389
1390         result->time = time->time;
1391         result->zone = time->zone;
1392
1393         AdjustTimeForTypmod(&(result->time), typmod);
1394
1395         PG_RETURN_TIMETZADT_P(result);
1396 }
1397
1398
1399 static int
1400 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
1401 {
1402         double          t1,
1403                                 t2;
1404
1405         /* Primary sort is by true (GMT-equivalent) time */
1406         t1 = time1->time + time1->zone;
1407         t2 = time2->time + time2->zone;
1408
1409         if (t1 > t2)
1410                 return 1;
1411         if (t1 < t2)
1412                 return -1;
1413
1414         /*
1415          * If same GMT time, sort by timezone; we only want to say that two
1416          * timetz's are equal if both the time and zone parts are equal.
1417          */
1418         if (time1->zone > time2->zone)
1419                 return 1;
1420         if (time1->zone < time2->zone)
1421                 return -1;
1422
1423         return 0;
1424 }
1425
1426 Datum
1427 timetz_eq(PG_FUNCTION_ARGS)
1428 {
1429         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1430         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1431
1432         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
1433 }
1434
1435 Datum
1436 timetz_ne(PG_FUNCTION_ARGS)
1437 {
1438         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1439         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1440
1441         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
1442 }
1443
1444 Datum
1445 timetz_lt(PG_FUNCTION_ARGS)
1446 {
1447         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1448         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1449
1450         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
1451 }
1452
1453 Datum
1454 timetz_le(PG_FUNCTION_ARGS)
1455 {
1456         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1457         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1458
1459         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
1460 }
1461
1462 Datum
1463 timetz_gt(PG_FUNCTION_ARGS)
1464 {
1465         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1466         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1467
1468         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
1469 }
1470
1471 Datum
1472 timetz_ge(PG_FUNCTION_ARGS)
1473 {
1474         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1475         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1476
1477         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
1478 }
1479
1480 Datum
1481 timetz_cmp(PG_FUNCTION_ARGS)
1482 {
1483         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1484         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1485
1486         PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
1487 }
1488
1489 /*
1490  * timetz, being an unusual size, needs a specialized hash function.
1491  */
1492 Datum
1493 timetz_hash(PG_FUNCTION_ARGS)
1494 {
1495         TimeTzADT  *key = PG_GETARG_TIMETZADT_P(0);
1496
1497         /*
1498          * Specify hash length as sizeof(double) + sizeof(int4), not as
1499          * sizeof(TimeTzADT), so that any garbage pad bytes in the structure
1500          * won't be included in the hash!
1501          */
1502         return hash_any((unsigned char *) key, sizeof(key->time) + sizeof(key->zone));
1503 }
1504
1505 Datum
1506 timetz_larger(PG_FUNCTION_ARGS)
1507 {
1508         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1509         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1510
1511         if (DatumGetBool(DirectFunctionCall2(timetz_gt,
1512                                                                                  TimeTzADTPGetDatum(time1),
1513                                                                                  TimeTzADTPGetDatum(time2))))
1514                 PG_RETURN_TIMETZADT_P(time1);
1515         PG_RETURN_TIMETZADT_P(time2);
1516 }
1517
1518 Datum
1519 timetz_smaller(PG_FUNCTION_ARGS)
1520 {
1521         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1522         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1523
1524         if (DatumGetBool(DirectFunctionCall2(timetz_lt,
1525                                                                                  TimeTzADTPGetDatum(time1),
1526                                                                                  TimeTzADTPGetDatum(time2))))
1527                 PG_RETURN_TIMETZADT_P(time1);
1528         PG_RETURN_TIMETZADT_P(time2);
1529 }
1530
1531 /* timetz_pl_interval()
1532  * Add interval to timetz.
1533  */
1534 Datum
1535 timetz_pl_interval(PG_FUNCTION_ARGS)
1536 {
1537         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1538         Interval   *span = PG_GETARG_INTERVAL_P(1);
1539         TimeTzADT  *result;
1540 #ifndef HAVE_INT64_TIMESTAMP
1541         TimeTzADT       time1;
1542 #endif
1543
1544         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1545
1546 #ifdef HAVE_INT64_TIMESTAMP
1547         result->time = (time->time + span->time);
1548         result->time -= (result->time / INT64CONST(86400000000) * INT64CONST(86400000000));
1549         if (result->time < INT64CONST(0))
1550                 result->time += INT64CONST(86400000000);
1551 #else
1552         result->time = (time->time + span->time);
1553         TMODULO(result->time, time1.time, 86400e0);
1554         if (result->time < 0)
1555                 result->time += 86400;
1556 #endif
1557
1558         result->zone = time->zone;
1559
1560         PG_RETURN_TIMETZADT_P(result);
1561 }
1562
1563 /* timetz_mi_interval()
1564  * Subtract interval from timetz.
1565  */
1566 Datum
1567 timetz_mi_interval(PG_FUNCTION_ARGS)
1568 {
1569         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1570         Interval   *span = PG_GETARG_INTERVAL_P(1);
1571         TimeTzADT  *result;
1572 #ifndef HAVE_INT64_TIMESTAMP
1573         TimeTzADT       time1;
1574 #endif
1575
1576         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1577
1578 #ifdef HAVE_INT64_TIMESTAMP
1579         result->time = (time->time - span->time);
1580         result->time -= (result->time / INT64CONST(86400000000) * INT64CONST(86400000000));
1581         if (result->time < INT64CONST(0))
1582                 result->time += INT64CONST(86400000000);
1583 #else
1584         result->time = (time->time - span->time);
1585         TMODULO(result->time, time1.time, 86400e0);
1586         if (result->time < 0)
1587                 result->time += 86400;
1588 #endif
1589
1590         result->zone = time->zone;
1591
1592         PG_RETURN_TIMETZADT_P(result);
1593 }
1594
1595 /* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
1596  *
1597  * Algorithm is per SQL92 spec.  This is much harder than you'd think
1598  * because the spec requires us to deliver a non-null answer in some cases
1599  * where some of the inputs are null.
1600  */
1601 Datum
1602 overlaps_timetz(PG_FUNCTION_ARGS)
1603 {
1604         /*
1605          * The arguments are TimeTzADT *, but we leave them as generic Datums
1606          * for convenience of notation --- and to avoid dereferencing nulls.
1607          */
1608         Datum           ts1 = PG_GETARG_DATUM(0);
1609         Datum           te1 = PG_GETARG_DATUM(1);
1610         Datum           ts2 = PG_GETARG_DATUM(2);
1611         Datum           te2 = PG_GETARG_DATUM(3);
1612         bool            ts1IsNull = PG_ARGISNULL(0);
1613         bool            te1IsNull = PG_ARGISNULL(1);
1614         bool            ts2IsNull = PG_ARGISNULL(2);
1615         bool            te2IsNull = PG_ARGISNULL(3);
1616
1617 #define TIMETZ_GT(t1,t2) \
1618         DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
1619 #define TIMETZ_LT(t1,t2) \
1620         DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
1621
1622         /*
1623          * If both endpoints of interval 1 are null, the result is null
1624          * (unknown). If just one endpoint is null, take ts1 as the non-null
1625          * one. Otherwise, take ts1 as the lesser endpoint.
1626          */
1627         if (ts1IsNull)
1628         {
1629                 if (te1IsNull)
1630                         PG_RETURN_NULL();
1631                 /* swap null for non-null */
1632                 ts1 = te1;
1633                 te1IsNull = true;
1634         }
1635         else if (!te1IsNull)
1636         {
1637                 if (TIMETZ_GT(ts1, te1))
1638                 {
1639                         Datum           tt = ts1;
1640
1641                         ts1 = te1;
1642                         te1 = tt;
1643                 }
1644         }
1645
1646         /* Likewise for interval 2. */
1647         if (ts2IsNull)
1648         {
1649                 if (te2IsNull)
1650                         PG_RETURN_NULL();
1651                 /* swap null for non-null */
1652                 ts2 = te2;
1653                 te2IsNull = true;
1654         }
1655         else if (!te2IsNull)
1656         {
1657                 if (TIMETZ_GT(ts2, te2))
1658                 {
1659                         Datum           tt = ts2;
1660
1661                         ts2 = te2;
1662                         te2 = tt;
1663                 }
1664         }
1665
1666         /*
1667          * At this point neither ts1 nor ts2 is null, so we can consider three
1668          * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1669          */
1670         if (TIMETZ_GT(ts1, ts2))
1671         {
1672                 /*
1673                  * This case is ts1 < te2 OR te1 < te2, which may look redundant
1674                  * but in the presence of nulls it's not quite completely so.
1675                  */
1676                 if (te2IsNull)
1677                         PG_RETURN_NULL();
1678                 if (TIMETZ_LT(ts1, te2))
1679                         PG_RETURN_BOOL(true);
1680                 if (te1IsNull)
1681                         PG_RETURN_NULL();
1682
1683                 /*
1684                  * If te1 is not null then we had ts1 <= te1 above, and we just
1685                  * found ts1 >= te2, hence te1 >= te2.
1686                  */
1687                 PG_RETURN_BOOL(false);
1688         }
1689         else if (TIMETZ_LT(ts1, ts2))
1690         {
1691                 /* This case is ts2 < te1 OR te2 < te1 */
1692                 if (te1IsNull)
1693                         PG_RETURN_NULL();
1694                 if (TIMETZ_LT(ts2, te1))
1695                         PG_RETURN_BOOL(true);
1696                 if (te2IsNull)
1697                         PG_RETURN_NULL();
1698
1699                 /*
1700                  * If te2 is not null then we had ts2 <= te2 above, and we just
1701                  * found ts2 >= te1, hence te2 >= te1.
1702                  */
1703                 PG_RETURN_BOOL(false);
1704         }
1705         else
1706         {
1707                 /*
1708                  * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1709                  * rather silly way of saying "true if both are nonnull, else
1710                  * null".
1711                  */
1712                 if (te1IsNull || te2IsNull)
1713                         PG_RETURN_NULL();
1714                 PG_RETURN_BOOL(true);
1715         }
1716
1717 #undef TIMETZ_GT
1718 #undef TIMETZ_LT
1719 }
1720
1721
1722 Datum
1723 timetz_time(PG_FUNCTION_ARGS)
1724 {
1725         TimeTzADT  *timetz = PG_GETARG_TIMETZADT_P(0);
1726         TimeADT         result;
1727
1728         /* swallow the time zone and just return the time */
1729         result = timetz->time;
1730
1731         PG_RETURN_TIMEADT(result);
1732 }
1733
1734
1735 Datum
1736 time_timetz(PG_FUNCTION_ARGS)
1737 {
1738         TimeADT         time = PG_GETARG_TIMEADT(0);
1739         TimeTzADT  *result;
1740         struct tm       tt,
1741                            *tm = &tt;
1742         fsec_t          fsec;
1743         int                     tz;
1744
1745         GetCurrentDateTime(tm);
1746         time2tm(time, tm, &fsec);
1747         tz = DetermineLocalTimeZone(tm);
1748
1749         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1750
1751         result->time = time;
1752         result->zone = tz;
1753
1754         PG_RETURN_TIMETZADT_P(result);
1755 }
1756
1757
1758 /* timestamptz_timetz()
1759  * Convert timestamp to timetz data type.
1760  */
1761 Datum
1762 timestamptz_timetz(PG_FUNCTION_ARGS)
1763 {
1764         TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1765         TimeTzADT  *result;
1766         struct tm       tt,
1767                            *tm = &tt;
1768         int                     tz;
1769         fsec_t          fsec;
1770         char       *tzn;
1771
1772         if (TIMESTAMP_NOT_FINITE(timestamp))
1773                 PG_RETURN_NULL();
1774
1775         if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
1776                 elog(ERROR, "Unable to convert timestamptz to timetz");
1777
1778         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1779
1780         tm2timetz(tm, fsec, tz, result);
1781
1782         PG_RETURN_TIMETZADT_P(result);
1783 }
1784
1785
1786 /* datetimetz_timestamptz()
1787  * Convert date and timetz to timestamp with time zone data type.
1788  * Timestamp is stored in GMT, so add the time zone
1789  * stored with the timetz to the result.
1790  * - thomas 2000-03-10
1791  */
1792 Datum
1793 datetimetz_timestamptz(PG_FUNCTION_ARGS)
1794 {
1795         DateADT         date = PG_GETARG_DATEADT(0);
1796         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
1797         TimestampTz result;
1798
1799 #ifdef HAVE_INT64_TIMESTAMP
1800         result = (((date * INT64CONST(86400000000)) + time->time)
1801                 + (time->zone * INT64CONST(1000000)));
1802 #else
1803         result = (((date * 86400.0) + time->time) + time->zone);
1804 #endif
1805
1806         PG_RETURN_TIMESTAMP(result);
1807 }
1808
1809
1810 /* timetz_text()
1811  * Convert timetz to text data type.
1812  */
1813 Datum
1814 timetz_text(PG_FUNCTION_ARGS)
1815 {
1816         /* Input is a Timetz, but may as well leave it in Datum form */
1817         Datum           timetz = PG_GETARG_DATUM(0);
1818         text       *result;
1819         char       *str;
1820         int                     len;
1821
1822         str = DatumGetCString(DirectFunctionCall1(timetz_out, timetz));
1823
1824         len = (strlen(str) + VARHDRSZ);
1825
1826         result = palloc(len);
1827
1828         VARATT_SIZEP(result) = len;
1829         memmove(VARDATA(result), str, (len - VARHDRSZ));
1830
1831         pfree(str);
1832
1833         PG_RETURN_TEXT_P(result);
1834 }
1835
1836
1837 /* text_timetz()
1838  * Convert text string to timetz.
1839  * Text type is not null terminated, so use temporary string
1840  *      then call the standard input routine.
1841  */
1842 Datum
1843 text_timetz(PG_FUNCTION_ARGS)
1844 {
1845         text       *str = PG_GETARG_TEXT_P(0);
1846         int                     i;
1847         char       *sp,
1848                            *dp,
1849                                 dstr[MAXDATELEN + 1];
1850
1851         if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
1852                 elog(ERROR, "Bad timetz external representation (too long)");
1853
1854         sp = VARDATA(str);
1855         dp = dstr;
1856         for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
1857                 *dp++ = *sp++;
1858         *dp = '\0';
1859
1860         return DirectFunctionCall3(timetz_in,
1861                                                            CStringGetDatum(dstr),
1862                                                            ObjectIdGetDatum(InvalidOid),
1863                                                            Int32GetDatum(-1));
1864 }
1865
1866 /* timetz_part()
1867  * Extract specified field from time type.
1868  */
1869 Datum
1870 timetz_part(PG_FUNCTION_ARGS)
1871 {
1872         text       *units = PG_GETARG_TEXT_P(0);
1873         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
1874         float8          result;
1875         int                     type,
1876                                 val;
1877         int                     i;
1878         char       *up,
1879                            *lp,
1880                                 lowunits[MAXDATELEN + 1];
1881
1882         if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
1883                 elog(ERROR, "TIMETZ units '%s' not recognized",
1884                          DatumGetCString(DirectFunctionCall1(textout,
1885                                                                                            PointerGetDatum(units))));
1886         up = VARDATA(units);
1887         lp = lowunits;
1888         for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
1889                 *lp++ = tolower((unsigned char) *up++);
1890         *lp = '\0';
1891
1892         type = DecodeUnits(0, lowunits, &val);
1893         if (type == UNKNOWN_FIELD)
1894                 type = DecodeSpecial(0, lowunits, &val);
1895
1896         if (type == UNITS)
1897         {
1898                 double          dummy;
1899                 int                     tz;
1900                 fsec_t          fsec;
1901                 struct tm       tt,
1902                                    *tm = &tt;
1903
1904                 timetz2tm(time, tm, &fsec, &tz);
1905
1906                 switch (val)
1907                 {
1908                         case DTK_TZ:
1909                                 result = tz;
1910                                 break;
1911
1912                         case DTK_TZ_MINUTE:
1913                                 result = tz / 60;
1914                                 TMODULO(result, dummy, 60e0);
1915                                 break;
1916
1917                         case DTK_TZ_HOUR:
1918                                 dummy = tz;
1919                                 TMODULO(dummy, result, 3600e0);
1920                                 break;
1921
1922                         case DTK_MICROSEC:
1923 #ifdef HAVE_INT64_TIMESTAMP
1924                                 result = ((tm->tm_sec * INT64CONST(1000000)) + fsec);
1925 #else
1926                                 result = ((tm->tm_sec + fsec) * 1000000);
1927 #endif
1928                                 break;
1929
1930                         case DTK_MILLISEC:
1931 #ifdef HAVE_INT64_TIMESTAMP
1932                                 result = ((tm->tm_sec * INT64CONST(1000))
1933                                                   + (fsec / INT64CONST(1000)));
1934 #else
1935                                 result = ((tm->tm_sec + fsec) * 1000);
1936 #endif
1937                                 break;
1938
1939                         case DTK_SECOND:
1940 #ifdef HAVE_INT64_TIMESTAMP
1941                                 result = (tm->tm_sec + (fsec / INT64CONST(1000000)));
1942 #else
1943                                 result = (tm->tm_sec + fsec);
1944 #endif
1945                                 break;
1946
1947                         case DTK_MINUTE:
1948                                 result = tm->tm_min;
1949                                 break;
1950
1951                         case DTK_HOUR:
1952                                 result = tm->tm_hour;
1953                                 break;
1954
1955                         case DTK_DAY:
1956                         case DTK_MONTH:
1957                         case DTK_QUARTER:
1958                         case DTK_YEAR:
1959                         case DTK_DECADE:
1960                         case DTK_CENTURY:
1961                         case DTK_MILLENNIUM:
1962                         default:
1963                                 elog(ERROR, "TIMETZ units '%s' not supported",
1964                                          DatumGetCString(DirectFunctionCall1(textout,
1965                                                                                            PointerGetDatum(units))));
1966                                 result = 0;
1967                 }
1968         }
1969         else if ((type == RESERV) && (val == DTK_EPOCH))
1970         {
1971 #ifdef HAVE_INT64_TIMESTAMP
1972                 result = ((time->time / 1000000e0) - time->zone);
1973 #else
1974                 result = (time->time - time->zone);
1975 #endif
1976         }
1977         else
1978         {
1979                 elog(ERROR, "TIMETZ units '%s' not recognized",
1980                          DatumGetCString(DirectFunctionCall1(textout,
1981                                                                                            PointerGetDatum(units))));
1982                 result = 0;
1983         }
1984
1985         PG_RETURN_FLOAT8(result);
1986 }
1987
1988 /* timetz_zone()
1989  * Encode time with time zone type with specified time zone.
1990  */
1991 Datum
1992 timetz_zone(PG_FUNCTION_ARGS)
1993 {
1994         text       *zone = PG_GETARG_TEXT_P(0);
1995         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
1996         TimeTzADT  *result;
1997         TimeADT         time1;
1998         int                     tz;
1999         int                     type,
2000                                 val;
2001         int                     i;
2002         char       *up,
2003                            *lp,
2004                                 lowzone[MAXDATELEN + 1];
2005
2006         if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN)
2007                 elog(ERROR, "Time zone '%s' not recognized",
2008                          DatumGetCString(DirectFunctionCall1(textout,
2009                                                                                                  PointerGetDatum(zone))));
2010         up = VARDATA(zone);
2011         lp = lowzone;
2012         for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++)
2013                 *lp++ = tolower((unsigned char) *up++);
2014         *lp = '\0';
2015
2016         type = DecodeSpecial(0, lowzone, &val);
2017
2018         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2019
2020         if ((type == TZ) || (type == DTZ))
2021         {
2022                 tz = val * 60;
2023 #ifdef HAVE_INT64_TIMESTAMP
2024                 time1 = (time->time - ((time->zone + tz) * INT64CONST(1000000)));
2025                 result->time -= ((result->time / time1) * time1);
2026                 if (result->time < INT64CONST(0))
2027                         result->time += INT64CONST(86400000000);
2028 #else
2029                 time1 = (time->time - time->zone + tz);
2030                 TMODULO(result->time, time1, 86400e0);
2031                 if (result->time < 0)
2032                         result->time += 86400;
2033 #endif
2034
2035                 result->zone = tz;
2036         }
2037         else
2038         {
2039                 elog(ERROR, "Time zone '%s' not recognized", lowzone);
2040                 PG_RETURN_NULL();
2041         }
2042
2043         PG_RETURN_TIMETZADT_P(result);
2044 }       /* timetz_zone() */
2045
2046 /* timetz_izone()
2047  * Encode time with time zone type with specified time interval as time zone.
2048  */
2049 Datum
2050 timetz_izone(PG_FUNCTION_ARGS)
2051 {
2052         Interval   *zone = PG_GETARG_INTERVAL_P(0);
2053         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
2054         TimeTzADT  *result;
2055         int                     tz;
2056
2057         if (zone->month != 0)
2058                 elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)",
2059                          DatumGetCString(DirectFunctionCall1(interval_out,
2060                                                                                                  PointerGetDatum(zone))));
2061
2062 #ifdef HAVE_INT64_TIMESTAMP
2063         tz = -(zone->time / INT64CONST(1000000));
2064 #else
2065         tz = -(zone->time);
2066 #endif
2067
2068         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2069
2070 #ifdef HAVE_INT64_TIMESTAMP
2071         result->time = (time->time + ((time->zone - tz) * INT64CONST(1000000)));
2072         while (result->time < INT64CONST(0))
2073                 result->time += INT64CONST(86400000000);
2074         while (result->time >= INT64CONST(86400000000))
2075                 result->time -= INT64CONST(86400000000);
2076 #else
2077         result->time = (time->time + (time->zone - tz));
2078         while (result->time < 0)
2079                 result->time += 86400;
2080         while (result->time >= 86400)
2081                 result->time -= 86400;
2082 #endif
2083
2084         result->zone = tz;
2085
2086         PG_RETURN_TIMETZADT_P(result);
2087 }       /* timetz_izone() */