]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/date.c
Accept an INTERVAL argument for SET TIME ZONE per SQL99.
[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-2001, 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.62 2001/10/18 17:30:15 thomas 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 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
32
33 /*****************************************************************************
34  *       Date ADT
35  *****************************************************************************/
36
37
38 /* date_in()
39  * Given date text string, convert to internal date format.
40  */
41 Datum
42 date_in(PG_FUNCTION_ARGS)
43 {
44         char       *str = PG_GETARG_CSTRING(0);
45         DateADT         date;
46         double          fsec;
47         struct tm       tt,
48                            *tm = &tt;
49         int                     tzp;
50         int                     dtype;
51         int                     nf;
52         char       *field[MAXDATEFIELDS];
53         int                     ftype[MAXDATEFIELDS];
54         char            lowstr[MAXDATELEN + 1];
55
56         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
57          || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp) != 0))
58                 elog(ERROR, "Bad date external representation '%s'", str);
59
60         switch (dtype)
61         {
62                 case DTK_DATE:
63                         break;
64
65                 case DTK_CURRENT:
66                         elog(ERROR, "Date CURRENT no longer supported"
67                                  "\n\tdate_in() internal coding error");
68                         GetCurrentTime(tm);
69                         break;
70
71                 case DTK_EPOCH:
72                         GetEpochTime(tm);
73                         break;
74
75                 default:
76                         elog(ERROR, "Unrecognized date external representation '%s'", str);
77         }
78
79         date = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
80
81         PG_RETURN_DATEADT(date);
82 }
83
84 /* date_out()
85  * Given internal format date, convert to text string.
86  */
87 Datum
88 date_out(PG_FUNCTION_ARGS)
89 {
90         DateADT         date = PG_GETARG_DATEADT(0);
91         char       *result;
92         struct tm       tt,
93                            *tm = &tt;
94         char            buf[MAXDATELEN + 1];
95
96         j2date((date + date2j(2000, 1, 1)),
97                    &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
98
99         EncodeDateOnly(tm, DateStyle, buf);
100
101         result = pstrdup(buf);
102         PG_RETURN_CSTRING(result);
103 }
104
105 Datum
106 date_eq(PG_FUNCTION_ARGS)
107 {
108         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
109         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
110
111         PG_RETURN_BOOL(dateVal1 == dateVal2);
112 }
113
114 Datum
115 date_ne(PG_FUNCTION_ARGS)
116 {
117         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
118         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
119
120         PG_RETURN_BOOL(dateVal1 != dateVal2);
121 }
122
123 Datum
124 date_lt(PG_FUNCTION_ARGS)
125 {
126         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
127         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
128
129         PG_RETURN_BOOL(dateVal1 < dateVal2);
130 }
131
132 Datum
133 date_le(PG_FUNCTION_ARGS)
134 {
135         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
136         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
137
138         PG_RETURN_BOOL(dateVal1 <= dateVal2);
139 }
140
141 Datum
142 date_gt(PG_FUNCTION_ARGS)
143 {
144         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
145         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
146
147         PG_RETURN_BOOL(dateVal1 > dateVal2);
148 }
149
150 Datum
151 date_ge(PG_FUNCTION_ARGS)
152 {
153         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
154         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
155
156         PG_RETURN_BOOL(dateVal1 >= dateVal2);
157 }
158
159 Datum
160 date_cmp(PG_FUNCTION_ARGS)
161 {
162         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
163         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
164
165         if (dateVal1 < dateVal2)
166                 PG_RETURN_INT32(-1);
167         else if (dateVal1 > dateVal2)
168                 PG_RETURN_INT32(1);
169         PG_RETURN_INT32(0);
170 }
171
172 Datum
173 date_larger(PG_FUNCTION_ARGS)
174 {
175         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
176         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
177
178         PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
179 }
180
181 Datum
182 date_smaller(PG_FUNCTION_ARGS)
183 {
184         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
185         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
186
187         PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
188 }
189
190 /* Compute difference between two dates in days.
191  */
192 Datum
193 date_mi(PG_FUNCTION_ARGS)
194 {
195         DateADT         dateVal1 = PG_GETARG_DATEADT(0);
196         DateADT         dateVal2 = PG_GETARG_DATEADT(1);
197
198         PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
199 }
200
201 /* Add a number of days to a date, giving a new date.
202  * Must handle both positive and negative numbers of days.
203  */
204 Datum
205 date_pli(PG_FUNCTION_ARGS)
206 {
207         DateADT         dateVal = PG_GETARG_DATEADT(0);
208         int32           days = PG_GETARG_INT32(1);
209
210         PG_RETURN_DATEADT(dateVal + days);
211 }
212
213 /* Subtract a number of days from a date, giving a new date.
214  */
215 Datum
216 date_mii(PG_FUNCTION_ARGS)
217 {
218         DateADT         dateVal = PG_GETARG_DATEADT(0);
219         int32           days = PG_GETARG_INT32(1);
220
221         PG_RETURN_DATEADT(dateVal - days);
222 }
223
224 /* date_timestamp()
225  * Convert date to timestamp data type.
226  */
227 Datum
228 date_timestamp(PG_FUNCTION_ARGS)
229 {
230         DateADT         dateVal = PG_GETARG_DATEADT(0);
231         Timestamp       result;
232
233         /* date is days since 2000, timestamp is seconds since same... */
234         result = dateVal * 86400.0;
235
236         PG_RETURN_TIMESTAMP(result);
237 }
238
239
240 /* timestamp_date()
241  * Convert timestamp to date data type.
242  */
243 Datum
244 timestamp_date(PG_FUNCTION_ARGS)
245 {
246         Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
247         DateADT         result;
248         struct tm       tt,
249                            *tm = &tt;
250         double          fsec;
251
252         if (TIMESTAMP_NOT_FINITE(timestamp))
253                 PG_RETURN_NULL();
254
255         if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
256                 elog(ERROR, "Unable to convert timestamp to date");
257
258         result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
259
260         PG_RETURN_DATEADT(result);
261 }
262
263
264 /* date_timestamptz()
265  * Convert date to timestamp with time zone data type.
266  */
267 Datum
268 date_timestamptz(PG_FUNCTION_ARGS)
269 {
270         DateADT         dateVal = PG_GETARG_DATEADT(0);
271         TimestampTz     result;
272         struct tm       tt,
273                            *tm = &tt;
274         time_t          utime;
275
276         j2date((dateVal + date2j(2000, 1, 1)), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
277
278         if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
279         {
280 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
281                 tm->tm_hour = 0;
282                 tm->tm_min = 0;
283                 tm->tm_sec = 0;
284                 tm->tm_isdst = -1;
285
286                 tm->tm_year -= 1900;
287                 tm->tm_mon -= 1;
288                 utime = mktime(tm);
289                 if (utime == -1)
290                         elog(ERROR, "Unable to convert date to tm");
291
292                 result = utime + ((date2j(1970, 1, 1) - date2j(2000, 1, 1)) * 86400.0);
293 #else
294                 result = dateVal * 86400.0 + CTimeZone;
295 #endif
296         }
297         else
298         {
299                 /* Outside of range for timezone support, so assume UTC */
300                 result = dateVal * 86400.0;
301         }
302
303         PG_RETURN_TIMESTAMP(result);
304 }
305
306
307 /* timestamptz_date()
308  * Convert timestamp with time zone to date data type.
309  */
310 Datum
311 timestamptz_date(PG_FUNCTION_ARGS)
312 {
313         TimestampTz     timestamp = PG_GETARG_TIMESTAMP(0);
314         DateADT         result;
315         struct tm       tt,
316                            *tm = &tt;
317         double          fsec;
318         int                     tz;
319         char       *tzn;
320
321         if (TIMESTAMP_NOT_FINITE(timestamp))
322                 PG_RETURN_NULL();
323
324         if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
325                 elog(ERROR, "Unable to convert timestamp to date");
326
327         result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
328
329         PG_RETURN_DATEADT(result);
330 }
331
332
333 /* abstime_date()
334  * Convert abstime to date data type.
335  */
336 Datum
337 abstime_date(PG_FUNCTION_ARGS)
338 {
339         AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
340         DateADT         result;
341         struct tm       tt,
342                            *tm = &tt;
343         int                     tz;
344
345         switch (abstime)
346         {
347                 case INVALID_ABSTIME:
348                 case NOSTART_ABSTIME:
349                 case NOEND_ABSTIME:
350                         elog(ERROR, "Unable to convert reserved abstime value to date");
351
352                         /*
353                          * pretend to drop through to make compiler think that result
354                          * will be set
355                          */
356
357                 default:
358                         abstime2tm(abstime, &tz, tm, NULL);
359                         result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1);
360                         break;
361         }
362
363         PG_RETURN_DATEADT(result);
364 }
365
366
367 /* date_text()
368  * Convert date to text data type.
369  */
370 Datum
371 date_text(PG_FUNCTION_ARGS)
372 {
373         /* Input is a Date, but may as well leave it in Datum form */
374         Datum           date = PG_GETARG_DATUM(0);
375         text       *result;
376         char       *str;
377         int                     len;
378
379         str = DatumGetCString(DirectFunctionCall1(date_out, date));
380
381         len = (strlen(str) + VARHDRSZ);
382
383         result = palloc(len);
384
385         VARATT_SIZEP(result) = len;
386         memmove(VARDATA(result), str, (len - VARHDRSZ));
387
388         pfree(str);
389
390         PG_RETURN_TEXT_P(result);
391 }
392
393
394 /* text_date()
395  * Convert text string to date.
396  * Text type is not null terminated, so use temporary string
397  *      then call the standard input routine.
398  */
399 Datum
400 text_date(PG_FUNCTION_ARGS)
401 {
402         text       *str = PG_GETARG_TEXT_P(0);
403         int                     i;
404         char       *sp,
405                            *dp,
406                                 dstr[MAXDATELEN + 1];
407
408         if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
409                 elog(ERROR, "Bad date external representation (too long)");
410
411         sp = VARDATA(str);
412         dp = dstr;
413         for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
414                 *dp++ = *sp++;
415         *dp = '\0';
416
417         return DirectFunctionCall1(date_in,
418                                                            CStringGetDatum(dstr));
419 }
420
421
422 /*****************************************************************************
423  *       Time ADT
424  *****************************************************************************/
425
426 Datum
427 time_in(PG_FUNCTION_ARGS)
428 {
429         char       *str = PG_GETARG_CSTRING(0);
430 #ifdef NOT_USED
431         Oid                     typelem = PG_GETARG_OID(1);
432 #endif
433         int32           typmod = PG_GETARG_INT32(2);
434         TimeADT         result;
435         double          fsec;
436         struct tm       tt,
437                            *tm = &tt;
438         int                     nf;
439         char            lowstr[MAXDATELEN + 1];
440         char       *field[MAXDATEFIELDS];
441         int                     dtype;
442         int                     ftype[MAXDATEFIELDS];
443
444         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
445          || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, NULL) != 0))
446                 elog(ERROR, "Bad time external representation '%s'", str);
447
448         result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
449
450         AdjustTimeForTypmod(&result, typmod);
451
452         PG_RETURN_TIMEADT(result);
453 }
454
455 Datum
456 time_out(PG_FUNCTION_ARGS)
457 {
458         TimeADT         time = PG_GETARG_TIMEADT(0);
459         char       *result;
460         struct tm       tt,
461                            *tm = &tt;
462         double          fsec;
463         double          trem;
464         char            buf[MAXDATELEN + 1];
465
466         trem = time;
467         TMODULO(trem, tm->tm_hour, 3600e0);
468         TMODULO(trem, tm->tm_min, 60e0);
469         TMODULO(trem, tm->tm_sec, 1e0);
470         fsec = trem;
471
472         EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
473
474         result = pstrdup(buf);
475         PG_RETURN_CSTRING(result);
476 }
477
478 /* time_scale()
479  * Adjust time type for specified scale factor.
480  * Used by PostgreSQL type system to stuff columns.
481  */
482 Datum
483 time_scale(PG_FUNCTION_ARGS)
484 {
485         TimeADT         time = PG_GETARG_TIMEADT(0);
486         int32           typmod = PG_GETARG_INT32(1);
487         TimeADT         result;
488
489         result = time;
490         AdjustTimeForTypmod(&result, typmod);
491
492         PG_RETURN_TIMEADT(result);
493 }
494
495 static void
496 AdjustTimeForTypmod(TimeADT *time, int32 typmod)
497 {
498         if ((typmod >= 0) && (typmod <= 13))
499         {
500                 static double TimeScale = 1;
501                 static int32 TimeTypmod = 0;
502
503                 if (typmod != TimeTypmod)
504                 {
505                         TimeScale = pow(10.0, typmod);
506                         TimeTypmod = typmod;
507                 }
508
509                 *time = (rint(((double) *time)*TimeScale)/TimeScale);
510
511                 if (*time >= 86400)
512                         *time -= 86400;
513         }
514
515         return;
516 }
517
518
519 Datum
520 time_eq(PG_FUNCTION_ARGS)
521 {
522         TimeADT         time1 = PG_GETARG_TIMEADT(0);
523         TimeADT         time2 = PG_GETARG_TIMEADT(1);
524
525         PG_RETURN_BOOL(time1 == time2);
526 }
527
528 Datum
529 time_ne(PG_FUNCTION_ARGS)
530 {
531         TimeADT         time1 = PG_GETARG_TIMEADT(0);
532         TimeADT         time2 = PG_GETARG_TIMEADT(1);
533
534         PG_RETURN_BOOL(time1 != time2);
535 }
536
537 Datum
538 time_lt(PG_FUNCTION_ARGS)
539 {
540         TimeADT         time1 = PG_GETARG_TIMEADT(0);
541         TimeADT         time2 = PG_GETARG_TIMEADT(1);
542
543         PG_RETURN_BOOL(time1 < time2);
544 }
545
546 Datum
547 time_le(PG_FUNCTION_ARGS)
548 {
549         TimeADT         time1 = PG_GETARG_TIMEADT(0);
550         TimeADT         time2 = PG_GETARG_TIMEADT(1);
551
552         PG_RETURN_BOOL(time1 <= time2);
553 }
554
555 Datum
556 time_gt(PG_FUNCTION_ARGS)
557 {
558         TimeADT         time1 = PG_GETARG_TIMEADT(0);
559         TimeADT         time2 = PG_GETARG_TIMEADT(1);
560
561         PG_RETURN_BOOL(time1 > time2);
562 }
563
564 Datum
565 time_ge(PG_FUNCTION_ARGS)
566 {
567         TimeADT         time1 = PG_GETARG_TIMEADT(0);
568         TimeADT         time2 = PG_GETARG_TIMEADT(1);
569
570         PG_RETURN_BOOL(time1 >= time2);
571 }
572
573 Datum
574 time_cmp(PG_FUNCTION_ARGS)
575 {
576         TimeADT         time1 = PG_GETARG_TIMEADT(0);
577         TimeADT         time2 = PG_GETARG_TIMEADT(1);
578
579         if (time1 < time2)
580                 PG_RETURN_INT32(-1);
581         if (time1 > time2)
582                 PG_RETURN_INT32(1);
583         PG_RETURN_INT32(0);
584 }
585
586 Datum
587 time_larger(PG_FUNCTION_ARGS)
588 {
589         TimeADT         time1 = PG_GETARG_TIMEADT(0);
590         TimeADT         time2 = PG_GETARG_TIMEADT(1);
591
592         PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
593 }
594
595 Datum
596 time_smaller(PG_FUNCTION_ARGS)
597 {
598         TimeADT         time1 = PG_GETARG_TIMEADT(0);
599         TimeADT         time2 = PG_GETARG_TIMEADT(1);
600
601         PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
602 }
603
604 /* overlaps_time() --- implements the SQL92 OVERLAPS operator.
605  *
606  * Algorithm is per SQL92 spec.  This is much harder than you'd think
607  * because the spec requires us to deliver a non-null answer in some cases
608  * where some of the inputs are null.
609  */
610 Datum
611 overlaps_time(PG_FUNCTION_ARGS)
612 {
613
614         /*
615          * The arguments are TimeADT, but we leave them as generic Datums to
616          * avoid dereferencing nulls (TimeADT is pass-by-reference!)
617          */
618         Datum           ts1 = PG_GETARG_DATUM(0);
619         Datum           te1 = PG_GETARG_DATUM(1);
620         Datum           ts2 = PG_GETARG_DATUM(2);
621         Datum           te2 = PG_GETARG_DATUM(3);
622         bool            ts1IsNull = PG_ARGISNULL(0);
623         bool            te1IsNull = PG_ARGISNULL(1);
624         bool            ts2IsNull = PG_ARGISNULL(2);
625         bool            te2IsNull = PG_ARGISNULL(3);
626
627 #define TIMEADT_GT(t1,t2) \
628         (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
629 #define TIMEADT_LT(t1,t2) \
630         (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
631
632         /*
633          * If both endpoints of interval 1 are null, the result is null
634          * (unknown). If just one endpoint is null, take ts1 as the non-null
635          * one. Otherwise, take ts1 as the lesser endpoint.
636          */
637         if (ts1IsNull)
638         {
639                 if (te1IsNull)
640                         PG_RETURN_NULL();
641                 /* swap null for non-null */
642                 ts1 = te1;
643                 te1IsNull = true;
644         }
645         else if (!te1IsNull)
646         {
647                 if (TIMEADT_GT(ts1, te1))
648                 {
649                         Datum           tt = ts1;
650
651                         ts1 = te1;
652                         te1 = tt;
653                 }
654         }
655
656         /* Likewise for interval 2. */
657         if (ts2IsNull)
658         {
659                 if (te2IsNull)
660                         PG_RETURN_NULL();
661                 /* swap null for non-null */
662                 ts2 = te2;
663                 te2IsNull = true;
664         }
665         else if (!te2IsNull)
666         {
667                 if (TIMEADT_GT(ts2, te2))
668                 {
669                         Datum           tt = ts2;
670
671                         ts2 = te2;
672                         te2 = tt;
673                 }
674         }
675
676         /*
677          * At this point neither ts1 nor ts2 is null, so we can consider three
678          * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
679          */
680         if (TIMEADT_GT(ts1, ts2))
681         {
682
683                 /*
684                  * This case is ts1 < te2 OR te1 < te2, which may look redundant
685                  * but in the presence of nulls it's not quite completely so.
686                  */
687                 if (te2IsNull)
688                         PG_RETURN_NULL();
689                 if (TIMEADT_LT(ts1, te2))
690                         PG_RETURN_BOOL(true);
691                 if (te1IsNull)
692                         PG_RETURN_NULL();
693
694                 /*
695                  * If te1 is not null then we had ts1 <= te1 above, and we just
696                  * found ts1 >= te2, hence te1 >= te2.
697                  */
698                 PG_RETURN_BOOL(false);
699         }
700         else if (TIMEADT_LT(ts1, ts2))
701         {
702                 /* This case is ts2 < te1 OR te2 < te1 */
703                 if (te1IsNull)
704                         PG_RETURN_NULL();
705                 if (TIMEADT_LT(ts2, te1))
706                         PG_RETURN_BOOL(true);
707                 if (te2IsNull)
708                         PG_RETURN_NULL();
709
710                 /*
711                  * If te2 is not null then we had ts2 <= te2 above, and we just
712                  * found ts2 >= te1, hence te2 >= te1.
713                  */
714                 PG_RETURN_BOOL(false);
715         }
716         else
717         {
718
719                 /*
720                  * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
721                  * rather silly way of saying "true if both are nonnull, else
722                  * null".
723                  */
724                 if (te1IsNull || te2IsNull)
725                         PG_RETURN_NULL();
726                 PG_RETURN_BOOL(true);
727         }
728
729 #undef TIMEADT_GT
730 #undef TIMEADT_LT
731 }
732
733 /* timestamp_time()
734  * Convert timestamp to time data type.
735  */
736 Datum
737 timestamp_time(PG_FUNCTION_ARGS)
738 {
739         Timestamp       timestamp = PG_GETARG_TIMESTAMP(0);
740         TimeADT         result;
741         struct tm       tt,
742                            *tm = &tt;
743         double          fsec;
744
745         if (TIMESTAMP_NOT_FINITE(timestamp))
746                 PG_RETURN_NULL();
747
748         if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
749                 elog(ERROR, "Unable to convert timestamp to date");
750
751         result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
752
753         PG_RETURN_TIMEADT(result);
754 }
755
756 /* datetime_timestamp()
757  * Convert date and time to timestamp data type.
758  */
759 Datum
760 datetime_timestamp(PG_FUNCTION_ARGS)
761 {
762         DateADT         date = PG_GETARG_DATEADT(0);
763         TimeADT         time = PG_GETARG_TIMEADT(1);
764         Timestamp       result;
765
766         result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
767                                                                                                  DateADTGetDatum(date)));
768         result += time;
769
770         PG_RETURN_TIMESTAMP(result);
771 }
772
773 /* time_interval()
774  * Convert time to interval data type.
775  */
776 Datum
777 time_interval(PG_FUNCTION_ARGS)
778 {
779         TimeADT         time = PG_GETARG_TIMEADT(0);
780         Interval   *result;
781
782         result = (Interval *) palloc(sizeof(Interval));
783
784         result->time = time;
785         result->month = 0;
786
787         PG_RETURN_INTERVAL_P(result);
788 }
789
790 /* interval_time()
791  * Convert interval to time data type.
792  */
793 Datum
794 interval_time(PG_FUNCTION_ARGS)
795 {
796         Interval   *span = PG_GETARG_INTERVAL_P(0);
797         TimeADT         result;
798         Interval        span1;
799
800         result = span->time;
801         TMODULO(result, span1.time, 86400e0);
802
803         PG_RETURN_TIMEADT(result);
804 }
805
806 /* time_mi_time()
807  * Subtract two times to produce an interval.
808  */
809 Datum
810 time_mi_time(PG_FUNCTION_ARGS)
811 {
812         TimeADT         time1 = PG_GETARG_TIMEADT(0);
813         TimeADT         time2 = PG_GETARG_TIMEADT(1);
814         Interval   *result;
815
816         result = (Interval *) palloc(sizeof(Interval));
817
818         result->time = time2 - time1;
819         result->month = 0;
820
821         PG_RETURN_INTERVAL_P(result);
822 }
823
824 /* time_pl_interval()
825  * Add interval to time.
826  */
827 Datum
828 time_pl_interval(PG_FUNCTION_ARGS)
829 {
830         TimeADT         time = PG_GETARG_TIMEADT(0);
831         Interval   *span = PG_GETARG_INTERVAL_P(1);
832         TimeADT         result;
833         TimeADT         time1;
834
835         result = (time + span->time);
836         TMODULO(result, time1, 86400e0);
837         if (result < 0)
838                 result += 86400;
839
840         PG_RETURN_TIMEADT(result);
841 }
842
843 /* time_mi_interval()
844  * Subtract interval from time.
845  */
846 Datum
847 time_mi_interval(PG_FUNCTION_ARGS)
848 {
849         TimeADT         time = PG_GETARG_TIMEADT(0);
850         Interval   *span = PG_GETARG_INTERVAL_P(1);
851         TimeADT         result;
852         TimeADT         time1;
853
854         result = (time - span->time);
855         TMODULO(result, time1, 86400e0);
856         if (result < 0)
857                 result += 86400;
858
859         PG_RETURN_TIMEADT(result);
860 }
861
862 /* interval_pl_time()
863  * Add time to interval.
864  */
865 Datum
866 interval_pl_time(PG_FUNCTION_ARGS)
867 {
868         Datum           span = PG_GETARG_DATUM(0);
869         Datum           time = PG_GETARG_DATUM(1);
870
871         return DirectFunctionCall2(time_pl_interval, time, span);
872 }
873
874
875 /* time_text()
876  * Convert time to text data type.
877  */
878 Datum
879 time_text(PG_FUNCTION_ARGS)
880 {
881         /* Input is a Time, but may as well leave it in Datum form */
882         Datum           time = PG_GETARG_DATUM(0);
883         text       *result;
884         char       *str;
885         int                     len;
886
887         str = DatumGetCString(DirectFunctionCall1(time_out, time));
888
889         len = (strlen(str) + VARHDRSZ);
890
891         result = palloc(len);
892
893         VARATT_SIZEP(result) = len;
894         memmove(VARDATA(result), str, (len - VARHDRSZ));
895
896         pfree(str);
897
898         PG_RETURN_TEXT_P(result);
899 }
900
901
902 /* text_time()
903  * Convert text string to time.
904  * Text type is not null terminated, so use temporary string
905  *      then call the standard input routine.
906  */
907 Datum
908 text_time(PG_FUNCTION_ARGS)
909 {
910         text       *str = PG_GETARG_TEXT_P(0);
911         int                     i;
912         char       *sp,
913                            *dp,
914                                 dstr[MAXDATELEN + 1];
915
916         if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
917                 elog(ERROR, "Bad time external representation (too long)");
918
919         sp = VARDATA(str);
920         dp = dstr;
921         for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
922                 *dp++ = *sp++;
923         *dp = '\0';
924
925         return DirectFunctionCall3(time_in,
926                                                            CStringGetDatum(dstr),
927                                                            ObjectIdGetDatum(InvalidOid),
928                                                            Int32GetDatum(-1));
929 }
930
931
932 /*****************************************************************************
933  *       Time With Time Zone ADT
934  *****************************************************************************/
935
936 Datum
937 timetz_in(PG_FUNCTION_ARGS)
938 {
939         char       *str = PG_GETARG_CSTRING(0);
940 #ifdef NOT_USED
941         Oid                     typelem = PG_GETARG_OID(1);
942 #endif
943         int32           typmod = PG_GETARG_INT32(2);
944         TimeTzADT  *result;
945         double          fsec;
946         struct tm       tt,
947                            *tm = &tt;
948         int                     tz;
949         int                     nf;
950         char            lowstr[MAXDATELEN + 1];
951         char       *field[MAXDATEFIELDS];
952         int                     dtype;
953         int                     ftype[MAXDATEFIELDS];
954
955         if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
956           || (DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
957                 elog(ERROR, "Bad time external representation '%s'", str);
958
959         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
960
961         result->time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
962         result->zone = tz;
963
964         AdjustTimeForTypmod(&(result->time), typmod);
965
966         PG_RETURN_TIMETZADT_P(result);
967 }
968
969 Datum
970 timetz_out(PG_FUNCTION_ARGS)
971 {
972         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
973         char       *result;
974         struct tm       tt,
975                            *tm = &tt;
976         double          fsec;
977         int                     tz;
978         double          trem;
979         char            buf[MAXDATELEN + 1];
980
981         trem = time->time;
982         TMODULO(trem, tm->tm_hour, 3600e0);
983         TMODULO(trem, tm->tm_min, 60e0);
984         TMODULO(trem, tm->tm_sec, 1e0);
985         fsec = trem;
986
987         tz = time->zone;
988
989         EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);
990
991         result = pstrdup(buf);
992         PG_RETURN_CSTRING(result);
993 }
994
995 /* timetz_scale()
996  * Adjust time type for specified scale factor.
997  * Used by PostgreSQL type system to stuff columns.
998  */
999 Datum
1000 timetz_scale(PG_FUNCTION_ARGS)
1001 {
1002         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1003         int32           typmod = PG_GETARG_INT32(1);
1004         TimeTzADT  *result;
1005
1006         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1007
1008         result->time = time->time;
1009         result->zone = time->zone;
1010
1011         AdjustTimeForTypmod(&(result->time), typmod);
1012
1013         PG_RETURN_TIMETZADT_P(result);
1014 }
1015
1016
1017 static int
1018 timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
1019 {
1020         double          t1,
1021                                 t2;
1022
1023         /* Primary sort is by true (GMT-equivalent) time */
1024         t1 = time1->time + time1->zone;
1025         t2 = time2->time + time2->zone;
1026
1027         if (t1 > t2)
1028                 return 1;
1029         if (t1 < t2)
1030                 return -1;
1031
1032         /*
1033          * If same GMT time, sort by timezone; we only want to say that two
1034          * timetz's are equal if both the time and zone parts are equal.
1035          */
1036         if (time1->zone > time2->zone)
1037                 return 1;
1038         if (time1->zone < time2->zone)
1039                 return -1;
1040
1041         return 0;
1042 }
1043
1044 Datum
1045 timetz_eq(PG_FUNCTION_ARGS)
1046 {
1047         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1048         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1049
1050         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
1051 }
1052
1053 Datum
1054 timetz_ne(PG_FUNCTION_ARGS)
1055 {
1056         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1057         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1058
1059         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
1060 }
1061
1062 Datum
1063 timetz_lt(PG_FUNCTION_ARGS)
1064 {
1065         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1066         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1067
1068         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
1069 }
1070
1071 Datum
1072 timetz_le(PG_FUNCTION_ARGS)
1073 {
1074         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1075         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1076
1077         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
1078 }
1079
1080 Datum
1081 timetz_gt(PG_FUNCTION_ARGS)
1082 {
1083         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1084         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1085
1086         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
1087 }
1088
1089 Datum
1090 timetz_ge(PG_FUNCTION_ARGS)
1091 {
1092         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1093         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1094
1095         PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
1096 }
1097
1098 Datum
1099 timetz_cmp(PG_FUNCTION_ARGS)
1100 {
1101         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1102         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1103
1104         PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
1105 }
1106
1107 /*
1108  * timetz, being an unusual size, needs a specialized hash function.
1109  */
1110 Datum
1111 timetz_hash(PG_FUNCTION_ARGS)
1112 {
1113         TimeTzADT  *key = PG_GETARG_TIMETZADT_P(0);
1114
1115         /*
1116          * Specify hash length as sizeof(double) + sizeof(int4), not as
1117          * sizeof(TimeTzADT), so that any garbage pad bytes in the structure
1118          * won't be included in the hash!
1119          */
1120         return hash_any((char *) key, sizeof(double) + sizeof(int4));
1121 }
1122
1123 Datum
1124 timetz_larger(PG_FUNCTION_ARGS)
1125 {
1126         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1127         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1128
1129         if (DatumGetBool(DirectFunctionCall2(timetz_gt,
1130                                                                                  TimeTzADTPGetDatum(time1),
1131                                                                                  TimeTzADTPGetDatum(time2))))
1132                 PG_RETURN_TIMETZADT_P(time1);
1133         PG_RETURN_TIMETZADT_P(time2);
1134 }
1135
1136 Datum
1137 timetz_smaller(PG_FUNCTION_ARGS)
1138 {
1139         TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
1140         TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1141
1142         if (DatumGetBool(DirectFunctionCall2(timetz_lt,
1143                                                                                  TimeTzADTPGetDatum(time1),
1144                                                                                  TimeTzADTPGetDatum(time2))))
1145                 PG_RETURN_TIMETZADT_P(time1);
1146         PG_RETURN_TIMETZADT_P(time2);
1147 }
1148
1149 /* timetz_pl_interval()
1150  * Add interval to timetz.
1151  */
1152 Datum
1153 timetz_pl_interval(PG_FUNCTION_ARGS)
1154 {
1155         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1156         Interval   *span = PG_GETARG_INTERVAL_P(1);
1157         TimeTzADT  *result;
1158         TimeTzADT       time1;
1159
1160         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1161
1162         result->time = (time->time + span->time);
1163         TMODULO(result->time, time1.time, 86400e0);
1164         if (result->time < 0)
1165                 result->time += 86400;
1166         result->zone = time->zone;
1167
1168         PG_RETURN_TIMETZADT_P(result);
1169 }
1170
1171 /* timetz_mi_interval()
1172  * Subtract interval from timetz.
1173  */
1174 Datum
1175 timetz_mi_interval(PG_FUNCTION_ARGS)
1176 {
1177         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1178         Interval   *span = PG_GETARG_INTERVAL_P(1);
1179         TimeTzADT  *result;
1180         TimeTzADT       time1;
1181
1182         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1183
1184         result->time = (time->time - span->time);
1185         TMODULO(result->time, time1.time, 86400e0);
1186         if (result->time < 0)
1187                 result->time += 86400;
1188         result->zone = time->zone;
1189
1190         PG_RETURN_TIMETZADT_P(result);
1191 }
1192
1193 /* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
1194  *
1195  * Algorithm is per SQL92 spec.  This is much harder than you'd think
1196  * because the spec requires us to deliver a non-null answer in some cases
1197  * where some of the inputs are null.
1198  */
1199 Datum
1200 overlaps_timetz(PG_FUNCTION_ARGS)
1201 {
1202
1203         /*
1204          * The arguments are TimeTzADT *, but we leave them as generic Datums
1205          * for convenience of notation --- and to avoid dereferencing nulls.
1206          */
1207         Datum           ts1 = PG_GETARG_DATUM(0);
1208         Datum           te1 = PG_GETARG_DATUM(1);
1209         Datum           ts2 = PG_GETARG_DATUM(2);
1210         Datum           te2 = PG_GETARG_DATUM(3);
1211         bool            ts1IsNull = PG_ARGISNULL(0);
1212         bool            te1IsNull = PG_ARGISNULL(1);
1213         bool            ts2IsNull = PG_ARGISNULL(2);
1214         bool            te2IsNull = PG_ARGISNULL(3);
1215
1216 #define TIMETZ_GT(t1,t2) \
1217         DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
1218 #define TIMETZ_LT(t1,t2) \
1219         DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
1220
1221         /*
1222          * If both endpoints of interval 1 are null, the result is null
1223          * (unknown). If just one endpoint is null, take ts1 as the non-null
1224          * one. Otherwise, take ts1 as the lesser endpoint.
1225          */
1226         if (ts1IsNull)
1227         {
1228                 if (te1IsNull)
1229                         PG_RETURN_NULL();
1230                 /* swap null for non-null */
1231                 ts1 = te1;
1232                 te1IsNull = true;
1233         }
1234         else if (!te1IsNull)
1235         {
1236                 if (TIMETZ_GT(ts1, te1))
1237                 {
1238                         Datum           tt = ts1;
1239
1240                         ts1 = te1;
1241                         te1 = tt;
1242                 }
1243         }
1244
1245         /* Likewise for interval 2. */
1246         if (ts2IsNull)
1247         {
1248                 if (te2IsNull)
1249                         PG_RETURN_NULL();
1250                 /* swap null for non-null */
1251                 ts2 = te2;
1252                 te2IsNull = true;
1253         }
1254         else if (!te2IsNull)
1255         {
1256                 if (TIMETZ_GT(ts2, te2))
1257                 {
1258                         Datum           tt = ts2;
1259
1260                         ts2 = te2;
1261                         te2 = tt;
1262                 }
1263         }
1264
1265         /*
1266          * At this point neither ts1 nor ts2 is null, so we can consider three
1267          * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1268          */
1269         if (TIMETZ_GT(ts1, ts2))
1270         {
1271
1272                 /*
1273                  * This case is ts1 < te2 OR te1 < te2, which may look redundant
1274                  * but in the presence of nulls it's not quite completely so.
1275                  */
1276                 if (te2IsNull)
1277                         PG_RETURN_NULL();
1278                 if (TIMETZ_LT(ts1, te2))
1279                         PG_RETURN_BOOL(true);
1280                 if (te1IsNull)
1281                         PG_RETURN_NULL();
1282
1283                 /*
1284                  * If te1 is not null then we had ts1 <= te1 above, and we just
1285                  * found ts1 >= te2, hence te1 >= te2.
1286                  */
1287                 PG_RETURN_BOOL(false);
1288         }
1289         else if (TIMETZ_LT(ts1, ts2))
1290         {
1291                 /* This case is ts2 < te1 OR te2 < te1 */
1292                 if (te1IsNull)
1293                         PG_RETURN_NULL();
1294                 if (TIMETZ_LT(ts2, te1))
1295                         PG_RETURN_BOOL(true);
1296                 if (te2IsNull)
1297                         PG_RETURN_NULL();
1298
1299                 /*
1300                  * If te2 is not null then we had ts2 <= te2 above, and we just
1301                  * found ts2 >= te1, hence te2 >= te1.
1302                  */
1303                 PG_RETURN_BOOL(false);
1304         }
1305         else
1306         {
1307
1308                 /*
1309                  * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1310                  * rather silly way of saying "true if both are nonnull, else
1311                  * null".
1312                  */
1313                 if (te1IsNull || te2IsNull)
1314                         PG_RETURN_NULL();
1315                 PG_RETURN_BOOL(true);
1316         }
1317
1318 #undef TIMETZ_GT
1319 #undef TIMETZ_LT
1320 }
1321
1322
1323 Datum
1324 timetz_time(PG_FUNCTION_ARGS)
1325 {
1326         TimeTzADT  *timetz = PG_GETARG_TIMETZADT_P(0);
1327         TimeADT         result;
1328
1329         /* swallow the time zone and just return the time */
1330         result = timetz->time;
1331
1332         PG_RETURN_TIMEADT(result);
1333 }
1334
1335
1336 Datum
1337 time_timetz(PG_FUNCTION_ARGS)
1338 {
1339         TimeADT         time = PG_GETARG_TIMEADT(0);
1340         TimeTzADT  *result;
1341         struct tm       tt,
1342                            *tm = &tt;
1343         int                     tz;
1344
1345         GetCurrentTime(tm);
1346         tz = DetermineLocalTimeZone(tm);
1347
1348         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1349
1350         result->time = time;
1351         result->zone = tz;
1352
1353         PG_RETURN_TIMETZADT_P(result);
1354 }
1355
1356
1357 /* timestamptz_timetz()
1358  * Convert timestamp to timetz data type.
1359  */
1360 Datum
1361 timestamptz_timetz(PG_FUNCTION_ARGS)
1362 {
1363         TimestampTz     timestamp = PG_GETARG_TIMESTAMP(0);
1364         TimeTzADT  *result;
1365         struct tm       tt,
1366                            *tm = &tt;
1367         int                     tz;
1368         double          fsec;
1369         char       *tzn;
1370
1371         if (TIMESTAMP_NOT_FINITE(timestamp))
1372                 PG_RETURN_NULL();
1373
1374         if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
1375                 elog(ERROR, "Unable to convert timestamp to date");
1376
1377         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1378
1379         result->time = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec + fsec);
1380         result->zone = tz;
1381
1382         PG_RETURN_TIMETZADT_P(result);
1383 }
1384
1385
1386 /* datetimetz_timestamptz()
1387  * Convert date and timetz to timestamp with time zone data type.
1388  * Timestamp is stored in GMT, so add the time zone
1389  * stored with the timetz to the result.
1390  * - thomas 2000-03-10
1391  */
1392 Datum
1393 datetimetz_timestamptz(PG_FUNCTION_ARGS)
1394 {
1395         DateADT         date = PG_GETARG_DATEADT(0);
1396         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
1397         TimestampTz     result;
1398
1399         result = date * 86400.0 + time->time + time->zone;
1400
1401         PG_RETURN_TIMESTAMP(result);
1402 }
1403
1404
1405 /* timetz_text()
1406  * Convert timetz to text data type.
1407  */
1408 Datum
1409 timetz_text(PG_FUNCTION_ARGS)
1410 {
1411         /* Input is a Timetz, but may as well leave it in Datum form */
1412         Datum           timetz = PG_GETARG_DATUM(0);
1413         text       *result;
1414         char       *str;
1415         int                     len;
1416
1417         str = DatumGetCString(DirectFunctionCall1(timetz_out, timetz));
1418
1419         len = (strlen(str) + VARHDRSZ);
1420
1421         result = palloc(len);
1422
1423         VARATT_SIZEP(result) = len;
1424         memmove(VARDATA(result), str, (len - VARHDRSZ));
1425
1426         pfree(str);
1427
1428         PG_RETURN_TEXT_P(result);
1429 }
1430
1431
1432 /* text_timetz()
1433  * Convert text string to timetz.
1434  * Text type is not null terminated, so use temporary string
1435  *      then call the standard input routine.
1436  */
1437 Datum
1438 text_timetz(PG_FUNCTION_ARGS)
1439 {
1440         text       *str = PG_GETARG_TEXT_P(0);
1441         int                     i;
1442         char       *sp,
1443                            *dp,
1444                                 dstr[MAXDATELEN + 1];
1445
1446         if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
1447                 elog(ERROR, "Bad timetz external representation (too long)");
1448
1449         sp = VARDATA(str);
1450         dp = dstr;
1451         for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
1452                 *dp++ = *sp++;
1453         *dp = '\0';
1454
1455         return DirectFunctionCall3(timetz_in,
1456                                                            CStringGetDatum(dstr),
1457                                                            ObjectIdGetDatum(InvalidOid),
1458                                                            Int32GetDatum(-1));
1459 }
1460
1461 /* timetz_part()
1462  * Extract specified field from time type.
1463  */
1464 Datum
1465 timetz_part(PG_FUNCTION_ARGS)
1466 {
1467         text       *units = PG_GETARG_TEXT_P(0);
1468         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
1469         float8          result;
1470         int                     type,
1471                                 val;
1472         int                     i;
1473         char       *up,
1474                            *lp,
1475                                 lowunits[MAXDATELEN + 1];
1476
1477         if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
1478                 elog(ERROR, "TIMETZ units '%s' not recognized",
1479                          DatumGetCString(DirectFunctionCall1(textout,
1480                                                                                                  PointerGetDatum(units))));
1481         up = VARDATA(units);
1482         lp = lowunits;
1483         for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
1484                 *lp++ = tolower((unsigned char) *up++);
1485         *lp = '\0';
1486
1487         type = DecodeUnits(0, lowunits, &val);
1488         if (type == UNKNOWN_FIELD)
1489                 type = DecodeSpecial(0, lowunits, &val);
1490
1491         if (type == UNITS)
1492         {
1493                 double          trem;
1494                 double          dummy;
1495                 int                     tz;
1496                 double          fsec;
1497                 struct tm       tt,
1498                                    *tm = &tt;
1499
1500                 trem = time->time;
1501                 TMODULO(trem, tm->tm_hour, 3600e0);
1502                 TMODULO(trem, tm->tm_min, 60e0);
1503                 TMODULO(trem, tm->tm_sec, 1e0);
1504                 fsec = trem;
1505                 tz = time->zone;
1506
1507                 switch (val)
1508                 {
1509                         case DTK_TZ:
1510                                 result = tz;
1511                                 break;
1512
1513                         case DTK_TZ_MINUTE:
1514                                 result = tz / 60;
1515                                 TMODULO(result, dummy, 60e0);
1516                                 break;
1517
1518                         case DTK_TZ_HOUR:
1519                                 dummy = tz;
1520                                 TMODULO(dummy, result, 3600e0);
1521                                 break;
1522
1523                         case DTK_MICROSEC:
1524                                 result = ((tm->tm_sec + fsec) * 1000000);
1525                                 break;
1526
1527                         case DTK_MILLISEC:
1528                                 result = ((tm->tm_sec + fsec) * 1000);
1529                                 break;
1530
1531                         case DTK_SECOND:
1532                                 result = (tm->tm_sec + fsec);
1533                                 break;
1534
1535                         case DTK_MINUTE:
1536                                 result = tm->tm_min;
1537                                 break;
1538
1539                         case DTK_HOUR:
1540                                 result = tm->tm_hour;
1541                                 break;
1542
1543                         case DTK_DAY:
1544                         case DTK_MONTH:
1545                         case DTK_QUARTER:
1546                         case DTK_YEAR:
1547                         case DTK_DECADE:
1548                         case DTK_CENTURY:
1549                         case DTK_MILLENNIUM:
1550                         default:
1551                                 elog(ERROR, "TIMETZ units '%s' not supported",
1552                                          DatumGetCString(DirectFunctionCall1(textout,
1553                                                                                                                  PointerGetDatum(units))));
1554                                 result = 0;
1555                 }
1556         }
1557         else if ((type == RESERV) && (val == DTK_EPOCH))
1558         {
1559                 result = time->time - time->zone;
1560         }
1561         else
1562         {
1563                 elog(ERROR, "TIMETZ units '%s' not recognized",
1564                          DatumGetCString(DirectFunctionCall1(textout,
1565                                                                                                  PointerGetDatum(units))));
1566                 result = 0;
1567         }
1568
1569         PG_RETURN_FLOAT8(result);
1570 }
1571
1572 /* timetz_zone()
1573  * Encode time with time zone type with specified time zone.
1574  */
1575 Datum
1576 timetz_zone(PG_FUNCTION_ARGS)
1577 {
1578         text       *zone = PG_GETARG_TEXT_P(0);
1579         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
1580         TimeTzADT  *result;
1581         TimeADT         time1;
1582         int                     tz;
1583         int                     type,
1584                                 val;
1585         int                     i;
1586         char       *up,
1587                            *lp,
1588                                 lowzone[MAXDATELEN + 1];
1589
1590         if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN)
1591                 elog(ERROR, "Time zone '%s' not recognized",
1592                          DatumGetCString(DirectFunctionCall1(textout,
1593                                                                                                  PointerGetDatum(zone))));
1594         up = VARDATA(zone);
1595         lp = lowzone;
1596         for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++)
1597                 *lp++ = tolower((unsigned char) *up++);
1598         *lp = '\0';
1599
1600         type = DecodeSpecial(0, lowzone, &val);
1601
1602         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1603
1604         if ((type == TZ) || (type == DTZ))
1605         {
1606                 tz = val * 60;
1607                 time1 = time->time - time->zone + tz;
1608                 TMODULO(result->time, time1, 86400e0);
1609                 if (result->time < 0)
1610                         result->time += 86400;
1611                 result->zone = tz;
1612         }
1613         else
1614         {
1615                 elog(ERROR, "Time zone '%s' not recognized", lowzone);
1616                 PG_RETURN_NULL();
1617         }
1618
1619         PG_RETURN_TIMETZADT_P(result);
1620 }       /* timetz_zone() */
1621
1622 /* timetz_izone()
1623  * Encode time with time zone type with specified time interval as time zone.
1624  */
1625 Datum
1626 timetz_izone(PG_FUNCTION_ARGS)
1627 {
1628         Interval   *zone = PG_GETARG_INTERVAL_P(0);
1629         TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
1630         TimeTzADT  *result;
1631         TimeADT         time1;
1632         int                     tz;
1633
1634         if (zone->month != 0)
1635                 elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)",
1636                          DatumGetCString(DirectFunctionCall1(interval_out,
1637                                                                                                  PointerGetDatum(zone))));
1638
1639         tz = -(zone->time);
1640
1641         result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1642
1643         time1 = time->time - time->zone + tz;
1644         TMODULO(result->time, time1, 86400e0);
1645         if (result->time < 0)
1646                 result->time += 86400;
1647         result->zone = tz;
1648
1649         PG_RETURN_TIMETZADT_P(result);
1650 }       /* timetz_izone() */