Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / backend / utils / adt / timestamp.c
blobe00f729e6fcda08fd2633faefd1e856417f8f5f6
1 /*-------------------------------------------------------------------------
3 * timestamp.c
4 * Functions for the built-in SQL92 types "timestamp" and "interval".
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
16 #include "postgres.h"
18 #include <ctype.h>
19 #include <math.h>
20 #include <float.h>
21 #include <limits.h>
22 #include <sys/time.h>
24 #include "access/hash.h"
25 #include "access/xact.h"
26 #include "catalog/pg_type.h"
27 #include "funcapi.h"
28 #include "libpq/pqformat.h"
29 #include "miscadmin.h"
30 #include "parser/scansup.h"
31 #include "utils/array.h"
32 #include "utils/builtins.h"
33 #include "utils/datetime.h"
36 * gcc's -ffast-math switch breaks routines that expect exact results from
37 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
39 #ifdef __FAST_MATH__
40 #error -ffast-math is known to break this code
41 #endif
44 /* Set at postmaster start */
45 TimestampTz PgStartTime;
47 /* Set at configuration reload */
48 TimestampTz PgReloadTime;
50 typedef struct
52 Timestamp current;
53 Timestamp finish;
54 Interval step;
55 int step_sign;
56 } generate_series_timestamp_fctx;
58 typedef struct
60 TimestampTz current;
61 TimestampTz finish;
62 Interval step;
63 int step_sign;
64 } generate_series_timestamptz_fctx;
67 static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec);
68 static void EncodeSpecialTimestamp(Timestamp dt, char *str);
69 static Timestamp dt2local(Timestamp dt, int timezone);
70 static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
71 static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
72 static TimestampTz timestamp2timestamptz(Timestamp timestamp);
75 /* common code for timestamptypmodin and timestamptztypmodin */
76 static int32
77 anytimestamp_typmodin(bool istz, ArrayType *ta)
79 int32 typmod;
80 int32 *tl;
81 int n;
83 tl = ArrayGetIntegerTypmods(ta, &n);
86 * we're not too tense about good error message here because grammar
87 * shouldn't allow wrong number of modifiers for TIMESTAMP
89 if (n != 1)
90 ereport(ERROR,
91 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
92 errmsg("invalid type modifier")));
94 if (*tl < 0)
95 ereport(ERROR,
96 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
97 errmsg("TIMESTAMP(%d)%s precision must not be negative",
98 *tl, (istz ? " WITH TIME ZONE" : ""))));
99 if (*tl > MAX_TIMESTAMP_PRECISION)
101 ereport(WARNING,
102 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
103 errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d",
104 *tl, (istz ? " WITH TIME ZONE" : ""),
105 MAX_TIMESTAMP_PRECISION)));
106 typmod = MAX_TIMESTAMP_PRECISION;
108 else
109 typmod = *tl;
111 return typmod;
114 /* common code for timestamptypmodout and timestamptztypmodout */
115 static char *
116 anytimestamp_typmodout(bool istz, int32 typmod)
118 char *res = (char *) palloc(64);
119 const char *tz = istz ? " with time zone" : " without time zone";
121 if (typmod >= 0)
122 snprintf(res, 64, "(%d)%s", (int) typmod, tz);
123 else
124 snprintf(res, 64, "%s", tz);
126 return res;
130 /*****************************************************************************
131 * USER I/O ROUTINES *
132 *****************************************************************************/
134 /* timestamp_in()
135 * Convert a string to internal form.
137 Datum
138 timestamp_in(PG_FUNCTION_ARGS)
140 char *str = PG_GETARG_CSTRING(0);
142 #ifdef NOT_USED
143 Oid typelem = PG_GETARG_OID(1);
144 #endif
145 int32 typmod = PG_GETARG_INT32(2);
146 Timestamp result;
147 fsec_t fsec;
148 struct pg_tm tt,
149 *tm = &tt;
150 int tz;
151 int dtype;
152 int nf;
153 int dterr;
154 char *field[MAXDATEFIELDS];
155 int ftype[MAXDATEFIELDS];
156 char workbuf[MAXDATELEN + MAXDATEFIELDS];
158 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
159 field, ftype, MAXDATEFIELDS, &nf);
160 if (dterr == 0)
161 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
162 if (dterr != 0)
163 DateTimeParseError(dterr, str, "timestamp");
165 switch (dtype)
167 case DTK_DATE:
168 if (tm2timestamp(tm, fsec, NULL, &result) != 0)
169 ereport(ERROR,
170 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
171 errmsg("timestamp out of range: \"%s\"", str)));
172 break;
174 case DTK_EPOCH:
175 result = SetEpochTimestamp();
176 break;
178 case DTK_LATE:
179 TIMESTAMP_NOEND(result);
180 break;
182 case DTK_EARLY:
183 TIMESTAMP_NOBEGIN(result);
184 break;
186 case DTK_INVALID:
187 ereport(ERROR,
188 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
189 errmsg("date/time value \"%s\" is no longer supported", str)));
191 TIMESTAMP_NOEND(result);
192 break;
194 default:
195 elog(ERROR, "unexpected dtype %d while parsing timestamp \"%s\"",
196 dtype, str);
197 TIMESTAMP_NOEND(result);
200 AdjustTimestampForTypmod(&result, typmod);
202 PG_RETURN_TIMESTAMP(result);
205 /* timestamp_out()
206 * Convert a timestamp to external form.
208 Datum
209 timestamp_out(PG_FUNCTION_ARGS)
211 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
212 char *result;
213 struct pg_tm tt,
214 *tm = &tt;
215 fsec_t fsec;
216 char *tzn = NULL;
217 char buf[MAXDATELEN + 1];
219 if (TIMESTAMP_NOT_FINITE(timestamp))
220 EncodeSpecialTimestamp(timestamp, buf);
221 else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
222 EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf);
223 else
224 ereport(ERROR,
225 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
226 errmsg("timestamp out of range")));
228 result = pstrdup(buf);
229 PG_RETURN_CSTRING(result);
233 * timestamp_recv - converts external binary format to timestamp
235 * We make no attempt to provide compatibility between int and float
236 * timestamp representations ...
238 Datum
239 timestamp_recv(PG_FUNCTION_ARGS)
241 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
243 #ifdef NOT_USED
244 Oid typelem = PG_GETARG_OID(1);
245 #endif
246 int32 typmod = PG_GETARG_INT32(2);
247 Timestamp timestamp;
248 struct pg_tm tt,
249 *tm = &tt;
250 fsec_t fsec;
252 #ifdef HAVE_INT64_TIMESTAMP
253 timestamp = (Timestamp) pq_getmsgint64(buf);
254 #else
255 timestamp = (Timestamp) pq_getmsgfloat8(buf);
256 #endif
258 /* rangecheck: see if timestamp_out would like it */
259 if (TIMESTAMP_NOT_FINITE(timestamp))
260 /* ok */ ;
261 else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
262 ereport(ERROR,
263 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
264 errmsg("timestamp out of range")));
266 AdjustTimestampForTypmod(&timestamp, typmod);
268 PG_RETURN_TIMESTAMP(timestamp);
272 * timestamp_send - converts timestamp to binary format
274 Datum
275 timestamp_send(PG_FUNCTION_ARGS)
277 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
278 StringInfoData buf;
280 pq_begintypsend(&buf);
281 #ifdef HAVE_INT64_TIMESTAMP
282 pq_sendint64(&buf, timestamp);
283 #else
284 pq_sendfloat8(&buf, timestamp);
285 #endif
286 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
289 Datum
290 timestamptypmodin(PG_FUNCTION_ARGS)
292 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
294 PG_RETURN_INT32(anytimestamp_typmodin(false, ta));
297 Datum
298 timestamptypmodout(PG_FUNCTION_ARGS)
300 int32 typmod = PG_GETARG_INT32(0);
302 PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod));
306 /* timestamp_scale()
307 * Adjust time type for specified scale factor.
308 * Used by PostgreSQL type system to stuff columns.
310 Datum
311 timestamp_scale(PG_FUNCTION_ARGS)
313 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
314 int32 typmod = PG_GETARG_INT32(1);
315 Timestamp result;
317 result = timestamp;
319 AdjustTimestampForTypmod(&result, typmod);
321 PG_RETURN_TIMESTAMP(result);
324 static void
325 AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
327 #ifdef HAVE_INT64_TIMESTAMP
328 static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
329 INT64CONST(1000000),
330 INT64CONST(100000),
331 INT64CONST(10000),
332 INT64CONST(1000),
333 INT64CONST(100),
334 INT64CONST(10),
335 INT64CONST(1)
338 static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
339 INT64CONST(500000),
340 INT64CONST(50000),
341 INT64CONST(5000),
342 INT64CONST(500),
343 INT64CONST(50),
344 INT64CONST(5),
345 INT64CONST(0)
347 #else
348 static const double TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
351 100,
352 1000,
353 10000,
354 100000,
355 1000000
357 #endif
359 if (!TIMESTAMP_NOT_FINITE(*time)
360 && (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
362 if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION)
363 ereport(ERROR,
364 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
365 errmsg("timestamp(%d) precision must be between %d and %d",
366 typmod, 0, MAX_TIMESTAMP_PRECISION)));
369 * Note: this round-to-nearest code is not completely consistent about
370 * rounding values that are exactly halfway between integral values.
371 * On most platforms, rint() will implement round-to-nearest-even, but
372 * the integer code always rounds up (away from zero). Is it worth
373 * trying to be consistent?
375 #ifdef HAVE_INT64_TIMESTAMP
376 if (*time >= INT64CONST(0))
378 *time = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) *
379 TimestampScales[typmod];
381 else
383 *time = -((((-*time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
384 * TimestampScales[typmod]);
386 #else
387 *time = rint((double) *time * TimestampScales[typmod]) / TimestampScales[typmod];
388 #endif
393 /* timestamptz_in()
394 * Convert a string to internal form.
396 Datum
397 timestamptz_in(PG_FUNCTION_ARGS)
399 char *str = PG_GETARG_CSTRING(0);
401 #ifdef NOT_USED
402 Oid typelem = PG_GETARG_OID(1);
403 #endif
404 int32 typmod = PG_GETARG_INT32(2);
405 TimestampTz result;
406 fsec_t fsec;
407 struct pg_tm tt,
408 *tm = &tt;
409 int tz;
410 int dtype;
411 int nf;
412 int dterr;
413 char *field[MAXDATEFIELDS];
414 int ftype[MAXDATEFIELDS];
415 char workbuf[MAXDATELEN + MAXDATEFIELDS];
417 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
418 field, ftype, MAXDATEFIELDS, &nf);
419 if (dterr == 0)
420 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
421 if (dterr != 0)
422 DateTimeParseError(dterr, str, "timestamp with time zone");
424 switch (dtype)
426 case DTK_DATE:
427 if (tm2timestamp(tm, fsec, &tz, &result) != 0)
428 ereport(ERROR,
429 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
430 errmsg("timestamp out of range: \"%s\"", str)));
431 break;
433 case DTK_EPOCH:
434 result = SetEpochTimestamp();
435 break;
437 case DTK_LATE:
438 TIMESTAMP_NOEND(result);
439 break;
441 case DTK_EARLY:
442 TIMESTAMP_NOBEGIN(result);
443 break;
445 case DTK_INVALID:
446 ereport(ERROR,
447 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
448 errmsg("date/time value \"%s\" is no longer supported", str)));
450 TIMESTAMP_NOEND(result);
451 break;
453 default:
454 elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"",
455 dtype, str);
456 TIMESTAMP_NOEND(result);
459 AdjustTimestampForTypmod(&result, typmod);
461 PG_RETURN_TIMESTAMPTZ(result);
464 /* timestamptz_out()
465 * Convert a timestamp to external form.
467 Datum
468 timestamptz_out(PG_FUNCTION_ARGS)
470 TimestampTz dt = PG_GETARG_TIMESTAMPTZ(0);
471 char *result;
472 int tz;
473 struct pg_tm tt,
474 *tm = &tt;
475 fsec_t fsec;
476 char *tzn;
477 char buf[MAXDATELEN + 1];
479 if (TIMESTAMP_NOT_FINITE(dt))
480 EncodeSpecialTimestamp(dt, buf);
481 else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0)
482 EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf);
483 else
484 ereport(ERROR,
485 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
486 errmsg("timestamp out of range")));
488 result = pstrdup(buf);
489 PG_RETURN_CSTRING(result);
493 * timestamptz_recv - converts external binary format to timestamptz
495 * We make no attempt to provide compatibility between int and float
496 * timestamp representations ...
498 Datum
499 timestamptz_recv(PG_FUNCTION_ARGS)
501 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
503 #ifdef NOT_USED
504 Oid typelem = PG_GETARG_OID(1);
505 #endif
506 int32 typmod = PG_GETARG_INT32(2);
507 TimestampTz timestamp;
508 int tz;
509 struct pg_tm tt,
510 *tm = &tt;
511 fsec_t fsec;
512 char *tzn;
514 #ifdef HAVE_INT64_TIMESTAMP
515 timestamp = (TimestampTz) pq_getmsgint64(buf);
516 #else
517 timestamp = (TimestampTz) pq_getmsgfloat8(buf);
518 #endif
520 /* rangecheck: see if timestamptz_out would like it */
521 if (TIMESTAMP_NOT_FINITE(timestamp))
522 /* ok */ ;
523 else if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
524 ereport(ERROR,
525 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
526 errmsg("timestamp out of range")));
528 AdjustTimestampForTypmod(&timestamp, typmod);
530 PG_RETURN_TIMESTAMPTZ(timestamp);
534 * timestamptz_send - converts timestamptz to binary format
536 Datum
537 timestamptz_send(PG_FUNCTION_ARGS)
539 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
540 StringInfoData buf;
542 pq_begintypsend(&buf);
543 #ifdef HAVE_INT64_TIMESTAMP
544 pq_sendint64(&buf, timestamp);
545 #else
546 pq_sendfloat8(&buf, timestamp);
547 #endif
548 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
551 Datum
552 timestamptztypmodin(PG_FUNCTION_ARGS)
554 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
556 PG_RETURN_INT32(anytimestamp_typmodin(true, ta));
559 Datum
560 timestamptztypmodout(PG_FUNCTION_ARGS)
562 int32 typmod = PG_GETARG_INT32(0);
564 PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod));
568 /* timestamptz_scale()
569 * Adjust time type for specified scale factor.
570 * Used by PostgreSQL type system to stuff columns.
572 Datum
573 timestamptz_scale(PG_FUNCTION_ARGS)
575 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
576 int32 typmod = PG_GETARG_INT32(1);
577 TimestampTz result;
579 result = timestamp;
581 AdjustTimestampForTypmod(&result, typmod);
583 PG_RETURN_TIMESTAMPTZ(result);
587 /* interval_in()
588 * Convert a string to internal form.
590 * External format(s):
591 * Uses the generic date/time parsing and decoding routines.
593 Datum
594 interval_in(PG_FUNCTION_ARGS)
596 char *str = PG_GETARG_CSTRING(0);
598 #ifdef NOT_USED
599 Oid typelem = PG_GETARG_OID(1);
600 #endif
601 int32 typmod = PG_GETARG_INT32(2);
602 Interval *result;
603 fsec_t fsec;
604 struct pg_tm tt,
605 *tm = &tt;
606 int dtype;
607 int nf;
608 int range;
609 int dterr;
610 char *field[MAXDATEFIELDS];
611 int ftype[MAXDATEFIELDS];
612 char workbuf[256];
614 tm->tm_year = 0;
615 tm->tm_mon = 0;
616 tm->tm_mday = 0;
617 tm->tm_hour = 0;
618 tm->tm_min = 0;
619 tm->tm_sec = 0;
620 fsec = 0;
622 if (typmod >= 0)
623 range = INTERVAL_RANGE(typmod);
624 else
625 range = INTERVAL_FULL_RANGE;
627 dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field,
628 ftype, MAXDATEFIELDS, &nf);
629 if (dterr == 0)
630 dterr = DecodeInterval(field, ftype, nf, range,
631 &dtype, tm, &fsec);
633 /* if those functions think it's a bad format, try ISO8601 style */
634 if (dterr == DTERR_BAD_FORMAT)
635 dterr = DecodeISO8601Interval(str,
636 &dtype, tm, &fsec);
638 if (dterr != 0)
640 if (dterr == DTERR_FIELD_OVERFLOW)
641 dterr = DTERR_INTERVAL_OVERFLOW;
642 DateTimeParseError(dterr, str, "interval");
645 result = (Interval *) palloc(sizeof(Interval));
647 switch (dtype)
649 case DTK_DELTA:
650 if (tm2interval(tm, fsec, result) != 0)
651 ereport(ERROR,
652 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
653 errmsg("interval out of range")));
654 break;
656 case DTK_INVALID:
657 ereport(ERROR,
658 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
659 errmsg("date/time value \"%s\" is no longer supported", str)));
660 break;
662 default:
663 elog(ERROR, "unexpected dtype %d while parsing interval \"%s\"",
664 dtype, str);
667 AdjustIntervalForTypmod(result, typmod);
669 PG_RETURN_INTERVAL_P(result);
672 /* interval_out()
673 * Convert a time span to external form.
675 Datum
676 interval_out(PG_FUNCTION_ARGS)
678 Interval *span = PG_GETARG_INTERVAL_P(0);
679 char *result;
680 struct pg_tm tt,
681 *tm = &tt;
682 fsec_t fsec;
683 char buf[MAXDATELEN + 1];
685 if (interval2tm(*span, tm, &fsec) != 0)
686 elog(ERROR, "could not convert interval to tm");
688 EncodeInterval(tm, fsec, IntervalStyle, buf);
690 result = pstrdup(buf);
691 PG_RETURN_CSTRING(result);
695 * interval_recv - converts external binary format to interval
697 Datum
698 interval_recv(PG_FUNCTION_ARGS)
700 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
702 #ifdef NOT_USED
703 Oid typelem = PG_GETARG_OID(1);
704 #endif
705 int32 typmod = PG_GETARG_INT32(2);
706 Interval *interval;
708 interval = (Interval *) palloc(sizeof(Interval));
710 #ifdef HAVE_INT64_TIMESTAMP
711 interval->time = pq_getmsgint64(buf);
712 #else
713 interval->time = pq_getmsgfloat8(buf);
714 #endif
715 interval->day = pq_getmsgint(buf, sizeof(interval->day));
716 interval->month = pq_getmsgint(buf, sizeof(interval->month));
718 AdjustIntervalForTypmod(interval, typmod);
720 PG_RETURN_INTERVAL_P(interval);
724 * interval_send - converts interval to binary format
726 Datum
727 interval_send(PG_FUNCTION_ARGS)
729 Interval *interval = PG_GETARG_INTERVAL_P(0);
730 StringInfoData buf;
732 pq_begintypsend(&buf);
733 #ifdef HAVE_INT64_TIMESTAMP
734 pq_sendint64(&buf, interval->time);
735 #else
736 pq_sendfloat8(&buf, interval->time);
737 #endif
738 pq_sendint(&buf, interval->day, sizeof(interval->day));
739 pq_sendint(&buf, interval->month, sizeof(interval->month));
740 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
743 Datum
744 intervaltypmodin(PG_FUNCTION_ARGS)
746 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
747 int32 *tl;
748 int n;
749 int32 typmod;
751 tl = ArrayGetIntegerTypmods(ta, &n);
754 * tl[0] - interval range (fields bitmask) tl[1] - precision (optional)
756 * Note we must validate tl[0] even though it's normally guaranteed
757 * correct by the grammar --- consider SELECT 'foo'::"interval"(1000).
759 if (n > 0)
761 switch (tl[0])
763 case INTERVAL_MASK(YEAR):
764 case INTERVAL_MASK(MONTH):
765 case INTERVAL_MASK(DAY):
766 case INTERVAL_MASK(HOUR):
767 case INTERVAL_MASK(MINUTE):
768 case INTERVAL_MASK(SECOND):
769 case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
770 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
771 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
772 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
773 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
774 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
775 case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
776 case INTERVAL_FULL_RANGE:
777 /* all OK */
778 break;
779 default:
780 ereport(ERROR,
781 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
782 errmsg("invalid INTERVAL type modifier")));
786 if (n == 1)
788 if (tl[0] != INTERVAL_FULL_RANGE)
789 typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, tl[0]);
790 else
791 typmod = -1;
793 else if (n == 2)
795 if (tl[1] < 0)
796 ereport(ERROR,
797 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
798 errmsg("INTERVAL(%d) precision must not be negative",
799 tl[1])));
800 if (tl[1] > MAX_INTERVAL_PRECISION)
802 ereport(WARNING,
803 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
804 errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
805 tl[1], MAX_INTERVAL_PRECISION)));
806 typmod = INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION, tl[0]);
808 else
809 typmod = INTERVAL_TYPMOD(tl[1], tl[0]);
811 else
813 ereport(ERROR,
814 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
815 errmsg("invalid INTERVAL type modifier")));
816 typmod = 0; /* keep compiler quiet */
819 PG_RETURN_INT32(typmod);
822 Datum
823 intervaltypmodout(PG_FUNCTION_ARGS)
825 int32 typmod = PG_GETARG_INT32(0);
826 char *res = (char *) palloc(64);
827 int fields;
828 int precision;
829 const char *fieldstr;
831 if (typmod < 0)
833 *res = '\0';
834 PG_RETURN_CSTRING(res);
837 fields = INTERVAL_RANGE(typmod);
838 precision = INTERVAL_PRECISION(typmod);
840 switch (fields)
842 case INTERVAL_MASK(YEAR):
843 fieldstr = " year";
844 break;
845 case INTERVAL_MASK(MONTH):
846 fieldstr = " month";
847 break;
848 case INTERVAL_MASK(DAY):
849 fieldstr = " day";
850 break;
851 case INTERVAL_MASK(HOUR):
852 fieldstr = " hour";
853 break;
854 case INTERVAL_MASK(MINUTE):
855 fieldstr = " minute";
856 break;
857 case INTERVAL_MASK(SECOND):
858 fieldstr = " second";
859 break;
860 case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
861 fieldstr = " year to month";
862 break;
863 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
864 fieldstr = " day to hour";
865 break;
866 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
867 fieldstr = " day to minute";
868 break;
869 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
870 fieldstr = " day to second";
871 break;
872 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
873 fieldstr = " hour to minute";
874 break;
875 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
876 fieldstr = " hour to second";
877 break;
878 case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
879 fieldstr = " minute to second";
880 break;
881 case INTERVAL_FULL_RANGE:
882 fieldstr = "";
883 break;
884 default:
885 elog(ERROR, "invalid INTERVAL typmod: 0x%x", typmod);
886 fieldstr = "";
887 break;
890 if (precision != INTERVAL_FULL_PRECISION)
891 snprintf(res, 64, "%s(%d)", fieldstr, precision);
892 else
893 snprintf(res, 64, "%s", fieldstr);
895 PG_RETURN_CSTRING(res);
899 /* interval_scale()
900 * Adjust interval type for specified fields.
901 * Used by PostgreSQL type system to stuff columns.
903 Datum
904 interval_scale(PG_FUNCTION_ARGS)
906 Interval *interval = PG_GETARG_INTERVAL_P(0);
907 int32 typmod = PG_GETARG_INT32(1);
908 Interval *result;
910 result = palloc(sizeof(Interval));
911 *result = *interval;
913 AdjustIntervalForTypmod(result, typmod);
915 PG_RETURN_INTERVAL_P(result);
919 * Adjust interval for specified precision, in both YEAR to SECOND
920 * range and sub-second precision.
922 static void
923 AdjustIntervalForTypmod(Interval *interval, int32 typmod)
925 #ifdef HAVE_INT64_TIMESTAMP
926 static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
927 INT64CONST(1000000),
928 INT64CONST(100000),
929 INT64CONST(10000),
930 INT64CONST(1000),
931 INT64CONST(100),
932 INT64CONST(10),
933 INT64CONST(1)
936 static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
937 INT64CONST(500000),
938 INT64CONST(50000),
939 INT64CONST(5000),
940 INT64CONST(500),
941 INT64CONST(50),
942 INT64CONST(5),
943 INT64CONST(0)
945 #else
946 static const double IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
949 100,
950 1000,
951 10000,
952 100000,
953 1000000
955 #endif
958 * Unspecified range and precision? Then not necessary to adjust. Setting
959 * typmod to -1 is the convention for all data types.
961 if (typmod >= 0)
963 int range = INTERVAL_RANGE(typmod);
964 int precision = INTERVAL_PRECISION(typmod);
967 * Our interpretation of intervals with a limited set of fields is
968 * that fields to the right of the last one specified are zeroed out,
969 * but those to the left of it remain valid. Thus for example there
970 * is no operational difference between INTERVAL YEAR TO MONTH and
971 * INTERVAL MONTH. In some cases we could meaningfully enforce that
972 * higher-order fields are zero; for example INTERVAL DAY could reject
973 * nonzero "month" field. However that seems a bit pointless when we
974 * can't do it consistently. (We cannot enforce a range limit on the
975 * highest expected field, since we do not have any equivalent of
976 * SQL's <interval leading field precision>.)
978 * Note: before PG 8.4 we interpreted a limited set of fields as
979 * actually causing a "modulo" operation on a given value, potentially
980 * losing high-order as well as low-order information. But there is
981 * no support for such behavior in the standard, and it seems fairly
982 * undesirable on data consistency grounds anyway. Now we only
983 * perform truncation or rounding of low-order fields.
985 if (range == INTERVAL_FULL_RANGE)
987 /* Do nothing... */
989 else if (range == INTERVAL_MASK(YEAR))
991 interval->month = (interval->month / MONTHS_PER_YEAR) * MONTHS_PER_YEAR;
992 interval->day = 0;
993 interval->time = 0;
995 else if (range == INTERVAL_MASK(MONTH))
997 interval->day = 0;
998 interval->time = 0;
1000 /* YEAR TO MONTH */
1001 else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
1003 interval->day = 0;
1004 interval->time = 0;
1006 else if (range == INTERVAL_MASK(DAY))
1008 interval->time = 0;
1010 else if (range == INTERVAL_MASK(HOUR))
1012 #ifdef HAVE_INT64_TIMESTAMP
1013 interval->time = (interval->time / USECS_PER_HOUR) *
1014 USECS_PER_HOUR;
1015 #else
1016 interval->time = ((int) (interval->time / SECS_PER_HOUR)) * (double) SECS_PER_HOUR;
1017 #endif
1019 else if (range == INTERVAL_MASK(MINUTE))
1021 #ifdef HAVE_INT64_TIMESTAMP
1022 interval->time = (interval->time / USECS_PER_MINUTE) *
1023 USECS_PER_MINUTE;
1024 #else
1025 interval->time = ((int) (interval->time / SECS_PER_MINUTE)) * (double) SECS_PER_MINUTE;
1026 #endif
1028 else if (range == INTERVAL_MASK(SECOND))
1030 /* fractional-second rounding will be dealt with below */
1032 /* DAY TO HOUR */
1033 else if (range == (INTERVAL_MASK(DAY) |
1034 INTERVAL_MASK(HOUR)))
1036 #ifdef HAVE_INT64_TIMESTAMP
1037 interval->time = (interval->time / USECS_PER_HOUR) *
1038 USECS_PER_HOUR;
1039 #else
1040 interval->time = ((int) (interval->time / SECS_PER_HOUR)) * (double) SECS_PER_HOUR;
1041 #endif
1043 /* DAY TO MINUTE */
1044 else if (range == (INTERVAL_MASK(DAY) |
1045 INTERVAL_MASK(HOUR) |
1046 INTERVAL_MASK(MINUTE)))
1048 #ifdef HAVE_INT64_TIMESTAMP
1049 interval->time = (interval->time / USECS_PER_MINUTE) *
1050 USECS_PER_MINUTE;
1051 #else
1052 interval->time = ((int) (interval->time / SECS_PER_MINUTE)) * (double) SECS_PER_MINUTE;
1053 #endif
1055 /* DAY TO SECOND */
1056 else if (range == (INTERVAL_MASK(DAY) |
1057 INTERVAL_MASK(HOUR) |
1058 INTERVAL_MASK(MINUTE) |
1059 INTERVAL_MASK(SECOND)))
1061 /* fractional-second rounding will be dealt with below */
1063 /* HOUR TO MINUTE */
1064 else if (range == (INTERVAL_MASK(HOUR) |
1065 INTERVAL_MASK(MINUTE)))
1067 #ifdef HAVE_INT64_TIMESTAMP
1068 interval->time = (interval->time / USECS_PER_MINUTE) *
1069 USECS_PER_MINUTE;
1070 #else
1071 interval->time = ((int) (interval->time / SECS_PER_MINUTE)) * (double) SECS_PER_MINUTE;
1072 #endif
1074 /* HOUR TO SECOND */
1075 else if (range == (INTERVAL_MASK(HOUR) |
1076 INTERVAL_MASK(MINUTE) |
1077 INTERVAL_MASK(SECOND)))
1079 /* fractional-second rounding will be dealt with below */
1081 /* MINUTE TO SECOND */
1082 else if (range == (INTERVAL_MASK(MINUTE) |
1083 INTERVAL_MASK(SECOND)))
1085 /* fractional-second rounding will be dealt with below */
1087 else
1088 elog(ERROR, "unrecognized interval typmod: %d", typmod);
1090 /* Need to adjust subsecond precision? */
1091 if (precision != INTERVAL_FULL_PRECISION)
1093 if (precision < 0 || precision > MAX_INTERVAL_PRECISION)
1094 ereport(ERROR,
1095 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1096 errmsg("interval(%d) precision must be between %d and %d",
1097 precision, 0, MAX_INTERVAL_PRECISION)));
1100 * Note: this round-to-nearest code is not completely consistent
1101 * about rounding values that are exactly halfway between integral
1102 * values. On most platforms, rint() will implement
1103 * round-to-nearest-even, but the integer code always rounds up
1104 * (away from zero). Is it worth trying to be consistent?
1106 #ifdef HAVE_INT64_TIMESTAMP
1107 if (interval->time >= INT64CONST(0))
1109 interval->time = ((interval->time +
1110 IntervalOffsets[precision]) /
1111 IntervalScales[precision]) *
1112 IntervalScales[precision];
1114 else
1116 interval->time = -(((-interval->time +
1117 IntervalOffsets[precision]) /
1118 IntervalScales[precision]) *
1119 IntervalScales[precision]);
1121 #else
1122 interval->time = rint(((double) interval->time) *
1123 IntervalScales[precision]) /
1124 IntervalScales[precision];
1125 #endif
1131 /* EncodeSpecialTimestamp()
1132 * Convert reserved timestamp data type to string.
1134 static void
1135 EncodeSpecialTimestamp(Timestamp dt, char *str)
1137 if (TIMESTAMP_IS_NOBEGIN(dt))
1138 strcpy(str, EARLY);
1139 else if (TIMESTAMP_IS_NOEND(dt))
1140 strcpy(str, LATE);
1141 else /* shouldn't happen */
1142 elog(ERROR, "invalid argument for EncodeSpecialTimestamp");
1145 Datum
1146 now(PG_FUNCTION_ARGS)
1148 PG_RETURN_TIMESTAMPTZ(GetCurrentTransactionStartTimestamp());
1151 Datum
1152 statement_timestamp(PG_FUNCTION_ARGS)
1154 PG_RETURN_TIMESTAMPTZ(GetCurrentStatementStartTimestamp());
1157 Datum
1158 clock_timestamp(PG_FUNCTION_ARGS)
1160 PG_RETURN_TIMESTAMPTZ(GetCurrentTimestamp());
1163 Datum
1164 pg_postmaster_start_time(PG_FUNCTION_ARGS)
1166 PG_RETURN_TIMESTAMPTZ(PgStartTime);
1169 Datum
1170 pg_conf_load_time(PG_FUNCTION_ARGS)
1172 PG_RETURN_TIMESTAMPTZ(PgReloadTime);
1176 * GetCurrentTimestamp -- get the current operating system time
1178 * Result is in the form of a TimestampTz value, and is expressed to the
1179 * full precision of the gettimeofday() syscall
1181 TimestampTz
1182 GetCurrentTimestamp(void)
1184 TimestampTz result;
1185 struct timeval tp;
1187 gettimeofday(&tp, NULL);
1189 result = (TimestampTz) tp.tv_sec -
1190 ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
1192 #ifdef HAVE_INT64_TIMESTAMP
1193 result = (result * USECS_PER_SEC) + tp.tv_usec;
1194 #else
1195 result = result + (tp.tv_usec / 1000000.0);
1196 #endif
1198 return result;
1202 * TimestampDifference -- convert the difference between two timestamps
1203 * into integer seconds and microseconds
1205 * Both inputs must be ordinary finite timestamps (in current usage,
1206 * they'll be results from GetCurrentTimestamp()).
1208 * We expect start_time <= stop_time. If not, we return zeroes; for current
1209 * callers there is no need to be tense about which way division rounds on
1210 * negative inputs.
1212 void
1213 TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
1214 long *secs, int *microsecs)
1216 TimestampTz diff = stop_time - start_time;
1218 if (diff <= 0)
1220 *secs = 0;
1221 *microsecs = 0;
1223 else
1225 #ifdef HAVE_INT64_TIMESTAMP
1226 *secs = (long) (diff / USECS_PER_SEC);
1227 *microsecs = (int) (diff % USECS_PER_SEC);
1228 #else
1229 *secs = (long) diff;
1230 *microsecs = (int) ((diff - *secs) * 1000000.0);
1231 #endif
1236 * TimestampDifferenceExceeds -- report whether the difference between two
1237 * timestamps is >= a threshold (expressed in milliseconds)
1239 * Both inputs must be ordinary finite timestamps (in current usage,
1240 * they'll be results from GetCurrentTimestamp()).
1242 bool
1243 TimestampDifferenceExceeds(TimestampTz start_time,
1244 TimestampTz stop_time,
1245 int msec)
1247 TimestampTz diff = stop_time - start_time;
1249 #ifdef HAVE_INT64_TIMESTAMP
1250 return (diff >= msec * INT64CONST(1000));
1251 #else
1252 return (diff * 1000.0 >= msec);
1253 #endif
1257 * Convert a time_t to TimestampTz.
1259 * We do not use time_t internally in Postgres, but this is provided for use
1260 * by functions that need to interpret, say, a stat(2) result.
1262 * To avoid having the function's ABI vary depending on the width of time_t,
1263 * we declare the argument as pg_time_t, which is cast-compatible with
1264 * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1265 * This detail should be invisible to callers, at least at source code level.
1267 TimestampTz
1268 time_t_to_timestamptz(pg_time_t tm)
1270 TimestampTz result;
1272 result = (TimestampTz) tm -
1273 ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
1275 #ifdef HAVE_INT64_TIMESTAMP
1276 result *= USECS_PER_SEC;
1277 #endif
1279 return result;
1283 * Convert a TimestampTz to time_t.
1285 * This too is just marginally useful, but some places need it.
1287 * To avoid having the function's ABI vary depending on the width of time_t,
1288 * we declare the result as pg_time_t, which is cast-compatible with
1289 * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1290 * This detail should be invisible to callers, at least at source code level.
1292 pg_time_t
1293 timestamptz_to_time_t(TimestampTz t)
1295 pg_time_t result;
1297 #ifdef HAVE_INT64_TIMESTAMP
1298 result = (pg_time_t) (t / USECS_PER_SEC +
1299 ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY));
1300 #else
1301 result = (pg_time_t) (t +
1302 ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY));
1303 #endif
1305 return result;
1309 * Produce a C-string representation of a TimestampTz.
1311 * This is mostly for use in emitting messages. The primary difference
1312 * from timestamptz_out is that we force the output format to ISO. Note
1313 * also that the result is in a static buffer, not pstrdup'd.
1315 const char *
1316 timestamptz_to_str(TimestampTz t)
1318 static char buf[MAXDATELEN + 1];
1319 int tz;
1320 struct pg_tm tt,
1321 *tm = &tt;
1322 fsec_t fsec;
1323 char *tzn;
1325 if (TIMESTAMP_NOT_FINITE(t))
1326 EncodeSpecialTimestamp(t, buf);
1327 else if (timestamp2tm(t, &tz, tm, &fsec, &tzn, NULL) == 0)
1328 EncodeDateTime(tm, fsec, &tz, &tzn, USE_ISO_DATES, buf);
1329 else
1330 strlcpy(buf, "(timestamp out of range)", sizeof(buf));
1332 return buf;
1336 void
1337 dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
1339 TimeOffset time;
1341 time = jd;
1343 #ifdef HAVE_INT64_TIMESTAMP
1344 *hour = time / USECS_PER_HOUR;
1345 time -= (*hour) * USECS_PER_HOUR;
1346 *min = time / USECS_PER_MINUTE;
1347 time -= (*min) * USECS_PER_MINUTE;
1348 *sec = time / USECS_PER_SEC;
1349 *fsec = time - (*sec * USECS_PER_SEC);
1350 #else
1351 *hour = time / SECS_PER_HOUR;
1352 time -= (*hour) * SECS_PER_HOUR;
1353 *min = time / SECS_PER_MINUTE;
1354 time -= (*min) * SECS_PER_MINUTE;
1355 *sec = time;
1356 *fsec = time - *sec;
1357 #endif
1358 } /* dt2time() */
1362 * timestamp2tm() - Convert timestamp data type to POSIX time structure.
1364 * Note that year is _not_ 1900-based, but is an explicit full value.
1365 * Also, month is one-based, _not_ zero-based.
1366 * Returns:
1367 * 0 on success
1368 * -1 on out of range
1370 * If attimezone is NULL, the global timezone (including possibly brute forced
1371 * timezone) will be used.
1374 timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn, pg_tz *attimezone)
1376 Timestamp date;
1377 Timestamp time;
1378 pg_time_t utime;
1381 * If HasCTZSet is true then we have a brute force time zone specified. Go
1382 * ahead and rotate to the local time zone since we will later bypass any
1383 * calls which adjust the tm fields.
1385 if (attimezone == NULL && HasCTZSet && tzp != NULL)
1387 #ifdef HAVE_INT64_TIMESTAMP
1388 dt -= CTimeZone * USECS_PER_SEC;
1389 #else
1390 dt -= CTimeZone;
1391 #endif
1394 #ifdef HAVE_INT64_TIMESTAMP
1395 time = dt;
1396 TMODULO(time, date, USECS_PER_DAY);
1398 if (time < INT64CONST(0))
1400 time += USECS_PER_DAY;
1401 date -= 1;
1404 /* add offset to go from J2000 back to standard Julian date */
1405 date += POSTGRES_EPOCH_JDATE;
1407 /* Julian day routine does not work for negative Julian days */
1408 if (date < 0 || date > (Timestamp) INT_MAX)
1409 return -1;
1411 j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1412 dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1413 #else
1414 time = dt;
1415 TMODULO(time, date, (double) SECS_PER_DAY);
1417 if (time < 0)
1419 time += SECS_PER_DAY;
1420 date -= 1;
1423 /* add offset to go from J2000 back to standard Julian date */
1424 date += POSTGRES_EPOCH_JDATE;
1426 recalc_d:
1427 /* Julian day routine does not work for negative Julian days */
1428 if (date < 0 || date > (Timestamp) INT_MAX)
1429 return -1;
1431 j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1432 recalc_t:
1433 dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1435 *fsec = TSROUND(*fsec);
1436 /* roundoff may need to propagate to higher-order fields */
1437 if (*fsec >= 1.0)
1439 time = ceil(time);
1440 if (time >= (double) SECS_PER_DAY)
1442 time = 0;
1443 date += 1;
1444 goto recalc_d;
1446 goto recalc_t;
1448 #endif
1450 /* Done if no TZ conversion wanted */
1451 if (tzp == NULL)
1453 tm->tm_isdst = -1;
1454 tm->tm_gmtoff = 0;
1455 tm->tm_zone = NULL;
1456 if (tzn != NULL)
1457 *tzn = NULL;
1458 return 0;
1462 * We have a brute force time zone per SQL99? Then use it without change
1463 * since we have already rotated to the time zone.
1465 if (attimezone == NULL && HasCTZSet)
1467 *tzp = CTimeZone;
1468 tm->tm_isdst = 0;
1469 tm->tm_gmtoff = CTimeZone;
1470 tm->tm_zone = NULL;
1471 if (tzn != NULL)
1472 *tzn = NULL;
1473 return 0;
1477 * If the time falls within the range of pg_time_t, use pg_localtime() to
1478 * rotate to the local time zone.
1480 * First, convert to an integral timestamp, avoiding possibly
1481 * platform-specific roundoff-in-wrong-direction errors, and adjust to
1482 * Unix epoch. Then see if we can convert to pg_time_t without loss. This
1483 * coding avoids hardwiring any assumptions about the width of pg_time_t,
1484 * so it should behave sanely on machines without int64.
1486 #ifdef HAVE_INT64_TIMESTAMP
1487 dt = (dt - *fsec) / USECS_PER_SEC +
1488 (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
1489 #else
1490 dt = rint(dt - *fsec +
1491 (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
1492 #endif
1493 utime = (pg_time_t) dt;
1494 if ((Timestamp) utime == dt)
1496 struct pg_tm *tx = pg_localtime(&utime,
1497 attimezone ? attimezone : session_timezone);
1499 tm->tm_year = tx->tm_year + 1900;
1500 tm->tm_mon = tx->tm_mon + 1;
1501 tm->tm_mday = tx->tm_mday;
1502 tm->tm_hour = tx->tm_hour;
1503 tm->tm_min = tx->tm_min;
1504 tm->tm_sec = tx->tm_sec;
1505 tm->tm_isdst = tx->tm_isdst;
1506 tm->tm_gmtoff = tx->tm_gmtoff;
1507 tm->tm_zone = tx->tm_zone;
1508 *tzp = -tm->tm_gmtoff;
1509 if (tzn != NULL)
1510 *tzn = (char *) tm->tm_zone;
1512 else
1515 * When out of range of pg_time_t, treat as GMT
1517 *tzp = 0;
1518 /* Mark this as *no* time zone available */
1519 tm->tm_isdst = -1;
1520 tm->tm_gmtoff = 0;
1521 tm->tm_zone = NULL;
1522 if (tzn != NULL)
1523 *tzn = NULL;
1526 return 0;
1530 /* tm2timestamp()
1531 * Convert a tm structure to a timestamp data type.
1532 * Note that year is _not_ 1900-based, but is an explicit full value.
1533 * Also, month is one-based, _not_ zero-based.
1535 * Returns -1 on failure (value out of range).
1538 tm2timestamp(struct pg_tm * tm, fsec_t fsec, int *tzp, Timestamp *result)
1540 TimeOffset date;
1541 TimeOffset time;
1543 /* Julian day routines are not correct for negative Julian days */
1544 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
1546 *result = 0; /* keep compiler quiet */
1547 return -1;
1550 date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1551 time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
1553 #ifdef HAVE_INT64_TIMESTAMP
1554 *result = date * USECS_PER_DAY + time;
1555 /* check for major overflow */
1556 if ((*result - time) / USECS_PER_DAY != date)
1558 *result = 0; /* keep compiler quiet */
1559 return -1;
1561 /* check for just-barely overflow (okay except time-of-day wraps) */
1562 if ((*result < 0 && date >= 0) ||
1563 (*result >= 0 && date < 0))
1565 *result = 0; /* keep compiler quiet */
1566 return -1;
1568 #else
1569 *result = date * SECS_PER_DAY + time;
1570 #endif
1571 if (tzp != NULL)
1572 *result = dt2local(*result, -(*tzp));
1574 return 0;
1578 /* interval2tm()
1579 * Convert a interval data type to a tm structure.
1582 interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
1584 TimeOffset time;
1585 TimeOffset tfrac;
1587 tm->tm_year = span.month / MONTHS_PER_YEAR;
1588 tm->tm_mon = span.month % MONTHS_PER_YEAR;
1589 tm->tm_mday = span.day;
1590 time = span.time;
1592 #ifdef HAVE_INT64_TIMESTAMP
1593 tfrac = time / USECS_PER_HOUR;
1594 time -= tfrac * USECS_PER_HOUR;
1595 tm->tm_hour = tfrac; /* could overflow ... */
1596 tfrac = time / USECS_PER_MINUTE;
1597 time -= tfrac * USECS_PER_MINUTE;
1598 tm->tm_min = tfrac;
1599 tfrac = time / USECS_PER_SEC;
1600 *fsec = time - (tfrac * USECS_PER_SEC);
1601 tm->tm_sec = tfrac;
1602 #else
1603 recalc:
1604 TMODULO(time, tfrac, (double) SECS_PER_HOUR);
1605 tm->tm_hour = tfrac; /* could overflow ... */
1606 TMODULO(time, tfrac, (double) SECS_PER_MINUTE);
1607 tm->tm_min = tfrac;
1608 TMODULO(time, tfrac, 1.0);
1609 tm->tm_sec = tfrac;
1610 time = TSROUND(time);
1611 /* roundoff may need to propagate to higher-order fields */
1612 if (time >= 1.0)
1614 time = ceil(span.time);
1615 goto recalc;
1617 *fsec = time;
1618 #endif
1620 return 0;
1624 tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span)
1626 span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
1627 span->day = tm->tm_mday;
1628 #ifdef HAVE_INT64_TIMESTAMP
1629 span->time = (((((tm->tm_hour * INT64CONST(60)) +
1630 tm->tm_min) * INT64CONST(60)) +
1631 tm->tm_sec) * USECS_PER_SEC) + fsec;
1632 #else
1633 span->time = (((tm->tm_hour * (double) MINS_PER_HOUR) +
1634 tm->tm_min) * (double) SECS_PER_MINUTE) +
1635 tm->tm_sec + fsec;
1636 #endif
1638 return 0;
1641 static TimeOffset
1642 time2t(const int hour, const int min, const int sec, const fsec_t fsec)
1644 #ifdef HAVE_INT64_TIMESTAMP
1645 return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
1646 #else
1647 return (((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec + fsec;
1648 #endif
1651 static Timestamp
1652 dt2local(Timestamp dt, int tz)
1654 #ifdef HAVE_INT64_TIMESTAMP
1655 dt -= (tz * USECS_PER_SEC);
1656 #else
1657 dt -= tz;
1658 #endif
1659 return dt;
1663 /*****************************************************************************
1664 * PUBLIC ROUTINES *
1665 *****************************************************************************/
1668 Datum
1669 timestamp_finite(PG_FUNCTION_ARGS)
1671 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1673 PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp));
1676 Datum
1677 interval_finite(PG_FUNCTION_ARGS)
1679 PG_RETURN_BOOL(true);
1683 /*----------------------------------------------------------
1684 * Relational operators for timestamp.
1685 *---------------------------------------------------------*/
1687 void
1688 GetEpochTime(struct pg_tm * tm)
1690 struct pg_tm *t0;
1691 pg_time_t epoch = 0;
1693 t0 = pg_gmtime(&epoch);
1695 tm->tm_year = t0->tm_year;
1696 tm->tm_mon = t0->tm_mon;
1697 tm->tm_mday = t0->tm_mday;
1698 tm->tm_hour = t0->tm_hour;
1699 tm->tm_min = t0->tm_min;
1700 tm->tm_sec = t0->tm_sec;
1702 tm->tm_year += 1900;
1703 tm->tm_mon++;
1706 Timestamp
1707 SetEpochTimestamp(void)
1709 Timestamp dt;
1710 struct pg_tm tt,
1711 *tm = &tt;
1713 GetEpochTime(tm);
1714 /* we don't bother to test for failure ... */
1715 tm2timestamp(tm, 0, NULL, &dt);
1717 return dt;
1718 } /* SetEpochTimestamp() */
1721 * We are currently sharing some code between timestamp and timestamptz.
1722 * The comparison functions are among them. - thomas 2001-09-25
1724 * timestamp_relop - is timestamp1 relop timestamp2
1726 * collate invalid timestamp at the end
1729 timestamp_cmp_internal(Timestamp dt1, Timestamp dt2)
1731 #ifdef HAVE_INT64_TIMESTAMP
1732 return (dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0);
1733 #else
1736 * When using float representation, we have to be wary of NaNs.
1738 * We consider all NANs to be equal and larger than any non-NAN. This is
1739 * somewhat arbitrary; the important thing is to have a consistent sort
1740 * order.
1742 if (isnan(dt1))
1744 if (isnan(dt2))
1745 return 0; /* NAN = NAN */
1746 else
1747 return 1; /* NAN > non-NAN */
1749 else if (isnan(dt2))
1751 return -1; /* non-NAN < NAN */
1753 else
1755 if (dt1 > dt2)
1756 return 1;
1757 else if (dt1 < dt2)
1758 return -1;
1759 else
1760 return 0;
1762 #endif
1765 Datum
1766 timestamp_eq(PG_FUNCTION_ARGS)
1768 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1769 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
1771 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
1774 Datum
1775 timestamp_ne(PG_FUNCTION_ARGS)
1777 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1778 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
1780 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
1783 Datum
1784 timestamp_lt(PG_FUNCTION_ARGS)
1786 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1787 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
1789 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
1792 Datum
1793 timestamp_gt(PG_FUNCTION_ARGS)
1795 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1796 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
1798 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
1801 Datum
1802 timestamp_le(PG_FUNCTION_ARGS)
1804 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1805 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
1807 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
1810 Datum
1811 timestamp_ge(PG_FUNCTION_ARGS)
1813 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1814 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
1816 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
1819 Datum
1820 timestamp_cmp(PG_FUNCTION_ARGS)
1822 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1823 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
1825 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
1828 Datum
1829 timestamp_hash(PG_FUNCTION_ARGS)
1831 /* We can use either hashint8 or hashfloat8 directly */
1832 #ifdef HAVE_INT64_TIMESTAMP
1833 return hashint8(fcinfo);
1834 #else
1835 return hashfloat8(fcinfo);
1836 #endif
1841 * Crosstype comparison functions for timestamp vs timestamptz
1844 Datum
1845 timestamp_eq_timestamptz(PG_FUNCTION_ARGS)
1847 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
1848 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
1849 TimestampTz dt1;
1851 dt1 = timestamp2timestamptz(timestampVal);
1853 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
1856 Datum
1857 timestamp_ne_timestamptz(PG_FUNCTION_ARGS)
1859 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
1860 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
1861 TimestampTz dt1;
1863 dt1 = timestamp2timestamptz(timestampVal);
1865 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
1868 Datum
1869 timestamp_lt_timestamptz(PG_FUNCTION_ARGS)
1871 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
1872 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
1873 TimestampTz dt1;
1875 dt1 = timestamp2timestamptz(timestampVal);
1877 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
1880 Datum
1881 timestamp_gt_timestamptz(PG_FUNCTION_ARGS)
1883 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
1884 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
1885 TimestampTz dt1;
1887 dt1 = timestamp2timestamptz(timestampVal);
1889 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
1892 Datum
1893 timestamp_le_timestamptz(PG_FUNCTION_ARGS)
1895 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
1896 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
1897 TimestampTz dt1;
1899 dt1 = timestamp2timestamptz(timestampVal);
1901 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
1904 Datum
1905 timestamp_ge_timestamptz(PG_FUNCTION_ARGS)
1907 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
1908 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
1909 TimestampTz dt1;
1911 dt1 = timestamp2timestamptz(timestampVal);
1913 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
1916 Datum
1917 timestamp_cmp_timestamptz(PG_FUNCTION_ARGS)
1919 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
1920 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
1921 TimestampTz dt1;
1923 dt1 = timestamp2timestamptz(timestampVal);
1925 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
1928 Datum
1929 timestamptz_eq_timestamp(PG_FUNCTION_ARGS)
1931 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1932 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
1933 TimestampTz dt2;
1935 dt2 = timestamp2timestamptz(timestampVal);
1937 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
1940 Datum
1941 timestamptz_ne_timestamp(PG_FUNCTION_ARGS)
1943 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1944 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
1945 TimestampTz dt2;
1947 dt2 = timestamp2timestamptz(timestampVal);
1949 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
1952 Datum
1953 timestamptz_lt_timestamp(PG_FUNCTION_ARGS)
1955 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1956 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
1957 TimestampTz dt2;
1959 dt2 = timestamp2timestamptz(timestampVal);
1961 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
1964 Datum
1965 timestamptz_gt_timestamp(PG_FUNCTION_ARGS)
1967 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1968 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
1969 TimestampTz dt2;
1971 dt2 = timestamp2timestamptz(timestampVal);
1973 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
1976 Datum
1977 timestamptz_le_timestamp(PG_FUNCTION_ARGS)
1979 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1980 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
1981 TimestampTz dt2;
1983 dt2 = timestamp2timestamptz(timestampVal);
1985 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
1988 Datum
1989 timestamptz_ge_timestamp(PG_FUNCTION_ARGS)
1991 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1992 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
1993 TimestampTz dt2;
1995 dt2 = timestamp2timestamptz(timestampVal);
1997 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
2000 Datum
2001 timestamptz_cmp_timestamp(PG_FUNCTION_ARGS)
2003 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
2004 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2005 TimestampTz dt2;
2007 dt2 = timestamp2timestamptz(timestampVal);
2009 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
2014 * interval_relop - is interval1 relop interval2
2016 * collate invalid interval at the end
2018 static inline TimeOffset
2019 interval_cmp_value(const Interval *interval)
2021 TimeOffset span;
2023 span = interval->time;
2025 #ifdef HAVE_INT64_TIMESTAMP
2026 span += interval->month * INT64CONST(30) * USECS_PER_DAY;
2027 span += interval->day * INT64CONST(24) * USECS_PER_HOUR;
2028 #else
2029 span += interval->month * ((double) DAYS_PER_MONTH * SECS_PER_DAY);
2030 span += interval->day * ((double) HOURS_PER_DAY * SECS_PER_HOUR);
2031 #endif
2033 return span;
2036 static int
2037 interval_cmp_internal(Interval *interval1, Interval *interval2)
2039 TimeOffset span1 = interval_cmp_value(interval1);
2040 TimeOffset span2 = interval_cmp_value(interval2);
2042 return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
2045 Datum
2046 interval_eq(PG_FUNCTION_ARGS)
2048 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2049 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2051 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0);
2054 Datum
2055 interval_ne(PG_FUNCTION_ARGS)
2057 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2058 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2060 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0);
2063 Datum
2064 interval_lt(PG_FUNCTION_ARGS)
2066 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2067 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2069 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0);
2072 Datum
2073 interval_gt(PG_FUNCTION_ARGS)
2075 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2076 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2078 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0);
2081 Datum
2082 interval_le(PG_FUNCTION_ARGS)
2084 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2085 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2087 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0);
2090 Datum
2091 interval_ge(PG_FUNCTION_ARGS)
2093 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2094 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2096 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0);
2099 Datum
2100 interval_cmp(PG_FUNCTION_ARGS)
2102 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2103 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2105 PG_RETURN_INT32(interval_cmp_internal(interval1, interval2));
2109 * Hashing for intervals
2111 * We must produce equal hashvals for values that interval_cmp_internal()
2112 * considers equal. So, compute the net span the same way it does,
2113 * and then hash that, using either int64 or float8 hashing.
2115 Datum
2116 interval_hash(PG_FUNCTION_ARGS)
2118 Interval *interval = PG_GETARG_INTERVAL_P(0);
2119 TimeOffset span = interval_cmp_value(interval);
2121 #ifdef HAVE_INT64_TIMESTAMP
2122 return DirectFunctionCall1(hashint8, Int64GetDatumFast(span));
2123 #else
2124 return DirectFunctionCall1(hashfloat8, Float8GetDatumFast(span));
2125 #endif
2128 /* overlaps_timestamp() --- implements the SQL92 OVERLAPS operator.
2130 * Algorithm is per SQL92 spec. This is much harder than you'd think
2131 * because the spec requires us to deliver a non-null answer in some cases
2132 * where some of the inputs are null.
2134 Datum
2135 overlaps_timestamp(PG_FUNCTION_ARGS)
2138 * The arguments are Timestamps, but we leave them as generic Datums to
2139 * avoid unnecessary conversions between value and reference forms --- not
2140 * to mention possible dereferences of null pointers.
2142 Datum ts1 = PG_GETARG_DATUM(0);
2143 Datum te1 = PG_GETARG_DATUM(1);
2144 Datum ts2 = PG_GETARG_DATUM(2);
2145 Datum te2 = PG_GETARG_DATUM(3);
2146 bool ts1IsNull = PG_ARGISNULL(0);
2147 bool te1IsNull = PG_ARGISNULL(1);
2148 bool ts2IsNull = PG_ARGISNULL(2);
2149 bool te2IsNull = PG_ARGISNULL(3);
2151 #define TIMESTAMP_GT(t1,t2) \
2152 DatumGetBool(DirectFunctionCall2(timestamp_gt,t1,t2))
2153 #define TIMESTAMP_LT(t1,t2) \
2154 DatumGetBool(DirectFunctionCall2(timestamp_lt,t1,t2))
2157 * If both endpoints of interval 1 are null, the result is null (unknown).
2158 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2159 * take ts1 as the lesser endpoint.
2161 if (ts1IsNull)
2163 if (te1IsNull)
2164 PG_RETURN_NULL();
2165 /* swap null for non-null */
2166 ts1 = te1;
2167 te1IsNull = true;
2169 else if (!te1IsNull)
2171 if (TIMESTAMP_GT(ts1, te1))
2173 Datum tt = ts1;
2175 ts1 = te1;
2176 te1 = tt;
2180 /* Likewise for interval 2. */
2181 if (ts2IsNull)
2183 if (te2IsNull)
2184 PG_RETURN_NULL();
2185 /* swap null for non-null */
2186 ts2 = te2;
2187 te2IsNull = true;
2189 else if (!te2IsNull)
2191 if (TIMESTAMP_GT(ts2, te2))
2193 Datum tt = ts2;
2195 ts2 = te2;
2196 te2 = tt;
2201 * At this point neither ts1 nor ts2 is null, so we can consider three
2202 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2204 if (TIMESTAMP_GT(ts1, ts2))
2207 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2208 * in the presence of nulls it's not quite completely so.
2210 if (te2IsNull)
2211 PG_RETURN_NULL();
2212 if (TIMESTAMP_LT(ts1, te2))
2213 PG_RETURN_BOOL(true);
2214 if (te1IsNull)
2215 PG_RETURN_NULL();
2218 * If te1 is not null then we had ts1 <= te1 above, and we just found
2219 * ts1 >= te2, hence te1 >= te2.
2221 PG_RETURN_BOOL(false);
2223 else if (TIMESTAMP_LT(ts1, ts2))
2225 /* This case is ts2 < te1 OR te2 < te1 */
2226 if (te1IsNull)
2227 PG_RETURN_NULL();
2228 if (TIMESTAMP_LT(ts2, te1))
2229 PG_RETURN_BOOL(true);
2230 if (te2IsNull)
2231 PG_RETURN_NULL();
2234 * If te2 is not null then we had ts2 <= te2 above, and we just found
2235 * ts2 >= te1, hence te2 >= te1.
2237 PG_RETURN_BOOL(false);
2239 else
2242 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2243 * rather silly way of saying "true if both are nonnull, else null".
2245 if (te1IsNull || te2IsNull)
2246 PG_RETURN_NULL();
2247 PG_RETURN_BOOL(true);
2250 #undef TIMESTAMP_GT
2251 #undef TIMESTAMP_LT
2255 /*----------------------------------------------------------
2256 * "Arithmetic" operators on date/times.
2257 *---------------------------------------------------------*/
2259 Datum
2260 timestamp_smaller(PG_FUNCTION_ARGS)
2262 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2263 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2264 Timestamp result;
2266 /* use timestamp_cmp_internal to be sure this agrees with comparisons */
2267 if (timestamp_cmp_internal(dt1, dt2) < 0)
2268 result = dt1;
2269 else
2270 result = dt2;
2271 PG_RETURN_TIMESTAMP(result);
2274 Datum
2275 timestamp_larger(PG_FUNCTION_ARGS)
2277 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2278 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2279 Timestamp result;
2281 if (timestamp_cmp_internal(dt1, dt2) > 0)
2282 result = dt1;
2283 else
2284 result = dt2;
2285 PG_RETURN_TIMESTAMP(result);
2289 Datum
2290 timestamp_mi(PG_FUNCTION_ARGS)
2292 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2293 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2294 Interval *result;
2296 result = (Interval *) palloc(sizeof(Interval));
2298 if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
2299 ereport(ERROR,
2300 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2301 errmsg("cannot subtract infinite timestamps")));
2303 result->time = dt1 - dt2;
2305 result->month = 0;
2306 result->day = 0;
2308 /*----------
2309 * This is wrong, but removing it breaks a lot of regression tests.
2310 * For example:
2312 * test=> SET timezone = 'EST5EDT';
2313 * test=> SELECT
2314 * test-> ('2005-10-30 13:22:00-05'::timestamptz -
2315 * test(> '2005-10-29 13:22:00-04'::timestamptz);
2316 * ?column?
2317 * ----------------
2318 * 1 day 01:00:00
2319 * (1 row)
2321 * so adding that to the first timestamp gets:
2323 * test=> SELECT
2324 * test-> ('2005-10-29 13:22:00-04'::timestamptz +
2325 * test(> ('2005-10-30 13:22:00-05'::timestamptz -
2326 * test(> '2005-10-29 13:22:00-04'::timestamptz)) at time zone 'EST';
2327 * timezone
2328 * --------------------
2329 * 2005-10-30 14:22:00
2330 * (1 row)
2331 *----------
2333 result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
2334 IntervalPGetDatum(result)));
2336 PG_RETURN_INTERVAL_P(result);
2340 * interval_justify_interval()
2342 * Adjust interval so 'month', 'day', and 'time' portions are within
2343 * customary bounds. Specifically:
2345 * 0 <= abs(time) < 24 hours
2346 * 0 <= abs(day) < 30 days
2348 * Also, the sign bit on all three fields is made equal, so either
2349 * all three fields are negative or all are positive.
2351 Datum
2352 interval_justify_interval(PG_FUNCTION_ARGS)
2354 Interval *span = PG_GETARG_INTERVAL_P(0);
2355 Interval *result;
2356 TimeOffset wholeday;
2357 int32 wholemonth;
2359 result = (Interval *) palloc(sizeof(Interval));
2360 result->month = span->month;
2361 result->day = span->day;
2362 result->time = span->time;
2364 #ifdef HAVE_INT64_TIMESTAMP
2365 TMODULO(result->time, wholeday, USECS_PER_DAY);
2366 #else
2367 TMODULO(result->time, wholeday, (double) SECS_PER_DAY);
2368 #endif
2369 result->day += wholeday; /* could overflow... */
2371 wholemonth = result->day / DAYS_PER_MONTH;
2372 result->day -= wholemonth * DAYS_PER_MONTH;
2373 result->month += wholemonth;
2375 if (result->month > 0 &&
2376 (result->day < 0 || (result->day == 0 && result->time < 0)))
2378 result->day += DAYS_PER_MONTH;
2379 result->month--;
2381 else if (result->month < 0 &&
2382 (result->day > 0 || (result->day == 0 && result->time > 0)))
2384 result->day -= DAYS_PER_MONTH;
2385 result->month++;
2388 if (result->day > 0 && result->time < 0)
2390 #ifdef HAVE_INT64_TIMESTAMP
2391 result->time += USECS_PER_DAY;
2392 #else
2393 result->time += (double) SECS_PER_DAY;
2394 #endif
2395 result->day--;
2397 else if (result->day < 0 && result->time > 0)
2399 #ifdef HAVE_INT64_TIMESTAMP
2400 result->time -= USECS_PER_DAY;
2401 #else
2402 result->time -= (double) SECS_PER_DAY;
2403 #endif
2404 result->day++;
2407 PG_RETURN_INTERVAL_P(result);
2411 * interval_justify_hours()
2413 * Adjust interval so 'time' contains less than a whole day, adding
2414 * the excess to 'day'. This is useful for
2415 * situations (such as non-TZ) where '1 day' = '24 hours' is valid,
2416 * e.g. interval subtraction and division.
2418 Datum
2419 interval_justify_hours(PG_FUNCTION_ARGS)
2421 Interval *span = PG_GETARG_INTERVAL_P(0);
2422 Interval *result;
2423 TimeOffset wholeday;
2425 result = (Interval *) palloc(sizeof(Interval));
2426 result->month = span->month;
2427 result->day = span->day;
2428 result->time = span->time;
2430 #ifdef HAVE_INT64_TIMESTAMP
2431 TMODULO(result->time, wholeday, USECS_PER_DAY);
2432 #else
2433 TMODULO(result->time, wholeday, (double) SECS_PER_DAY);
2434 #endif
2435 result->day += wholeday; /* could overflow... */
2437 if (result->day > 0 && result->time < 0)
2439 #ifdef HAVE_INT64_TIMESTAMP
2440 result->time += USECS_PER_DAY;
2441 #else
2442 result->time += (double) SECS_PER_DAY;
2443 #endif
2444 result->day--;
2446 else if (result->day < 0 && result->time > 0)
2448 #ifdef HAVE_INT64_TIMESTAMP
2449 result->time -= USECS_PER_DAY;
2450 #else
2451 result->time -= (double) SECS_PER_DAY;
2452 #endif
2453 result->day++;
2456 PG_RETURN_INTERVAL_P(result);
2460 * interval_justify_days()
2462 * Adjust interval so 'day' contains less than 30 days, adding
2463 * the excess to 'month'.
2465 Datum
2466 interval_justify_days(PG_FUNCTION_ARGS)
2468 Interval *span = PG_GETARG_INTERVAL_P(0);
2469 Interval *result;
2470 int32 wholemonth;
2472 result = (Interval *) palloc(sizeof(Interval));
2473 result->month = span->month;
2474 result->day = span->day;
2475 result->time = span->time;
2477 wholemonth = result->day / DAYS_PER_MONTH;
2478 result->day -= wholemonth * DAYS_PER_MONTH;
2479 result->month += wholemonth;
2481 if (result->month > 0 && result->day < 0)
2483 result->day += DAYS_PER_MONTH;
2484 result->month--;
2486 else if (result->month < 0 && result->day > 0)
2488 result->day -= DAYS_PER_MONTH;
2489 result->month++;
2492 PG_RETURN_INTERVAL_P(result);
2495 /* timestamp_pl_interval()
2496 * Add a interval to a timestamp data type.
2497 * Note that interval has provisions for qualitative year/month and day
2498 * units, so try to do the right thing with them.
2499 * To add a month, increment the month, and use the same day of month.
2500 * Then, if the next month has fewer days, set the day of month
2501 * to the last day of month.
2502 * To add a day, increment the mday, and use the same time of day.
2503 * Lastly, add in the "quantitative time".
2505 Datum
2506 timestamp_pl_interval(PG_FUNCTION_ARGS)
2508 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
2509 Interval *span = PG_GETARG_INTERVAL_P(1);
2510 Timestamp result;
2512 if (TIMESTAMP_NOT_FINITE(timestamp))
2513 result = timestamp;
2514 else
2516 if (span->month != 0)
2518 struct pg_tm tt,
2519 *tm = &tt;
2520 fsec_t fsec;
2522 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2523 ereport(ERROR,
2524 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2525 errmsg("timestamp out of range")));
2527 tm->tm_mon += span->month;
2528 if (tm->tm_mon > MONTHS_PER_YEAR)
2530 tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
2531 tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2533 else if (tm->tm_mon < 1)
2535 tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
2536 tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2539 /* adjust for end of month boundary problems... */
2540 if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2541 tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
2543 if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2544 ereport(ERROR,
2545 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2546 errmsg("timestamp out of range")));
2549 if (span->day != 0)
2551 struct pg_tm tt,
2552 *tm = &tt;
2553 fsec_t fsec;
2554 int julian;
2556 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2557 ereport(ERROR,
2558 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2559 errmsg("timestamp out of range")));
2561 /* Add days by converting to and from julian */
2562 julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
2563 j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2565 if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2566 ereport(ERROR,
2567 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2568 errmsg("timestamp out of range")));
2571 timestamp += span->time;
2572 result = timestamp;
2575 PG_RETURN_TIMESTAMP(result);
2578 Datum
2579 timestamp_mi_interval(PG_FUNCTION_ARGS)
2581 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
2582 Interval *span = PG_GETARG_INTERVAL_P(1);
2583 Interval tspan;
2585 tspan.month = -span->month;
2586 tspan.day = -span->day;
2587 tspan.time = -span->time;
2589 return DirectFunctionCall2(timestamp_pl_interval,
2590 TimestampGetDatum(timestamp),
2591 PointerGetDatum(&tspan));
2595 /* timestamptz_pl_interval()
2596 * Add a interval to a timestamp with time zone data type.
2597 * Note that interval has provisions for qualitative year/month
2598 * units, so try to do the right thing with them.
2599 * To add a month, increment the month, and use the same day of month.
2600 * Then, if the next month has fewer days, set the day of month
2601 * to the last day of month.
2602 * Lastly, add in the "quantitative time".
2604 Datum
2605 timestamptz_pl_interval(PG_FUNCTION_ARGS)
2607 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2608 Interval *span = PG_GETARG_INTERVAL_P(1);
2609 TimestampTz result;
2610 int tz;
2611 char *tzn;
2613 if (TIMESTAMP_NOT_FINITE(timestamp))
2614 result = timestamp;
2615 else
2617 if (span->month != 0)
2619 struct pg_tm tt,
2620 *tm = &tt;
2621 fsec_t fsec;
2623 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2624 ereport(ERROR,
2625 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2626 errmsg("timestamp out of range")));
2628 tm->tm_mon += span->month;
2629 if (tm->tm_mon > MONTHS_PER_YEAR)
2631 tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
2632 tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2634 else if (tm->tm_mon < 1)
2636 tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
2637 tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2640 /* adjust for end of month boundary problems... */
2641 if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2642 tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
2644 tz = DetermineTimeZoneOffset(tm, session_timezone);
2646 if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2647 ereport(ERROR,
2648 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2649 errmsg("timestamp out of range")));
2652 if (span->day != 0)
2654 struct pg_tm tt,
2655 *tm = &tt;
2656 fsec_t fsec;
2657 int julian;
2659 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2660 ereport(ERROR,
2661 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2662 errmsg("timestamp out of range")));
2664 /* Add days by converting to and from julian */
2665 julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
2666 j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2668 tz = DetermineTimeZoneOffset(tm, session_timezone);
2670 if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2671 ereport(ERROR,
2672 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2673 errmsg("timestamp out of range")));
2676 timestamp += span->time;
2677 result = timestamp;
2680 PG_RETURN_TIMESTAMP(result);
2683 Datum
2684 timestamptz_mi_interval(PG_FUNCTION_ARGS)
2686 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2687 Interval *span = PG_GETARG_INTERVAL_P(1);
2688 Interval tspan;
2690 tspan.month = -span->month;
2691 tspan.day = -span->day;
2692 tspan.time = -span->time;
2694 return DirectFunctionCall2(timestamptz_pl_interval,
2695 TimestampGetDatum(timestamp),
2696 PointerGetDatum(&tspan));
2700 Datum
2701 interval_um(PG_FUNCTION_ARGS)
2703 Interval *interval = PG_GETARG_INTERVAL_P(0);
2704 Interval *result;
2706 result = (Interval *) palloc(sizeof(Interval));
2708 result->time = -interval->time;
2709 result->day = -interval->day;
2710 result->month = -interval->month;
2712 PG_RETURN_INTERVAL_P(result);
2716 Datum
2717 interval_smaller(PG_FUNCTION_ARGS)
2719 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2720 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2721 Interval *result;
2723 /* use interval_cmp_internal to be sure this agrees with comparisons */
2724 if (interval_cmp_internal(interval1, interval2) < 0)
2725 result = interval1;
2726 else
2727 result = interval2;
2728 PG_RETURN_INTERVAL_P(result);
2731 Datum
2732 interval_larger(PG_FUNCTION_ARGS)
2734 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2735 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2736 Interval *result;
2738 if (interval_cmp_internal(interval1, interval2) > 0)
2739 result = interval1;
2740 else
2741 result = interval2;
2742 PG_RETURN_INTERVAL_P(result);
2745 Datum
2746 interval_pl(PG_FUNCTION_ARGS)
2748 Interval *span1 = PG_GETARG_INTERVAL_P(0);
2749 Interval *span2 = PG_GETARG_INTERVAL_P(1);
2750 Interval *result;
2752 result = (Interval *) palloc(sizeof(Interval));
2754 result->month = span1->month + span2->month;
2755 result->day = span1->day + span2->day;
2756 result->time = span1->time + span2->time;
2758 PG_RETURN_INTERVAL_P(result);
2761 Datum
2762 interval_mi(PG_FUNCTION_ARGS)
2764 Interval *span1 = PG_GETARG_INTERVAL_P(0);
2765 Interval *span2 = PG_GETARG_INTERVAL_P(1);
2766 Interval *result;
2768 result = (Interval *) palloc(sizeof(Interval));
2770 result->month = span1->month - span2->month;
2771 result->day = span1->day - span2->day;
2772 result->time = span1->time - span2->time;
2774 PG_RETURN_INTERVAL_P(result);
2777 Datum
2778 interval_mul(PG_FUNCTION_ARGS)
2780 Interval *span = PG_GETARG_INTERVAL_P(0);
2781 float8 factor = PG_GETARG_FLOAT8(1);
2782 double month_remainder_days,
2783 sec_remainder;
2784 int32 orig_month = span->month,
2785 orig_day = span->day;
2786 Interval *result;
2788 result = (Interval *) palloc(sizeof(Interval));
2790 result->month = (int32) (span->month * factor);
2791 result->day = (int32) (span->day * factor);
2794 * The above correctly handles the whole-number part of the month and day
2795 * products, but we have to do something with any fractional part
2796 * resulting when the factor is nonintegral. We cascade the fractions
2797 * down to lower units using the conversion factors DAYS_PER_MONTH and
2798 * SECS_PER_DAY. Note we do NOT cascade up, since we are not forced to do
2799 * so by the representation. The user can choose to cascade up later,
2800 * using justify_hours and/or justify_days.
2804 * Fractional months full days into days.
2806 * Floating point calculation are inherently inprecise, so these
2807 * calculations are crafted to produce the most reliable result possible.
2808 * TSROUND() is needed to more accurately produce whole numbers where
2809 * appropriate.
2811 month_remainder_days = (orig_month * factor - result->month) * DAYS_PER_MONTH;
2812 month_remainder_days = TSROUND(month_remainder_days);
2813 sec_remainder = (orig_day * factor - result->day +
2814 month_remainder_days - (int) month_remainder_days) * SECS_PER_DAY;
2815 sec_remainder = TSROUND(sec_remainder);
2818 * Might have 24:00:00 hours due to rounding, or >24 hours because of time
2819 * cascade from months and days. It might still be >24 if the combination
2820 * of cascade and the seconds factor operation itself.
2822 if (Abs(sec_remainder) >= SECS_PER_DAY)
2824 result->day += (int) (sec_remainder / SECS_PER_DAY);
2825 sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY;
2828 /* cascade units down */
2829 result->day += (int32) month_remainder_days;
2830 #ifdef HAVE_INT64_TIMESTAMP
2831 result->time = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
2832 #else
2833 result->time = span->time * factor + sec_remainder;
2834 #endif
2836 PG_RETURN_INTERVAL_P(result);
2839 Datum
2840 mul_d_interval(PG_FUNCTION_ARGS)
2842 /* Args are float8 and Interval *, but leave them as generic Datum */
2843 Datum factor = PG_GETARG_DATUM(0);
2844 Datum span = PG_GETARG_DATUM(1);
2846 return DirectFunctionCall2(interval_mul, span, factor);
2849 Datum
2850 interval_div(PG_FUNCTION_ARGS)
2852 Interval *span = PG_GETARG_INTERVAL_P(0);
2853 float8 factor = PG_GETARG_FLOAT8(1);
2854 double month_remainder_days,
2855 sec_remainder;
2856 int32 orig_month = span->month,
2857 orig_day = span->day;
2858 Interval *result;
2860 result = (Interval *) palloc(sizeof(Interval));
2862 if (factor == 0.0)
2863 ereport(ERROR,
2864 (errcode(ERRCODE_DIVISION_BY_ZERO),
2865 errmsg("division by zero")));
2867 result->month = (int32) (span->month / factor);
2868 result->day = (int32) (span->day / factor);
2871 * Fractional months full days into days. See comment in interval_mul().
2873 month_remainder_days = (orig_month / factor - result->month) * DAYS_PER_MONTH;
2874 month_remainder_days = TSROUND(month_remainder_days);
2875 sec_remainder = (orig_day / factor - result->day +
2876 month_remainder_days - (int) month_remainder_days) * SECS_PER_DAY;
2877 sec_remainder = TSROUND(sec_remainder);
2878 if (Abs(sec_remainder) >= SECS_PER_DAY)
2880 result->day += (int) (sec_remainder / SECS_PER_DAY);
2881 sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY;
2884 /* cascade units down */
2885 result->day += (int32) month_remainder_days;
2886 #ifdef HAVE_INT64_TIMESTAMP
2887 result->time = rint(span->time / factor + sec_remainder * USECS_PER_SEC);
2888 #else
2889 /* See TSROUND comment in interval_mul(). */
2890 result->time = span->time / factor + sec_remainder;
2891 #endif
2893 PG_RETURN_INTERVAL_P(result);
2897 * interval_accum and interval_avg implement the AVG(interval) aggregate.
2899 * The transition datatype for this aggregate is a 2-element array of
2900 * intervals, where the first is the running sum and the second contains
2901 * the number of values so far in its 'time' field. This is a bit ugly
2902 * but it beats inventing a specialized datatype for the purpose.
2905 Datum
2906 interval_accum(PG_FUNCTION_ARGS)
2908 ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
2909 Interval *newval = PG_GETARG_INTERVAL_P(1);
2910 Datum *transdatums;
2911 int ndatums;
2912 Interval sumX,
2914 Interval *newsum;
2915 ArrayType *result;
2917 deconstruct_array(transarray,
2918 INTERVALOID, sizeof(Interval), false, 'd',
2919 &transdatums, NULL, &ndatums);
2920 if (ndatums != 2)
2921 elog(ERROR, "expected 2-element interval array");
2924 * XXX memcpy, instead of just extracting a pointer, to work around buggy
2925 * array code: it won't ensure proper alignment of Interval objects on
2926 * machines where double requires 8-byte alignment. That should be fixed,
2927 * but in the meantime...
2929 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
2930 * compilers optimize into double-aligned load/store anyway.
2932 memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
2933 memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2935 newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
2936 IntervalPGetDatum(&sumX),
2937 IntervalPGetDatum(newval)));
2938 N.time += 1;
2940 transdatums[0] = IntervalPGetDatum(newsum);
2941 transdatums[1] = IntervalPGetDatum(&N);
2943 result = construct_array(transdatums, 2,
2944 INTERVALOID, sizeof(Interval), false, 'd');
2946 PG_RETURN_ARRAYTYPE_P(result);
2949 Datum
2950 interval_avg(PG_FUNCTION_ARGS)
2952 ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
2953 Datum *transdatums;
2954 int ndatums;
2955 Interval sumX,
2958 deconstruct_array(transarray,
2959 INTERVALOID, sizeof(Interval), false, 'd',
2960 &transdatums, NULL, &ndatums);
2961 if (ndatums != 2)
2962 elog(ERROR, "expected 2-element interval array");
2965 * XXX memcpy, instead of just extracting a pointer, to work around buggy
2966 * array code: it won't ensure proper alignment of Interval objects on
2967 * machines where double requires 8-byte alignment. That should be fixed,
2968 * but in the meantime...
2970 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
2971 * compilers optimize into double-aligned load/store anyway.
2973 memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
2974 memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2976 /* SQL92 defines AVG of no values to be NULL */
2977 if (N.time == 0)
2978 PG_RETURN_NULL();
2980 return DirectFunctionCall2(interval_div,
2981 IntervalPGetDatum(&sumX),
2982 Float8GetDatum(N.time));
2986 /* timestamp_age()
2987 * Calculate time difference while retaining year/month fields.
2988 * Note that this does not result in an accurate absolute time span
2989 * since year and month are out of context once the arithmetic
2990 * is done.
2992 Datum
2993 timestamp_age(PG_FUNCTION_ARGS)
2995 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2996 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2997 Interval *result;
2998 fsec_t fsec,
2999 fsec1,
3000 fsec2;
3001 struct pg_tm tt,
3002 *tm = &tt;
3003 struct pg_tm tt1,
3004 *tm1 = &tt1;
3005 struct pg_tm tt2,
3006 *tm2 = &tt2;
3008 result = (Interval *) palloc(sizeof(Interval));
3010 if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 &&
3011 timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0)
3013 /* form the symbolic difference */
3014 fsec = fsec1 - fsec2;
3015 tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
3016 tm->tm_min = tm1->tm_min - tm2->tm_min;
3017 tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
3018 tm->tm_mday = tm1->tm_mday - tm2->tm_mday;
3019 tm->tm_mon = tm1->tm_mon - tm2->tm_mon;
3020 tm->tm_year = tm1->tm_year - tm2->tm_year;
3022 /* flip sign if necessary... */
3023 if (dt1 < dt2)
3025 fsec = -fsec;
3026 tm->tm_sec = -tm->tm_sec;
3027 tm->tm_min = -tm->tm_min;
3028 tm->tm_hour = -tm->tm_hour;
3029 tm->tm_mday = -tm->tm_mday;
3030 tm->tm_mon = -tm->tm_mon;
3031 tm->tm_year = -tm->tm_year;
3034 /* propagate any negative fields into the next higher field */
3035 while (fsec < 0)
3037 #ifdef HAVE_INT64_TIMESTAMP
3038 fsec += USECS_PER_SEC;
3039 #else
3040 fsec += 1.0;
3041 #endif
3042 tm->tm_sec--;
3045 while (tm->tm_sec < 0)
3047 tm->tm_sec += SECS_PER_MINUTE;
3048 tm->tm_min--;
3051 while (tm->tm_min < 0)
3053 tm->tm_min += MINS_PER_HOUR;
3054 tm->tm_hour--;
3057 while (tm->tm_hour < 0)
3059 tm->tm_hour += HOURS_PER_DAY;
3060 tm->tm_mday--;
3063 while (tm->tm_mday < 0)
3065 if (dt1 < dt2)
3067 tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
3068 tm->tm_mon--;
3070 else
3072 tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
3073 tm->tm_mon--;
3077 while (tm->tm_mon < 0)
3079 tm->tm_mon += MONTHS_PER_YEAR;
3080 tm->tm_year--;
3083 /* recover sign if necessary... */
3084 if (dt1 < dt2)
3086 fsec = -fsec;
3087 tm->tm_sec = -tm->tm_sec;
3088 tm->tm_min = -tm->tm_min;
3089 tm->tm_hour = -tm->tm_hour;
3090 tm->tm_mday = -tm->tm_mday;
3091 tm->tm_mon = -tm->tm_mon;
3092 tm->tm_year = -tm->tm_year;
3095 if (tm2interval(tm, fsec, result) != 0)
3096 ereport(ERROR,
3097 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3098 errmsg("interval out of range")));
3100 else
3101 ereport(ERROR,
3102 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3103 errmsg("timestamp out of range")));
3105 PG_RETURN_INTERVAL_P(result);
3109 /* timestamptz_age()
3110 * Calculate time difference while retaining year/month fields.
3111 * Note that this does not result in an accurate absolute time span
3112 * since year and month are out of context once the arithmetic
3113 * is done.
3115 Datum
3116 timestamptz_age(PG_FUNCTION_ARGS)
3118 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
3119 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
3120 Interval *result;
3121 fsec_t fsec,
3122 fsec1,
3123 fsec2;
3124 struct pg_tm tt,
3125 *tm = &tt;
3126 struct pg_tm tt1,
3127 *tm1 = &tt1;
3128 struct pg_tm tt2,
3129 *tm2 = &tt2;
3130 int tz1;
3131 int tz2;
3132 char *tzn;
3134 result = (Interval *) palloc(sizeof(Interval));
3136 if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn, NULL) == 0 &&
3137 timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn, NULL) == 0)
3139 /* form the symbolic difference */
3140 fsec = fsec1 - fsec2;
3141 tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
3142 tm->tm_min = tm1->tm_min - tm2->tm_min;
3143 tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
3144 tm->tm_mday = tm1->tm_mday - tm2->tm_mday;
3145 tm->tm_mon = tm1->tm_mon - tm2->tm_mon;
3146 tm->tm_year = tm1->tm_year - tm2->tm_year;
3148 /* flip sign if necessary... */
3149 if (dt1 < dt2)
3151 fsec = -fsec;
3152 tm->tm_sec = -tm->tm_sec;
3153 tm->tm_min = -tm->tm_min;
3154 tm->tm_hour = -tm->tm_hour;
3155 tm->tm_mday = -tm->tm_mday;
3156 tm->tm_mon = -tm->tm_mon;
3157 tm->tm_year = -tm->tm_year;
3160 /* propagate any negative fields into the next higher field */
3161 while (fsec < 0)
3163 #ifdef HAVE_INT64_TIMESTAMP
3164 fsec += USECS_PER_SEC;
3165 #else
3166 fsec += 1.0;
3167 #endif
3168 tm->tm_sec--;
3171 while (tm->tm_sec < 0)
3173 tm->tm_sec += SECS_PER_MINUTE;
3174 tm->tm_min--;
3177 while (tm->tm_min < 0)
3179 tm->tm_min += MINS_PER_HOUR;
3180 tm->tm_hour--;
3183 while (tm->tm_hour < 0)
3185 tm->tm_hour += HOURS_PER_DAY;
3186 tm->tm_mday--;
3189 while (tm->tm_mday < 0)
3191 if (dt1 < dt2)
3193 tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
3194 tm->tm_mon--;
3196 else
3198 tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
3199 tm->tm_mon--;
3203 while (tm->tm_mon < 0)
3205 tm->tm_mon += MONTHS_PER_YEAR;
3206 tm->tm_year--;
3210 * Note: we deliberately ignore any difference between tz1 and tz2.
3213 /* recover sign if necessary... */
3214 if (dt1 < dt2)
3216 fsec = -fsec;
3217 tm->tm_sec = -tm->tm_sec;
3218 tm->tm_min = -tm->tm_min;
3219 tm->tm_hour = -tm->tm_hour;
3220 tm->tm_mday = -tm->tm_mday;
3221 tm->tm_mon = -tm->tm_mon;
3222 tm->tm_year = -tm->tm_year;
3225 if (tm2interval(tm, fsec, result) != 0)
3226 ereport(ERROR,
3227 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3228 errmsg("interval out of range")));
3230 else
3231 ereport(ERROR,
3232 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3233 errmsg("timestamp out of range")));
3235 PG_RETURN_INTERVAL_P(result);
3239 /*----------------------------------------------------------
3240 * Conversion operators.
3241 *---------------------------------------------------------*/
3244 /* timestamp_trunc()
3245 * Truncate timestamp to specified units.
3247 Datum
3248 timestamp_trunc(PG_FUNCTION_ARGS)
3250 text *units = PG_GETARG_TEXT_PP(0);
3251 Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3252 Timestamp result;
3253 int type,
3254 val;
3255 char *lowunits;
3256 fsec_t fsec;
3257 struct pg_tm tt,
3258 *tm = &tt;
3260 if (TIMESTAMP_NOT_FINITE(timestamp))
3261 PG_RETURN_TIMESTAMP(timestamp);
3263 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
3264 VARSIZE_ANY_EXHDR(units),
3265 false);
3267 type = DecodeUnits(0, lowunits, &val);
3269 if (type == UNITS)
3271 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3272 ereport(ERROR,
3273 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3274 errmsg("timestamp out of range")));
3276 switch (val)
3278 case DTK_WEEK:
3280 int woy;
3282 woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
3285 * If it is week 52/53 and the month is January, then the
3286 * week must belong to the previous year. Also, some
3287 * December dates belong to the next year.
3289 if (woy >= 52 && tm->tm_mon == 1)
3290 --tm->tm_year;
3291 if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
3292 ++tm->tm_year;
3293 isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
3294 tm->tm_hour = 0;
3295 tm->tm_min = 0;
3296 tm->tm_sec = 0;
3297 fsec = 0;
3298 break;
3300 case DTK_MILLENNIUM:
3301 /* see comments in timestamptz_trunc */
3302 if (tm->tm_year > 0)
3303 tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
3304 else
3305 tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
3306 case DTK_CENTURY:
3307 /* see comments in timestamptz_trunc */
3308 if (tm->tm_year > 0)
3309 tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
3310 else
3311 tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
3312 case DTK_DECADE:
3313 /* see comments in timestamptz_trunc */
3314 if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
3316 if (tm->tm_year > 0)
3317 tm->tm_year = (tm->tm_year / 10) * 10;
3318 else
3319 tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
3321 case DTK_YEAR:
3322 tm->tm_mon = 1;
3323 case DTK_QUARTER:
3324 tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
3325 case DTK_MONTH:
3326 tm->tm_mday = 1;
3327 case DTK_DAY:
3328 tm->tm_hour = 0;
3329 case DTK_HOUR:
3330 tm->tm_min = 0;
3331 case DTK_MINUTE:
3332 tm->tm_sec = 0;
3333 case DTK_SECOND:
3334 fsec = 0;
3335 break;
3337 case DTK_MILLISEC:
3338 #ifdef HAVE_INT64_TIMESTAMP
3339 fsec = (fsec / 1000) * 1000;
3340 #else
3341 fsec = floor(fsec * 1000) / 1000;
3342 #endif
3343 break;
3345 case DTK_MICROSEC:
3346 #ifndef HAVE_INT64_TIMESTAMP
3347 fsec = floor(fsec * 1000000) / 1000000;
3348 #endif
3349 break;
3351 default:
3352 ereport(ERROR,
3353 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3354 errmsg("timestamp units \"%s\" not supported",
3355 lowunits)));
3356 result = 0;
3359 if (tm2timestamp(tm, fsec, NULL, &result) != 0)
3360 ereport(ERROR,
3361 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3362 errmsg("timestamp out of range")));
3364 else
3366 ereport(ERROR,
3367 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3368 errmsg("timestamp units \"%s\" not recognized",
3369 lowunits)));
3370 result = 0;
3373 PG_RETURN_TIMESTAMP(result);
3376 /* timestamptz_trunc()
3377 * Truncate timestamp to specified units.
3379 Datum
3380 timestamptz_trunc(PG_FUNCTION_ARGS)
3382 text *units = PG_GETARG_TEXT_PP(0);
3383 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3384 TimestampTz result;
3385 int tz;
3386 int type,
3387 val;
3388 bool redotz = false;
3389 char *lowunits;
3390 fsec_t fsec;
3391 char *tzn;
3392 struct pg_tm tt,
3393 *tm = &tt;
3395 if (TIMESTAMP_NOT_FINITE(timestamp))
3396 PG_RETURN_TIMESTAMPTZ(timestamp);
3398 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
3399 VARSIZE_ANY_EXHDR(units),
3400 false);
3402 type = DecodeUnits(0, lowunits, &val);
3404 if (type == UNITS)
3406 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3407 ereport(ERROR,
3408 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3409 errmsg("timestamp out of range")));
3411 switch (val)
3413 case DTK_WEEK:
3415 int woy;
3417 woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
3420 * If it is week 52/53 and the month is January, then the
3421 * week must belong to the previous year. Also, some
3422 * December dates belong to the next year.
3424 if (woy >= 52 && tm->tm_mon == 1)
3425 --tm->tm_year;
3426 if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
3427 ++tm->tm_year;
3428 isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
3429 tm->tm_hour = 0;
3430 tm->tm_min = 0;
3431 tm->tm_sec = 0;
3432 fsec = 0;
3433 redotz = true;
3434 break;
3436 /* one may consider DTK_THOUSAND and DTK_HUNDRED... */
3437 case DTK_MILLENNIUM:
3440 * truncating to the millennium? what is this supposed to
3441 * mean? let us put the first year of the millennium... i.e.
3442 * -1000, 1, 1001, 2001...
3444 if (tm->tm_year > 0)
3445 tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
3446 else
3447 tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
3448 /* FALL THRU */
3449 case DTK_CENTURY:
3450 /* truncating to the century? as above: -100, 1, 101... */
3451 if (tm->tm_year > 0)
3452 tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
3453 else
3454 tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
3455 /* FALL THRU */
3456 case DTK_DECADE:
3459 * truncating to the decade? first year of the decade. must
3460 * not be applied if year was truncated before!
3462 if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
3464 if (tm->tm_year > 0)
3465 tm->tm_year = (tm->tm_year / 10) * 10;
3466 else
3467 tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
3469 /* FALL THRU */
3470 case DTK_YEAR:
3471 tm->tm_mon = 1;
3472 /* FALL THRU */
3473 case DTK_QUARTER:
3474 tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
3475 /* FALL THRU */
3476 case DTK_MONTH:
3477 tm->tm_mday = 1;
3478 /* FALL THRU */
3479 case DTK_DAY:
3480 tm->tm_hour = 0;
3481 redotz = true; /* for all cases >= DAY */
3482 /* FALL THRU */
3483 case DTK_HOUR:
3484 tm->tm_min = 0;
3485 /* FALL THRU */
3486 case DTK_MINUTE:
3487 tm->tm_sec = 0;
3488 /* FALL THRU */
3489 case DTK_SECOND:
3490 fsec = 0;
3491 break;
3493 case DTK_MILLISEC:
3494 #ifdef HAVE_INT64_TIMESTAMP
3495 fsec = (fsec / 1000) * 1000;
3496 #else
3497 fsec = floor(fsec * 1000) / 1000;
3498 #endif
3499 break;
3500 case DTK_MICROSEC:
3501 #ifndef HAVE_INT64_TIMESTAMP
3502 fsec = floor(fsec * 1000000) / 1000000;
3503 #endif
3504 break;
3506 default:
3507 ereport(ERROR,
3508 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3509 errmsg("timestamp with time zone units \"%s\" not "
3510 "supported", lowunits)));
3511 result = 0;
3514 if (redotz)
3515 tz = DetermineTimeZoneOffset(tm, session_timezone);
3517 if (tm2timestamp(tm, fsec, &tz, &result) != 0)
3518 ereport(ERROR,
3519 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3520 errmsg("timestamp out of range")));
3522 else
3524 ereport(ERROR,
3525 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3526 errmsg("timestamp with time zone units \"%s\" not recognized",
3527 lowunits)));
3528 result = 0;
3531 PG_RETURN_TIMESTAMPTZ(result);
3534 /* interval_trunc()
3535 * Extract specified field from interval.
3537 Datum
3538 interval_trunc(PG_FUNCTION_ARGS)
3540 text *units = PG_GETARG_TEXT_PP(0);
3541 Interval *interval = PG_GETARG_INTERVAL_P(1);
3542 Interval *result;
3543 int type,
3544 val;
3545 char *lowunits;
3546 fsec_t fsec;
3547 struct pg_tm tt,
3548 *tm = &tt;
3550 result = (Interval *) palloc(sizeof(Interval));
3552 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
3553 VARSIZE_ANY_EXHDR(units),
3554 false);
3556 type = DecodeUnits(0, lowunits, &val);
3558 if (type == UNITS)
3560 if (interval2tm(*interval, tm, &fsec) == 0)
3562 switch (val)
3564 /* fall through */
3565 case DTK_MILLENNIUM:
3566 /* caution: C division may have negative remainder */
3567 tm->tm_year = (tm->tm_year / 1000) * 1000;
3568 case DTK_CENTURY:
3569 /* caution: C division may have negative remainder */
3570 tm->tm_year = (tm->tm_year / 100) * 100;
3571 case DTK_DECADE:
3572 /* caution: C division may have negative remainder */
3573 tm->tm_year = (tm->tm_year / 10) * 10;
3574 case DTK_YEAR:
3575 tm->tm_mon = 0;
3576 case DTK_QUARTER:
3577 tm->tm_mon = 3 * (tm->tm_mon / 3);
3578 case DTK_MONTH:
3579 tm->tm_mday = 0;
3580 case DTK_DAY:
3581 tm->tm_hour = 0;
3582 case DTK_HOUR:
3583 tm->tm_min = 0;
3584 case DTK_MINUTE:
3585 tm->tm_sec = 0;
3586 case DTK_SECOND:
3587 fsec = 0;
3588 break;
3590 case DTK_MILLISEC:
3591 #ifdef HAVE_INT64_TIMESTAMP
3592 fsec = (fsec / 1000) * 1000;
3593 #else
3594 fsec = floor(fsec * 1000) / 1000;
3595 #endif
3596 break;
3597 case DTK_MICROSEC:
3598 #ifndef HAVE_INT64_TIMESTAMP
3599 fsec = floor(fsec * 1000000) / 1000000;
3600 #endif
3601 break;
3603 default:
3604 ereport(ERROR,
3605 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3606 errmsg("interval units \"%s\" not supported",
3607 lowunits)));
3610 if (tm2interval(tm, fsec, result) != 0)
3611 ereport(ERROR,
3612 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3613 errmsg("interval out of range")));
3615 else
3616 elog(ERROR, "could not convert interval to tm");
3618 else
3620 ereport(ERROR,
3621 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3622 errmsg("interval units \"%s\" not recognized",
3623 lowunits)));
3626 PG_RETURN_INTERVAL_P(result);
3629 /* isoweek2j()
3631 * Return the Julian day which corresponds to the first day (Monday) of the given ISO 8601 year and week.
3632 * Julian days are used to convert between ISO week dates and Gregorian dates.
3635 isoweek2j(int year, int week)
3637 int day0,
3638 day4;
3640 /* fourth day of current year */
3641 day4 = date2j(year, 1, 4);
3643 /* day0 == offset to first day of week (Monday) */
3644 day0 = j2day(day4 - 1);
3646 return ((week - 1) * 7) + (day4 - day0);
3649 /* isoweek2date()
3650 * Convert ISO week of year number to date.
3651 * The year field must be specified with the ISO year!
3652 * karel 2000/08/07
3654 void
3655 isoweek2date(int woy, int *year, int *mon, int *mday)
3657 j2date(isoweek2j(*year, woy), year, mon, mday);
3660 /* isoweekdate2date()
3662 * Convert an ISO 8601 week date (ISO year, ISO week and day of week) into a Gregorian date.
3663 * Populates year, mon, and mday with the correct Gregorian values.
3664 * year must be passed in as the ISO year.
3666 void
3667 isoweekdate2date(int isoweek, int isowday, int *year, int *mon, int *mday)
3669 int jday;
3671 jday = isoweek2j(*year, isoweek);
3672 jday += isowday - 1;
3674 j2date(jday, year, mon, mday);
3677 /* date2isoweek()
3679 * Returns ISO week number of year.
3682 date2isoweek(int year, int mon, int mday)
3684 float8 result;
3685 int day0,
3686 day4,
3687 dayn;
3689 /* current day */
3690 dayn = date2j(year, mon, mday);
3692 /* fourth day of current year */
3693 day4 = date2j(year, 1, 4);
3695 /* day0 == offset to first day of week (Monday) */
3696 day0 = j2day(day4 - 1);
3699 * We need the first week containing a Thursday, otherwise this day falls
3700 * into the previous year for purposes of counting weeks
3702 if (dayn < day4 - day0)
3704 day4 = date2j(year - 1, 1, 4);
3706 /* day0 == offset to first day of week (Monday) */
3707 day0 = j2day(day4 - 1);
3710 result = (dayn - (day4 - day0)) / 7 + 1;
3713 * Sometimes the last few days in a year will fall into the first week of
3714 * the next year, so check for this.
3716 if (result >= 52)
3718 day4 = date2j(year + 1, 1, 4);
3720 /* day0 == offset to first day of week (Monday) */
3721 day0 = j2day(day4 - 1);
3723 if (dayn >= day4 - day0)
3724 result = (dayn - (day4 - day0)) / 7 + 1;
3727 return (int) result;
3731 /* date2isoyear()
3733 * Returns ISO 8601 year number.
3736 date2isoyear(int year, int mon, int mday)
3738 float8 result;
3739 int day0,
3740 day4,
3741 dayn;
3743 /* current day */
3744 dayn = date2j(year, mon, mday);
3746 /* fourth day of current year */
3747 day4 = date2j(year, 1, 4);
3749 /* day0 == offset to first day of week (Monday) */
3750 day0 = j2day(day4 - 1);
3753 * We need the first week containing a Thursday, otherwise this day falls
3754 * into the previous year for purposes of counting weeks
3756 if (dayn < day4 - day0)
3758 day4 = date2j(year - 1, 1, 4);
3760 /* day0 == offset to first day of week (Monday) */
3761 day0 = j2day(day4 - 1);
3763 year--;
3766 result = (dayn - (day4 - day0)) / 7 + 1;
3769 * Sometimes the last few days in a year will fall into the first week of
3770 * the next year, so check for this.
3772 if (result >= 52)
3774 day4 = date2j(year + 1, 1, 4);
3776 /* day0 == offset to first day of week (Monday) */
3777 day0 = j2day(day4 - 1);
3779 if (dayn >= day4 - day0)
3780 year++;
3783 return year;
3787 /* date2isoyearday()
3789 * Returns the ISO 8601 day-of-year, given a Gregorian year, month and day.
3790 * Possible return values are 1 through 371 (364 in non-leap years).
3793 date2isoyearday(int year, int mon, int mday)
3795 return date2j(year, mon, mday) - isoweek2j(date2isoyear(year, mon, mday), 1) + 1;
3798 /* timestamp_part()
3799 * Extract specified field from timestamp.
3801 Datum
3802 timestamp_part(PG_FUNCTION_ARGS)
3804 text *units = PG_GETARG_TEXT_PP(0);
3805 Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3806 float8 result;
3807 int type,
3808 val;
3809 char *lowunits;
3810 fsec_t fsec;
3811 struct pg_tm tt,
3812 *tm = &tt;
3814 if (TIMESTAMP_NOT_FINITE(timestamp))
3816 result = 0;
3817 PG_RETURN_FLOAT8(result);
3820 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
3821 VARSIZE_ANY_EXHDR(units),
3822 false);
3824 type = DecodeUnits(0, lowunits, &val);
3825 if (type == UNKNOWN_FIELD)
3826 type = DecodeSpecial(0, lowunits, &val);
3828 if (type == UNITS)
3830 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3831 ereport(ERROR,
3832 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3833 errmsg("timestamp out of range")));
3835 switch (val)
3837 case DTK_MICROSEC:
3838 #ifdef HAVE_INT64_TIMESTAMP
3839 result = tm->tm_sec * 1000000.0 + fsec;
3840 #else
3841 result = (tm->tm_sec + fsec) * 1000000;
3842 #endif
3843 break;
3845 case DTK_MILLISEC:
3846 #ifdef HAVE_INT64_TIMESTAMP
3847 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3848 #else
3849 result = (tm->tm_sec + fsec) * 1000;
3850 #endif
3851 break;
3853 case DTK_SECOND:
3854 #ifdef HAVE_INT64_TIMESTAMP
3855 result = tm->tm_sec + fsec / 1000000.0;
3856 #else
3857 result = tm->tm_sec + fsec;
3858 #endif
3859 break;
3861 case DTK_MINUTE:
3862 result = tm->tm_min;
3863 break;
3865 case DTK_HOUR:
3866 result = tm->tm_hour;
3867 break;
3869 case DTK_DAY:
3870 result = tm->tm_mday;
3871 break;
3873 case DTK_MONTH:
3874 result = tm->tm_mon;
3875 break;
3877 case DTK_QUARTER:
3878 result = (tm->tm_mon - 1) / 3 + 1;
3879 break;
3881 case DTK_WEEK:
3882 result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
3883 break;
3885 case DTK_YEAR:
3886 if (tm->tm_year > 0)
3887 result = tm->tm_year;
3888 else
3889 /* there is no year 0, just 1 BC and 1 AD */
3890 result = tm->tm_year - 1;
3891 break;
3893 case DTK_DECADE:
3896 * what is a decade wrt dates? let us assume that decade 199
3897 * is 1990 thru 1999... decade 0 starts on year 1 BC, and -1
3898 * is 11 BC thru 2 BC...
3900 if (tm->tm_year >= 0)
3901 result = tm->tm_year / 10;
3902 else
3903 result = -((8 - (tm->tm_year - 1)) / 10);
3904 break;
3906 case DTK_CENTURY:
3908 /* ----
3909 * centuries AD, c>0: year in [ (c-1)* 100 + 1 : c*100 ]
3910 * centuries BC, c<0: year in [ c*100 : (c+1) * 100 - 1]
3911 * there is no number 0 century.
3912 * ----
3914 if (tm->tm_year > 0)
3915 result = (tm->tm_year + 99) / 100;
3916 else
3917 /* caution: C division may have negative remainder */
3918 result = -((99 - (tm->tm_year - 1)) / 100);
3919 break;
3921 case DTK_MILLENNIUM:
3922 /* see comments above. */
3923 if (tm->tm_year > 0)
3924 result = (tm->tm_year + 999) / 1000;
3925 else
3926 result = -((999 - (tm->tm_year - 1)) / 1000);
3927 break;
3929 case DTK_JULIAN:
3930 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3931 #ifdef HAVE_INT64_TIMESTAMP
3932 result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
3933 tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY;
3934 #else
3935 result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
3936 tm->tm_sec + fsec) / (double) SECS_PER_DAY;
3937 #endif
3938 break;
3940 case DTK_ISOYEAR:
3941 result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday);
3942 break;
3944 case DTK_TZ:
3945 case DTK_TZ_MINUTE:
3946 case DTK_TZ_HOUR:
3947 default:
3948 ereport(ERROR,
3949 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3950 errmsg("timestamp units \"%s\" not supported",
3951 lowunits)));
3952 result = 0;
3955 else if (type == RESERV)
3957 switch (val)
3959 case DTK_EPOCH:
3961 int tz;
3962 TimestampTz timestamptz;
3965 * convert to timestamptz to produce consistent results
3967 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3968 ereport(ERROR,
3969 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3970 errmsg("timestamp out of range")));
3972 tz = DetermineTimeZoneOffset(tm, session_timezone);
3974 if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
3975 ereport(ERROR,
3976 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3977 errmsg("timestamp out of range")));
3979 #ifdef HAVE_INT64_TIMESTAMP
3980 result = (timestamptz - SetEpochTimestamp()) / 1000000.0;
3981 #else
3982 result = timestamptz - SetEpochTimestamp();
3983 #endif
3984 break;
3986 case DTK_DOW:
3987 case DTK_ISODOW:
3988 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3989 ereport(ERROR,
3990 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3991 errmsg("timestamp out of range")));
3992 result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
3993 if (val == DTK_ISODOW && result == 0)
3994 result = 7;
3995 break;
3997 case DTK_DOY:
3998 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3999 ereport(ERROR,
4000 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4001 errmsg("timestamp out of range")));
4002 result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
4003 - date2j(tm->tm_year, 1, 1) + 1);
4004 break;
4006 default:
4007 ereport(ERROR,
4008 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4009 errmsg("timestamp units \"%s\" not supported",
4010 lowunits)));
4011 result = 0;
4015 else
4017 ereport(ERROR,
4018 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4019 errmsg("timestamp units \"%s\" not recognized", lowunits)));
4020 result = 0;
4023 PG_RETURN_FLOAT8(result);
4026 /* timestamptz_part()
4027 * Extract specified field from timestamp with time zone.
4029 Datum
4030 timestamptz_part(PG_FUNCTION_ARGS)
4032 text *units = PG_GETARG_TEXT_PP(0);
4033 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4034 float8 result;
4035 int tz;
4036 int type,
4037 val;
4038 char *lowunits;
4039 double dummy;
4040 fsec_t fsec;
4041 char *tzn;
4042 struct pg_tm tt,
4043 *tm = &tt;
4045 if (TIMESTAMP_NOT_FINITE(timestamp))
4047 result = 0;
4048 PG_RETURN_FLOAT8(result);
4051 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
4052 VARSIZE_ANY_EXHDR(units),
4053 false);
4055 type = DecodeUnits(0, lowunits, &val);
4056 if (type == UNKNOWN_FIELD)
4057 type = DecodeSpecial(0, lowunits, &val);
4059 if (type == UNITS)
4061 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
4062 ereport(ERROR,
4063 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4064 errmsg("timestamp out of range")));
4066 switch (val)
4068 case DTK_TZ:
4069 result = -tz;
4070 break;
4072 case DTK_TZ_MINUTE:
4073 result = -tz;
4074 result /= MINS_PER_HOUR;
4075 FMODULO(result, dummy, (double) MINS_PER_HOUR);
4076 break;
4078 case DTK_TZ_HOUR:
4079 dummy = -tz;
4080 FMODULO(dummy, result, (double) SECS_PER_HOUR);
4081 break;
4083 case DTK_MICROSEC:
4084 #ifdef HAVE_INT64_TIMESTAMP
4085 result = tm->tm_sec * 1000000.0 + fsec;
4086 #else
4087 result = (tm->tm_sec + fsec) * 1000000;
4088 #endif
4089 break;
4091 case DTK_MILLISEC:
4092 #ifdef HAVE_INT64_TIMESTAMP
4093 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
4094 #else
4095 result = (tm->tm_sec + fsec) * 1000;
4096 #endif
4097 break;
4099 case DTK_SECOND:
4100 #ifdef HAVE_INT64_TIMESTAMP
4101 result = tm->tm_sec + fsec / 1000000.0;
4102 #else
4103 result = tm->tm_sec + fsec;
4104 #endif
4105 break;
4107 case DTK_MINUTE:
4108 result = tm->tm_min;
4109 break;
4111 case DTK_HOUR:
4112 result = tm->tm_hour;
4113 break;
4115 case DTK_DAY:
4116 result = tm->tm_mday;
4117 break;
4119 case DTK_MONTH:
4120 result = tm->tm_mon;
4121 break;
4123 case DTK_QUARTER:
4124 result = (tm->tm_mon - 1) / 3 + 1;
4125 break;
4127 case DTK_WEEK:
4128 result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
4129 break;
4131 case DTK_YEAR:
4132 if (tm->tm_year > 0)
4133 result = tm->tm_year;
4134 else
4135 /* there is no year 0, just 1 BC and 1 AD */
4136 result = tm->tm_year - 1;
4137 break;
4139 case DTK_DECADE:
4140 /* see comments in timestamp_part */
4141 if (tm->tm_year > 0)
4142 result = tm->tm_year / 10;
4143 else
4144 result = -((8 - (tm->tm_year - 1)) / 10);
4145 break;
4147 case DTK_CENTURY:
4148 /* see comments in timestamp_part */
4149 if (tm->tm_year > 0)
4150 result = (tm->tm_year + 99) / 100;
4151 else
4152 result = -((99 - (tm->tm_year - 1)) / 100);
4153 break;
4155 case DTK_MILLENNIUM:
4156 /* see comments in timestamp_part */
4157 if (tm->tm_year > 0)
4158 result = (tm->tm_year + 999) / 1000;
4159 else
4160 result = -((999 - (tm->tm_year - 1)) / 1000);
4161 break;
4163 case DTK_JULIAN:
4164 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
4165 #ifdef HAVE_INT64_TIMESTAMP
4166 result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
4167 tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY;
4168 #else
4169 result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
4170 tm->tm_sec + fsec) / (double) SECS_PER_DAY;
4171 #endif
4172 break;
4174 case DTK_ISOYEAR:
4175 result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday);
4176 break;
4178 default:
4179 ereport(ERROR,
4180 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4181 errmsg("timestamp with time zone units \"%s\" not supported",
4182 lowunits)));
4183 result = 0;
4187 else if (type == RESERV)
4189 switch (val)
4191 case DTK_EPOCH:
4192 #ifdef HAVE_INT64_TIMESTAMP
4193 result = (timestamp - SetEpochTimestamp()) / 1000000.0;
4194 #else
4195 result = timestamp - SetEpochTimestamp();
4196 #endif
4197 break;
4199 case DTK_DOW:
4200 case DTK_ISODOW:
4201 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
4202 ereport(ERROR,
4203 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4204 errmsg("timestamp out of range")));
4205 result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
4206 if (val == DTK_ISODOW && result == 0)
4207 result = 7;
4208 break;
4210 case DTK_DOY:
4211 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
4212 ereport(ERROR,
4213 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4214 errmsg("timestamp out of range")));
4215 result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
4216 - date2j(tm->tm_year, 1, 1) + 1);
4217 break;
4219 default:
4220 ereport(ERROR,
4221 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4222 errmsg("timestamp with time zone units \"%s\" not supported",
4223 lowunits)));
4224 result = 0;
4227 else
4229 ereport(ERROR,
4230 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4231 errmsg("timestamp with time zone units \"%s\" not recognized",
4232 lowunits)));
4234 result = 0;
4237 PG_RETURN_FLOAT8(result);
4241 /* interval_part()
4242 * Extract specified field from interval.
4244 Datum
4245 interval_part(PG_FUNCTION_ARGS)
4247 text *units = PG_GETARG_TEXT_PP(0);
4248 Interval *interval = PG_GETARG_INTERVAL_P(1);
4249 float8 result;
4250 int type,
4251 val;
4252 char *lowunits;
4253 fsec_t fsec;
4254 struct pg_tm tt,
4255 *tm = &tt;
4257 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
4258 VARSIZE_ANY_EXHDR(units),
4259 false);
4261 type = DecodeUnits(0, lowunits, &val);
4262 if (type == UNKNOWN_FIELD)
4263 type = DecodeSpecial(0, lowunits, &val);
4265 if (type == UNITS)
4267 if (interval2tm(*interval, tm, &fsec) == 0)
4269 switch (val)
4271 case DTK_MICROSEC:
4272 #ifdef HAVE_INT64_TIMESTAMP
4273 result = tm->tm_sec * 1000000.0 + fsec;
4274 #else
4275 result = (tm->tm_sec + fsec) * 1000000;
4276 #endif
4277 break;
4279 case DTK_MILLISEC:
4280 #ifdef HAVE_INT64_TIMESTAMP
4281 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
4282 #else
4283 result = (tm->tm_sec + fsec) * 1000;
4284 #endif
4285 break;
4287 case DTK_SECOND:
4288 #ifdef HAVE_INT64_TIMESTAMP
4289 result = tm->tm_sec + fsec / 1000000.0;
4290 #else
4291 result = tm->tm_sec + fsec;
4292 #endif
4293 break;
4295 case DTK_MINUTE:
4296 result = tm->tm_min;
4297 break;
4299 case DTK_HOUR:
4300 result = tm->tm_hour;
4301 break;
4303 case DTK_DAY:
4304 result = tm->tm_mday;
4305 break;
4307 case DTK_MONTH:
4308 result = tm->tm_mon;
4309 break;
4311 case DTK_QUARTER:
4312 result = (tm->tm_mon / 3) + 1;
4313 break;
4315 case DTK_YEAR:
4316 result = tm->tm_year;
4317 break;
4319 case DTK_DECADE:
4320 /* caution: C division may have negative remainder */
4321 result = tm->tm_year / 10;
4322 break;
4324 case DTK_CENTURY:
4325 /* caution: C division may have negative remainder */
4326 result = tm->tm_year / 100;
4327 break;
4329 case DTK_MILLENNIUM:
4330 /* caution: C division may have negative remainder */
4331 result = tm->tm_year / 1000;
4332 break;
4334 default:
4335 ereport(ERROR,
4336 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4337 errmsg("interval units \"%s\" not supported",
4338 lowunits)));
4339 result = 0;
4343 else
4345 elog(ERROR, "could not convert interval to tm");
4346 result = 0;
4349 else if (type == RESERV && val == DTK_EPOCH)
4351 #ifdef HAVE_INT64_TIMESTAMP
4352 result = interval->time / 1000000.0;
4353 #else
4354 result = interval->time;
4355 #endif
4356 result += ((double) DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR);
4357 result += ((double) DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR);
4358 result += ((double) SECS_PER_DAY) * interval->day;
4360 else
4362 ereport(ERROR,
4363 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4364 errmsg("interval units \"%s\" not recognized",
4365 lowunits)));
4366 result = 0;
4369 PG_RETURN_FLOAT8(result);
4373 /* timestamp_zone()
4374 * Encode timestamp type with specified time zone.
4375 * This function is just timestamp2timestamptz() except instead of
4376 * shifting to the global timezone, we shift to the specified timezone.
4377 * This is different from the other AT TIME ZONE cases because instead
4378 * of shifting to a _to_ a new time zone, it sets the time to _be_ the
4379 * specified timezone.
4381 Datum
4382 timestamp_zone(PG_FUNCTION_ARGS)
4384 text *zone = PG_GETARG_TEXT_PP(0);
4385 Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
4386 TimestampTz result;
4387 int tz;
4388 char tzname[TZ_STRLEN_MAX + 1];
4389 char *lowzone;
4390 int type,
4391 val;
4392 pg_tz *tzp;
4394 if (TIMESTAMP_NOT_FINITE(timestamp))
4395 PG_RETURN_TIMESTAMPTZ(timestamp);
4398 * Look up the requested timezone. First we look in the date token table
4399 * (to handle cases like "EST"), and if that fails, we look in the
4400 * timezone database (to handle cases like "America/New_York"). (This
4401 * matches the order in which timestamp input checks the cases; it's
4402 * important because the timezone database unwisely uses a few zone names
4403 * that are identical to offset abbreviations.)
4405 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
4406 lowzone = downcase_truncate_identifier(tzname,
4407 strlen(tzname),
4408 false);
4410 type = DecodeSpecial(0, lowzone, &val);
4412 if (type == TZ || type == DTZ)
4414 tz = -(val * 60);
4415 result = dt2local(timestamp, tz);
4417 else
4419 tzp = pg_tzset(tzname);
4420 if (tzp)
4422 /* Apply the timezone change */
4423 struct pg_tm tm;
4424 fsec_t fsec;
4426 if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
4427 ereport(ERROR,
4428 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4429 errmsg("timestamp out of range")));
4430 tz = DetermineTimeZoneOffset(&tm, tzp);
4431 if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
4432 ereport(ERROR,
4433 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4434 errmsg("could not convert to time zone \"%s\"",
4435 tzname)));
4437 else
4439 ereport(ERROR,
4440 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4441 errmsg("time zone \"%s\" not recognized", tzname)));
4442 result = 0; /* keep compiler quiet */
4446 PG_RETURN_TIMESTAMPTZ(result);
4449 /* timestamp_izone()
4450 * Encode timestamp type with specified time interval as time zone.
4452 Datum
4453 timestamp_izone(PG_FUNCTION_ARGS)
4455 Interval *zone = PG_GETARG_INTERVAL_P(0);
4456 Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
4457 TimestampTz result;
4458 int tz;
4460 if (TIMESTAMP_NOT_FINITE(timestamp))
4461 PG_RETURN_TIMESTAMPTZ(timestamp);
4463 if (zone->month != 0)
4464 ereport(ERROR,
4465 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4466 errmsg("interval time zone \"%s\" must not specify month",
4467 DatumGetCString(DirectFunctionCall1(interval_out,
4468 PointerGetDatum(zone))))));
4470 #ifdef HAVE_INT64_TIMESTAMP
4471 tz = zone->time / USECS_PER_SEC;
4472 #else
4473 tz = zone->time;
4474 #endif
4476 result = dt2local(timestamp, tz);
4478 PG_RETURN_TIMESTAMPTZ(result);
4479 } /* timestamp_izone() */
4481 /* timestamp_timestamptz()
4482 * Convert local timestamp to timestamp at GMT
4484 Datum
4485 timestamp_timestamptz(PG_FUNCTION_ARGS)
4487 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
4489 PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
4492 static TimestampTz
4493 timestamp2timestamptz(Timestamp timestamp)
4495 TimestampTz result;
4496 struct pg_tm tt,
4497 *tm = &tt;
4498 fsec_t fsec;
4499 int tz;
4501 if (TIMESTAMP_NOT_FINITE(timestamp))
4502 result = timestamp;
4503 else
4505 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
4506 ereport(ERROR,
4507 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4508 errmsg("timestamp out of range")));
4510 tz = DetermineTimeZoneOffset(tm, session_timezone);
4512 if (tm2timestamp(tm, fsec, &tz, &result) != 0)
4513 ereport(ERROR,
4514 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4515 errmsg("timestamp out of range")));
4518 return result;
4521 /* timestamptz_timestamp()
4522 * Convert timestamp at GMT to local timestamp
4524 Datum
4525 timestamptz_timestamp(PG_FUNCTION_ARGS)
4527 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
4528 Timestamp result;
4529 struct pg_tm tt,
4530 *tm = &tt;
4531 fsec_t fsec;
4532 char *tzn;
4533 int tz;
4535 if (TIMESTAMP_NOT_FINITE(timestamp))
4536 result = timestamp;
4537 else
4539 if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
4540 ereport(ERROR,
4541 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4542 errmsg("timestamp out of range")));
4543 if (tm2timestamp(tm, fsec, NULL, &result) != 0)
4544 ereport(ERROR,
4545 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4546 errmsg("timestamp out of range")));
4548 PG_RETURN_TIMESTAMP(result);
4551 /* timestamptz_zone()
4552 * Evaluate timestamp with time zone type at the specified time zone.
4553 * Returns a timestamp without time zone.
4555 Datum
4556 timestamptz_zone(PG_FUNCTION_ARGS)
4558 text *zone = PG_GETARG_TEXT_PP(0);
4559 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4560 Timestamp result;
4561 int tz;
4562 char tzname[TZ_STRLEN_MAX + 1];
4563 char *lowzone;
4564 int type,
4565 val;
4566 pg_tz *tzp;
4568 if (TIMESTAMP_NOT_FINITE(timestamp))
4569 PG_RETURN_TIMESTAMP(timestamp);
4572 * Look up the requested timezone. First we look in the date token table
4573 * (to handle cases like "EST"), and if that fails, we look in the
4574 * timezone database (to handle cases like "America/New_York"). (This
4575 * matches the order in which timestamp input checks the cases; it's
4576 * important because the timezone database unwisely uses a few zone names
4577 * that are identical to offset abbreviations.)
4579 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
4580 lowzone = downcase_truncate_identifier(tzname,
4581 strlen(tzname),
4582 false);
4584 type = DecodeSpecial(0, lowzone, &val);
4586 if (type == TZ || type == DTZ)
4588 tz = val * 60;
4589 result = dt2local(timestamp, tz);
4591 else
4593 tzp = pg_tzset(tzname);
4594 if (tzp)
4596 /* Apply the timezone change */
4597 struct pg_tm tm;
4598 fsec_t fsec;
4600 if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
4601 ereport(ERROR,
4602 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4603 errmsg("timestamp out of range")));
4604 if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
4605 ereport(ERROR,
4606 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4607 errmsg("could not convert to time zone \"%s\"",
4608 tzname)));
4610 else
4612 ereport(ERROR,
4613 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4614 errmsg("time zone \"%s\" not recognized", tzname)));
4615 result = 0; /* keep compiler quiet */
4619 PG_RETURN_TIMESTAMP(result);
4622 /* timestamptz_izone()
4623 * Encode timestamp with time zone type with specified time interval as time zone.
4624 * Returns a timestamp without time zone.
4626 Datum
4627 timestamptz_izone(PG_FUNCTION_ARGS)
4629 Interval *zone = PG_GETARG_INTERVAL_P(0);
4630 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4631 Timestamp result;
4632 int tz;
4634 if (TIMESTAMP_NOT_FINITE(timestamp))
4635 PG_RETURN_TIMESTAMP(timestamp);
4637 if (zone->month != 0)
4638 ereport(ERROR,
4639 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4640 errmsg("interval time zone \"%s\" must not specify month",
4641 DatumGetCString(DirectFunctionCall1(interval_out,
4642 PointerGetDatum(zone))))));
4644 #ifdef HAVE_INT64_TIMESTAMP
4645 tz = -(zone->time / USECS_PER_SEC);
4646 #else
4647 tz = -zone->time;
4648 #endif
4650 result = dt2local(timestamp, tz);
4652 PG_RETURN_TIMESTAMP(result);
4655 /* generate_series_timestamp()
4656 * Generate the set of timestamps from start to finish by step
4658 Datum
4659 generate_series_timestamp(PG_FUNCTION_ARGS)
4661 FuncCallContext *funcctx;
4662 generate_series_timestamp_fctx *fctx;
4663 Timestamp result;
4665 /* stuff done only on the first call of the function */
4666 if (SRF_IS_FIRSTCALL())
4668 Timestamp start = PG_GETARG_TIMESTAMP(0);
4669 Timestamp finish = PG_GETARG_TIMESTAMP(1);
4670 Interval *step = PG_GETARG_INTERVAL_P(2);
4671 MemoryContext oldcontext;
4672 Interval interval_zero;
4674 /* create a function context for cross-call persistence */
4675 funcctx = SRF_FIRSTCALL_INIT();
4678 * switch to memory context appropriate for multiple function calls
4680 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4682 /* allocate memory for user context */
4683 fctx = (generate_series_timestamp_fctx *)
4684 palloc(sizeof(generate_series_timestamp_fctx));
4687 * Use fctx to keep state from call to call. Seed current with the
4688 * original start value
4690 fctx->current = start;
4691 fctx->finish = finish;
4692 fctx->step = *step;
4694 /* Determine sign of the interval */
4695 MemSet(&interval_zero, 0, sizeof(Interval));
4696 fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero);
4698 if (fctx->step_sign == 0)
4699 ereport(ERROR,
4700 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4701 errmsg("step size cannot equal zero")));
4703 funcctx->user_fctx = fctx;
4704 MemoryContextSwitchTo(oldcontext);
4707 /* stuff done on every call of the function */
4708 funcctx = SRF_PERCALL_SETUP();
4711 * get the saved state and use current as the result for this iteration
4713 fctx = funcctx->user_fctx;
4714 result = fctx->current;
4716 if (fctx->step_sign > 0 ?
4717 timestamp_cmp_internal(result, fctx->finish) <= 0 :
4718 timestamp_cmp_internal(result, fctx->finish) >= 0)
4720 /* increment current in preparation for next iteration */
4721 fctx->current = DatumGetTimestamp(
4722 DirectFunctionCall2(timestamp_pl_interval,
4723 TimestampGetDatum(fctx->current),
4724 PointerGetDatum(&fctx->step)));
4726 /* do when there is more left to send */
4727 SRF_RETURN_NEXT(funcctx, TimestampGetDatum(result));
4729 else
4731 /* do when there is no more left */
4732 SRF_RETURN_DONE(funcctx);
4736 /* generate_series_timestamptz()
4737 * Generate the set of timestamps from start to finish by step
4739 Datum
4740 generate_series_timestamptz(PG_FUNCTION_ARGS)
4742 FuncCallContext *funcctx;
4743 generate_series_timestamptz_fctx *fctx;
4744 TimestampTz result;
4746 /* stuff done only on the first call of the function */
4747 if (SRF_IS_FIRSTCALL())
4749 TimestampTz start = PG_GETARG_TIMESTAMPTZ(0);
4750 TimestampTz finish = PG_GETARG_TIMESTAMPTZ(1);
4751 Interval *step = PG_GETARG_INTERVAL_P(2);
4752 MemoryContext oldcontext;
4753 Interval interval_zero;
4755 /* create a function context for cross-call persistence */
4756 funcctx = SRF_FIRSTCALL_INIT();
4759 * switch to memory context appropriate for multiple function calls
4761 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
4763 /* allocate memory for user context */
4764 fctx = (generate_series_timestamptz_fctx *)
4765 palloc(sizeof(generate_series_timestamptz_fctx));
4768 * Use fctx to keep state from call to call. Seed current with the
4769 * original start value
4771 fctx->current = start;
4772 fctx->finish = finish;
4773 fctx->step = *step;
4775 /* Determine sign of the interval */
4776 MemSet(&interval_zero, 0, sizeof(Interval));
4777 fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero);
4779 if (fctx->step_sign == 0)
4780 ereport(ERROR,
4781 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4782 errmsg("step size cannot equal zero")));
4784 funcctx->user_fctx = fctx;
4785 MemoryContextSwitchTo(oldcontext);
4788 /* stuff done on every call of the function */
4789 funcctx = SRF_PERCALL_SETUP();
4792 * get the saved state and use current as the result for this iteration
4794 fctx = funcctx->user_fctx;
4795 result = fctx->current;
4797 if (fctx->step_sign > 0 ?
4798 timestamp_cmp_internal(result, fctx->finish) <= 0 :
4799 timestamp_cmp_internal(result, fctx->finish) >= 0)
4801 /* increment current in preparation for next iteration */
4802 fctx->current = DatumGetTimestampTz(
4803 DirectFunctionCall2(timestamptz_pl_interval,
4804 TimestampTzGetDatum(fctx->current),
4805 PointerGetDatum(&fctx->step)));
4807 /* do when there is more left to send */
4808 SRF_RETURN_NEXT(funcctx, TimestampTzGetDatum(result));
4810 else
4812 /* do when there is no more left */
4813 SRF_RETURN_DONE(funcctx);