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