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