1/*-------------------------------------------------------------------------
2 *
3 * timestamp.c
4 * Functions for the built-in SQL types "timestamp" and "interval".
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/utils/adt/timestamp.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16#include "postgres.h"
17
18#include <ctype.h>
19#include <math.h>
20#include <limits.h>
21#include <sys/time.h>
22
23#include "access/xact.h"
24#include "catalog/pg_type.h"
25#include "common/int128.h"
26#include "funcapi.h"
27#include "libpq/pqformat.h"
28#include "miscadmin.h"
29#include "nodes/makefuncs.h"
30#include "nodes/nodeFuncs.h"
31#include "nodes/supportnodes.h"
32#include "parser/scansup.h"
33#include "utils/array.h"
34#include "utils/builtins.h"
35#include "utils/datetime.h"
36#include "utils/float.h"
37
38/*
39 * gcc's -ffast-math switch breaks routines that expect exact results from
40 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
41 */
42#ifdef __FAST_MATH__
43#error -ffast-math is known to break this code
44#endif
45
46#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
47
48/* Set at postmaster start */
49TimestampTz PgStartTime;
50
51/* Set at configuration reload */
52TimestampTz PgReloadTime;
53
54typedef struct
55{
56 Timestamp current;
57 Timestamp finish;
58 Interval step;
59 int step_sign;
60} generate_series_timestamp_fctx;
61
62typedef struct
63{
64 TimestampTz current;
65 TimestampTz finish;
66 Interval step;
67 int step_sign;
68} generate_series_timestamptz_fctx;
69
70
71static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec);
72static Timestamp dt2local(Timestamp dt, int timezone);
73static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
74static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
75static TimestampTz timestamp2timestamptz(Timestamp timestamp);
76static Timestamp timestamptz2timestamp(TimestampTz timestamp);
77
78
79/* common code for timestamptypmodin and timestamptztypmodin */
80static int32
81anytimestamp_typmodin(bool istz, ArrayType *ta)
82{
83 int32 *tl;
84 int n;
85
86 tl = ArrayGetIntegerTypmods(ta, &n);
87
88 /*
89 * we're not too tense about good error message here because grammar
90 * shouldn't allow wrong number of modifiers for TIMESTAMP
91 */
92 if (n != 1)
93 ereport(ERROR,
94 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
95 errmsg("invalid type modifier")));
96
97 return anytimestamp_typmod_check(istz, tl[0]);
98}
99
100/* exported so parse_expr.c can use it */
101int32
102anytimestamp_typmod_check(bool istz, int32 typmod)
103{
104 if (typmod < 0)
105 ereport(ERROR,
106 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
107 errmsg("TIMESTAMP(%d)%s precision must not be negative",
108 typmod, (istz ? " WITH TIME ZONE" : ""))));
109 if (typmod > MAX_TIMESTAMP_PRECISION)
110 {
111 ereport(WARNING,
112 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
113 errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d",
114 typmod, (istz ? " WITH TIME ZONE" : ""),
115 MAX_TIMESTAMP_PRECISION)));
116 typmod = MAX_TIMESTAMP_PRECISION;
117 }
118
119 return typmod;
120}
121
122/* common code for timestamptypmodout and timestamptztypmodout */
123static char *
124anytimestamp_typmodout(bool istz, int32 typmod)
125{
126 const char *tz = istz ? " with time zone" : " without time zone";
127
128 if (typmod >= 0)
129 return psprintf("(%d)%s", (int) typmod, tz);
130 else
131 return psprintf("%s", tz);
132}
133
134
135/*****************************************************************************
136 * USER I/O ROUTINES *
137 *****************************************************************************/
138
139/* timestamp_in()
140 * Convert a string to internal form.
141 */
142Datum
143timestamp_in(PG_FUNCTION_ARGS)
144{
145 char *str = PG_GETARG_CSTRING(0);
146
147#ifdef NOT_USED
148 Oid typelem = PG_GETARG_OID(1);
149#endif
150 int32 typmod = PG_GETARG_INT32(2);
151 Timestamp result;
152 fsec_t fsec;
153 struct pg_tm tt,
154 *tm = &tt;
155 int tz;
156 int dtype;
157 int nf;
158 int dterr;
159 char *field[MAXDATEFIELDS];
160 int ftype[MAXDATEFIELDS];
161 char workbuf[MAXDATELEN + MAXDATEFIELDS];
162
163 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
164 field, ftype, MAXDATEFIELDS, &nf);
165 if (dterr == 0)
166 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
167 if (dterr != 0)
168 DateTimeParseError(dterr, str, "timestamp");
169
170 switch (dtype)
171 {
172 case DTK_DATE:
173 if (tm2timestamp(tm, fsec, NULL, &result) != 0)
174 ereport(ERROR,
175 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
176 errmsg("timestamp out of range: \"%s\"", str)));
177 break;
178
179 case DTK_EPOCH:
180 result = SetEpochTimestamp();
181 break;
182
183 case DTK_LATE:
184 TIMESTAMP_NOEND(result);
185 break;
186
187 case DTK_EARLY:
188 TIMESTAMP_NOBEGIN(result);
189 break;
190
191 default:
192 elog(ERROR, "unexpected dtype %d while parsing timestamp \"%s\"",
193 dtype, str);
194 TIMESTAMP_NOEND(result);
195 }
196
197 AdjustTimestampForTypmod(&result, typmod);
198
199 PG_RETURN_TIMESTAMP(result);
200}
201
202/* timestamp_out()
203 * Convert a timestamp to external form.
204 */
205Datum
206timestamp_out(PG_FUNCTION_ARGS)
207{
208 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
209 char *result;
210 struct pg_tm tt,
211 *tm = &tt;
212 fsec_t fsec;
213 char buf[MAXDATELEN + 1];
214
215 if (TIMESTAMP_NOT_FINITE(timestamp))
216 EncodeSpecialTimestamp(timestamp, buf);
217 else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
218 EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf);
219 else
220 ereport(ERROR,
221 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
222 errmsg("timestamp out of range")));
223
224 result = pstrdup(buf);
225 PG_RETURN_CSTRING(result);
226}
227
228/*
229 * timestamp_recv - converts external binary format to timestamp
230 */
231Datum
232timestamp_recv(PG_FUNCTION_ARGS)
233{
234 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
235
236#ifdef NOT_USED
237 Oid typelem = PG_GETARG_OID(1);
238#endif
239 int32 typmod = PG_GETARG_INT32(2);
240 Timestamp timestamp;
241 struct pg_tm tt,
242 *tm = &tt;
243 fsec_t fsec;
244
245 timestamp = (Timestamp) pq_getmsgint64(buf);
246
247 /* range check: see if timestamp_out would like it */
248 if (TIMESTAMP_NOT_FINITE(timestamp))
249 /* ok */ ;
250 else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0 ||
251 !IS_VALID_TIMESTAMP(timestamp))
252 ereport(ERROR,
253 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
254 errmsg("timestamp out of range")));
255
256 AdjustTimestampForTypmod(&timestamp, typmod);
257
258 PG_RETURN_TIMESTAMP(timestamp);
259}
260
261/*
262 * timestamp_send - converts timestamp to binary format
263 */
264Datum
265timestamp_send(PG_FUNCTION_ARGS)
266{
267 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
268 StringInfoData buf;
269
270 pq_begintypsend(&buf);
271 pq_sendint64(&buf, timestamp);
272 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
273}
274
275Datum
276timestamptypmodin(PG_FUNCTION_ARGS)
277{
278 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
279
280 PG_RETURN_INT32(anytimestamp_typmodin(false, ta));
281}
282
283Datum
284timestamptypmodout(PG_FUNCTION_ARGS)
285{
286 int32 typmod = PG_GETARG_INT32(0);
287
288 PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod));
289}
290
291
292/*
293 * timestamp_support()
294 *
295 * Planner support function for the timestamp_scale() and timestamptz_scale()
296 * length coercion functions (we need not distinguish them here).
297 */
298Datum
299timestamp_support(PG_FUNCTION_ARGS)
300{
301 Node *rawreq = (Node *) PG_GETARG_POINTER(0);
302 Node *ret = NULL;
303
304 if (IsA(rawreq, SupportRequestSimplify))
305 {
306 SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
307
308 ret = TemporalSimplify(MAX_TIMESTAMP_PRECISION, (Node *) req->fcall);
309 }
310
311 PG_RETURN_POINTER(ret);
312}
313
314/* timestamp_scale()
315 * Adjust time type for specified scale factor.
316 * Used by PostgreSQL type system to stuff columns.
317 */
318Datum
319timestamp_scale(PG_FUNCTION_ARGS)
320{
321 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
322 int32 typmod = PG_GETARG_INT32(1);
323 Timestamp result;
324
325 result = timestamp;
326
327 AdjustTimestampForTypmod(&result, typmod);
328
329 PG_RETURN_TIMESTAMP(result);
330}
331
332/*
333 * AdjustTimestampForTypmod --- round off a timestamp to suit given typmod
334 * Works for either timestamp or timestamptz.
335 */
336static void
337AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
338{
339 static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
340 INT64CONST(1000000),
341 INT64CONST(100000),
342 INT64CONST(10000),
343 INT64CONST(1000),
344 INT64CONST(100),
345 INT64CONST(10),
346 INT64CONST(1)
347 };
348
349 static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
350 INT64CONST(500000),
351 INT64CONST(50000),
352 INT64CONST(5000),
353 INT64CONST(500),
354 INT64CONST(50),
355 INT64CONST(5),
356 INT64CONST(0)
357 };
358
359 if (!TIMESTAMP_NOT_FINITE(*time)
360 && (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
361 {
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)));
367
368 if (*time >= INT64CONST(0))
369 {
370 *time = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) *
371 TimestampScales[typmod];
372 }
373 else
374 {
375 *time = -((((-*time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
376 * TimestampScales[typmod]);
377 }
378 }
379}
380
381
382/* timestamptz_in()
383 * Convert a string to internal form.
384 */
385Datum
386timestamptz_in(PG_FUNCTION_ARGS)
387{
388 char *str = PG_GETARG_CSTRING(0);
389
390#ifdef NOT_USED
391 Oid typelem = PG_GETARG_OID(1);
392#endif
393 int32 typmod = PG_GETARG_INT32(2);
394 TimestampTz result;
395 fsec_t fsec;
396 struct pg_tm tt,
397 *tm = &tt;
398 int tz;
399 int dtype;
400 int nf;
401 int dterr;
402 char *field[MAXDATEFIELDS];
403 int ftype[MAXDATEFIELDS];
404 char workbuf[MAXDATELEN + MAXDATEFIELDS];
405
406 dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
407 field, ftype, MAXDATEFIELDS, &nf);
408 if (dterr == 0)
409 dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
410 if (dterr != 0)
411 DateTimeParseError(dterr, str, "timestamp with time zone");
412
413 switch (dtype)
414 {
415 case DTK_DATE:
416 if (tm2timestamp(tm, fsec, &tz, &result) != 0)
417 ereport(ERROR,
418 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
419 errmsg("timestamp out of range: \"%s\"", str)));
420 break;
421
422 case DTK_EPOCH:
423 result = SetEpochTimestamp();
424 break;
425
426 case DTK_LATE:
427 TIMESTAMP_NOEND(result);
428 break;
429
430 case DTK_EARLY:
431 TIMESTAMP_NOBEGIN(result);
432 break;
433
434 default:
435 elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"",
436 dtype, str);
437 TIMESTAMP_NOEND(result);
438 }
439
440 AdjustTimestampForTypmod(&result, typmod);
441
442 PG_RETURN_TIMESTAMPTZ(result);
443}
444
445/*
446 * Try to parse a timezone specification, and return its timezone offset value
447 * if it's acceptable. Otherwise, an error is thrown.
448 *
449 * Note: some code paths update tm->tm_isdst, and some don't; current callers
450 * don't care, so we don't bother being consistent.
451 */
452static int
453parse_sane_timezone(struct pg_tm *tm, text *zone)
454{
455 char tzname[TZ_STRLEN_MAX + 1];
456 int rt;
457 int tz;
458
459 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
460
461 /*
462 * Look up the requested timezone. First we try to interpret it as a
463 * numeric timezone specification; if DecodeTimezone decides it doesn't
464 * like the format, we look in the timezone abbreviation table (to handle
465 * cases like "EST"), and if that also fails, we look in the timezone
466 * database (to handle cases like "America/New_York"). (This matches the
467 * order in which timestamp input checks the cases; it's important because
468 * the timezone database unwisely uses a few zone names that are identical
469 * to offset abbreviations.)
470 *
471 * Note pg_tzset happily parses numeric input that DecodeTimezone would
472 * reject. To avoid having it accept input that would otherwise be seen
473 * as invalid, it's enough to disallow having a digit in the first
474 * position of our input string.
475 */
476 if (isdigit((unsigned char) *tzname))
477 ereport(ERROR,
478 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
479 errmsg("invalid input syntax for type %s: \"%s\"",
480 "numeric time zone", tzname),
481 errhint("Numeric time zones must have \"-\" or \"+\" as first character.")));
482
483 rt = DecodeTimezone(tzname, &tz);
484 if (rt != 0)
485 {
486 char *lowzone;
487 int type,
488 val;
489 pg_tz *tzp;
490
491 if (rt == DTERR_TZDISP_OVERFLOW)
492 ereport(ERROR,
493 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
494 errmsg("numeric time zone \"%s\" out of range", tzname)));
495 else if (rt != DTERR_BAD_FORMAT)
496 ereport(ERROR,
497 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
498 errmsg("time zone \"%s\" not recognized", tzname)));
499
500 /* DecodeTimezoneAbbrev requires lowercase input */
501 lowzone = downcase_truncate_identifier(tzname,
502 strlen(tzname),
503 false);
504 type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
505
506 if (type == TZ || type == DTZ)
507 {
508 /* fixed-offset abbreviation */
509 tz = -val;
510 }
511 else if (type == DYNTZ)
512 {
513 /* dynamic-offset abbreviation, resolve using specified time */
514 tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
515 }
516 else
517 {
518 /* try it as a full zone name */
519 tzp = pg_tzset(tzname);
520 if (tzp)
521 tz = DetermineTimeZoneOffset(tm, tzp);
522 else
523 ereport(ERROR,
524 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
525 errmsg("time zone \"%s\" not recognized", tzname)));
526 }
527 }
528
529 return tz;
530}
531
532/*
533 * make_timestamp_internal
534 * workhorse for make_timestamp and make_timestamptz
535 */
536static Timestamp
537make_timestamp_internal(int year, int month, int day,
538 int hour, int min, double sec)
539{
540 struct pg_tm tm;
541 TimeOffset date;
542 TimeOffset time;
543 int dterr;
544 Timestamp result;
545
546 tm.tm_year = year;
547 tm.tm_mon = month;
548 tm.tm_mday = day;
549
550 /*
551 * Note: we'll reject zero or negative year values. Perhaps negatives
552 * should be allowed to represent BC years?
553 */
554 dterr = ValidateDate(DTK_DATE_M, false, false, false, &tm);
555
556 if (dterr != 0)
557 ereport(ERROR,
558 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
559 errmsg("date field value out of range: %d-%02d-%02d",
560 year, month, day)));
561
562 if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
563 ereport(ERROR,
564 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
565 errmsg("date out of range: %d-%02d-%02d",
566 year, month, day)));
567
568 date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
569
570 /*
571 * This should match the checks in DecodeTimeOnly, except that since we're
572 * dealing with a float "sec" value, we also explicitly reject NaN. (An
573 * infinity input should get rejected by the range comparisons, but we
574 * can't be sure how those will treat a NaN.)
575 */
576 if (hour < 0 || min < 0 || min > MINS_PER_HOUR - 1 ||
577 isnan(sec) ||
578 sec < 0 || sec > SECS_PER_MINUTE ||
579 hour > HOURS_PER_DAY ||
580 /* test for > 24:00:00 */
581 (hour == HOURS_PER_DAY && (min > 0 || sec > 0)))
582 ereport(ERROR,
583 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
584 errmsg("time field value out of range: %d:%02d:%02g",
585 hour, min, sec)));
586
587 /* This should match tm2time */
588 time = (((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
589 * USECS_PER_SEC) + rint(sec * USECS_PER_SEC);
590
591 result = date * USECS_PER_DAY + time;
592 /* check for major overflow */
593 if ((result - time) / USECS_PER_DAY != date)
594 ereport(ERROR,
595 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
596 errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
597 year, month, day,
598 hour, min, sec)));
599
600 /* check for just-barely overflow (okay except time-of-day wraps) */
601 /* caution: we want to allow 1999-12-31 24:00:00 */
602 if ((result < 0 && date > 0) ||
603 (result > 0 && date < -1))
604 ereport(ERROR,
605 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
606 errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
607 year, month, day,
608 hour, min, sec)));
609
610 /* final range check catches just-out-of-range timestamps */
611 if (!IS_VALID_TIMESTAMP(result))
612 ereport(ERROR,
613 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
614 errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
615 year, month, day,
616 hour, min, sec)));
617
618 return result;
619}
620
621/*
622 * make_timestamp() - timestamp constructor
623 */
624Datum
625make_timestamp(PG_FUNCTION_ARGS)
626{
627 int32 year = PG_GETARG_INT32(0);
628 int32 month = PG_GETARG_INT32(1);
629 int32 mday = PG_GETARG_INT32(2);
630 int32 hour = PG_GETARG_INT32(3);
631 int32 min = PG_GETARG_INT32(4);
632 float8 sec = PG_GETARG_FLOAT8(5);
633 Timestamp result;
634
635 result = make_timestamp_internal(year, month, mday,
636 hour, min, sec);
637
638 PG_RETURN_TIMESTAMP(result);
639}
640
641/*
642 * make_timestamptz() - timestamp with time zone constructor
643 */
644Datum
645make_timestamptz(PG_FUNCTION_ARGS)
646{
647 int32 year = PG_GETARG_INT32(0);
648 int32 month = PG_GETARG_INT32(1);
649 int32 mday = PG_GETARG_INT32(2);
650 int32 hour = PG_GETARG_INT32(3);
651 int32 min = PG_GETARG_INT32(4);
652 float8 sec = PG_GETARG_FLOAT8(5);
653 Timestamp result;
654
655 result = make_timestamp_internal(year, month, mday,
656 hour, min, sec);
657
658 PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result));
659}
660
661/*
662 * Construct a timestamp with time zone.
663 * As above, but the time zone is specified as seventh argument.
664 */
665Datum
666make_timestamptz_at_timezone(PG_FUNCTION_ARGS)
667{
668 int32 year = PG_GETARG_INT32(0);
669 int32 month = PG_GETARG_INT32(1);
670 int32 mday = PG_GETARG_INT32(2);
671 int32 hour = PG_GETARG_INT32(3);
672 int32 min = PG_GETARG_INT32(4);
673 float8 sec = PG_GETARG_FLOAT8(5);
674 text *zone = PG_GETARG_TEXT_PP(6);
675 TimestampTz result;
676 Timestamp timestamp;
677 struct pg_tm tt;
678 int tz;
679 fsec_t fsec;
680
681 timestamp = make_timestamp_internal(year, month, mday,
682 hour, min, sec);
683
684 if (timestamp2tm(timestamp, NULL, &tt, &fsec, NULL, NULL) != 0)
685 ereport(ERROR,
686 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
687 errmsg("timestamp out of range")));
688
689 tz = parse_sane_timezone(&tt, zone);
690
691 result = dt2local(timestamp, -tz);
692
693 if (!IS_VALID_TIMESTAMP(result))
694 ereport(ERROR,
695 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
696 errmsg("timestamp out of range")));
697
698 PG_RETURN_TIMESTAMPTZ(result);
699}
700
701/*
702 * to_timestamp(double precision)
703 * Convert UNIX epoch to timestamptz.
704 */
705Datum
706float8_timestamptz(PG_FUNCTION_ARGS)
707{
708 float8 seconds = PG_GETARG_FLOAT8(0);
709 TimestampTz result;
710
711 /* Deal with NaN and infinite inputs ... */
712 if (isnan(seconds))
713 ereport(ERROR,
714 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
715 errmsg("timestamp cannot be NaN")));
716
717 if (isinf(seconds))
718 {
719 if (seconds < 0)
720 TIMESTAMP_NOBEGIN(result);
721 else
722 TIMESTAMP_NOEND(result);
723 }
724 else
725 {
726 /* Out of range? */
727 if (seconds <
728 (float8) SECS_PER_DAY * (DATETIME_MIN_JULIAN - UNIX_EPOCH_JDATE)
729 || seconds >=
730 (float8) SECS_PER_DAY * (TIMESTAMP_END_JULIAN - UNIX_EPOCH_JDATE))
731 ereport(ERROR,
732 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
733 errmsg("timestamp out of range: \"%g\"", seconds)));
734
735 /* Convert UNIX epoch to Postgres epoch */
736 seconds -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
737
738 seconds = rint(seconds * USECS_PER_SEC);
739 result = (int64) seconds;
740
741 /* Recheck in case roundoff produces something just out of range */
742 if (!IS_VALID_TIMESTAMP(result))
743 ereport(ERROR,
744 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
745 errmsg("timestamp out of range: \"%g\"",
746 PG_GETARG_FLOAT8(0))));
747 }
748
749 PG_RETURN_TIMESTAMP(result);
750}
751
752/* timestamptz_out()
753 * Convert a timestamp to external form.
754 */
755Datum
756timestamptz_out(PG_FUNCTION_ARGS)
757{
758 TimestampTz dt = PG_GETARG_TIMESTAMPTZ(0);
759 char *result;
760 int tz;
761 struct pg_tm tt,
762 *tm = &tt;
763 fsec_t fsec;
764 const char *tzn;
765 char buf[MAXDATELEN + 1];
766
767 if (TIMESTAMP_NOT_FINITE(dt))
768 EncodeSpecialTimestamp(dt, buf);
769 else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0)
770 EncodeDateTime(tm, fsec, true, tz, tzn, DateStyle, buf);
771 else
772 ereport(ERROR,
773 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
774 errmsg("timestamp out of range")));
775
776 result = pstrdup(buf);
777 PG_RETURN_CSTRING(result);
778}
779
780/*
781 * timestamptz_recv - converts external binary format to timestamptz
782 */
783Datum
784timestamptz_recv(PG_FUNCTION_ARGS)
785{
786 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
787
788#ifdef NOT_USED
789 Oid typelem = PG_GETARG_OID(1);
790#endif
791 int32 typmod = PG_GETARG_INT32(2);
792 TimestampTz timestamp;
793 int tz;
794 struct pg_tm tt,
795 *tm = &tt;
796 fsec_t fsec;
797
798 timestamp = (TimestampTz) pq_getmsgint64(buf);
799
800 /* range check: see if timestamptz_out would like it */
801 if (TIMESTAMP_NOT_FINITE(timestamp))
802 /* ok */ ;
803 else if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0 ||
804 !IS_VALID_TIMESTAMP(timestamp))
805 ereport(ERROR,
806 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
807 errmsg("timestamp out of range")));
808
809 AdjustTimestampForTypmod(&timestamp, typmod);
810
811 PG_RETURN_TIMESTAMPTZ(timestamp);
812}
813
814/*
815 * timestamptz_send - converts timestamptz to binary format
816 */
817Datum
818timestamptz_send(PG_FUNCTION_ARGS)
819{
820 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
821 StringInfoData buf;
822
823 pq_begintypsend(&buf);
824 pq_sendint64(&buf, timestamp);
825 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
826}
827
828Datum
829timestamptztypmodin(PG_FUNCTION_ARGS)
830{
831 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
832
833 PG_RETURN_INT32(anytimestamp_typmodin(true, ta));
834}
835
836Datum
837timestamptztypmodout(PG_FUNCTION_ARGS)
838{
839 int32 typmod = PG_GETARG_INT32(0);
840
841 PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod));
842}
843
844
845/* timestamptz_scale()
846 * Adjust time type for specified scale factor.
847 * Used by PostgreSQL type system to stuff columns.
848 */
849Datum
850timestamptz_scale(PG_FUNCTION_ARGS)
851{
852 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
853 int32 typmod = PG_GETARG_INT32(1);
854 TimestampTz result;
855
856 result = timestamp;
857
858 AdjustTimestampForTypmod(&result, typmod);
859
860 PG_RETURN_TIMESTAMPTZ(result);
861}
862
863
864/* interval_in()
865 * Convert a string to internal form.
866 *
867 * External format(s):
868 * Uses the generic date/time parsing and decoding routines.
869 */
870Datum
871interval_in(PG_FUNCTION_ARGS)
872{
873 char *str = PG_GETARG_CSTRING(0);
874
875#ifdef NOT_USED
876 Oid typelem = PG_GETARG_OID(1);
877#endif
878 int32 typmod = PG_GETARG_INT32(2);
879 Interval *result;
880 fsec_t fsec;
881 struct pg_tm tt,
882 *tm = &tt;
883 int dtype;
884 int nf;
885 int range;
886 int dterr;
887 char *field[MAXDATEFIELDS];
888 int ftype[MAXDATEFIELDS];
889 char workbuf[256];
890
891 tm->tm_year = 0;
892 tm->tm_mon = 0;
893 tm->tm_mday = 0;
894 tm->tm_hour = 0;
895 tm->tm_min = 0;
896 tm->tm_sec = 0;
897 fsec = 0;
898
899 if (typmod >= 0)
900 range = INTERVAL_RANGE(typmod);
901 else
902 range = INTERVAL_FULL_RANGE;
903
904 dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field,
905 ftype, MAXDATEFIELDS, &nf);
906 if (dterr == 0)
907 dterr = DecodeInterval(field, ftype, nf, range,
908 &dtype, tm, &fsec);
909
910 /* if those functions think it's a bad format, try ISO8601 style */
911 if (dterr == DTERR_BAD_FORMAT)
912 dterr = DecodeISO8601Interval(str,
913 &dtype, tm, &fsec);
914
915 if (dterr != 0)
916 {
917 if (dterr == DTERR_FIELD_OVERFLOW)
918 dterr = DTERR_INTERVAL_OVERFLOW;
919 DateTimeParseError(dterr, str, "interval");
920 }
921
922 result = (Interval *) palloc(sizeof(Interval));
923
924 switch (dtype)
925 {
926 case DTK_DELTA:
927 if (tm2interval(tm, fsec, result) != 0)
928 ereport(ERROR,
929 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
930 errmsg("interval out of range")));
931 break;
932
933 default:
934 elog(ERROR, "unexpected dtype %d while parsing interval \"%s\"",
935 dtype, str);
936 }
937
938 AdjustIntervalForTypmod(result, typmod);
939
940 PG_RETURN_INTERVAL_P(result);
941}
942
943/* interval_out()
944 * Convert a time span to external form.
945 */
946Datum
947interval_out(PG_FUNCTION_ARGS)
948{
949 Interval *span = PG_GETARG_INTERVAL_P(0);
950 char *result;
951 struct pg_tm tt,
952 *tm = &tt;
953 fsec_t fsec;
954 char buf[MAXDATELEN + 1];
955
956 if (interval2tm(*span, tm, &fsec) != 0)
957 elog(ERROR, "could not convert interval to tm");
958
959 EncodeInterval(tm, fsec, IntervalStyle, buf);
960
961 result = pstrdup(buf);
962 PG_RETURN_CSTRING(result);
963}
964
965/*
966 * interval_recv - converts external binary format to interval
967 */
968Datum
969interval_recv(PG_FUNCTION_ARGS)
970{
971 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
972
973#ifdef NOT_USED
974 Oid typelem = PG_GETARG_OID(1);
975#endif
976 int32 typmod = PG_GETARG_INT32(2);
977 Interval *interval;
978
979 interval = (Interval *) palloc(sizeof(Interval));
980
981 interval->time = pq_getmsgint64(buf);
982 interval->day = pq_getmsgint(buf, sizeof(interval->day));
983 interval->month = pq_getmsgint(buf, sizeof(interval->month));
984
985 AdjustIntervalForTypmod(interval, typmod);
986
987 PG_RETURN_INTERVAL_P(interval);
988}
989
990/*
991 * interval_send - converts interval to binary format
992 */
993Datum
994interval_send(PG_FUNCTION_ARGS)
995{
996 Interval *interval = PG_GETARG_INTERVAL_P(0);
997 StringInfoData buf;
998
999 pq_begintypsend(&buf);
1000 pq_sendint64(&buf, interval->time);
1001 pq_sendint32(&buf, interval->day);
1002 pq_sendint32(&buf, interval->month);
1003 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1004}
1005
1006/*
1007 * The interval typmod stores a "range" in its high 16 bits and a "precision"
1008 * in its low 16 bits. Both contribute to defining the resolution of the
1009 * type. Range addresses resolution granules larger than one second, and
1010 * precision specifies resolution below one second. This representation can
1011 * express all SQL standard resolutions, but we implement them all in terms of
1012 * truncating rightward from some position. Range is a bitmap of permitted
1013 * fields, but only the temporally-smallest such field is significant to our
1014 * calculations. Precision is a count of sub-second decimal places to retain.
1015 * Setting all bits (INTERVAL_FULL_PRECISION) gives the same truncation
1016 * semantics as choosing MAX_INTERVAL_PRECISION.
1017 */
1018Datum
1019intervaltypmodin(PG_FUNCTION_ARGS)
1020{
1021 ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1022 int32 *tl;
1023 int n;
1024 int32 typmod;
1025
1026 tl = ArrayGetIntegerTypmods(ta, &n);
1027
1028 /*
1029 * tl[0] - interval range (fields bitmask) tl[1] - precision (optional)
1030 *
1031 * Note we must validate tl[0] even though it's normally guaranteed
1032 * correct by the grammar --- consider SELECT 'foo'::"interval"(1000).
1033 */
1034 if (n > 0)
1035 {
1036 switch (tl[0])
1037 {
1038 case INTERVAL_MASK(YEAR):
1039 case INTERVAL_MASK(MONTH):
1040 case INTERVAL_MASK(DAY):
1041 case INTERVAL_MASK(HOUR):
1042 case INTERVAL_MASK(MINUTE):
1043 case INTERVAL_MASK(SECOND):
1044 case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
1045 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
1046 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1047 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1048 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1049 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1050 case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1051 case INTERVAL_FULL_RANGE:
1052 /* all OK */
1053 break;
1054 default:
1055 ereport(ERROR,
1056 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1057 errmsg("invalid INTERVAL type modifier")));
1058 }
1059 }
1060
1061 if (n == 1)
1062 {
1063 if (tl[0] != INTERVAL_FULL_RANGE)
1064 typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, tl[0]);
1065 else
1066 typmod = -1;
1067 }
1068 else if (n == 2)
1069 {
1070 if (tl[1] < 0)
1071 ereport(ERROR,
1072 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1073 errmsg("INTERVAL(%d) precision must not be negative",
1074 tl[1])));
1075 if (tl[1] > MAX_INTERVAL_PRECISION)
1076 {
1077 ereport(WARNING,
1078 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1079 errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
1080 tl[1], MAX_INTERVAL_PRECISION)));
1081 typmod = INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION, tl[0]);
1082 }
1083 else
1084 typmod = INTERVAL_TYPMOD(tl[1], tl[0]);
1085 }
1086 else
1087 {
1088 ereport(ERROR,
1089 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1090 errmsg("invalid INTERVAL type modifier")));
1091 typmod = 0; /* keep compiler quiet */
1092 }
1093
1094 PG_RETURN_INT32(typmod);
1095}
1096
1097Datum
1098intervaltypmodout(PG_FUNCTION_ARGS)
1099{
1100 int32 typmod = PG_GETARG_INT32(0);
1101 char *res = (char *) palloc(64);
1102 int fields;
1103 int precision;
1104 const char *fieldstr;
1105
1106 if (typmod < 0)
1107 {
1108 *res = '\0';
1109 PG_RETURN_CSTRING(res);
1110 }
1111
1112 fields = INTERVAL_RANGE(typmod);
1113 precision = INTERVAL_PRECISION(typmod);
1114
1115 switch (fields)
1116 {
1117 case INTERVAL_MASK(YEAR):
1118 fieldstr = " year";
1119 break;
1120 case INTERVAL_MASK(MONTH):
1121 fieldstr = " month";
1122 break;
1123 case INTERVAL_MASK(DAY):
1124 fieldstr = " day";
1125 break;
1126 case INTERVAL_MASK(HOUR):
1127 fieldstr = " hour";
1128 break;
1129 case INTERVAL_MASK(MINUTE):
1130 fieldstr = " minute";
1131 break;
1132 case INTERVAL_MASK(SECOND):
1133 fieldstr = " second";
1134 break;
1135 case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
1136 fieldstr = " year to month";
1137 break;
1138 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
1139 fieldstr = " day to hour";
1140 break;
1141 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1142 fieldstr = " day to minute";
1143 break;
1144 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1145 fieldstr = " day to second";
1146 break;
1147 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1148 fieldstr = " hour to minute";
1149 break;
1150 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1151 fieldstr = " hour to second";
1152 break;
1153 case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1154 fieldstr = " minute to second";
1155 break;
1156 case INTERVAL_FULL_RANGE:
1157 fieldstr = "";
1158 break;
1159 default:
1160 elog(ERROR, "invalid INTERVAL typmod: 0x%x", typmod);
1161 fieldstr = "";
1162 break;
1163 }
1164
1165 if (precision != INTERVAL_FULL_PRECISION)
1166 snprintf(res, 64, "%s(%d)", fieldstr, precision);
1167 else
1168 snprintf(res, 64, "%s", fieldstr);
1169
1170 PG_RETURN_CSTRING(res);
1171}
1172
1173/*
1174 * Given an interval typmod value, return a code for the least-significant
1175 * field that the typmod allows to be nonzero, for instance given
1176 * INTERVAL DAY TO HOUR we want to identify "hour".
1177 *
1178 * The results should be ordered by field significance, which means
1179 * we can't use the dt.h macros YEAR etc, because for some odd reason
1180 * they aren't ordered that way. Instead, arbitrarily represent
1181 * SECOND = 0, MINUTE = 1, HOUR = 2, DAY = 3, MONTH = 4, YEAR = 5.
1182 */
1183static int
1184intervaltypmodleastfield(int32 typmod)
1185{
1186 if (typmod < 0)
1187 return 0; /* SECOND */
1188
1189 switch (INTERVAL_RANGE(typmod))
1190 {
1191 case INTERVAL_MASK(YEAR):
1192 return 5; /* YEAR */
1193 case INTERVAL_MASK(MONTH):
1194 return 4; /* MONTH */
1195 case INTERVAL_MASK(DAY):
1196 return 3; /* DAY */
1197 case INTERVAL_MASK(HOUR):
1198 return 2; /* HOUR */
1199 case INTERVAL_MASK(MINUTE):
1200 return 1; /* MINUTE */
1201 case INTERVAL_MASK(SECOND):
1202 return 0; /* SECOND */
1203 case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
1204 return 4; /* MONTH */
1205 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
1206 return 2; /* HOUR */
1207 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1208 return 1; /* MINUTE */
1209 case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1210 return 0; /* SECOND */
1211 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1212 return 1; /* MINUTE */
1213 case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1214 return 0; /* SECOND */
1215 case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1216 return 0; /* SECOND */
1217 case INTERVAL_FULL_RANGE:
1218 return 0; /* SECOND */
1219 default:
1220 elog(ERROR, "invalid INTERVAL typmod: 0x%x", typmod);
1221 break;
1222 }
1223 return 0; /* can't get here, but keep compiler quiet */
1224}
1225
1226
1227/*
1228 * interval_support()
1229 *
1230 * Planner support function for interval_scale().
1231 *
1232 * Flatten superfluous calls to interval_scale(). The interval typmod is
1233 * complex to permit accepting and regurgitating all SQL standard variations.
1234 * For truncation purposes, it boils down to a single, simple granularity.
1235 */
1236Datum
1237interval_support(PG_FUNCTION_ARGS)
1238{
1239 Node *rawreq = (Node *) PG_GETARG_POINTER(0);
1240 Node *ret = NULL;
1241
1242 if (IsA(rawreq, SupportRequestSimplify))
1243 {
1244 SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
1245 FuncExpr *expr = req->fcall;
1246 Node *typmod;
1247
1248 Assert(list_length(expr->args) >= 2);
1249
1250 typmod = (Node *) lsecond(expr->args);
1251
1252 if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull)
1253 {
1254 Node *source = (Node *) linitial(expr->args);
1255 int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
1256 bool noop;
1257
1258 if (new_typmod < 0)
1259 noop = true;
1260 else
1261 {
1262 int32 old_typmod = exprTypmod(source);
1263 int old_least_field;
1264 int new_least_field;
1265 int old_precis;
1266 int new_precis;
1267
1268 old_least_field = intervaltypmodleastfield(old_typmod);
1269 new_least_field = intervaltypmodleastfield(new_typmod);
1270 if (old_typmod < 0)
1271 old_precis = INTERVAL_FULL_PRECISION;
1272 else
1273 old_precis = INTERVAL_PRECISION(old_typmod);
1274 new_precis = INTERVAL_PRECISION(new_typmod);
1275
1276 /*
1277 * Cast is a no-op if least field stays the same or decreases
1278 * while precision stays the same or increases. But
1279 * precision, which is to say, sub-second precision, only
1280 * affects ranges that include SECOND.
1281 */
1282 noop = (new_least_field <= old_least_field) &&
1283 (old_least_field > 0 /* SECOND */ ||
1284 new_precis >= MAX_INTERVAL_PRECISION ||
1285 new_precis >= old_precis);
1286 }
1287 if (noop)
1288 ret = relabel_to_typmod(source, new_typmod);
1289 }
1290 }
1291
1292 PG_RETURN_POINTER(ret);
1293}
1294
1295/* interval_scale()
1296 * Adjust interval type for specified fields.
1297 * Used by PostgreSQL type system to stuff columns.
1298 */
1299Datum
1300interval_scale(PG_FUNCTION_ARGS)
1301{
1302 Interval *interval = PG_GETARG_INTERVAL_P(0);
1303 int32 typmod = PG_GETARG_INT32(1);
1304 Interval *result;
1305
1306 result = palloc(sizeof(Interval));
1307 *result = *interval;
1308
1309 AdjustIntervalForTypmod(result, typmod);
1310
1311 PG_RETURN_INTERVAL_P(result);
1312}
1313
1314/*
1315 * Adjust interval for specified precision, in both YEAR to SECOND
1316 * range and sub-second precision.
1317 */
1318static void
1319AdjustIntervalForTypmod(Interval *interval, int32 typmod)
1320{
1321 static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
1322 INT64CONST(1000000),
1323 INT64CONST(100000),
1324 INT64CONST(10000),
1325 INT64CONST(1000),
1326 INT64CONST(100),
1327 INT64CONST(10),
1328 INT64CONST(1)
1329 };
1330
1331 static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
1332 INT64CONST(500000),
1333 INT64CONST(50000),
1334 INT64CONST(5000),
1335 INT64CONST(500),
1336 INT64CONST(50),
1337 INT64CONST(5),
1338 INT64CONST(0)
1339 };
1340
1341 /*
1342 * Unspecified range and precision? Then not necessary to adjust. Setting
1343 * typmod to -1 is the convention for all data types.
1344 */
1345 if (typmod >= 0)
1346 {
1347 int range = INTERVAL_RANGE(typmod);
1348 int precision = INTERVAL_PRECISION(typmod);
1349
1350 /*
1351 * Our interpretation of intervals with a limited set of fields is
1352 * that fields to the right of the last one specified are zeroed out,
1353 * but those to the left of it remain valid. Thus for example there
1354 * is no operational difference between INTERVAL YEAR TO MONTH and
1355 * INTERVAL MONTH. In some cases we could meaningfully enforce that
1356 * higher-order fields are zero; for example INTERVAL DAY could reject
1357 * nonzero "month" field. However that seems a bit pointless when we
1358 * can't do it consistently. (We cannot enforce a range limit on the
1359 * highest expected field, since we do not have any equivalent of
1360 * SQL's <interval leading field precision>.) If we ever decide to
1361 * revisit this, interval_support will likely require adjusting.
1362 *
1363 * Note: before PG 8.4 we interpreted a limited set of fields as
1364 * actually causing a "modulo" operation on a given value, potentially
1365 * losing high-order as well as low-order information. But there is
1366 * no support for such behavior in the standard, and it seems fairly
1367 * undesirable on data consistency grounds anyway. Now we only
1368 * perform truncation or rounding of low-order fields.
1369 */
1370 if (range == INTERVAL_FULL_RANGE)
1371 {
1372 /* Do nothing... */
1373 }
1374 else if (range == INTERVAL_MASK(YEAR))
1375 {
1376 interval->month = (interval->month / MONTHS_PER_YEAR) * MONTHS_PER_YEAR;
1377 interval->day = 0;
1378 interval->time = 0;
1379 }
1380 else if (range == INTERVAL_MASK(MONTH))
1381 {
1382 interval->day = 0;
1383 interval->time = 0;
1384 }
1385 /* YEAR TO MONTH */
1386 else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
1387 {
1388 interval->day = 0;
1389 interval->time = 0;
1390 }
1391 else if (range == INTERVAL_MASK(DAY))
1392 {
1393 interval->time = 0;
1394 }
1395 else if (range == INTERVAL_MASK(HOUR))
1396 {
1397 interval->time = (interval->time / USECS_PER_HOUR) *
1398 USECS_PER_HOUR;
1399 }
1400 else if (range == INTERVAL_MASK(MINUTE))
1401 {
1402 interval->time = (interval->time / USECS_PER_MINUTE) *
1403 USECS_PER_MINUTE;
1404 }
1405 else if (range == INTERVAL_MASK(SECOND))
1406 {
1407 /* fractional-second rounding will be dealt with below */
1408 }
1409 /* DAY TO HOUR */
1410 else if (range == (INTERVAL_MASK(DAY) |
1411 INTERVAL_MASK(HOUR)))
1412 {
1413 interval->time = (interval->time / USECS_PER_HOUR) *
1414 USECS_PER_HOUR;
1415 }
1416 /* DAY TO MINUTE */
1417 else if (range == (INTERVAL_MASK(DAY) |
1418 INTERVAL_MASK(HOUR) |
1419 INTERVAL_MASK(MINUTE)))
1420 {
1421 interval->time = (interval->time / USECS_PER_MINUTE) *
1422 USECS_PER_MINUTE;
1423 }
1424 /* DAY TO SECOND */
1425 else if (range == (INTERVAL_MASK(DAY) |
1426 INTERVAL_MASK(HOUR) |
1427 INTERVAL_MASK(MINUTE) |
1428 INTERVAL_MASK(SECOND)))
1429 {
1430 /* fractional-second rounding will be dealt with below */
1431 }
1432 /* HOUR TO MINUTE */
1433 else if (range == (INTERVAL_MASK(HOUR) |
1434 INTERVAL_MASK(MINUTE)))
1435 {
1436 interval->time = (interval->time / USECS_PER_MINUTE) *
1437 USECS_PER_MINUTE;
1438 }
1439 /* HOUR TO SECOND */
1440 else if (range == (INTERVAL_MASK(HOUR) |
1441 INTERVAL_MASK(MINUTE) |
1442 INTERVAL_MASK(SECOND)))
1443 {
1444 /* fractional-second rounding will be dealt with below */
1445 }
1446 /* MINUTE TO SECOND */
1447 else if (range == (INTERVAL_MASK(MINUTE) |
1448 INTERVAL_MASK(SECOND)))
1449 {
1450 /* fractional-second rounding will be dealt with below */
1451 }
1452 else
1453 elog(ERROR, "unrecognized interval typmod: %d", typmod);
1454
1455 /* Need to adjust sub-second precision? */
1456 if (precision != INTERVAL_FULL_PRECISION)
1457 {
1458 if (precision < 0 || precision > MAX_INTERVAL_PRECISION)
1459 ereport(ERROR,
1460 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1461 errmsg("interval(%d) precision must be between %d and %d",
1462 precision, 0, MAX_INTERVAL_PRECISION)));
1463
1464 if (interval->time >= INT64CONST(0))
1465 {
1466 interval->time = ((interval->time +
1467 IntervalOffsets[precision]) /
1468 IntervalScales[precision]) *
1469 IntervalScales[precision];
1470 }
1471 else
1472 {
1473 interval->time = -(((-interval->time +
1474 IntervalOffsets[precision]) /
1475 IntervalScales[precision]) *
1476 IntervalScales[precision]);
1477 }
1478 }
1479 }
1480}
1481
1482/*
1483 * make_interval - numeric Interval constructor
1484 */
1485Datum
1486make_interval(PG_FUNCTION_ARGS)
1487{
1488 int32 years = PG_GETARG_INT32(0);
1489 int32 months = PG_GETARG_INT32(1);
1490 int32 weeks = PG_GETARG_INT32(2);
1491 int32 days = PG_GETARG_INT32(3);
1492 int32 hours = PG_GETARG_INT32(4);
1493 int32 mins = PG_GETARG_INT32(5);
1494 double secs = PG_GETARG_FLOAT8(6);
1495 Interval *result;
1496
1497 /*
1498 * Reject out-of-range inputs. We really ought to check the integer
1499 * inputs as well, but it's not entirely clear what limits to apply.
1500 */
1501 if (isinf(secs) || isnan(secs))
1502 ereport(ERROR,
1503 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1504 errmsg("interval out of range")));
1505
1506 result = (Interval *) palloc(sizeof(Interval));
1507 result->month = years * MONTHS_PER_YEAR + months;
1508 result->day = weeks * 7 + days;
1509
1510 secs = rint(secs * USECS_PER_SEC);
1511 result->time = hours * ((int64) SECS_PER_HOUR * USECS_PER_SEC) +
1512 mins * ((int64) SECS_PER_MINUTE * USECS_PER_SEC) +
1513 (int64) secs;
1514
1515 PG_RETURN_INTERVAL_P(result);
1516}
1517
1518/* EncodeSpecialTimestamp()
1519 * Convert reserved timestamp data type to string.
1520 */
1521void
1522EncodeSpecialTimestamp(Timestamp dt, char *str)
1523{
1524 if (TIMESTAMP_IS_NOBEGIN(dt))
1525 strcpy(str, EARLY);
1526 else if (TIMESTAMP_IS_NOEND(dt))
1527 strcpy(str, LATE);
1528 else /* shouldn't happen */
1529 elog(ERROR, "invalid argument for EncodeSpecialTimestamp");
1530}
1531
1532Datum
1533now(PG_FUNCTION_ARGS)
1534{
1535 PG_RETURN_TIMESTAMPTZ(GetCurrentTransactionStartTimestamp());
1536}
1537
1538Datum
1539statement_timestamp(PG_FUNCTION_ARGS)
1540{
1541 PG_RETURN_TIMESTAMPTZ(GetCurrentStatementStartTimestamp());
1542}
1543
1544Datum
1545clock_timestamp(PG_FUNCTION_ARGS)
1546{
1547 PG_RETURN_TIMESTAMPTZ(GetCurrentTimestamp());
1548}
1549
1550Datum
1551pg_postmaster_start_time(PG_FUNCTION_ARGS)
1552{
1553 PG_RETURN_TIMESTAMPTZ(PgStartTime);
1554}
1555
1556Datum
1557pg_conf_load_time(PG_FUNCTION_ARGS)
1558{
1559 PG_RETURN_TIMESTAMPTZ(PgReloadTime);
1560}
1561
1562/*
1563 * GetCurrentTimestamp -- get the current operating system time
1564 *
1565 * Result is in the form of a TimestampTz value, and is expressed to the
1566 * full precision of the gettimeofday() syscall
1567 */
1568TimestampTz
1569GetCurrentTimestamp(void)
1570{
1571 TimestampTz result;
1572 struct timeval tp;
1573
1574 gettimeofday(&tp, NULL);
1575
1576 result = (TimestampTz) tp.tv_sec -
1577 ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
1578 result = (result * USECS_PER_SEC) + tp.tv_usec;
1579
1580 return result;
1581}
1582
1583/*
1584 * GetSQLCurrentTimestamp -- implements CURRENT_TIMESTAMP, CURRENT_TIMESTAMP(n)
1585 */
1586TimestampTz
1587GetSQLCurrentTimestamp(int32 typmod)
1588{
1589 TimestampTz ts;
1590
1591 ts = GetCurrentTransactionStartTimestamp();
1592 if (typmod >= 0)
1593 AdjustTimestampForTypmod(&ts, typmod);
1594 return ts;
1595}
1596
1597/*
1598 * GetSQLLocalTimestamp -- implements LOCALTIMESTAMP, LOCALTIMESTAMP(n)
1599 */
1600Timestamp
1601GetSQLLocalTimestamp(int32 typmod)
1602{
1603 Timestamp ts;
1604
1605 ts = timestamptz2timestamp(GetCurrentTransactionStartTimestamp());
1606 if (typmod >= 0)
1607 AdjustTimestampForTypmod(&ts, typmod);
1608 return ts;
1609}
1610
1611/*
1612 * timeofday(*) -- returns the current time as a text.
1613 */
1614Datum
1615timeofday(PG_FUNCTION_ARGS)
1616{
1617 struct timeval tp;
1618 char templ[128];
1619 char buf[128];
1620 pg_time_t tt;
1621
1622 gettimeofday(&tp, NULL);
1623 tt = (pg_time_t) tp.tv_sec;
1624 pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z",
1625 pg_localtime(&tt, session_timezone));
1626 snprintf(buf, sizeof(buf), templ, tp.tv_usec);
1627
1628 PG_RETURN_TEXT_P(cstring_to_text(buf));
1629}
1630
1631/*
1632 * TimestampDifference -- convert the difference between two timestamps
1633 * into integer seconds and microseconds
1634 *
1635 * Both inputs must be ordinary finite timestamps (in current usage,
1636 * they'll be results from GetCurrentTimestamp()).
1637 *
1638 * We expect start_time <= stop_time. If not, we return zeros; for current
1639 * callers there is no need to be tense about which way division rounds on
1640 * negative inputs.
1641 */
1642void
1643TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
1644 long *secs, int *microsecs)
1645{
1646 TimestampTz diff = stop_time - start_time;
1647
1648 if (diff <= 0)
1649 {
1650 *secs = 0;
1651 *microsecs = 0;
1652 }
1653 else
1654 {
1655 *secs = (long) (diff / USECS_PER_SEC);
1656 *microsecs = (int) (diff % USECS_PER_SEC);
1657 }
1658}
1659
1660/*
1661 * TimestampDifferenceExceeds -- report whether the difference between two
1662 * timestamps is >= a threshold (expressed in milliseconds)
1663 *
1664 * Both inputs must be ordinary finite timestamps (in current usage,
1665 * they'll be results from GetCurrentTimestamp()).
1666 */
1667bool
1668TimestampDifferenceExceeds(TimestampTz start_time,
1669 TimestampTz stop_time,
1670 int msec)
1671{
1672 TimestampTz diff = stop_time - start_time;
1673
1674 return (diff >= msec * INT64CONST(1000));
1675}
1676
1677/*
1678 * Convert a time_t to TimestampTz.
1679 *
1680 * We do not use time_t internally in Postgres, but this is provided for use
1681 * by functions that need to interpret, say, a stat(2) result.
1682 *
1683 * To avoid having the function's ABI vary depending on the width of time_t,
1684 * we declare the argument as pg_time_t, which is cast-compatible with
1685 * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1686 * This detail should be invisible to callers, at least at source code level.
1687 */
1688TimestampTz
1689time_t_to_timestamptz(pg_time_t tm)
1690{
1691 TimestampTz result;
1692
1693 result = (TimestampTz) tm -
1694 ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
1695 result *= USECS_PER_SEC;
1696
1697 return result;
1698}
1699
1700/*
1701 * Convert a TimestampTz to time_t.
1702 *
1703 * This too is just marginally useful, but some places need it.
1704 *
1705 * To avoid having the function's ABI vary depending on the width of time_t,
1706 * we declare the result as pg_time_t, which is cast-compatible with
1707 * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1708 * This detail should be invisible to callers, at least at source code level.
1709 */
1710pg_time_t
1711timestamptz_to_time_t(TimestampTz t)
1712{
1713 pg_time_t result;
1714
1715 result = (pg_time_t) (t / USECS_PER_SEC +
1716 ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY));
1717
1718 return result;
1719}
1720
1721/*
1722 * Produce a C-string representation of a TimestampTz.
1723 *
1724 * This is mostly for use in emitting messages. The primary difference
1725 * from timestamptz_out is that we force the output format to ISO. Note
1726 * also that the result is in a static buffer, not pstrdup'd.
1727 */
1728const char *
1729timestamptz_to_str(TimestampTz t)
1730{
1731 static char buf[MAXDATELEN + 1];
1732 int tz;
1733 struct pg_tm tt,
1734 *tm = &tt;
1735 fsec_t fsec;
1736 const char *tzn;
1737
1738 if (TIMESTAMP_NOT_FINITE(t))
1739 EncodeSpecialTimestamp(t, buf);
1740 else if (timestamp2tm(t, &tz, tm, &fsec, &tzn, NULL) == 0)
1741 EncodeDateTime(tm, fsec, true, tz, tzn, USE_ISO_DATES, buf);
1742 else
1743 strlcpy(buf, "(timestamp out of range)", sizeof(buf));
1744
1745 return buf;
1746}
1747
1748
1749void
1750dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
1751{
1752 TimeOffset time;
1753
1754 time = jd;
1755
1756 *hour = time / USECS_PER_HOUR;
1757 time -= (*hour) * USECS_PER_HOUR;
1758 *min = time / USECS_PER_MINUTE;
1759 time -= (*min) * USECS_PER_MINUTE;
1760 *sec = time / USECS_PER_SEC;
1761 *fsec = time - (*sec * USECS_PER_SEC);
1762} /* dt2time() */
1763
1764
1765/*
1766 * timestamp2tm() - Convert timestamp data type to POSIX time structure.
1767 *
1768 * Note that year is _not_ 1900-based, but is an explicit full value.
1769 * Also, month is one-based, _not_ zero-based.
1770 * Returns:
1771 * 0 on success
1772 * -1 on out of range
1773 *
1774 * If attimezone is NULL, the global timezone setting will be used.
1775 */
1776int
1777timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, const char **tzn, pg_tz *attimezone)
1778{
1779 Timestamp date;
1780 Timestamp time;
1781 pg_time_t utime;
1782
1783 /* Use session timezone if caller asks for default */
1784 if (attimezone == NULL)
1785 attimezone = session_timezone;
1786
1787 time = dt;
1788 TMODULO(time, date, USECS_PER_DAY);
1789
1790 if (time < INT64CONST(0))
1791 {
1792 time += USECS_PER_DAY;
1793 date -= 1;
1794 }
1795
1796 /* add offset to go from J2000 back to standard Julian date */
1797 date += POSTGRES_EPOCH_JDATE;
1798
1799 /* Julian day routine does not work for negative Julian days */
1800 if (date < 0 || date > (Timestamp) INT_MAX)
1801 return -1;
1802
1803 j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1804 dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1805
1806 /* Done if no TZ conversion wanted */
1807 if (tzp == NULL)
1808 {
1809 tm->tm_isdst = -1;
1810 tm->tm_gmtoff = 0;
1811 tm->tm_zone = NULL;
1812 if (tzn != NULL)
1813 *tzn = NULL;
1814 return 0;
1815 }
1816
1817 /*
1818 * If the time falls within the range of pg_time_t, use pg_localtime() to
1819 * rotate to the local time zone.
1820 *
1821 * First, convert to an integral timestamp, avoiding possibly
1822 * platform-specific roundoff-in-wrong-direction errors, and adjust to
1823 * Unix epoch. Then see if we can convert to pg_time_t without loss. This
1824 * coding avoids hardwiring any assumptions about the width of pg_time_t,
1825 * so it should behave sanely on machines without int64.
1826 */
1827 dt = (dt - *fsec) / USECS_PER_SEC +
1828 (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
1829 utime = (pg_time_t) dt;
1830 if ((Timestamp) utime == dt)
1831 {
1832 struct pg_tm *tx = pg_localtime(&utime, attimezone);
1833
1834 tm->tm_year = tx->tm_year + 1900;
1835 tm->tm_mon = tx->tm_mon + 1;
1836 tm->tm_mday = tx->tm_mday;
1837 tm->tm_hour = tx->tm_hour;
1838 tm->tm_min = tx->tm_min;
1839 tm->tm_sec = tx->tm_sec;
1840 tm->tm_isdst = tx->tm_isdst;
1841 tm->tm_gmtoff = tx->tm_gmtoff;
1842 tm->tm_zone = tx->tm_zone;
1843 *tzp = -tm->tm_gmtoff;
1844 if (tzn != NULL)
1845 *tzn = tm->tm_zone;
1846 }
1847 else
1848 {
1849 /*
1850 * When out of range of pg_time_t, treat as GMT
1851 */
1852 *tzp = 0;
1853 /* Mark this as *no* time zone available */
1854 tm->tm_isdst = -1;
1855 tm->tm_gmtoff = 0;
1856 tm->tm_zone = NULL;
1857 if (tzn != NULL)
1858 *tzn = NULL;
1859 }
1860
1861 return 0;
1862}
1863
1864
1865/* tm2timestamp()
1866 * Convert a tm structure to a timestamp data type.
1867 * Note that year is _not_ 1900-based, but is an explicit full value.
1868 * Also, month is one-based, _not_ zero-based.
1869 *
1870 * Returns -1 on failure (value out of range).
1871 */
1872int
1873tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp *result)
1874{
1875 TimeOffset date;
1876 TimeOffset time;
1877
1878 /* Prevent overflow in Julian-day routines */
1879 if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
1880 {
1881 *result = 0; /* keep compiler quiet */
1882 return -1;
1883 }
1884
1885 date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1886 time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
1887
1888 *result = date * USECS_PER_DAY + time;
1889 /* check for major overflow */
1890 if ((*result - time) / USECS_PER_DAY != date)
1891 {
1892 *result = 0; /* keep compiler quiet */
1893 return -1;
1894 }
1895 /* check for just-barely overflow (okay except time-of-day wraps) */
1896 /* caution: we want to allow 1999-12-31 24:00:00 */
1897 if ((*result < 0 && date > 0) ||
1898 (*result > 0 && date < -1))
1899 {
1900 *result = 0; /* keep compiler quiet */
1901 return -1;
1902 }
1903 if (tzp != NULL)
1904 *result = dt2local(*result, -(*tzp));
1905
1906 /* final range check catches just-out-of-range timestamps */
1907 if (!IS_VALID_TIMESTAMP(*result))
1908 {
1909 *result = 0; /* keep compiler quiet */
1910 return -1;
1911 }
1912
1913 return 0;
1914}
1915
1916
1917/* interval2tm()
1918 * Convert an interval data type to a tm structure.
1919 */
1920int
1921interval2tm(Interval span, struct pg_tm *tm, fsec_t *fsec)
1922{
1923 TimeOffset time;
1924 TimeOffset tfrac;
1925
1926 tm->tm_year = span.month / MONTHS_PER_YEAR;
1927 tm->tm_mon = span.month % MONTHS_PER_YEAR;
1928 tm->tm_mday = span.day;
1929 time = span.time;
1930
1931 tfrac = time / USECS_PER_HOUR;
1932 time -= tfrac * USECS_PER_HOUR;
1933 tm->tm_hour = tfrac;
1934 if (!SAMESIGN(tm->tm_hour, tfrac))
1935 ereport(ERROR,
1936 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1937 errmsg("interval out of range")));
1938 tfrac = time / USECS_PER_MINUTE;
1939 time -= tfrac * USECS_PER_MINUTE;
1940 tm->tm_min = tfrac;
1941 tfrac = time / USECS_PER_SEC;
1942 *fsec = time - (tfrac * USECS_PER_SEC);
1943 tm->tm_sec = tfrac;
1944
1945 return 0;
1946}
1947
1948int
1949tm2interval(struct pg_tm *tm, fsec_t fsec, Interval *span)
1950{
1951 double total_months = (double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
1952
1953 if (total_months > INT_MAX || total_months < INT_MIN)
1954 return -1;
1955 span->month = total_months;
1956 span->day = tm->tm_mday;
1957 span->time = (((((tm->tm_hour * INT64CONST(60)) +
1958 tm->tm_min) * INT64CONST(60)) +
1959 tm->tm_sec) * USECS_PER_SEC) + fsec;
1960
1961 return 0;
1962}
1963
1964static TimeOffset
1965time2t(const int hour, const int min, const int sec, const fsec_t fsec)
1966{
1967 return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
1968}
1969
1970static Timestamp
1971dt2local(Timestamp dt, int tz)
1972{
1973 dt -= (tz * USECS_PER_SEC);
1974 return dt;
1975}
1976
1977
1978/*****************************************************************************
1979 * PUBLIC ROUTINES *
1980 *****************************************************************************/
1981
1982
1983Datum
1984timestamp_finite(PG_FUNCTION_ARGS)
1985{
1986 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1987
1988 PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp));
1989}
1990
1991Datum
1992interval_finite(PG_FUNCTION_ARGS)
1993{
1994 PG_RETURN_BOOL(true);
1995}
1996
1997
1998/*----------------------------------------------------------
1999 * Relational operators for timestamp.
2000 *---------------------------------------------------------*/
2001
2002void
2003GetEpochTime(struct pg_tm *tm)
2004{
2005 struct pg_tm *t0;
2006 pg_time_t epoch = 0;
2007
2008 t0 = pg_gmtime(&epoch);
2009
2010 if (t0 == NULL)
2011 elog(ERROR, "could not convert epoch to timestamp: %m");
2012
2013 tm->tm_year = t0->tm_year;
2014 tm->tm_mon = t0->tm_mon;
2015 tm->tm_mday = t0->tm_mday;
2016 tm->tm_hour = t0->tm_hour;
2017 tm->tm_min = t0->tm_min;
2018 tm->tm_sec = t0->tm_sec;
2019
2020 tm->tm_year += 1900;
2021 tm->tm_mon++;
2022}
2023
2024Timestamp
2025SetEpochTimestamp(void)
2026{
2027 Timestamp dt;
2028 struct pg_tm tt,
2029 *tm = &tt;
2030
2031 GetEpochTime(tm);
2032 /* we don't bother to test for failure ... */
2033 tm2timestamp(tm, 0, NULL, &dt);
2034
2035 return dt;
2036} /* SetEpochTimestamp() */
2037
2038/*
2039 * We are currently sharing some code between timestamp and timestamptz.
2040 * The comparison functions are among them. - thomas 2001-09-25
2041 *
2042 * timestamp_relop - is timestamp1 relop timestamp2
2043 */
2044int
2045timestamp_cmp_internal(Timestamp dt1, Timestamp dt2)
2046{
2047 return (dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0);
2048}
2049
2050Datum
2051timestamp_eq(PG_FUNCTION_ARGS)
2052{
2053 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2054 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2055
2056 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
2057}
2058
2059Datum
2060timestamp_ne(PG_FUNCTION_ARGS)
2061{
2062 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2063 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2064
2065 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
2066}
2067
2068Datum
2069timestamp_lt(PG_FUNCTION_ARGS)
2070{
2071 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2072 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2073
2074 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
2075}
2076
2077Datum
2078timestamp_gt(PG_FUNCTION_ARGS)
2079{
2080 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2081 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2082
2083 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
2084}
2085
2086Datum
2087timestamp_le(PG_FUNCTION_ARGS)
2088{
2089 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2090 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2091
2092 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
2093}
2094
2095Datum
2096timestamp_ge(PG_FUNCTION_ARGS)
2097{
2098 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2099 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2100
2101 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
2102}
2103
2104Datum
2105timestamp_cmp(PG_FUNCTION_ARGS)
2106{
2107 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2108 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2109
2110 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
2111}
2112
2113/* note: this is used for timestamptz also */
2114static int
2115timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
2116{
2117 Timestamp a = DatumGetTimestamp(x);
2118 Timestamp b = DatumGetTimestamp(y);
2119
2120 return timestamp_cmp_internal(a, b);
2121}
2122
2123Datum
2124timestamp_sortsupport(PG_FUNCTION_ARGS)
2125{
2126 SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
2127
2128 ssup->comparator = timestamp_fastcmp;
2129 PG_RETURN_VOID();
2130}
2131
2132Datum
2133timestamp_hash(PG_FUNCTION_ARGS)
2134{
2135 return hashint8(fcinfo);
2136}
2137
2138Datum
2139timestamp_hash_extended(PG_FUNCTION_ARGS)
2140{
2141 return hashint8extended(fcinfo);
2142}
2143
2144/*
2145 * Cross-type comparison functions for timestamp vs timestamptz
2146 */
2147
2148Datum
2149timestamp_eq_timestamptz(PG_FUNCTION_ARGS)
2150{
2151 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
2152 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2153 TimestampTz dt1;
2154
2155 dt1 = timestamp2timestamptz(timestampVal);
2156
2157 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
2158}
2159
2160Datum
2161timestamp_ne_timestamptz(PG_FUNCTION_ARGS)
2162{
2163 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
2164 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2165 TimestampTz dt1;
2166
2167 dt1 = timestamp2timestamptz(timestampVal);
2168
2169 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
2170}
2171
2172Datum
2173timestamp_lt_timestamptz(PG_FUNCTION_ARGS)
2174{
2175 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
2176 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2177 TimestampTz dt1;
2178
2179 dt1 = timestamp2timestamptz(timestampVal);
2180
2181 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
2182}
2183
2184Datum
2185timestamp_gt_timestamptz(PG_FUNCTION_ARGS)
2186{
2187 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
2188 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2189 TimestampTz dt1;
2190
2191 dt1 = timestamp2timestamptz(timestampVal);
2192
2193 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
2194}
2195
2196Datum
2197timestamp_le_timestamptz(PG_FUNCTION_ARGS)
2198{
2199 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
2200 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2201 TimestampTz dt1;
2202
2203 dt1 = timestamp2timestamptz(timestampVal);
2204
2205 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
2206}
2207
2208Datum
2209timestamp_ge_timestamptz(PG_FUNCTION_ARGS)
2210{
2211 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
2212 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2213 TimestampTz dt1;
2214
2215 dt1 = timestamp2timestamptz(timestampVal);
2216
2217 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
2218}
2219
2220Datum
2221timestamp_cmp_timestamptz(PG_FUNCTION_ARGS)
2222{
2223 Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
2224 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2225 TimestampTz dt1;
2226
2227 dt1 = timestamp2timestamptz(timestampVal);
2228
2229 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
2230}
2231
2232Datum
2233timestamptz_eq_timestamp(PG_FUNCTION_ARGS)
2234{
2235 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
2236 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2237 TimestampTz dt2;
2238
2239 dt2 = timestamp2timestamptz(timestampVal);
2240
2241 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
2242}
2243
2244Datum
2245timestamptz_ne_timestamp(PG_FUNCTION_ARGS)
2246{
2247 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
2248 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2249 TimestampTz dt2;
2250
2251 dt2 = timestamp2timestamptz(timestampVal);
2252
2253 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
2254}
2255
2256Datum
2257timestamptz_lt_timestamp(PG_FUNCTION_ARGS)
2258{
2259 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
2260 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2261 TimestampTz dt2;
2262
2263 dt2 = timestamp2timestamptz(timestampVal);
2264
2265 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
2266}
2267
2268Datum
2269timestamptz_gt_timestamp(PG_FUNCTION_ARGS)
2270{
2271 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
2272 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2273 TimestampTz dt2;
2274
2275 dt2 = timestamp2timestamptz(timestampVal);
2276
2277 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
2278}
2279
2280Datum
2281timestamptz_le_timestamp(PG_FUNCTION_ARGS)
2282{
2283 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
2284 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2285 TimestampTz dt2;
2286
2287 dt2 = timestamp2timestamptz(timestampVal);
2288
2289 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
2290}
2291
2292Datum
2293timestamptz_ge_timestamp(PG_FUNCTION_ARGS)
2294{
2295 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
2296 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2297 TimestampTz dt2;
2298
2299 dt2 = timestamp2timestamptz(timestampVal);
2300
2301 PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
2302}
2303
2304Datum
2305timestamptz_cmp_timestamp(PG_FUNCTION_ARGS)
2306{
2307 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
2308 Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2309 TimestampTz dt2;
2310
2311 dt2 = timestamp2timestamptz(timestampVal);
2312
2313 PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
2314}
2315
2316
2317/*
2318 * interval_relop - is interval1 relop interval2
2319 *
2320 * Interval comparison is based on converting interval values to a linear
2321 * representation expressed in the units of the time field (microseconds,
2322 * in the case of integer timestamps) with days assumed to be always 24 hours
2323 * and months assumed to be always 30 days. To avoid overflow, we need a
2324 * wider-than-int64 datatype for the linear representation, so use INT128.
2325 */
2326
2327static inline INT128
2328interval_cmp_value(const Interval *interval)
2329{
2330 INT128 span;
2331 int64 dayfraction;
2332 int64 days;
2333
2334 /*
2335 * Separate time field into days and dayfraction, then add the month and
2336 * day fields to the days part. We cannot overflow int64 days here.
2337 */
2338 dayfraction = interval->time % USECS_PER_DAY;
2339 days = interval->time / USECS_PER_DAY;
2340 days += interval->month * INT64CONST(30);
2341 days += interval->day;
2342
2343 /* Widen dayfraction to 128 bits */
2344 span = int64_to_int128(dayfraction);
2345
2346 /* Scale up days to microseconds, forming a 128-bit product */
2347 int128_add_int64_mul_int64(&span, days, USECS_PER_DAY);
2348
2349 return span;
2350}
2351
2352static int
2353interval_cmp_internal(Interval *interval1, Interval *interval2)
2354{
2355 INT128 span1 = interval_cmp_value(interval1);
2356 INT128 span2 = interval_cmp_value(interval2);
2357
2358 return int128_compare(span1, span2);
2359}
2360
2361Datum
2362interval_eq(PG_FUNCTION_ARGS)
2363{
2364 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2365 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2366
2367 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0);
2368}
2369
2370Datum
2371interval_ne(PG_FUNCTION_ARGS)
2372{
2373 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2374 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2375
2376 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0);
2377}
2378
2379Datum
2380interval_lt(PG_FUNCTION_ARGS)
2381{
2382 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2383 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2384
2385 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0);
2386}
2387
2388Datum
2389interval_gt(PG_FUNCTION_ARGS)
2390{
2391 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2392 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2393
2394 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0);
2395}
2396
2397Datum
2398interval_le(PG_FUNCTION_ARGS)
2399{
2400 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2401 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2402
2403 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0);
2404}
2405
2406Datum
2407interval_ge(PG_FUNCTION_ARGS)
2408{
2409 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2410 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2411
2412 PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0);
2413}
2414
2415Datum
2416interval_cmp(PG_FUNCTION_ARGS)
2417{
2418 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2419 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2420
2421 PG_RETURN_INT32(interval_cmp_internal(interval1, interval2));
2422}
2423
2424/*
2425 * Hashing for intervals
2426 *
2427 * We must produce equal hashvals for values that interval_cmp_internal()
2428 * considers equal. So, compute the net span the same way it does,
2429 * and then hash that.
2430 */
2431Datum
2432interval_hash(PG_FUNCTION_ARGS)
2433{
2434 Interval *interval = PG_GETARG_INTERVAL_P(0);
2435 INT128 span = interval_cmp_value(interval);
2436 int64 span64;
2437
2438 /*
2439 * Use only the least significant 64 bits for hashing. The upper 64 bits
2440 * seldom add any useful information, and besides we must do it like this
2441 * for compatibility with hashes calculated before use of INT128 was
2442 * introduced.
2443 */
2444 span64 = int128_to_int64(span);
2445
2446 return DirectFunctionCall1(hashint8, Int64GetDatumFast(span64));
2447}
2448
2449Datum
2450interval_hash_extended(PG_FUNCTION_ARGS)
2451{
2452 Interval *interval = PG_GETARG_INTERVAL_P(0);
2453 INT128 span = interval_cmp_value(interval);
2454 int64 span64;
2455
2456 /* Same approach as interval_hash */
2457 span64 = int128_to_int64(span);
2458
2459 return DirectFunctionCall2(hashint8extended, Int64GetDatumFast(span64),
2460 PG_GETARG_DATUM(1));
2461}
2462
2463/* overlaps_timestamp() --- implements the SQL OVERLAPS operator.
2464 *
2465 * Algorithm is per SQL spec. This is much harder than you'd think
2466 * because the spec requires us to deliver a non-null answer in some cases
2467 * where some of the inputs are null.
2468 */
2469Datum
2470overlaps_timestamp(PG_FUNCTION_ARGS)
2471{
2472 /*
2473 * The arguments are Timestamps, but we leave them as generic Datums to
2474 * avoid unnecessary conversions between value and reference forms --- not
2475 * to mention possible dereferences of null pointers.
2476 */
2477 Datum ts1 = PG_GETARG_DATUM(0);
2478 Datum te1 = PG_GETARG_DATUM(1);
2479 Datum ts2 = PG_GETARG_DATUM(2);
2480 Datum te2 = PG_GETARG_DATUM(3);
2481 bool ts1IsNull = PG_ARGISNULL(0);
2482 bool te1IsNull = PG_ARGISNULL(1);
2483 bool ts2IsNull = PG_ARGISNULL(2);
2484 bool te2IsNull = PG_ARGISNULL(3);
2485
2486#define TIMESTAMP_GT(t1,t2) \
2487 DatumGetBool(DirectFunctionCall2(timestamp_gt,t1,t2))
2488#define TIMESTAMP_LT(t1,t2) \
2489 DatumGetBool(DirectFunctionCall2(timestamp_lt,t1,t2))
2490
2491 /*
2492 * If both endpoints of interval 1 are null, the result is null (unknown).
2493 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2494 * take ts1 as the lesser endpoint.
2495 */
2496 if (ts1IsNull)
2497 {
2498 if (te1IsNull)
2499 PG_RETURN_NULL();
2500 /* swap null for non-null */
2501 ts1 = te1;
2502 te1IsNull = true;
2503 }
2504 else if (!te1IsNull)
2505 {
2506 if (TIMESTAMP_GT(ts1, te1))
2507 {
2508 Datum tt = ts1;
2509
2510 ts1 = te1;
2511 te1 = tt;
2512 }
2513 }
2514
2515 /* Likewise for interval 2. */
2516 if (ts2IsNull)
2517 {
2518 if (te2IsNull)
2519 PG_RETURN_NULL();
2520 /* swap null for non-null */
2521 ts2 = te2;
2522 te2IsNull = true;
2523 }
2524 else if (!te2IsNull)
2525 {
2526 if (TIMESTAMP_GT(ts2, te2))
2527 {
2528 Datum tt = ts2;
2529
2530 ts2 = te2;
2531 te2 = tt;
2532 }
2533 }
2534
2535 /*
2536 * At this point neither ts1 nor ts2 is null, so we can consider three
2537 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2538 */
2539 if (TIMESTAMP_GT(ts1, ts2))
2540 {
2541 /*
2542 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2543 * in the presence of nulls it's not quite completely so.
2544 */
2545 if (te2IsNull)
2546 PG_RETURN_NULL();
2547 if (TIMESTAMP_LT(ts1, te2))
2548 PG_RETURN_BOOL(true);
2549 if (te1IsNull)
2550 PG_RETURN_NULL();
2551
2552 /*
2553 * If te1 is not null then we had ts1 <= te1 above, and we just found
2554 * ts1 >= te2, hence te1 >= te2.
2555 */
2556 PG_RETURN_BOOL(false);
2557 }
2558 else if (TIMESTAMP_LT(ts1, ts2))
2559 {
2560 /* This case is ts2 < te1 OR te2 < te1 */
2561 if (te1IsNull)
2562 PG_RETURN_NULL();
2563 if (TIMESTAMP_LT(ts2, te1))
2564 PG_RETURN_BOOL(true);
2565 if (te2IsNull)
2566 PG_RETURN_NULL();
2567
2568 /*
2569 * If te2 is not null then we had ts2 <= te2 above, and we just found
2570 * ts2 >= te1, hence te2 >= te1.
2571 */
2572 PG_RETURN_BOOL(false);
2573 }
2574 else
2575 {
2576 /*
2577 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2578 * rather silly way of saying "true if both are non-null, else null".
2579 */
2580 if (te1IsNull || te2IsNull)
2581 PG_RETURN_NULL();
2582 PG_RETURN_BOOL(true);
2583 }
2584
2585#undef TIMESTAMP_GT
2586#undef TIMESTAMP_LT
2587}
2588
2589
2590/*----------------------------------------------------------
2591 * "Arithmetic" operators on date/times.
2592 *---------------------------------------------------------*/
2593
2594Datum
2595timestamp_smaller(PG_FUNCTION_ARGS)
2596{
2597 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2598 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2599 Timestamp result;
2600
2601 /* use timestamp_cmp_internal to be sure this agrees with comparisons */
2602 if (timestamp_cmp_internal(dt1, dt2) < 0)
2603 result = dt1;
2604 else
2605 result = dt2;
2606 PG_RETURN_TIMESTAMP(result);
2607}
2608
2609Datum
2610timestamp_larger(PG_FUNCTION_ARGS)
2611{
2612 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2613 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2614 Timestamp result;
2615
2616 if (timestamp_cmp_internal(dt1, dt2) > 0)
2617 result = dt1;
2618 else
2619 result = dt2;
2620 PG_RETURN_TIMESTAMP(result);
2621}
2622
2623
2624Datum
2625timestamp_mi(PG_FUNCTION_ARGS)
2626{
2627 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2628 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2629 Interval *result;
2630
2631 result = (Interval *) palloc(sizeof(Interval));
2632
2633 if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
2634 ereport(ERROR,
2635 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2636 errmsg("cannot subtract infinite timestamps")));
2637
2638 result->time = dt1 - dt2;
2639
2640 result->month = 0;
2641 result->day = 0;
2642
2643 /*----------
2644 * This is wrong, but removing it breaks a lot of regression tests.
2645 * For example:
2646 *
2647 * test=> SET timezone = 'EST5EDT';
2648 * test=> SELECT
2649 * test-> ('2005-10-30 13:22:00-05'::timestamptz -
2650 * test(> '2005-10-29 13:22:00-04'::timestamptz);
2651 * ?column?
2652 * ----------------
2653 * 1 day 01:00:00
2654 * (1 row)
2655 *
2656 * so adding that to the first timestamp gets:
2657 *
2658 * test=> SELECT
2659 * test-> ('2005-10-29 13:22:00-04'::timestamptz +
2660 * test(> ('2005-10-30 13:22:00-05'::timestamptz -
2661 * test(> '2005-10-29 13:22:00-04'::timestamptz)) at time zone 'EST';
2662 * timezone
2663 * --------------------
2664 * 2005-10-30 14:22:00
2665 * (1 row)
2666 *----------
2667 */
2668 result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
2669 IntervalPGetDatum(result)));
2670
2671 PG_RETURN_INTERVAL_P(result);
2672}
2673
2674/*
2675 * interval_justify_interval()
2676 *
2677 * Adjust interval so 'month', 'day', and 'time' portions are within
2678 * customary bounds. Specifically:
2679 *
2680 * 0 <= abs(time) < 24 hours
2681 * 0 <= abs(day) < 30 days
2682 *
2683 * Also, the sign bit on all three fields is made equal, so either
2684 * all three fields are negative or all are positive.
2685 */
2686Datum
2687interval_justify_interval(PG_FUNCTION_ARGS)
2688{
2689 Interval *span = PG_GETARG_INTERVAL_P(0);
2690 Interval *result;
2691 TimeOffset wholeday;
2692 int32 wholemonth;
2693
2694 result = (Interval *) palloc(sizeof(Interval));
2695 result->month = span->month;
2696 result->day = span->day;
2697 result->time = span->time;
2698
2699 TMODULO(result->time, wholeday, USECS_PER_DAY);
2700 result->day += wholeday; /* could overflow... */
2701
2702 wholemonth = result->day / DAYS_PER_MONTH;
2703 result->day -= wholemonth * DAYS_PER_MONTH;
2704 result->month += wholemonth;
2705
2706 if (result->month > 0 &&
2707 (result->day < 0 || (result->day == 0 && result->time < 0)))
2708 {
2709 result->day += DAYS_PER_MONTH;
2710 result->month--;
2711 }
2712 else if (result->month < 0 &&
2713 (result->day > 0 || (result->day == 0 && result->time > 0)))
2714 {
2715 result->day -= DAYS_PER_MONTH;
2716 result->month++;
2717 }
2718
2719 if (result->day > 0 && result->time < 0)
2720 {
2721 result->time += USECS_PER_DAY;
2722 result->day--;
2723 }
2724 else if (result->day < 0 && result->time > 0)
2725 {
2726 result->time -= USECS_PER_DAY;
2727 result->day++;
2728 }
2729
2730 PG_RETURN_INTERVAL_P(result);
2731}
2732
2733/*
2734 * interval_justify_hours()
2735 *
2736 * Adjust interval so 'time' contains less than a whole day, adding
2737 * the excess to 'day'. This is useful for
2738 * situations (such as non-TZ) where '1 day' = '24 hours' is valid,
2739 * e.g. interval subtraction and division.
2740 */
2741Datum
2742interval_justify_hours(PG_FUNCTION_ARGS)
2743{
2744 Interval *span = PG_GETARG_INTERVAL_P(0);
2745 Interval *result;
2746 TimeOffset wholeday;
2747
2748 result = (Interval *) palloc(sizeof(Interval));
2749 result->month = span->month;
2750 result->day = span->day;
2751 result->time = span->time;
2752
2753 TMODULO(result->time, wholeday, USECS_PER_DAY);
2754 result->day += wholeday; /* could overflow... */
2755
2756 if (result->day > 0 && result->time < 0)
2757 {
2758 result->time += USECS_PER_DAY;
2759 result->day--;
2760 }
2761 else if (result->day < 0 && result->time > 0)
2762 {
2763 result->time -= USECS_PER_DAY;
2764 result->day++;
2765 }
2766
2767 PG_RETURN_INTERVAL_P(result);
2768}
2769
2770/*
2771 * interval_justify_days()
2772 *
2773 * Adjust interval so 'day' contains less than 30 days, adding
2774 * the excess to 'month'.
2775 */
2776Datum
2777interval_justify_days(PG_FUNCTION_ARGS)
2778{
2779 Interval *span = PG_GETARG_INTERVAL_P(0);
2780 Interval *result;
2781 int32 wholemonth;
2782
2783 result = (Interval *) palloc(sizeof(Interval));
2784 result->month = span->month;
2785 result->day = span->day;
2786 result->time = span->time;
2787
2788 wholemonth = result->day / DAYS_PER_MONTH;
2789 result->day -= wholemonth * DAYS_PER_MONTH;
2790 result->month += wholemonth;
2791
2792 if (result->month > 0 && result->day < 0)
2793 {
2794 result->day += DAYS_PER_MONTH;
2795 result->month--;
2796 }
2797 else if (result->month < 0 && result->day > 0)
2798 {
2799 result->day -= DAYS_PER_MONTH;
2800 result->month++;
2801 }
2802
2803 PG_RETURN_INTERVAL_P(result);
2804}
2805
2806/* timestamp_pl_interval()
2807 * Add an interval to a timestamp data type.
2808 * Note that interval has provisions for qualitative year/month and day
2809 * units, so try to do the right thing with them.
2810 * To add a month, increment the month, and use the same day of month.
2811 * Then, if the next month has fewer days, set the day of month
2812 * to the last day of month.
2813 * To add a day, increment the mday, and use the same time of day.
2814 * Lastly, add in the "quantitative time".
2815 */
2816Datum
2817timestamp_pl_interval(PG_FUNCTION_ARGS)
2818{
2819 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
2820 Interval *span = PG_GETARG_INTERVAL_P(1);
2821 Timestamp result;
2822
2823 if (TIMESTAMP_NOT_FINITE(timestamp))
2824 result = timestamp;
2825 else
2826 {
2827 if (span->month != 0)
2828 {
2829 struct pg_tm tt,
2830 *tm = &tt;
2831 fsec_t fsec;
2832
2833 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2834 ereport(ERROR,
2835 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2836 errmsg("timestamp out of range")));
2837
2838 tm->tm_mon += span->month;
2839 if (tm->tm_mon > MONTHS_PER_YEAR)
2840 {
2841 tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
2842 tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2843 }
2844 else if (tm->tm_mon < 1)
2845 {
2846 tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
2847 tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2848 }
2849
2850 /* adjust for end of month boundary problems... */
2851 if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2852 tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
2853
2854 if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2855 ereport(ERROR,
2856 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2857 errmsg("timestamp out of range")));
2858 }
2859
2860 if (span->day != 0)
2861 {
2862 struct pg_tm tt,
2863 *tm = &tt;
2864 fsec_t fsec;
2865 int julian;
2866
2867 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2868 ereport(ERROR,
2869 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2870 errmsg("timestamp out of range")));
2871
2872 /* Add days by converting to and from Julian */
2873 julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
2874 j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2875
2876 if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2877 ereport(ERROR,
2878 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2879 errmsg("timestamp out of range")));
2880 }
2881
2882 timestamp += span->time;
2883
2884 if (!IS_VALID_TIMESTAMP(timestamp))
2885 ereport(ERROR,
2886 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2887 errmsg("timestamp out of range")));
2888
2889 result = timestamp;
2890 }
2891
2892 PG_RETURN_TIMESTAMP(result);
2893}
2894
2895Datum
2896timestamp_mi_interval(PG_FUNCTION_ARGS)
2897{
2898 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
2899 Interval *span = PG_GETARG_INTERVAL_P(1);
2900 Interval tspan;
2901
2902 tspan.month = -span->month;
2903 tspan.day = -span->day;
2904 tspan.time = -span->time;
2905
2906 return DirectFunctionCall2(timestamp_pl_interval,
2907 TimestampGetDatum(timestamp),
2908 PointerGetDatum(&tspan));
2909}
2910
2911
2912/* timestamptz_pl_interval()
2913 * Add an interval to a timestamp with time zone data type.
2914 * Note that interval has provisions for qualitative year/month
2915 * units, so try to do the right thing with them.
2916 * To add a month, increment the month, and use the same day of month.
2917 * Then, if the next month has fewer days, set the day of month
2918 * to the last day of month.
2919 * Lastly, add in the "quantitative time".
2920 */
2921Datum
2922timestamptz_pl_interval(PG_FUNCTION_ARGS)
2923{
2924 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2925 Interval *span = PG_GETARG_INTERVAL_P(1);
2926 TimestampTz result;
2927 int tz;
2928
2929 if (TIMESTAMP_NOT_FINITE(timestamp))
2930 result = timestamp;
2931 else
2932 {
2933 if (span->month != 0)
2934 {
2935 struct pg_tm tt,
2936 *tm = &tt;
2937 fsec_t fsec;
2938
2939 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
2940 ereport(ERROR,
2941 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2942 errmsg("timestamp out of range")));
2943
2944 tm->tm_mon += span->month;
2945 if (tm->tm_mon > MONTHS_PER_YEAR)
2946 {
2947 tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
2948 tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2949 }
2950 else if (tm->tm_mon < 1)
2951 {
2952 tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
2953 tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2954 }
2955
2956 /* adjust for end of month boundary problems... */
2957 if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
2958 tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
2959
2960 tz = DetermineTimeZoneOffset(tm, session_timezone);
2961
2962 if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2963 ereport(ERROR,
2964 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2965 errmsg("timestamp out of range")));
2966 }
2967
2968 if (span->day != 0)
2969 {
2970 struct pg_tm tt,
2971 *tm = &tt;
2972 fsec_t fsec;
2973 int julian;
2974
2975 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
2976 ereport(ERROR,
2977 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2978 errmsg("timestamp out of range")));
2979
2980 /* Add days by converting to and from Julian */
2981 julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
2982 j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
2983
2984 tz = DetermineTimeZoneOffset(tm, session_timezone);
2985
2986 if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2987 ereport(ERROR,
2988 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2989 errmsg("timestamp out of range")));
2990 }
2991
2992 timestamp += span->time;
2993
2994 if (!IS_VALID_TIMESTAMP(timestamp))
2995 ereport(ERROR,
2996 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2997 errmsg("timestamp out of range")));
2998
2999 result = timestamp;
3000 }
3001
3002 PG_RETURN_TIMESTAMP(result);
3003}
3004
3005Datum
3006timestamptz_mi_interval(PG_FUNCTION_ARGS)
3007{
3008 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
3009 Interval *span = PG_GETARG_INTERVAL_P(1);
3010 Interval tspan;
3011
3012 tspan.month = -span->month;
3013 tspan.day = -span->day;
3014 tspan.time = -span->time;
3015
3016 return DirectFunctionCall2(timestamptz_pl_interval,
3017 TimestampGetDatum(timestamp),
3018 PointerGetDatum(&tspan));
3019}
3020
3021
3022Datum
3023interval_um(PG_FUNCTION_ARGS)
3024{
3025 Interval *interval = PG_GETARG_INTERVAL_P(0);
3026 Interval *result;
3027
3028 result = (Interval *) palloc(sizeof(Interval));
3029
3030 result->time = -interval->time;
3031 /* overflow check copied from int4um */
3032 if (interval->time != 0 && SAMESIGN(result->time, interval->time))
3033 ereport(ERROR,
3034 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3035 errmsg("interval out of range")));
3036 result->day = -interval->day;
3037 if (interval->day != 0 && SAMESIGN(result->day, interval->day))
3038 ereport(ERROR,
3039 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3040 errmsg("interval out of range")));
3041 result->month = -interval->month;
3042 if (interval->month != 0 && SAMESIGN(result->month, interval->month))
3043 ereport(ERROR,
3044 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3045 errmsg("interval out of range")));
3046
3047 PG_RETURN_INTERVAL_P(result);
3048}
3049
3050
3051Datum
3052interval_smaller(PG_FUNCTION_ARGS)
3053{
3054 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
3055 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
3056 Interval *result;
3057
3058 /* use interval_cmp_internal to be sure this agrees with comparisons */
3059 if (interval_cmp_internal(interval1, interval2) < 0)
3060 result = interval1;
3061 else
3062 result = interval2;
3063 PG_RETURN_INTERVAL_P(result);
3064}
3065
3066Datum
3067interval_larger(PG_FUNCTION_ARGS)
3068{
3069 Interval *interval1 = PG_GETARG_INTERVAL_P(0);
3070 Interval *interval2 = PG_GETARG_INTERVAL_P(1);
3071 Interval *result;
3072
3073 if (interval_cmp_internal(interval1, interval2) > 0)
3074 result = interval1;
3075 else
3076 result = interval2;
3077 PG_RETURN_INTERVAL_P(result);
3078}
3079
3080Datum
3081interval_pl(PG_FUNCTION_ARGS)
3082{
3083 Interval *span1 = PG_GETARG_INTERVAL_P(0);
3084 Interval *span2 = PG_GETARG_INTERVAL_P(1);
3085 Interval *result;
3086
3087 result = (Interval *) palloc(sizeof(Interval));
3088
3089 result->month = span1->month + span2->month;
3090 /* overflow check copied from int4pl */
3091 if (SAMESIGN(span1->month, span2->month) &&
3092 !SAMESIGN(result->month, span1->month))
3093 ereport(ERROR,
3094 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3095 errmsg("interval out of range")));
3096
3097 result->day = span1->day + span2->day;
3098 if (SAMESIGN(span1->day, span2->day) &&
3099 !SAMESIGN(result->day, span1->day))
3100 ereport(ERROR,
3101 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3102 errmsg("interval out of range")));
3103
3104 result->time = span1->time + span2->time;
3105 if (SAMESIGN(span1->time, span2->time) &&
3106 !SAMESIGN(result->time, span1->time))
3107 ereport(ERROR,
3108 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3109 errmsg("interval out of range")));
3110
3111 PG_RETURN_INTERVAL_P(result);
3112}
3113
3114Datum
3115interval_mi(PG_FUNCTION_ARGS)
3116{
3117 Interval *span1 = PG_GETARG_INTERVAL_P(0);
3118 Interval *span2 = PG_GETARG_INTERVAL_P(1);
3119 Interval *result;
3120
3121 result = (Interval *) palloc(sizeof(Interval));
3122
3123 result->month = span1->month - span2->month;
3124 /* overflow check copied from int4mi */
3125 if (!SAMESIGN(span1->month, span2->month) &&
3126 !SAMESIGN(result->month, span1->month))
3127 ereport(ERROR,
3128 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3129 errmsg("interval out of range")));
3130
3131 result->day = span1->day - span2->day;
3132 if (!SAMESIGN(span1->day, span2->day) &&
3133 !SAMESIGN(result->day, span1->day))
3134 ereport(ERROR,
3135 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3136 errmsg("interval out of range")));
3137
3138 result->time = span1->time - span2->time;
3139 if (!SAMESIGN(span1->time, span2->time) &&
3140 !SAMESIGN(result->time, span1->time))
3141 ereport(ERROR,
3142 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3143 errmsg("interval out of range")));
3144
3145 PG_RETURN_INTERVAL_P(result);
3146}
3147
3148/*
3149 * There is no interval_abs(): it is unclear what value to return:
3150 * http://archives.postgresql.org/pgsql-general/2009-10/msg01031.php
3151 * http://archives.postgresql.org/pgsql-general/2009-11/msg00041.php
3152 */
3153
3154Datum
3155interval_mul(PG_FUNCTION_ARGS)
3156{
3157 Interval *span = PG_GETARG_INTERVAL_P(0);
3158 float8 factor = PG_GETARG_FLOAT8(1);
3159 double month_remainder_days,
3160 sec_remainder,
3161 result_double;
3162 int32 orig_month = span->month,
3163 orig_day = span->day;
3164 Interval *result;
3165
3166 result = (Interval *) palloc(sizeof(Interval));
3167
3168 result_double = span->month * factor;
3169 if (isnan(result_double) ||
3170 result_double > INT_MAX || result_double < INT_MIN)
3171 ereport(ERROR,
3172 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3173 errmsg("interval out of range")));
3174 result->month = (int32) result_double;
3175
3176 result_double = span->day * factor;
3177 if (isnan(result_double) ||
3178 result_double > INT_MAX || result_double < INT_MIN)
3179 ereport(ERROR,
3180 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3181 errmsg("interval out of range")));
3182 result->day = (int32) result_double;
3183
3184 /*
3185 * The above correctly handles the whole-number part of the month and day
3186 * products, but we have to do something with any fractional part
3187 * resulting when the factor is non-integral. We cascade the fractions
3188 * down to lower units using the conversion factors DAYS_PER_MONTH and
3189 * SECS_PER_DAY. Note we do NOT cascade up, since we are not forced to do
3190 * so by the representation. The user can choose to cascade up later,
3191 * using justify_hours and/or justify_days.
3192 */
3193
3194 /*
3195 * Fractional months full days into days.
3196 *
3197 * Floating point calculation are inherently imprecise, so these
3198 * calculations are crafted to produce the most reliable result possible.
3199 * TSROUND() is needed to more accurately produce whole numbers where
3200 * appropriate.
3201 */
3202 month_remainder_days = (orig_month * factor - result->month) * DAYS_PER_MONTH;
3203 month_remainder_days = TSROUND(month_remainder_days);
3204 sec_remainder = (orig_day * factor - result->day +
3205 month_remainder_days - (int) month_remainder_days) * SECS_PER_DAY;
3206 sec_remainder = TSROUND(sec_remainder);
3207
3208 /*
3209 * Might have 24:00:00 hours due to rounding, or >24 hours because of time
3210 * cascade from months and days. It might still be >24 if the combination
3211 * of cascade and the seconds factor operation itself.
3212 */
3213 if (Abs(sec_remainder) >= SECS_PER_DAY)
3214 {
3215 result->day += (int) (sec_remainder / SECS_PER_DAY);
3216 sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY;
3217 }
3218
3219 /* cascade units down */
3220 result->day += (int32) month_remainder_days;
3221 result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
3222 if (result_double > PG_INT64_MAX || result_double < PG_INT64_MIN)
3223 ereport(ERROR,
3224 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3225 errmsg("interval out of range")));
3226 result->time = (int64) result_double;
3227
3228 PG_RETURN_INTERVAL_P(result);
3229}
3230
3231Datum
3232mul_d_interval(PG_FUNCTION_ARGS)
3233{
3234 /* Args are float8 and Interval *, but leave them as generic Datum */
3235 Datum factor = PG_GETARG_DATUM(0);
3236 Datum span = PG_GETARG_DATUM(1);
3237
3238 return DirectFunctionCall2(interval_mul, span, factor);
3239}
3240
3241Datum
3242interval_div(PG_FUNCTION_ARGS)
3243{
3244 Interval *span = PG_GETARG_INTERVAL_P(0);
3245 float8 factor = PG_GETARG_FLOAT8(1);
3246 double month_remainder_days,
3247 sec_remainder;
3248 int32 orig_month = span->month,
3249 orig_day = span->day;
3250 Interval *result;
3251
3252 result = (Interval *) palloc(sizeof(Interval));
3253
3254 if (factor == 0.0)
3255 ereport(ERROR,
3256 (errcode(ERRCODE_DIVISION_BY_ZERO),
3257 errmsg("division by zero")));
3258
3259 result->month = (int32) (span->month / factor);
3260 result->day = (int32) (span->day / factor);
3261
3262 /*
3263 * Fractional months full days into days. See comment in interval_mul().
3264 */
3265 month_remainder_days = (orig_month / factor - result->month) * DAYS_PER_MONTH;
3266 month_remainder_days = TSROUND(month_remainder_days);
3267 sec_remainder = (orig_day / factor - result->day +
3268 month_remainder_days - (int) month_remainder_days) * SECS_PER_DAY;
3269 sec_remainder = TSROUND(sec_remainder);
3270 if (Abs(sec_remainder) >= SECS_PER_DAY)
3271 {
3272 result->day += (int) (sec_remainder / SECS_PER_DAY);
3273 sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY;
3274 }
3275
3276 /* cascade units down */
3277 result->day += (int32) month_remainder_days;
3278 result->time = rint(span->time / factor + sec_remainder * USECS_PER_SEC);
3279
3280 PG_RETURN_INTERVAL_P(result);
3281}
3282
3283
3284/*
3285 * in_range support functions for timestamps and intervals.
3286 *
3287 * Per SQL spec, we support these with interval as the offset type.
3288 * The spec's restriction that the offset not be negative is a bit hard to
3289 * decipher for intervals, but we choose to interpret it the same as our
3290 * interval comparison operators would.
3291 */
3292
3293Datum
3294in_range_timestamptz_interval(PG_FUNCTION_ARGS)
3295{
3296 TimestampTz val = PG_GETARG_TIMESTAMPTZ(0);
3297 TimestampTz base = PG_GETARG_TIMESTAMPTZ(1);
3298 Interval *offset = PG_GETARG_INTERVAL_P(2);
3299 bool sub = PG_GETARG_BOOL(3);
3300 bool less = PG_GETARG_BOOL(4);
3301 TimestampTz sum;
3302
3303 if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0)
3304 ereport(ERROR,
3305 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
3306 errmsg("invalid preceding or following size in window function")));
3307
3308 /* We don't currently bother to avoid overflow hazards here */
3309 if (sub)
3310 sum = DatumGetTimestampTz(DirectFunctionCall2(timestamptz_mi_interval,
3311 TimestampTzGetDatum(base),
3312 IntervalPGetDatum(offset)));
3313 else
3314 sum = DatumGetTimestampTz(DirectFunctionCall2(timestamptz_pl_interval,
3315 TimestampTzGetDatum(base),
3316 IntervalPGetDatum(offset)));
3317
3318 if (less)
3319 PG_RETURN_BOOL(val <= sum);
3320 else
3321 PG_RETURN_BOOL(val >= sum);
3322}
3323
3324Datum
3325in_range_timestamp_interval(PG_FUNCTION_ARGS)
3326{
3327 Timestamp val = PG_GETARG_TIMESTAMP(0);
3328 Timestamp base = PG_GETARG_TIMESTAMP(1);
3329 Interval *offset = PG_GETARG_INTERVAL_P(2);
3330 bool sub = PG_GETARG_BOOL(3);
3331 bool less = PG_GETARG_BOOL(4);
3332 Timestamp sum;
3333
3334 if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0)
3335 ereport(ERROR,
3336 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
3337 errmsg("invalid preceding or following size in window function")));
3338
3339 /* We don't currently bother to avoid overflow hazards here */
3340 if (sub)
3341 sum = DatumGetTimestamp(DirectFunctionCall2(timestamp_mi_interval,
3342 TimestampGetDatum(base),
3343 IntervalPGetDatum(offset)));
3344 else
3345 sum = DatumGetTimestamp(DirectFunctionCall2(timestamp_pl_interval,
3346 TimestampGetDatum(base),
3347 IntervalPGetDatum(offset)));
3348
3349 if (less)
3350 PG_RETURN_BOOL(val <= sum);
3351 else
3352 PG_RETURN_BOOL(val >= sum);
3353}
3354
3355Datum
3356in_range_interval_interval(PG_FUNCTION_ARGS)
3357{
3358 Interval *val = PG_GETARG_INTERVAL_P(0);
3359 Interval *base = PG_GETARG_INTERVAL_P(1);
3360 Interval *offset = PG_GETARG_INTERVAL_P(2);
3361 bool sub = PG_GETARG_BOOL(3);
3362 bool less = PG_GETARG_BOOL(4);
3363 Interval *sum;
3364
3365 if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0)
3366 ereport(ERROR,
3367 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
3368 errmsg("invalid preceding or following size in window function")));
3369
3370 /* We don't currently bother to avoid overflow hazards here */
3371 if (sub)
3372 sum = DatumGetIntervalP(DirectFunctionCall2(interval_mi,
3373 IntervalPGetDatum(base),
3374 IntervalPGetDatum(offset)));
3375 else
3376 sum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
3377 IntervalPGetDatum(base),
3378 IntervalPGetDatum(offset)));
3379
3380 if (less)
3381 PG_RETURN_BOOL(interval_cmp_internal(val, sum) <= 0);
3382 else
3383 PG_RETURN_BOOL(interval_cmp_internal(val, sum) >= 0);
3384}
3385
3386
3387/*
3388 * interval_accum, interval_accum_inv, and interval_avg implement the
3389 * AVG(interval) aggregate.
3390 *
3391 * The transition datatype for this aggregate is a 2-element array of
3392 * intervals, where the first is the running sum and the second contains
3393 * the number of values so far in its 'time' field. This is a bit ugly
3394 * but it beats inventing a specialized datatype for the purpose.
3395 */
3396
3397Datum
3398interval_accum(PG_FUNCTION_ARGS)
3399{
3400 ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3401 Interval *newval = PG_GETARG_INTERVAL_P(1);
3402 Datum *transdatums;
3403 int ndatums;
3404 Interval sumX,
3405 N;
3406 Interval *newsum;
3407 ArrayType *result;
3408
3409 deconstruct_array(transarray,
3410 INTERVALOID, sizeof(Interval), false, 'd',
3411 &transdatums, NULL, &ndatums);
3412 if (ndatums != 2)
3413 elog(ERROR, "expected 2-element interval array");
3414
3415 sumX = *(DatumGetIntervalP(transdatums[0]));
3416 N = *(DatumGetIntervalP(transdatums[1]));
3417
3418 newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
3419 IntervalPGetDatum(&sumX),
3420 IntervalPGetDatum(newval)));
3421 N.time += 1;
3422
3423 transdatums[0] = IntervalPGetDatum(newsum);
3424 transdatums[1] = IntervalPGetDatum(&N);
3425
3426 result = construct_array(transdatums, 2,
3427 INTERVALOID, sizeof(Interval), false, 'd');
3428
3429 PG_RETURN_ARRAYTYPE_P(result);
3430}
3431
3432Datum
3433interval_combine(PG_FUNCTION_ARGS)
3434{
3435 ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0);
3436 ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1);
3437 Datum *transdatums1;
3438 Datum *transdatums2;
3439 int ndatums1;
3440 int ndatums2;
3441 Interval sum1,
3442 N1;
3443 Interval sum2,
3444 N2;
3445
3446 Interval *newsum;
3447 ArrayType *result;
3448
3449 deconstruct_array(transarray1,
3450 INTERVALOID, sizeof(Interval), false, 'd',
3451 &transdatums1, NULL, &ndatums1);
3452 if (ndatums1 != 2)
3453 elog(ERROR, "expected 2-element interval array");
3454
3455 sum1 = *(DatumGetIntervalP(transdatums1[0]));
3456 N1 = *(DatumGetIntervalP(transdatums1[1]));
3457
3458 deconstruct_array(transarray2,
3459 INTERVALOID, sizeof(Interval), false, 'd',
3460 &transdatums2, NULL, &ndatums2);
3461 if (ndatums2 != 2)
3462 elog(ERROR, "expected 2-element interval array");
3463
3464 sum2 = *(DatumGetIntervalP(transdatums2[0]));
3465 N2 = *(DatumGetIntervalP(transdatums2[1]));
3466
3467 newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
3468 IntervalPGetDatum(&sum1),
3469 IntervalPGetDatum(&sum2)));
3470 N1.time += N2.time;
3471
3472 transdatums1[0] = IntervalPGetDatum(newsum);
3473 transdatums1[1] = IntervalPGetDatum(&N1);
3474
3475 result = construct_array(transdatums1, 2,
3476 INTERVALOID, sizeof(Interval), false, 'd');
3477
3478 PG_RETURN_ARRAYTYPE_P(result);
3479}
3480
3481Datum
3482interval_accum_inv(PG_FUNCTION_ARGS)
3483{
3484 ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3485 Interval *newval = PG_GETARG_INTERVAL_P(1);
3486 Datum *transdatums;
3487 int ndatums;
3488 Interval sumX,
3489 N;
3490 Interval *newsum;
3491 ArrayType *result;
3492
3493 deconstruct_array(transarray,
3494 INTERVALOID, sizeof(Interval), false, 'd',
3495 &transdatums, NULL, &ndatums);
3496 if (ndatums != 2)
3497 elog(ERROR, "expected 2-element interval array");
3498
3499 sumX = *(DatumGetIntervalP(transdatums[0]));
3500 N = *(DatumGetIntervalP(transdatums[1]));
3501
3502 newsum = DatumGetIntervalP(DirectFunctionCall2(interval_mi,
3503 IntervalPGetDatum(&sumX),
3504 IntervalPGetDatum(newval)));
3505 N.time -= 1;
3506
3507 transdatums[0] = IntervalPGetDatum(newsum);
3508 transdatums[1] = IntervalPGetDatum(&N);
3509
3510 result = construct_array(transdatums, 2,
3511 INTERVALOID, sizeof(Interval), false, 'd');
3512
3513 PG_RETURN_ARRAYTYPE_P(result);
3514}
3515
3516Datum
3517interval_avg(PG_FUNCTION_ARGS)
3518{
3519 ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
3520 Datum *transdatums;
3521 int ndatums;
3522 Interval sumX,
3523 N;
3524
3525 deconstruct_array(transarray,
3526 INTERVALOID, sizeof(Interval), false, 'd',
3527 &transdatums, NULL, &ndatums);
3528 if (ndatums != 2)
3529 elog(ERROR, "expected 2-element interval array");
3530
3531 sumX = *(DatumGetIntervalP(transdatums[0]));
3532 N = *(DatumGetIntervalP(transdatums[1]));
3533
3534 /* SQL defines AVG of no values to be NULL */
3535 if (N.time == 0)
3536 PG_RETURN_NULL();
3537
3538 return DirectFunctionCall2(interval_div,
3539 IntervalPGetDatum(&sumX),
3540 Float8GetDatum((double) N.time));
3541}
3542
3543
3544/* timestamp_age()
3545 * Calculate time difference while retaining year/month fields.
3546 * Note that this does not result in an accurate absolute time span
3547 * since year and month are out of context once the arithmetic
3548 * is done.
3549 */
3550Datum
3551timestamp_age(PG_FUNCTION_ARGS)
3552{
3553 Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
3554 Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
3555 Interval *result;
3556 fsec_t fsec,
3557 fsec1,
3558 fsec2;
3559 struct pg_tm tt,
3560 *tm = &tt;
3561 struct pg_tm tt1,
3562 *tm1 = &tt1;
3563 struct pg_tm tt2,
3564 *tm2 = &tt2;
3565
3566 result = (Interval *) palloc(sizeof(Interval));
3567
3568 if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 &&
3569 timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0)
3570 {
3571 /* form the symbolic difference */
3572 fsec = fsec1 - fsec2;
3573 tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
3574 tm->tm_min = tm1->tm_min - tm2->tm_min;
3575 tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
3576 tm->tm_mday = tm1->tm_mday - tm2->tm_mday;
3577 tm->tm_mon = tm1->tm_mon - tm2->tm_mon;
3578 tm->tm_year = tm1->tm_year - tm2->tm_year;
3579
3580 /* flip sign if necessary... */
3581 if (dt1 < dt2)
3582 {
3583 fsec = -fsec;
3584 tm->tm_sec = -tm->tm_sec;
3585 tm->tm_min = -tm->tm_min;
3586 tm->tm_hour = -tm->tm_hour;
3587 tm->tm_mday = -tm->tm_mday;
3588 tm->tm_mon = -tm->tm_mon;
3589 tm->tm_year = -tm->tm_year;
3590 }
3591
3592 /* propagate any negative fields into the next higher field */
3593 while (fsec < 0)
3594 {
3595 fsec += USECS_PER_SEC;
3596 tm->tm_sec--;
3597 }
3598
3599 while (tm->tm_sec < 0)
3600 {
3601 tm->tm_sec += SECS_PER_MINUTE;
3602 tm->tm_min--;
3603 }
3604
3605 while (tm->tm_min < 0)
3606 {
3607 tm->tm_min += MINS_PER_HOUR;
3608 tm->tm_hour--;
3609 }
3610
3611 while (tm->tm_hour < 0)
3612 {
3613 tm->tm_hour += HOURS_PER_DAY;
3614 tm->tm_mday--;
3615 }
3616
3617 while (tm->tm_mday < 0)
3618 {
3619 if (dt1 < dt2)
3620 {
3621 tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
3622 tm->tm_mon--;
3623 }
3624 else
3625 {
3626 tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
3627 tm->tm_mon--;
3628 }
3629 }
3630
3631 while (tm->tm_mon < 0)
3632 {
3633 tm->tm_mon += MONTHS_PER_YEAR;
3634 tm->tm_year--;
3635 }
3636
3637 /* recover sign if necessary... */
3638 if (dt1 < dt2)
3639 {
3640 fsec = -fsec;
3641 tm->tm_sec = -tm->tm_sec;
3642 tm->tm_min = -tm->tm_min;
3643 tm->tm_hour = -tm->tm_hour;
3644 tm->tm_mday = -tm->tm_mday;
3645 tm->tm_mon = -tm->tm_mon;
3646 tm->tm_year = -tm->tm_year;
3647 }
3648
3649 if (tm2interval(tm, fsec, result) != 0)
3650 ereport(ERROR,
3651 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3652 errmsg("interval out of range")));
3653 }
3654 else
3655 ereport(ERROR,
3656 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3657 errmsg("timestamp out of range")));
3658
3659 PG_RETURN_INTERVAL_P(result);
3660}
3661
3662
3663/* timestamptz_age()
3664 * Calculate time difference while retaining year/month fields.
3665 * Note that this does not result in an accurate absolute time span
3666 * since year and month are out of context once the arithmetic
3667 * is done.
3668 */
3669Datum
3670timestamptz_age(PG_FUNCTION_ARGS)
3671{
3672 TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
3673 TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
3674 Interval *result;
3675 fsec_t fsec,
3676 fsec1,
3677 fsec2;
3678 struct pg_tm tt,
3679 *tm = &tt;
3680 struct pg_tm tt1,
3681 *tm1 = &tt1;
3682 struct pg_tm tt2,
3683 *tm2 = &tt2;
3684 int tz1;
3685 int tz2;
3686
3687 result = (Interval *) palloc(sizeof(Interval));
3688
3689 if (timestamp2tm(dt1, &tz1, tm1, &fsec1, NULL, NULL) == 0 &&
3690 timestamp2tm(dt2, &tz2, tm2, &fsec2, NULL, NULL) == 0)
3691 {
3692 /* form the symbolic difference */
3693 fsec = fsec1 - fsec2;
3694 tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
3695 tm->tm_min = tm1->tm_min - tm2->tm_min;
3696 tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
3697 tm->tm_mday = tm1->tm_mday - tm2->tm_mday;
3698 tm->tm_mon = tm1->tm_mon - tm2->tm_mon;
3699 tm->tm_year = tm1->tm_year - tm2->tm_year;
3700
3701 /* flip sign if necessary... */
3702 if (dt1 < dt2)
3703 {
3704 fsec = -fsec;
3705 tm->tm_sec = -tm->tm_sec;
3706 tm->tm_min = -tm->tm_min;
3707 tm->tm_hour = -tm->tm_hour;
3708 tm->tm_mday = -tm->tm_mday;
3709 tm->tm_mon = -tm->tm_mon;
3710 tm->tm_year = -tm->tm_year;
3711 }
3712
3713 /* propagate any negative fields into the next higher field */
3714 while (fsec < 0)
3715 {
3716 fsec += USECS_PER_SEC;
3717 tm->tm_sec--;
3718 }
3719
3720 while (tm->tm_sec < 0)
3721 {
3722 tm->tm_sec += SECS_PER_MINUTE;
3723 tm->tm_min--;
3724 }
3725
3726 while (tm->tm_min < 0)
3727 {
3728 tm->tm_min += MINS_PER_HOUR;
3729 tm->tm_hour--;
3730 }
3731
3732 while (tm->tm_hour < 0)
3733 {
3734 tm->tm_hour += HOURS_PER_DAY;
3735 tm->tm_mday--;
3736 }
3737
3738 while (tm->tm_mday < 0)
3739 {
3740 if (dt1 < dt2)
3741 {
3742 tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
3743 tm->tm_mon--;
3744 }
3745 else
3746 {
3747 tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
3748 tm->tm_mon--;
3749 }
3750 }
3751
3752 while (tm->tm_mon < 0)
3753 {
3754 tm->tm_mon += MONTHS_PER_YEAR;
3755 tm->tm_year--;
3756 }
3757
3758 /*
3759 * Note: we deliberately ignore any difference between tz1 and tz2.
3760 */
3761
3762 /* recover sign if necessary... */
3763 if (dt1 < dt2)
3764 {
3765 fsec = -fsec;
3766 tm->tm_sec = -tm->tm_sec;
3767 tm->tm_min = -tm->tm_min;
3768 tm->tm_hour = -tm->tm_hour;
3769 tm->tm_mday = -tm->tm_mday;
3770 tm->tm_mon = -tm->tm_mon;
3771 tm->tm_year = -tm->tm_year;
3772 }
3773
3774 if (tm2interval(tm, fsec, result) != 0)
3775 ereport(ERROR,
3776 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3777 errmsg("interval out of range")));
3778 }
3779 else
3780 ereport(ERROR,
3781 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3782 errmsg("timestamp out of range")));
3783
3784 PG_RETURN_INTERVAL_P(result);
3785}
3786
3787
3788/*----------------------------------------------------------
3789 * Conversion operators.
3790 *---------------------------------------------------------*/
3791
3792
3793/* timestamp_trunc()
3794 * Truncate timestamp to specified units.
3795 */
3796Datum
3797timestamp_trunc(PG_FUNCTION_ARGS)
3798{
3799 text *units = PG_GETARG_TEXT_PP(0);
3800 Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3801 Timestamp result;
3802 int type,
3803 val;
3804 char *lowunits;
3805 fsec_t fsec;
3806 struct pg_tm tt,
3807 *tm = &tt;
3808
3809 if (TIMESTAMP_NOT_FINITE(timestamp))
3810 PG_RETURN_TIMESTAMP(timestamp);
3811
3812 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
3813 VARSIZE_ANY_EXHDR(units),
3814 false);
3815
3816 type = DecodeUnits(0, lowunits, &val);
3817
3818 if (type == UNITS)
3819 {
3820 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3821 ereport(ERROR,
3822 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3823 errmsg("timestamp out of range")));
3824
3825 switch (val)
3826 {
3827 case DTK_WEEK:
3828 {
3829 int woy;
3830
3831 woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
3832
3833 /*
3834 * If it is week 52/53 and the month is January, then the
3835 * week must belong to the previous year. Also, some
3836 * December dates belong to the next year.
3837 */
3838 if (woy >= 52 && tm->tm_mon == 1)
3839 --tm->tm_year;
3840 if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
3841 ++tm->tm_year;
3842 isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
3843 tm->tm_hour = 0;
3844 tm->tm_min = 0;
3845 tm->tm_sec = 0;
3846 fsec = 0;
3847 break;
3848 }
3849 case DTK_MILLENNIUM:
3850 /* see comments in timestamptz_trunc */
3851 if (tm->tm_year > 0)
3852 tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
3853 else
3854 tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
3855 /* FALL THRU */
3856 case DTK_CENTURY:
3857 /* see comments in timestamptz_trunc */
3858 if (tm->tm_year > 0)
3859 tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
3860 else
3861 tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
3862 /* FALL THRU */
3863 case DTK_DECADE:
3864 /* see comments in timestamptz_trunc */
3865 if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
3866 {
3867 if (tm->tm_year > 0)
3868 tm->tm_year = (tm->tm_year / 10) * 10;
3869 else
3870 tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
3871 }
3872 /* FALL THRU */
3873 case DTK_YEAR:
3874 tm->tm_mon = 1;
3875 /* FALL THRU */
3876 case DTK_QUARTER:
3877 tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
3878 /* FALL THRU */
3879 case DTK_MONTH:
3880 tm->tm_mday = 1;
3881 /* FALL THRU */
3882 case DTK_DAY:
3883 tm->tm_hour = 0;
3884 /* FALL THRU */
3885 case DTK_HOUR:
3886 tm->tm_min = 0;
3887 /* FALL THRU */
3888 case DTK_MINUTE:
3889 tm->tm_sec = 0;
3890 /* FALL THRU */
3891 case DTK_SECOND:
3892 fsec = 0;
3893 break;
3894
3895 case DTK_MILLISEC:
3896 fsec = (fsec / 1000) * 1000;
3897 break;
3898
3899 case DTK_MICROSEC:
3900 break;
3901
3902 default:
3903 ereport(ERROR,
3904 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3905 errmsg("timestamp units \"%s\" not supported",
3906 lowunits)));
3907 result = 0;
3908 }
3909
3910 if (tm2timestamp(tm, fsec, NULL, &result) != 0)
3911 ereport(ERROR,
3912 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3913 errmsg("timestamp out of range")));
3914 }
3915 else
3916 {
3917 ereport(ERROR,
3918 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3919 errmsg("timestamp units \"%s\" not recognized",
3920 lowunits)));
3921 result = 0;
3922 }
3923
3924 PG_RETURN_TIMESTAMP(result);
3925}
3926
3927/*
3928 * Common code for timestamptz_trunc() and timestamptz_trunc_zone().
3929 *
3930 * tzp identifies the zone to truncate with respect to. We assume
3931 * infinite timestamps have already been rejected.
3932 */
3933static TimestampTz
3934timestamptz_trunc_internal(text *units, TimestampTz timestamp, pg_tz *tzp)
3935{
3936 TimestampTz result;
3937 int tz;
3938 int type,
3939 val;
3940 bool redotz = false;
3941 char *lowunits;
3942 fsec_t fsec;
3943 struct pg_tm tt,
3944 *tm = &tt;
3945
3946 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
3947 VARSIZE_ANY_EXHDR(units),
3948 false);
3949
3950 type = DecodeUnits(0, lowunits, &val);
3951
3952 if (type == UNITS)
3953 {
3954 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, tzp) != 0)
3955 ereport(ERROR,
3956 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3957 errmsg("timestamp out of range")));
3958
3959 switch (val)
3960 {
3961 case DTK_WEEK:
3962 {
3963 int woy;
3964
3965 woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
3966
3967 /*
3968 * If it is week 52/53 and the month is January, then the
3969 * week must belong to the previous year. Also, some
3970 * December dates belong to the next year.
3971 */
3972 if (woy >= 52 && tm->tm_mon == 1)
3973 --tm->tm_year;
3974 if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
3975 ++tm->tm_year;
3976 isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
3977 tm->tm_hour = 0;
3978 tm->tm_min = 0;
3979 tm->tm_sec = 0;
3980 fsec = 0;
3981 redotz = true;
3982 break;
3983 }
3984 /* one may consider DTK_THOUSAND and DTK_HUNDRED... */
3985 case DTK_MILLENNIUM:
3986
3987 /*
3988 * truncating to the millennium? what is this supposed to
3989 * mean? let us put the first year of the millennium... i.e.
3990 * -1000, 1, 1001, 2001...
3991 */
3992 if (tm->tm_year > 0)
3993 tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
3994 else
3995 tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
3996 /* FALL THRU */
3997 case DTK_CENTURY:
3998 /* truncating to the century? as above: -100, 1, 101... */
3999 if (tm->tm_year > 0)
4000 tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
4001 else
4002 tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
4003 /* FALL THRU */
4004 case DTK_DECADE:
4005
4006 /*
4007 * truncating to the decade? first year of the decade. must
4008 * not be applied if year was truncated before!
4009 */
4010 if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
4011 {
4012 if (tm->tm_year > 0)
4013 tm->tm_year = (tm->tm_year / 10) * 10;
4014 else
4015 tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
4016 }
4017 /* FALL THRU */
4018 case DTK_YEAR:
4019 tm->tm_mon = 1;
4020 /* FALL THRU */
4021 case DTK_QUARTER:
4022 tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
4023 /* FALL THRU */
4024 case DTK_MONTH:
4025 tm->tm_mday = 1;
4026 /* FALL THRU */
4027 case DTK_DAY:
4028 tm->tm_hour = 0;
4029 redotz = true; /* for all cases >= DAY */
4030 /* FALL THRU */
4031 case DTK_HOUR:
4032 tm->tm_min = 0;
4033 /* FALL THRU */
4034 case DTK_MINUTE:
4035 tm->tm_sec = 0;
4036 /* FALL THRU */
4037 case DTK_SECOND:
4038 fsec = 0;
4039 break;
4040 case DTK_MILLISEC:
4041 fsec = (fsec / 1000) * 1000;
4042 break;
4043 case DTK_MICROSEC:
4044 break;
4045
4046 default:
4047 ereport(ERROR,
4048 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4049 errmsg("timestamp with time zone units \"%s\" not "
4050 "supported", lowunits)));
4051 result = 0;
4052 }
4053
4054 if (redotz)
4055 tz = DetermineTimeZoneOffset(tm, tzp);
4056
4057 if (tm2timestamp(tm, fsec, &tz, &result) != 0)
4058 ereport(ERROR,
4059 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4060 errmsg("timestamp out of range")));
4061 }
4062 else
4063 {
4064 ereport(ERROR,
4065 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4066 errmsg("timestamp with time zone units \"%s\" not recognized",
4067 lowunits)));
4068 result = 0;
4069 }
4070
4071 return result;
4072}
4073
4074/* timestamptz_trunc()
4075 * Truncate timestamptz to specified units in session timezone.
4076 */
4077Datum
4078timestamptz_trunc(PG_FUNCTION_ARGS)
4079{
4080 text *units = PG_GETARG_TEXT_PP(0);
4081 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4082 TimestampTz result;
4083
4084 if (TIMESTAMP_NOT_FINITE(timestamp))
4085 PG_RETURN_TIMESTAMPTZ(timestamp);
4086
4087 result = timestamptz_trunc_internal(units, timestamp, session_timezone);
4088
4089 PG_RETURN_TIMESTAMPTZ(result);
4090}
4091
4092/* timestamptz_trunc_zone()
4093 * Truncate timestamptz to specified units in specified timezone.
4094 */
4095Datum
4096timestamptz_trunc_zone(PG_FUNCTION_ARGS)
4097{
4098 text *units = PG_GETARG_TEXT_PP(0);
4099 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4100 text *zone = PG_GETARG_TEXT_PP(2);
4101 TimestampTz result;
4102 char tzname[TZ_STRLEN_MAX + 1];
4103 char *lowzone;
4104 int type,
4105 val;
4106 pg_tz *tzp;
4107
4108 /*
4109 * timestamptz_zone() doesn't look up the zone for infinite inputs, so we
4110 * don't do so here either.
4111 */
4112 if (TIMESTAMP_NOT_FINITE(timestamp))
4113 PG_RETURN_TIMESTAMP(timestamp);
4114
4115 /*
4116 * Look up the requested timezone (see notes in timestamptz_zone()).
4117 */
4118 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
4119
4120 /* DecodeTimezoneAbbrev requires lowercase input */
4121 lowzone = downcase_truncate_identifier(tzname,
4122 strlen(tzname),
4123 false);
4124
4125 type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
4126
4127 if (type == TZ || type == DTZ)
4128 {
4129 /* fixed-offset abbreviation, get a pg_tz descriptor for that */
4130 tzp = pg_tzset_offset(-val);
4131 }
4132 else if (type == DYNTZ)
4133 {
4134 /* dynamic-offset abbreviation, use its referenced timezone */
4135 }
4136 else
4137 {
4138 /* try it as a full zone name */
4139 tzp = pg_tzset(tzname);
4140 if (!tzp)
4141 ereport(ERROR,
4142 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4143 errmsg("time zone \"%s\" not recognized", tzname)));
4144 }
4145
4146 result = timestamptz_trunc_internal(units, timestamp, tzp);
4147
4148 PG_RETURN_TIMESTAMPTZ(result);
4149}
4150
4151/* interval_trunc()
4152 * Extract specified field from interval.
4153 */
4154Datum
4155interval_trunc(PG_FUNCTION_ARGS)
4156{
4157 text *units = PG_GETARG_TEXT_PP(0);
4158 Interval *interval = PG_GETARG_INTERVAL_P(1);
4159 Interval *result;
4160 int type,
4161 val;
4162 char *lowunits;
4163 fsec_t fsec;
4164 struct pg_tm tt,
4165 *tm = &tt;
4166
4167 result = (Interval *) palloc(sizeof(Interval));
4168
4169 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
4170 VARSIZE_ANY_EXHDR(units),
4171 false);
4172
4173 type = DecodeUnits(0, lowunits, &val);
4174
4175 if (type == UNITS)
4176 {
4177 if (interval2tm(*interval, tm, &fsec) == 0)
4178 {
4179 switch (val)
4180 {
4181 case DTK_MILLENNIUM:
4182 /* caution: C division may have negative remainder */
4183 tm->tm_year = (tm->tm_year / 1000) * 1000;
4184 /* FALL THRU */
4185 case DTK_CENTURY:
4186 /* caution: C division may have negative remainder */
4187 tm->tm_year = (tm->tm_year / 100) * 100;
4188 /* FALL THRU */
4189 case DTK_DECADE:
4190 /* caution: C division may have negative remainder */
4191 tm->tm_year = (tm->tm_year / 10) * 10;
4192 /* FALL THRU */
4193 case DTK_YEAR:
4194 tm->tm_mon = 0;
4195 /* FALL THRU */
4196 case DTK_QUARTER:
4197 tm->tm_mon = 3 * (tm->tm_mon / 3);
4198 /* FALL THRU */
4199 case DTK_MONTH:
4200 tm->tm_mday = 0;
4201 /* FALL THRU */
4202 case DTK_DAY:
4203 tm->tm_hour = 0;
4204 /* FALL THRU */
4205 case DTK_HOUR:
4206 tm->tm_min = 0;
4207 /* FALL THRU */
4208 case DTK_MINUTE:
4209 tm->tm_sec = 0;
4210 /* FALL THRU */
4211 case DTK_SECOND:
4212 fsec = 0;
4213 break;
4214 case DTK_MILLISEC:
4215 fsec = (fsec / 1000) * 1000;
4216 break;
4217 case DTK_MICROSEC:
4218 break;
4219
4220 default:
4221 if (val == DTK_WEEK)
4222 ereport(ERROR,
4223 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4224 errmsg("interval units \"%s\" not supported "
4225 "because months usually have fractional weeks",
4226 lowunits)));
4227 else
4228 ereport(ERROR,
4229 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4230 errmsg("interval units \"%s\" not supported",
4231 lowunits)));
4232 }
4233
4234 if (tm2interval(tm, fsec, result) != 0)
4235 ereport(ERROR,
4236 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4237 errmsg("interval out of range")));
4238 }
4239 else
4240 elog(ERROR, "could not convert interval to tm");
4241 }
4242 else
4243 {
4244 ereport(ERROR,
4245 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4246 errmsg("interval units \"%s\" not recognized",
4247 lowunits)));
4248 }
4249
4250 PG_RETURN_INTERVAL_P(result);
4251}
4252
4253/* isoweek2j()
4254 *
4255 * Return the Julian day which corresponds to the first day (Monday) of the given ISO 8601 year and week.
4256 * Julian days are used to convert between ISO week dates and Gregorian dates.
4257 */
4258int
4259isoweek2j(int year, int week)
4260{
4261 int day0,
4262 day4;
4263
4264 /* fourth day of current year */
4265 day4 = date2j(year, 1, 4);
4266
4267 /* day0 == offset to first day of week (Monday) */
4268 day0 = j2day(day4 - 1);
4269
4270 return ((week - 1) * 7) + (day4 - day0);
4271}
4272
4273/* isoweek2date()
4274 * Convert ISO week of year number to date.
4275 * The year field must be specified with the ISO year!
4276 * karel 2000/08/07
4277 */
4278void
4279isoweek2date(int woy, int *year, int *mon, int *mday)
4280{
4281 j2date(isoweek2j(*year, woy), year, mon, mday);
4282}
4283
4284/* isoweekdate2date()
4285 *
4286 * Convert an ISO 8601 week date (ISO year, ISO week) into a Gregorian date.
4287 * Gregorian day of week sent so weekday strings can be supplied.
4288 * Populates year, mon, and mday with the correct Gregorian values.
4289 * year must be passed in as the ISO year.
4290 */
4291void
4292isoweekdate2date(int isoweek, int wday, int *year, int *mon, int *mday)
4293{
4294 int jday;
4295
4296 jday = isoweek2j(*year, isoweek);
4297 /* convert Gregorian week start (Sunday=1) to ISO week start (Monday=1) */
4298 if (wday > 1)
4299 jday += wday - 2;
4300 else
4301 jday += 6;
4302 j2date(jday, year, mon, mday);
4303}
4304
4305/* date2isoweek()
4306 *
4307 * Returns ISO week number of year.
4308 */
4309int
4310date2isoweek(int year, int mon, int mday)
4311{
4312 float8 result;
4313 int day0,
4314 day4,
4315 dayn;
4316
4317 /* current day */
4318 dayn = date2j(year, mon, mday);
4319
4320 /* fourth day of current year */
4321 day4 = date2j(year, 1, 4);
4322
4323 /* day0 == offset to first day of week (Monday) */
4324 day0 = j2day(day4 - 1);
4325
4326 /*
4327 * We need the first week containing a Thursday, otherwise this day falls
4328 * into the previous year for purposes of counting weeks
4329 */
4330 if (dayn < day4 - day0)
4331 {
4332 day4 = date2j(year - 1, 1, 4);
4333
4334 /* day0 == offset to first day of week (Monday) */
4335 day0 = j2day(day4 - 1);
4336 }
4337
4338 result = (dayn - (day4 - day0)) / 7 + 1;
4339
4340 /*
4341 * Sometimes the last few days in a year will fall into the first week of
4342 * the next year, so check for this.
4343 */
4344 if (result >= 52)
4345 {
4346 day4 = date2j(year + 1, 1, 4);
4347
4348 /* day0 == offset to first day of week (Monday) */
4349 day0 = j2day(day4 - 1);
4350
4351 if (dayn >= day4 - day0)
4352 result = (dayn - (day4 - day0)) / 7 + 1;
4353 }
4354
4355 return (int) result;
4356}
4357
4358
4359/* date2isoyear()
4360 *
4361 * Returns ISO 8601 year number.
4362 */
4363int
4364date2isoyear(int year, int mon, int mday)
4365{
4366 float8 result;
4367 int day0,
4368 day4,
4369 dayn;
4370
4371 /* current day */
4372 dayn = date2j(year, mon, mday);
4373
4374 /* fourth day of current year */
4375 day4 = date2j(year, 1, 4);
4376
4377 /* day0 == offset to first day of week (Monday) */
4378 day0 = j2day(day4 - 1);
4379
4380 /*
4381 * We need the first week containing a Thursday, otherwise this day falls
4382 * into the previous year for purposes of counting weeks
4383 */
4384 if (dayn < day4 - day0)
4385 {
4386 day4 = date2j(year - 1, 1, 4);
4387
4388 /* day0 == offset to first day of week (Monday) */
4389 day0 = j2day(day4 - 1);
4390
4391 year--;
4392 }
4393
4394 result = (dayn - (day4 - day0)) / 7 + 1;
4395
4396 /*
4397 * Sometimes the last few days in a year will fall into the first week of
4398 * the next year, so check for this.
4399 */
4400 if (result >= 52)
4401 {
4402 day4 = date2j(year + 1, 1, 4);
4403
4404 /* day0 == offset to first day of week (Monday) */
4405 day0 = j2day(day4 - 1);
4406
4407 if (dayn >= day4 - day0)
4408 year++;
4409 }
4410
4411 return year;
4412}
4413
4414
4415/* date2isoyearday()
4416 *
4417 * Returns the ISO 8601 day-of-year, given a Gregorian year, month and day.
4418 * Possible return values are 1 through 371 (364 in non-leap years).
4419 */
4420int
4421date2isoyearday(int year, int mon, int mday)
4422{
4423 return date2j(year, mon, mday) - isoweek2j(date2isoyear(year, mon, mday), 1) + 1;
4424}
4425
4426/*
4427 * NonFiniteTimestampTzPart
4428 *
4429 * Used by timestamp_part and timestamptz_part when extracting from infinite
4430 * timestamp[tz]. Returns +/-Infinity if that is the appropriate result,
4431 * otherwise returns zero (which should be taken as meaning to return NULL).
4432 *
4433 * Errors thrown here for invalid units should exactly match those that
4434 * would be thrown in the calling functions, else there will be unexpected
4435 * discrepancies between finite- and infinite-input cases.
4436 */
4437static float8
4438NonFiniteTimestampTzPart(int type, int unit, char *lowunits,
4439 bool isNegative, bool isTz)
4440{
4441 if ((type != UNITS) && (type != RESERV))
4442 {
4443 if (isTz)
4444 ereport(ERROR,
4445 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4446 errmsg("timestamp with time zone units \"%s\" not recognized",
4447 lowunits)));
4448 else
4449 ereport(ERROR,
4450 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4451 errmsg("timestamp units \"%s\" not recognized",
4452 lowunits)));
4453 }
4454
4455 switch (unit)
4456 {
4457 /* Oscillating units */
4458 case DTK_MICROSEC:
4459 case DTK_MILLISEC:
4460 case DTK_SECOND:
4461 case DTK_MINUTE:
4462 case DTK_HOUR:
4463 case DTK_DAY:
4464 case DTK_MONTH:
4465 case DTK_QUARTER:
4466 case DTK_WEEK:
4467 case DTK_DOW:
4468 case DTK_ISODOW:
4469 case DTK_DOY:
4470 case DTK_TZ:
4471 case DTK_TZ_MINUTE:
4472 case DTK_TZ_HOUR:
4473 return 0.0;
4474
4475 /* Monotonically-increasing units */
4476 case DTK_YEAR:
4477 case DTK_DECADE:
4478 case DTK_CENTURY:
4479 case DTK_MILLENNIUM:
4480 case DTK_JULIAN:
4481 case DTK_ISOYEAR:
4482 case DTK_EPOCH:
4483 if (isNegative)
4484 return -get_float8_infinity();
4485 else
4486 return get_float8_infinity();
4487
4488 default:
4489 if (isTz)
4490 ereport(ERROR,
4491 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4492 errmsg("timestamp with time zone units \"%s\" not supported",
4493 lowunits)));
4494 else
4495 ereport(ERROR,
4496 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4497 errmsg("timestamp units \"%s\" not supported",
4498 lowunits)));
4499 return 0.0; /* keep compiler quiet */
4500 }
4501}
4502
4503/* timestamp_part()
4504 * Extract specified field from timestamp.
4505 */
4506Datum
4507timestamp_part(PG_FUNCTION_ARGS)
4508{
4509 text *units = PG_GETARG_TEXT_PP(0);
4510 Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
4511 float8 result;
4512 Timestamp epoch;
4513 int type,
4514 val;
4515 char *lowunits;
4516 fsec_t fsec;
4517 struct pg_tm tt,
4518 *tm = &tt;
4519
4520 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
4521 VARSIZE_ANY_EXHDR(units),
4522 false);
4523
4524 type = DecodeUnits(0, lowunits, &val);
4525 if (type == UNKNOWN_FIELD)
4526 type = DecodeSpecial(0, lowunits, &val);
4527
4528 if (TIMESTAMP_NOT_FINITE(timestamp))
4529 {
4530 result = NonFiniteTimestampTzPart(type, val, lowunits,
4531 TIMESTAMP_IS_NOBEGIN(timestamp),
4532 false);
4533 if (result)
4534 PG_RETURN_FLOAT8(result);
4535 else
4536 PG_RETURN_NULL();
4537 }
4538
4539 if (type == UNITS)
4540 {
4541 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
4542 ereport(ERROR,
4543 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4544 errmsg("timestamp out of range")));
4545
4546 switch (val)
4547 {
4548 case DTK_MICROSEC:
4549 result = tm->tm_sec * 1000000.0 + fsec;
4550 break;
4551
4552 case DTK_MILLISEC:
4553 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
4554 break;
4555
4556 case DTK_SECOND:
4557 result = tm->tm_sec + fsec / 1000000.0;
4558 break;
4559
4560 case DTK_MINUTE:
4561 result = tm->tm_min;
4562 break;
4563
4564 case DTK_HOUR:
4565 result = tm->tm_hour;
4566 break;
4567
4568 case DTK_DAY:
4569 result = tm->tm_mday;
4570 break;
4571
4572 case DTK_MONTH:
4573 result = tm->tm_mon;
4574 break;
4575
4576 case DTK_QUARTER:
4577 result = (tm->tm_mon - 1) / 3 + 1;
4578 break;
4579
4580 case DTK_WEEK:
4581 result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
4582 break;
4583
4584 case DTK_YEAR:
4585 if (tm->tm_year > 0)
4586 result = tm->tm_year;
4587 else
4588 /* there is no year 0, just 1 BC and 1 AD */
4589 result = tm->tm_year - 1;
4590 break;
4591
4592 case DTK_DECADE:
4593
4594 /*
4595 * what is a decade wrt dates? let us assume that decade 199
4596 * is 1990 thru 1999... decade 0 starts on year 1 BC, and -1
4597 * is 11 BC thru 2 BC...
4598 */
4599 if (tm->tm_year >= 0)
4600 result = tm->tm_year / 10;
4601 else
4602 result = -((8 - (tm->tm_year - 1)) / 10);
4603 break;
4604
4605 case DTK_CENTURY:
4606
4607 /* ----
4608 * centuries AD, c>0: year in [ (c-1)* 100 + 1 : c*100 ]
4609 * centuries BC, c<0: year in [ c*100 : (c+1) * 100 - 1]
4610 * there is no number 0 century.
4611 * ----
4612 */
4613 if (tm->tm_year > 0)
4614 result = (tm->tm_year + 99) / 100;
4615 else
4616 /* caution: C division may have negative remainder */
4617 result = -((99 - (tm->tm_year - 1)) / 100);
4618 break;
4619
4620 case DTK_MILLENNIUM:
4621 /* see comments above. */
4622 if (tm->tm_year > 0)
4623 result = (tm->tm_year + 999) / 1000;
4624 else
4625 result = -((999 - (tm->tm_year - 1)) / 1000);
4626 break;
4627
4628 case DTK_JULIAN:
4629 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
4630 result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
4631 tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY;
4632 break;
4633
4634 case DTK_ISOYEAR:
4635 result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday);
4636 break;
4637
4638 case DTK_DOW:
4639 case DTK_ISODOW:
4640 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
4641 ereport(ERROR,
4642 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4643 errmsg("timestamp out of range")));
4644 result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
4645 if (val == DTK_ISODOW && result == 0)
4646 result = 7;
4647 break;
4648
4649 case DTK_DOY:
4650 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
4651 ereport(ERROR,
4652 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4653 errmsg("timestamp out of range")));
4654 result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
4655 - date2j(tm->tm_year, 1, 1) + 1);
4656 break;
4657
4658 case DTK_TZ:
4659 case DTK_TZ_MINUTE:
4660 case DTK_TZ_HOUR:
4661 default:
4662 ereport(ERROR,
4663 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4664 errmsg("timestamp units \"%s\" not supported",
4665 lowunits)));
4666 result = 0;
4667 }
4668 }
4669 else if (type == RESERV)
4670 {
4671 switch (val)
4672 {
4673 case DTK_EPOCH:
4674 epoch = SetEpochTimestamp();
4675 /* try to avoid precision loss in subtraction */
4676 if (timestamp < (PG_INT64_MAX + epoch))
4677 result = (timestamp - epoch) / 1000000.0;
4678 else
4679 result = ((float8) timestamp - epoch) / 1000000.0;
4680 break;
4681
4682 default:
4683 ereport(ERROR,
4684 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4685 errmsg("timestamp units \"%s\" not supported",
4686 lowunits)));
4687 result = 0;
4688 }
4689
4690 }
4691 else
4692 {
4693 ereport(ERROR,
4694 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4695 errmsg("timestamp units \"%s\" not recognized", lowunits)));
4696 result = 0;
4697 }
4698
4699 PG_RETURN_FLOAT8(result);
4700}
4701
4702/* timestamptz_part()
4703 * Extract specified field from timestamp with time zone.
4704 */
4705Datum
4706timestamptz_part(PG_FUNCTION_ARGS)
4707{
4708 text *units = PG_GETARG_TEXT_PP(0);
4709 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4710 float8 result;
4711 Timestamp epoch;
4712 int tz;
4713 int type,
4714 val;
4715 char *lowunits;
4716 double dummy;
4717 fsec_t fsec;
4718 struct pg_tm tt,
4719 *tm = &tt;
4720
4721 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
4722 VARSIZE_ANY_EXHDR(units),
4723 false);
4724
4725 type = DecodeUnits(0, lowunits, &val);
4726 if (type == UNKNOWN_FIELD)
4727 type = DecodeSpecial(0, lowunits, &val);
4728
4729 if (TIMESTAMP_NOT_FINITE(timestamp))
4730 {
4731 result = NonFiniteTimestampTzPart(type, val, lowunits,
4732 TIMESTAMP_IS_NOBEGIN(timestamp),
4733 true);
4734 if (result)
4735 PG_RETURN_FLOAT8(result);
4736 else
4737 PG_RETURN_NULL();
4738 }
4739
4740 if (type == UNITS)
4741 {
4742 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
4743 ereport(ERROR,
4744 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4745 errmsg("timestamp out of range")));
4746
4747 switch (val)
4748 {
4749 case DTK_TZ:
4750 result = -tz;
4751 break;
4752
4753 case DTK_TZ_MINUTE:
4754 result = -tz;
4755 result /= MINS_PER_HOUR;
4756 FMODULO(result, dummy, (double) MINS_PER_HOUR);
4757 break;
4758
4759 case DTK_TZ_HOUR:
4760 dummy = -tz;
4761 FMODULO(dummy, result, (double) SECS_PER_HOUR);
4762 break;
4763
4764 case DTK_MICROSEC:
4765 result = tm->tm_sec * 1000000.0 + fsec;
4766 break;
4767
4768 case DTK_MILLISEC:
4769 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
4770 break;
4771
4772 case DTK_SECOND:
4773 result = tm->tm_sec + fsec / 1000000.0;
4774 break;
4775
4776 case DTK_MINUTE:
4777 result = tm->tm_min;
4778 break;
4779
4780 case DTK_HOUR:
4781 result = tm->tm_hour;
4782 break;
4783
4784 case DTK_DAY:
4785 result = tm->tm_mday;
4786 break;
4787
4788 case DTK_MONTH:
4789 result = tm->tm_mon;
4790 break;
4791
4792 case DTK_QUARTER:
4793 result = (tm->tm_mon - 1) / 3 + 1;
4794 break;
4795
4796 case DTK_WEEK:
4797 result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
4798 break;
4799
4800 case DTK_YEAR:
4801 if (tm->tm_year > 0)
4802 result = tm->tm_year;
4803 else
4804 /* there is no year 0, just 1 BC and 1 AD */
4805 result = tm->tm_year - 1;
4806 break;
4807
4808 case DTK_DECADE:
4809 /* see comments in timestamp_part */
4810 if (tm->tm_year > 0)
4811 result = tm->tm_year / 10;
4812 else
4813 result = -((8 - (tm->tm_year - 1)) / 10);
4814 break;
4815
4816 case DTK_CENTURY:
4817 /* see comments in timestamp_part */
4818 if (tm->tm_year > 0)
4819 result = (tm->tm_year + 99) / 100;
4820 else
4821 result = -((99 - (tm->tm_year - 1)) / 100);
4822 break;
4823
4824 case DTK_MILLENNIUM:
4825 /* see comments in timestamp_part */
4826 if (tm->tm_year > 0)
4827 result = (tm->tm_year + 999) / 1000;
4828 else
4829 result = -((999 - (tm->tm_year - 1)) / 1000);
4830 break;
4831
4832 case DTK_JULIAN:
4833 result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
4834 result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
4835 tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY;
4836 break;
4837
4838 case DTK_ISOYEAR:
4839 result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday);
4840 break;
4841
4842 case DTK_DOW:
4843 case DTK_ISODOW:
4844 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
4845 ereport(ERROR,
4846 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4847 errmsg("timestamp out of range")));
4848 result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
4849 if (val == DTK_ISODOW && result == 0)
4850 result = 7;
4851 break;
4852
4853 case DTK_DOY:
4854 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
4855 ereport(ERROR,
4856 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4857 errmsg("timestamp out of range")));
4858 result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
4859 - date2j(tm->tm_year, 1, 1) + 1);
4860 break;
4861
4862 default:
4863 ereport(ERROR,
4864 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4865 errmsg("timestamp with time zone units \"%s\" not supported",
4866 lowunits)));
4867 result = 0;
4868 }
4869
4870 }
4871 else if (type == RESERV)
4872 {
4873 switch (val)
4874 {
4875 case DTK_EPOCH:
4876 epoch = SetEpochTimestamp();
4877 /* try to avoid precision loss in subtraction */
4878 if (timestamp < (PG_INT64_MAX + epoch))
4879 result = (timestamp - epoch) / 1000000.0;
4880 else
4881 result = ((float8) timestamp - epoch) / 1000000.0;
4882 break;
4883
4884 default:
4885 ereport(ERROR,
4886 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4887 errmsg("timestamp with time zone units \"%s\" not supported",
4888 lowunits)));
4889 result = 0;
4890 }
4891 }
4892 else
4893 {
4894 ereport(ERROR,
4895 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4896 errmsg("timestamp with time zone units \"%s\" not recognized",
4897 lowunits)));
4898
4899 result = 0;
4900 }
4901
4902 PG_RETURN_FLOAT8(result);
4903}
4904
4905
4906/* interval_part()
4907 * Extract specified field from interval.
4908 */
4909Datum
4910interval_part(PG_FUNCTION_ARGS)
4911{
4912 text *units = PG_GETARG_TEXT_PP(0);
4913 Interval *interval = PG_GETARG_INTERVAL_P(1);
4914 float8 result;
4915 int type,
4916 val;
4917 char *lowunits;
4918 fsec_t fsec;
4919 struct pg_tm tt,
4920 *tm = &tt;
4921
4922 lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
4923 VARSIZE_ANY_EXHDR(units),
4924 false);
4925
4926 type = DecodeUnits(0, lowunits, &val);
4927 if (type == UNKNOWN_FIELD)
4928 type = DecodeSpecial(0, lowunits, &val);
4929
4930 if (type == UNITS)
4931 {
4932 if (interval2tm(*interval, tm, &fsec) == 0)
4933 {
4934 switch (val)
4935 {
4936 case DTK_MICROSEC:
4937 result = tm->tm_sec * 1000000.0 + fsec;
4938 break;
4939
4940 case DTK_MILLISEC:
4941 result = tm->tm_sec * 1000.0 + fsec / 1000.0;
4942 break;
4943
4944 case DTK_SECOND:
4945 result = tm->tm_sec + fsec / 1000000.0;
4946 break;
4947
4948 case DTK_MINUTE:
4949 result = tm->tm_min;
4950 break;
4951
4952 case DTK_HOUR:
4953 result = tm->tm_hour;
4954 break;
4955
4956 case DTK_DAY:
4957 result = tm->tm_mday;
4958 break;
4959
4960 case DTK_MONTH:
4961 result = tm->tm_mon;
4962 break;
4963
4964 case DTK_QUARTER:
4965 result = (tm->tm_mon / 3) + 1;
4966 break;
4967
4968 case DTK_YEAR:
4969 result = tm->tm_year;
4970 break;
4971
4972 case DTK_DECADE:
4973 /* caution: C division may have negative remainder */
4974 result = tm->tm_year / 10;
4975 break;
4976
4977 case DTK_CENTURY:
4978 /* caution: C division may have negative remainder */
4979 result = tm->tm_year / 100;
4980 break;
4981
4982 case DTK_MILLENNIUM:
4983 /* caution: C division may have negative remainder */
4984 result = tm->tm_year / 1000;
4985 break;
4986
4987 default:
4988 ereport(ERROR,
4989 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4990 errmsg("interval units \"%s\" not supported",
4991 lowunits)));
4992 result = 0;
4993 }
4994
4995 }
4996 else
4997 {
4998 elog(ERROR, "could not convert interval to tm");
4999 result = 0;
5000 }
5001 }
5002 else if (type == RESERV && val == DTK_EPOCH)
5003 {
5004 result = interval->time / 1000000.0;
5005 result += ((double) DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR);
5006 result += ((double) DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR);
5007 result += ((double) SECS_PER_DAY) * interval->day;
5008 }
5009 else
5010 {
5011 ereport(ERROR,
5012 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5013 errmsg("interval units \"%s\" not recognized",
5014 lowunits)));
5015 result = 0;
5016 }
5017
5018 PG_RETURN_FLOAT8(result);
5019}
5020
5021
5022/* timestamp_zone()
5023 * Encode timestamp type with specified time zone.
5024 * This function is just timestamp2timestamptz() except instead of
5025 * shifting to the global timezone, we shift to the specified timezone.
5026 * This is different from the other AT TIME ZONE cases because instead
5027 * of shifting _to_ a new time zone, it sets the time to _be_ the
5028 * specified timezone.
5029 */
5030Datum
5031timestamp_zone(PG_FUNCTION_ARGS)
5032{
5033 text *zone = PG_GETARG_TEXT_PP(0);
5034 Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
5035 TimestampTz result;
5036 int tz;
5037 char tzname[TZ_STRLEN_MAX + 1];
5038 char *lowzone;
5039 int type,
5040 val;
5041 pg_tz *tzp;
5042 struct pg_tm tm;
5043 fsec_t fsec;
5044
5045 if (TIMESTAMP_NOT_FINITE(timestamp))
5046 PG_RETURN_TIMESTAMPTZ(timestamp);
5047
5048 /*
5049 * Look up the requested timezone. First we look in the timezone
5050 * abbreviation table (to handle cases like "EST"), and if that fails, we
5051 * look in the timezone database (to handle cases like
5052 * "America/New_York"). (This matches the order in which timestamp input
5053 * checks the cases; it's important because the timezone database unwisely
5054 * uses a few zone names that are identical to offset abbreviations.)
5055 */
5056 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
5057
5058 /* DecodeTimezoneAbbrev requires lowercase input */
5059 lowzone = downcase_truncate_identifier(tzname,
5060 strlen(tzname),
5061 false);
5062
5063 type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
5064
5065 if (type == TZ || type == DTZ)
5066 {
5067 /* fixed-offset abbreviation */
5068 tz = val;
5069 result = dt2local(timestamp, tz);
5070 }
5071 else if (type == DYNTZ)
5072 {
5073 /* dynamic-offset abbreviation, resolve using specified time */
5074 if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
5075 ereport(ERROR,
5076 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5077 errmsg("timestamp out of range")));
5078 tz = -DetermineTimeZoneAbbrevOffset(&tm, tzname, tzp);
5079 result = dt2local(timestamp, tz);
5080 }
5081 else
5082 {
5083 /* try it as a full zone name */
5084 tzp = pg_tzset(tzname);
5085 if (tzp)
5086 {
5087 /* Apply the timezone change */
5088 if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
5089 ereport(ERROR,
5090 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5091 errmsg("timestamp out of range")));
5092 tz = DetermineTimeZoneOffset(&tm, tzp);
5093 if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
5094 ereport(ERROR,
5095 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5096 errmsg("timestamp out of range")));
5097 }
5098 else
5099 {
5100 ereport(ERROR,
5101 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5102 errmsg("time zone \"%s\" not recognized", tzname)));
5103 result = 0; /* keep compiler quiet */
5104 }
5105 }
5106
5107 if (!IS_VALID_TIMESTAMP(result))
5108 ereport(ERROR,
5109 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5110 errmsg("timestamp out of range")));
5111
5112 PG_RETURN_TIMESTAMPTZ(result);
5113}
5114
5115/* timestamp_izone()
5116 * Encode timestamp type with specified time interval as time zone.
5117 */
5118Datum
5119timestamp_izone(PG_FUNCTION_ARGS)
5120{
5121 Interval *zone = PG_GETARG_INTERVAL_P(0);
5122 Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
5123 TimestampTz result;
5124 int tz;
5125
5126 if (TIMESTAMP_NOT_FINITE(timestamp))
5127 PG_RETURN_TIMESTAMPTZ(timestamp);
5128
5129 if (zone->month != 0 || zone->day != 0)
5130 ereport(ERROR,
5131 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5132 errmsg("interval time zone \"%s\" must not include months or days",
5133 DatumGetCString(DirectFunctionCall1(interval_out,
5134 PointerGetDatum(zone))))));
5135
5136 tz = zone->time / USECS_PER_SEC;
5137
5138 result = dt2local(timestamp, tz);
5139
5140 if (!IS_VALID_TIMESTAMP(result))
5141 ereport(ERROR,
5142 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5143 errmsg("timestamp out of range")));
5144
5145 PG_RETURN_TIMESTAMPTZ(result);
5146} /* timestamp_izone() */
5147
5148/* TimestampTimestampTzRequiresRewrite()
5149 *
5150 * Returns false if the TimeZone GUC setting causes timestamp_timestamptz and
5151 * timestamptz_timestamp to be no-ops, where the return value has the same
5152 * bits as the argument. Since project convention is to assume a GUC changes
5153 * no more often than STABLE functions change, the answer is valid that long.
5154 */
5155bool
5156TimestampTimestampTzRequiresRewrite(void)
5157{
5158 long offset;
5159
5160 if (pg_get_timezone_offset(session_timezone, &offset) && offset == 0)
5161 return false;
5162 return true;
5163}
5164
5165/* timestamp_timestamptz()
5166 * Convert local timestamp to timestamp at GMT
5167 */
5168Datum
5169timestamp_timestamptz(PG_FUNCTION_ARGS)
5170{
5171 Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
5172
5173 PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
5174}
5175
5176static TimestampTz
5177timestamp2timestamptz(Timestamp timestamp)
5178{
5179 TimestampTz result;
5180 struct pg_tm tt,
5181 *tm = &tt;
5182 fsec_t fsec;
5183 int tz;
5184
5185 if (TIMESTAMP_NOT_FINITE(timestamp))
5186 result = timestamp;
5187 else
5188 {
5189 if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
5190 ereport(ERROR,
5191 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5192 errmsg("timestamp out of range")));
5193
5194 tz = DetermineTimeZoneOffset(tm, session_timezone);
5195
5196 if (tm2timestamp(tm, fsec, &tz, &result) != 0)
5197 ereport(ERROR,
5198 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5199 errmsg("timestamp out of range")));
5200 }
5201
5202 return result;
5203}
5204
5205/* timestamptz_timestamp()
5206 * Convert timestamp at GMT to local timestamp
5207 */
5208Datum
5209timestamptz_timestamp(PG_FUNCTION_ARGS)
5210{
5211 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
5212
5213 PG_RETURN_TIMESTAMP(timestamptz2timestamp(timestamp));
5214}
5215
5216static Timestamp
5217timestamptz2timestamp(TimestampTz timestamp)
5218{
5219 Timestamp result;
5220 struct pg_tm tt,
5221 *tm = &tt;
5222 fsec_t fsec;
5223 int tz;
5224
5225 if (TIMESTAMP_NOT_FINITE(timestamp))
5226 result = timestamp;
5227 else
5228 {
5229 if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
5230 ereport(ERROR,
5231 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5232 errmsg("timestamp out of range")));
5233 if (tm2timestamp(tm, fsec, NULL, &result) != 0)
5234 ereport(ERROR,
5235 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5236 errmsg("timestamp out of range")));
5237 }
5238 return result;
5239}
5240
5241/* timestamptz_zone()
5242 * Evaluate timestamp with time zone type at the specified time zone.
5243 * Returns a timestamp without time zone.
5244 */
5245Datum
5246timestamptz_zone(PG_FUNCTION_ARGS)
5247{
5248 text *zone = PG_GETARG_TEXT_PP(0);
5249 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
5250 Timestamp result;
5251 int tz;
5252 char tzname[TZ_STRLEN_MAX + 1];
5253 char *lowzone;
5254 int type,
5255 val;
5256 pg_tz *tzp;
5257
5258 if (TIMESTAMP_NOT_FINITE(timestamp))
5259 PG_RETURN_TIMESTAMP(timestamp);
5260
5261 /*
5262 * Look up the requested timezone. First we look in the timezone
5263 * abbreviation table (to handle cases like "EST"), and if that fails, we
5264 * look in the timezone database (to handle cases like
5265 * "America/New_York"). (This matches the order in which timestamp input
5266 * checks the cases; it's important because the timezone database unwisely
5267 * uses a few zone names that are identical to offset abbreviations.)
5268 */
5269 text_to_cstring_buffer(zone, tzname, sizeof(tzname));
5270
5271 /* DecodeTimezoneAbbrev requires lowercase input */
5272 lowzone = downcase_truncate_identifier(tzname,
5273 strlen(tzname),
5274 false);
5275
5276 type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp);
5277
5278 if (type == TZ || type == DTZ)
5279 {
5280 /* fixed-offset abbreviation */
5281 tz = -val;
5282 result = dt2local(timestamp, tz);
5283 }
5284 else if (type == DYNTZ)
5285 {
5286 /* dynamic-offset abbreviation, resolve using specified time */
5287 int isdst;
5288
5289 tz = DetermineTimeZoneAbbrevOffsetTS(timestamp, tzname, tzp, &isdst);
5290 result = dt2local(timestamp, tz);
5291 }
5292 else
5293 {
5294 /* try it as a full zone name */
5295 tzp = pg_tzset(tzname);
5296 if (tzp)
5297 {
5298 /* Apply the timezone change */
5299 struct pg_tm tm;
5300 fsec_t fsec;
5301
5302 if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
5303 ereport(ERROR,
5304 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5305 errmsg("timestamp out of range")));
5306 if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
5307 ereport(ERROR,
5308 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5309 errmsg("timestamp out of range")));
5310 }
5311 else
5312 {
5313 ereport(ERROR,
5314 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5315 errmsg("time zone \"%s\" not recognized", tzname)));
5316 result = 0; /* keep compiler quiet */
5317 }
5318 }
5319
5320 if (!IS_VALID_TIMESTAMP(result))
5321 ereport(ERROR,
5322 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5323 errmsg("timestamp out of range")));
5324
5325 PG_RETURN_TIMESTAMP(result);
5326}
5327
5328/* timestamptz_izone()
5329 * Encode timestamp with time zone type with specified time interval as time zone.
5330 * Returns a timestamp without time zone.
5331 */
5332Datum
5333timestamptz_izone(PG_FUNCTION_ARGS)
5334{
5335 Interval *zone = PG_GETARG_INTERVAL_P(0);
5336 TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
5337 Timestamp result;
5338 int tz;
5339
5340 if (TIMESTAMP_NOT_FINITE(timestamp))
5341 PG_RETURN_TIMESTAMP(timestamp);
5342
5343 if (zone->month != 0 || zone->day != 0)
5344 ereport(ERROR,
5345 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5346 errmsg("interval time zone \"%s\" must not include months or days",
5347 DatumGetCString(DirectFunctionCall1(interval_out,
5348 PointerGetDatum(zone))))));
5349
5350 tz = -(zone->time / USECS_PER_SEC);
5351
5352 result = dt2local(timestamp, tz);
5353
5354 if (!IS_VALID_TIMESTAMP(result))
5355 ereport(ERROR,
5356 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5357 errmsg("timestamp out of range")));
5358
5359 PG_RETURN_TIMESTAMP(result);
5360}
5361
5362/* generate_series_timestamp()
5363 * Generate the set of timestamps from start to finish by step
5364 */
5365Datum
5366generate_series_timestamp(PG_FUNCTION_ARGS)
5367{
5368 FuncCallContext *funcctx;
5369 generate_series_timestamp_fctx *fctx;
5370 Timestamp result;
5371
5372 /* stuff done only on the first call of the function */
5373 if (SRF_IS_FIRSTCALL())
5374 {
5375 Timestamp start = PG_GETARG_TIMESTAMP(0);
5376 Timestamp finish = PG_GETARG_TIMESTAMP(1);
5377 Interval *step = PG_GETARG_INTERVAL_P(2);
5378 MemoryContext oldcontext;
5379 Interval interval_zero;
5380
5381 /* create a function context for cross-call persistence */
5382 funcctx = SRF_FIRSTCALL_INIT();
5383
5384 /*
5385 * switch to memory context appropriate for multiple function calls
5386 */
5387 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5388
5389 /* allocate memory for user context */
5390 fctx = (generate_series_timestamp_fctx *)
5391 palloc(sizeof(generate_series_timestamp_fctx));
5392
5393 /*
5394 * Use fctx to keep state from call to call. Seed current with the
5395 * original start value
5396 */
5397 fctx->current = start;
5398 fctx->finish = finish;
5399 fctx->step = *step;
5400
5401 /* Determine sign of the interval */
5402 MemSet(&interval_zero, 0, sizeof(Interval));
5403 fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero);
5404
5405 if (fctx->step_sign == 0)
5406 ereport(ERROR,
5407 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5408 errmsg("step size cannot equal zero")));
5409
5410 funcctx->user_fctx = fctx;
5411 MemoryContextSwitchTo(oldcontext);
5412 }
5413
5414 /* stuff done on every call of the function */
5415 funcctx = SRF_PERCALL_SETUP();
5416
5417 /*
5418 * get the saved state and use current as the result for this iteration
5419 */
5420 fctx = funcctx->user_fctx;
5421 result = fctx->current;
5422
5423 if (fctx->step_sign > 0 ?
5424 timestamp_cmp_internal(result, fctx->finish) <= 0 :
5425 timestamp_cmp_internal(result, fctx->finish) >= 0)
5426 {
5427 /* increment current in preparation for next iteration */
5428 fctx->current = DatumGetTimestamp(
5429 DirectFunctionCall2(timestamp_pl_interval,
5430 TimestampGetDatum(fctx->current),
5431 PointerGetDatum(&fctx->step)));
5432
5433 /* do when there is more left to send */
5434 SRF_RETURN_NEXT(funcctx, TimestampGetDatum(result));
5435 }
5436 else
5437 {
5438 /* do when there is no more left */
5439 SRF_RETURN_DONE(funcctx);
5440 }
5441}
5442
5443/* generate_series_timestamptz()
5444 * Generate the set of timestamps from start to finish by step
5445 */
5446Datum
5447generate_series_timestamptz(PG_FUNCTION_ARGS)
5448{
5449 FuncCallContext *funcctx;
5450 generate_series_timestamptz_fctx *fctx;
5451 TimestampTz result;
5452
5453 /* stuff done only on the first call of the function */
5454 if (SRF_IS_FIRSTCALL())
5455 {
5456 TimestampTz start = PG_GETARG_TIMESTAMPTZ(0);
5457 TimestampTz finish = PG_GETARG_TIMESTAMPTZ(1);
5458 Interval *step = PG_GETARG_INTERVAL_P(2);
5459 MemoryContext oldcontext;
5460 Interval interval_zero;
5461
5462 /* create a function context for cross-call persistence */
5463 funcctx = SRF_FIRSTCALL_INIT();
5464
5465 /*
5466 * switch to memory context appropriate for multiple function calls
5467 */
5468 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5469
5470 /* allocate memory for user context */
5471 fctx = (generate_series_timestamptz_fctx *)
5472 palloc(sizeof(generate_series_timestamptz_fctx));
5473
5474 /*
5475 * Use fctx to keep state from call to call. Seed current with the
5476 * original start value
5477 */
5478 fctx->current = start;
5479 fctx->finish = finish;
5480 fctx->step = *step;
5481
5482 /* Determine sign of the interval */
5483 MemSet(&interval_zero, 0, sizeof(Interval));
5484 fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero);
5485
5486 if (fctx->step_sign == 0)
5487 ereport(ERROR,
5488 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5489 errmsg("step size cannot equal zero")));
5490
5491 funcctx->user_fctx = fctx;
5492 MemoryContextSwitchTo(oldcontext);
5493 }
5494
5495 /* stuff done on every call of the function */
5496 funcctx = SRF_PERCALL_SETUP();
5497
5498 /*
5499 * get the saved state and use current as the result for this iteration
5500 */
5501 fctx = funcctx->user_fctx;
5502 result = fctx->current;
5503
5504 if (fctx->step_sign > 0 ?
5505 timestamp_cmp_internal(result, fctx->finish) <= 0 :
5506 timestamp_cmp_internal(result, fctx->finish) >= 0)
5507 {
5508 /* increment current in preparation for next iteration */
5509 fctx->current = DatumGetTimestampTz(
5510 DirectFunctionCall2(timestamptz_pl_interval,
5511 TimestampTzGetDatum(fctx->current),
5512 PointerGetDatum(&fctx->step)));
5513
5514 /* do when there is more left to send */
5515 SRF_RETURN_NEXT(funcctx, TimestampTzGetDatum(result));
5516 }
5517 else
5518 {
5519 /* do when there is no more left */
5520 SRF_RETURN_DONE(funcctx);
5521 }
5522}
5523