1 /*-------------------------------------------------------------------------
4 * Functions for the built-in SQL types "timestamp" and "interval".
6 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/utils/adt/timestamp.c
13 *-------------------------------------------------------------------------
23 #include "access/xact.h"
24 #include "catalog/pg_type.h"
25 #include "common/int.h"
26 #include "common/int128.h"
28 #include "libpq/pqformat.h"
29 #include "miscadmin.h"
30 #include "nodes/makefuncs.h"
31 #include "nodes/nodeFuncs.h"
32 #include "nodes/supportnodes.h"
33 #include "parser/scansup.h"
34 #include "utils/array.h"
35 #include "utils/builtins.h"
36 #include "utils/date.h"
37 #include "utils/datetime.h"
38 #include "utils/float.h"
39 #include "utils/numeric.h"
40 #include "utils/sortsupport.h"
43 * gcc's -ffast-math switch breaks routines that expect exact results from
44 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
47 #error -ffast-math is known to break this code
50 #define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
52 /* Set at postmaster start */
53 TimestampTz PgStartTime
;
55 /* Set at configuration reload */
56 TimestampTz PgReloadTime
;
64 } generate_series_timestamp_fctx
;
72 } generate_series_timestamptz_fctx
;
75 static TimeOffset
time2t(const int hour
, const int min
, const int sec
, const fsec_t fsec
);
76 static Timestamp
dt2local(Timestamp dt
, int timezone
);
77 static bool AdjustIntervalForTypmod(Interval
*interval
, int32 typmod
,
79 static TimestampTz
timestamp2timestamptz(Timestamp timestamp
);
80 static Timestamp
timestamptz2timestamp(TimestampTz timestamp
);
83 /* common code for timestamptypmodin and timestamptztypmodin */
85 anytimestamp_typmod_check(bool istz
, int32 typmod
)
89 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
90 errmsg("TIMESTAMP(%d)%s precision must not be negative",
91 typmod
, (istz
? " WITH TIME ZONE" : ""))));
92 if (typmod
> MAX_TIMESTAMP_PRECISION
)
95 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
96 errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d",
97 typmod
, (istz
? " WITH TIME ZONE" : ""),
98 MAX_TIMESTAMP_PRECISION
)));
99 typmod
= MAX_TIMESTAMP_PRECISION
;
106 anytimestamp_typmodin(bool istz
, ArrayType
*ta
)
111 tl
= ArrayGetIntegerTypmods(ta
, &n
);
114 * we're not too tense about good error message here because grammar
115 * shouldn't allow wrong number of modifiers for TIMESTAMP
119 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
120 errmsg("invalid type modifier")));
122 return anytimestamp_typmod_check(istz
, tl
[0]);
125 /* common code for timestamptypmodout and timestamptztypmodout */
127 anytimestamp_typmodout(bool istz
, int32 typmod
)
129 const char *tz
= istz
? " with time zone" : " without time zone";
132 return psprintf("(%d)%s", (int) typmod
, tz
);
138 /*****************************************************************************
139 * USER I/O ROUTINES *
140 *****************************************************************************/
143 * Convert a string to internal form.
146 timestamp_in(PG_FUNCTION_ARGS
)
148 char *str
= PG_GETARG_CSTRING(0);
150 Oid typelem
= PG_GETARG_OID(1);
152 int32 typmod
= PG_GETARG_INT32(2);
153 Node
*escontext
= fcinfo
->context
;
162 char *field
[MAXDATEFIELDS
];
163 int ftype
[MAXDATEFIELDS
];
164 char workbuf
[MAXDATELEN
+ MAXDATEFIELDS
];
165 DateTimeErrorExtra extra
;
167 dterr
= ParseDateTime(str
, workbuf
, sizeof(workbuf
),
168 field
, ftype
, MAXDATEFIELDS
, &nf
);
170 dterr
= DecodeDateTime(field
, ftype
, nf
,
171 &dtype
, tm
, &fsec
, &tz
, &extra
);
174 DateTimeParseError(dterr
, &extra
, str
, "timestamp", escontext
);
181 if (tm2timestamp(tm
, fsec
, NULL
, &result
) != 0)
182 ereturn(escontext
, (Datum
) 0,
183 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
184 errmsg("timestamp out of range: \"%s\"", str
)));
188 result
= SetEpochTimestamp();
192 TIMESTAMP_NOEND(result
);
196 TIMESTAMP_NOBEGIN(result
);
200 elog(ERROR
, "unexpected dtype %d while parsing timestamp \"%s\"",
202 TIMESTAMP_NOEND(result
);
205 AdjustTimestampForTypmod(&result
, typmod
, escontext
);
207 PG_RETURN_TIMESTAMP(result
);
211 * Convert a timestamp to external form.
214 timestamp_out(PG_FUNCTION_ARGS
)
216 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
221 char buf
[MAXDATELEN
+ 1];
223 if (TIMESTAMP_NOT_FINITE(timestamp
))
224 EncodeSpecialTimestamp(timestamp
, buf
);
225 else if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) == 0)
226 EncodeDateTime(tm
, fsec
, false, 0, NULL
, DateStyle
, buf
);
229 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
230 errmsg("timestamp out of range")));
232 result
= pstrdup(buf
);
233 PG_RETURN_CSTRING(result
);
237 * timestamp_recv - converts external binary format to timestamp
240 timestamp_recv(PG_FUNCTION_ARGS
)
242 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
245 Oid typelem
= PG_GETARG_OID(1);
247 int32 typmod
= PG_GETARG_INT32(2);
253 timestamp
= (Timestamp
) pq_getmsgint64(buf
);
255 /* range check: see if timestamp_out would like it */
256 if (TIMESTAMP_NOT_FINITE(timestamp
))
258 else if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0 ||
259 !IS_VALID_TIMESTAMP(timestamp
))
261 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
262 errmsg("timestamp out of range")));
264 AdjustTimestampForTypmod(×tamp
, typmod
, NULL
);
266 PG_RETURN_TIMESTAMP(timestamp
);
270 * timestamp_send - converts timestamp to binary format
273 timestamp_send(PG_FUNCTION_ARGS
)
275 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
278 pq_begintypsend(&buf
);
279 pq_sendint64(&buf
, timestamp
);
280 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
284 timestamptypmodin(PG_FUNCTION_ARGS
)
286 ArrayType
*ta
= PG_GETARG_ARRAYTYPE_P(0);
288 PG_RETURN_INT32(anytimestamp_typmodin(false, ta
));
292 timestamptypmodout(PG_FUNCTION_ARGS
)
294 int32 typmod
= PG_GETARG_INT32(0);
296 PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod
));
301 * timestamp_support()
303 * Planner support function for the timestamp_scale() and timestamptz_scale()
304 * length coercion functions (we need not distinguish them here).
307 timestamp_support(PG_FUNCTION_ARGS
)
309 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
312 if (IsA(rawreq
, SupportRequestSimplify
))
314 SupportRequestSimplify
*req
= (SupportRequestSimplify
*) rawreq
;
316 ret
= TemporalSimplify(MAX_TIMESTAMP_PRECISION
, (Node
*) req
->fcall
);
319 PG_RETURN_POINTER(ret
);
323 * Adjust time type for specified scale factor.
324 * Used by PostgreSQL type system to stuff columns.
327 timestamp_scale(PG_FUNCTION_ARGS
)
329 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
330 int32 typmod
= PG_GETARG_INT32(1);
335 AdjustTimestampForTypmod(&result
, typmod
, NULL
);
337 PG_RETURN_TIMESTAMP(result
);
341 * AdjustTimestampForTypmod --- round off a timestamp to suit given typmod
342 * Works for either timestamp or timestamptz.
344 * Returns true on success, false on failure (if escontext points to an
345 * ErrorSaveContext; otherwise errors are thrown).
348 AdjustTimestampForTypmod(Timestamp
*time
, int32 typmod
, Node
*escontext
)
350 static const int64 TimestampScales
[MAX_TIMESTAMP_PRECISION
+ 1] = {
360 static const int64 TimestampOffsets
[MAX_TIMESTAMP_PRECISION
+ 1] = {
370 if (!TIMESTAMP_NOT_FINITE(*time
)
371 && (typmod
!= -1) && (typmod
!= MAX_TIMESTAMP_PRECISION
))
373 if (typmod
< 0 || typmod
> MAX_TIMESTAMP_PRECISION
)
374 ereturn(escontext
, false,
375 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
376 errmsg("timestamp(%d) precision must be between %d and %d",
377 typmod
, 0, MAX_TIMESTAMP_PRECISION
)));
379 if (*time
>= INT64CONST(0))
381 *time
= ((*time
+ TimestampOffsets
[typmod
]) / TimestampScales
[typmod
]) *
382 TimestampScales
[typmod
];
386 *time
= -((((-*time
) + TimestampOffsets
[typmod
]) / TimestampScales
[typmod
])
387 * TimestampScales
[typmod
]);
395 * Convert a string to internal form.
398 timestamptz_in(PG_FUNCTION_ARGS
)
400 char *str
= PG_GETARG_CSTRING(0);
402 Oid typelem
= PG_GETARG_OID(1);
404 int32 typmod
= PG_GETARG_INT32(2);
405 Node
*escontext
= fcinfo
->context
;
414 char *field
[MAXDATEFIELDS
];
415 int ftype
[MAXDATEFIELDS
];
416 char workbuf
[MAXDATELEN
+ MAXDATEFIELDS
];
417 DateTimeErrorExtra extra
;
419 dterr
= ParseDateTime(str
, workbuf
, sizeof(workbuf
),
420 field
, ftype
, MAXDATEFIELDS
, &nf
);
422 dterr
= DecodeDateTime(field
, ftype
, nf
,
423 &dtype
, tm
, &fsec
, &tz
, &extra
);
426 DateTimeParseError(dterr
, &extra
, str
, "timestamp with time zone",
434 if (tm2timestamp(tm
, fsec
, &tz
, &result
) != 0)
435 ereturn(escontext
, (Datum
) 0,
436 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
437 errmsg("timestamp out of range: \"%s\"", str
)));
441 result
= SetEpochTimestamp();
445 TIMESTAMP_NOEND(result
);
449 TIMESTAMP_NOBEGIN(result
);
453 elog(ERROR
, "unexpected dtype %d while parsing timestamptz \"%s\"",
455 TIMESTAMP_NOEND(result
);
458 AdjustTimestampForTypmod(&result
, typmod
, escontext
);
460 PG_RETURN_TIMESTAMPTZ(result
);
464 * Try to parse a timezone specification, and return its timezone offset value
465 * if it's acceptable. Otherwise, an error is thrown.
467 * Note: some code paths update tm->tm_isdst, and some don't; current callers
468 * don't care, so we don't bother being consistent.
471 parse_sane_timezone(struct pg_tm
*tm
, text
*zone
)
473 char tzname
[TZ_STRLEN_MAX
+ 1];
477 text_to_cstring_buffer(zone
, tzname
, sizeof(tzname
));
480 * Look up the requested timezone. First we try to interpret it as a
481 * numeric timezone specification; if DecodeTimezone decides it doesn't
482 * like the format, we look in the timezone abbreviation table (to handle
483 * cases like "EST"), and if that also fails, we look in the timezone
484 * database (to handle cases like "America/New_York"). (This matches the
485 * order in which timestamp input checks the cases; it's important because
486 * the timezone database unwisely uses a few zone names that are identical
487 * to offset abbreviations.)
489 * Note pg_tzset happily parses numeric input that DecodeTimezone would
490 * reject. To avoid having it accept input that would otherwise be seen
491 * as invalid, it's enough to disallow having a digit in the first
492 * position of our input string.
494 if (isdigit((unsigned char) *tzname
))
496 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
497 errmsg("invalid input syntax for type %s: \"%s\"",
498 "numeric time zone", tzname
),
499 errhint("Numeric time zones must have \"-\" or \"+\" as first character.")));
501 dterr
= DecodeTimezone(tzname
, &tz
);
508 DateTimeErrorExtra extra
;
510 if (dterr
== DTERR_TZDISP_OVERFLOW
)
512 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
513 errmsg("numeric time zone \"%s\" out of range", tzname
)));
514 else if (dterr
!= DTERR_BAD_FORMAT
)
516 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
517 errmsg("time zone \"%s\" not recognized", tzname
)));
519 /* DecodeTimezoneAbbrev requires lowercase input */
520 lowzone
= downcase_truncate_identifier(tzname
,
523 dterr
= DecodeTimezoneAbbrev(0, lowzone
, &type
, &val
, &tzp
, &extra
);
525 DateTimeParseError(dterr
, &extra
, NULL
, NULL
, NULL
);
527 if (type
== TZ
|| type
== DTZ
)
529 /* fixed-offset abbreviation */
532 else if (type
== DYNTZ
)
534 /* dynamic-offset abbreviation, resolve using specified time */
535 tz
= DetermineTimeZoneAbbrevOffset(tm
, tzname
, tzp
);
539 /* try it as a full zone name */
540 tzp
= pg_tzset(tzname
);
542 tz
= DetermineTimeZoneOffset(tm
, tzp
);
545 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
546 errmsg("time zone \"%s\" not recognized", tzname
)));
554 * make_timestamp_internal
555 * workhorse for make_timestamp and make_timestamptz
558 make_timestamp_internal(int year
, int month
, int day
,
559 int hour
, int min
, double sec
)
572 /* Handle negative years as BC */
576 tm
.tm_year
= -tm
.tm_year
;
579 dterr
= ValidateDate(DTK_DATE_M
, false, false, bc
, &tm
);
583 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW
),
584 errmsg("date field value out of range: %d-%02d-%02d",
587 if (!IS_VALID_JULIAN(tm
.tm_year
, tm
.tm_mon
, tm
.tm_mday
))
589 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
590 errmsg("date out of range: %d-%02d-%02d",
593 date
= date2j(tm
.tm_year
, tm
.tm_mon
, tm
.tm_mday
) - POSTGRES_EPOCH_JDATE
;
595 /* Check for time overflow */
596 if (float_time_overflows(hour
, min
, sec
))
598 (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW
),
599 errmsg("time field value out of range: %d:%02d:%02g",
602 /* This should match tm2time */
603 time
= (((hour
* MINS_PER_HOUR
+ min
) * SECS_PER_MINUTE
)
604 * USECS_PER_SEC
) + (int64
) rint(sec
* USECS_PER_SEC
);
606 result
= date
* USECS_PER_DAY
+ time
;
607 /* check for major overflow */
608 if ((result
- time
) / USECS_PER_DAY
!= date
)
610 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
611 errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
615 /* check for just-barely overflow (okay except time-of-day wraps) */
616 /* caution: we want to allow 1999-12-31 24:00:00 */
617 if ((result
< 0 && date
> 0) ||
618 (result
> 0 && date
< -1))
620 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
621 errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
625 /* final range check catches just-out-of-range timestamps */
626 if (!IS_VALID_TIMESTAMP(result
))
628 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
629 errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
637 * make_timestamp() - timestamp constructor
640 make_timestamp(PG_FUNCTION_ARGS
)
642 int32 year
= PG_GETARG_INT32(0);
643 int32 month
= PG_GETARG_INT32(1);
644 int32 mday
= PG_GETARG_INT32(2);
645 int32 hour
= PG_GETARG_INT32(3);
646 int32 min
= PG_GETARG_INT32(4);
647 float8 sec
= PG_GETARG_FLOAT8(5);
650 result
= make_timestamp_internal(year
, month
, mday
,
653 PG_RETURN_TIMESTAMP(result
);
657 * make_timestamptz() - timestamp with time zone constructor
660 make_timestamptz(PG_FUNCTION_ARGS
)
662 int32 year
= PG_GETARG_INT32(0);
663 int32 month
= PG_GETARG_INT32(1);
664 int32 mday
= PG_GETARG_INT32(2);
665 int32 hour
= PG_GETARG_INT32(3);
666 int32 min
= PG_GETARG_INT32(4);
667 float8 sec
= PG_GETARG_FLOAT8(5);
670 result
= make_timestamp_internal(year
, month
, mday
,
673 PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result
));
677 * Construct a timestamp with time zone.
678 * As above, but the time zone is specified as seventh argument.
681 make_timestamptz_at_timezone(PG_FUNCTION_ARGS
)
683 int32 year
= PG_GETARG_INT32(0);
684 int32 month
= PG_GETARG_INT32(1);
685 int32 mday
= PG_GETARG_INT32(2);
686 int32 hour
= PG_GETARG_INT32(3);
687 int32 min
= PG_GETARG_INT32(4);
688 float8 sec
= PG_GETARG_FLOAT8(5);
689 text
*zone
= PG_GETARG_TEXT_PP(6);
696 timestamp
= make_timestamp_internal(year
, month
, mday
,
699 if (timestamp2tm(timestamp
, NULL
, &tt
, &fsec
, NULL
, NULL
) != 0)
701 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
702 errmsg("timestamp out of range")));
704 tz
= parse_sane_timezone(&tt
, zone
);
706 result
= dt2local(timestamp
, -tz
);
708 if (!IS_VALID_TIMESTAMP(result
))
710 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
711 errmsg("timestamp out of range")));
713 PG_RETURN_TIMESTAMPTZ(result
);
717 * to_timestamp(double precision)
718 * Convert UNIX epoch to timestamptz.
721 float8_timestamptz(PG_FUNCTION_ARGS
)
723 float8 seconds
= PG_GETARG_FLOAT8(0);
726 /* Deal with NaN and infinite inputs ... */
729 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
730 errmsg("timestamp cannot be NaN")));
735 TIMESTAMP_NOBEGIN(result
);
737 TIMESTAMP_NOEND(result
);
743 (float8
) SECS_PER_DAY
* (DATETIME_MIN_JULIAN
- UNIX_EPOCH_JDATE
)
745 (float8
) SECS_PER_DAY
* (TIMESTAMP_END_JULIAN
- UNIX_EPOCH_JDATE
))
747 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
748 errmsg("timestamp out of range: \"%g\"", seconds
)));
750 /* Convert UNIX epoch to Postgres epoch */
751 seconds
-= ((POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
);
753 seconds
= rint(seconds
* USECS_PER_SEC
);
754 result
= (int64
) seconds
;
756 /* Recheck in case roundoff produces something just out of range */
757 if (!IS_VALID_TIMESTAMP(result
))
759 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
760 errmsg("timestamp out of range: \"%g\"",
761 PG_GETARG_FLOAT8(0))));
764 PG_RETURN_TIMESTAMP(result
);
768 * Convert a timestamp to external form.
771 timestamptz_out(PG_FUNCTION_ARGS
)
773 TimestampTz dt
= PG_GETARG_TIMESTAMPTZ(0);
780 char buf
[MAXDATELEN
+ 1];
782 if (TIMESTAMP_NOT_FINITE(dt
))
783 EncodeSpecialTimestamp(dt
, buf
);
784 else if (timestamp2tm(dt
, &tz
, tm
, &fsec
, &tzn
, NULL
) == 0)
785 EncodeDateTime(tm
, fsec
, true, tz
, tzn
, DateStyle
, buf
);
788 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
789 errmsg("timestamp out of range")));
791 result
= pstrdup(buf
);
792 PG_RETURN_CSTRING(result
);
796 * timestamptz_recv - converts external binary format to timestamptz
799 timestamptz_recv(PG_FUNCTION_ARGS
)
801 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
804 Oid typelem
= PG_GETARG_OID(1);
806 int32 typmod
= PG_GETARG_INT32(2);
807 TimestampTz timestamp
;
813 timestamp
= (TimestampTz
) pq_getmsgint64(buf
);
815 /* range check: see if timestamptz_out would like it */
816 if (TIMESTAMP_NOT_FINITE(timestamp
))
818 else if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, NULL
, NULL
) != 0 ||
819 !IS_VALID_TIMESTAMP(timestamp
))
821 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
822 errmsg("timestamp out of range")));
824 AdjustTimestampForTypmod(×tamp
, typmod
, NULL
);
826 PG_RETURN_TIMESTAMPTZ(timestamp
);
830 * timestamptz_send - converts timestamptz to binary format
833 timestamptz_send(PG_FUNCTION_ARGS
)
835 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
838 pq_begintypsend(&buf
);
839 pq_sendint64(&buf
, timestamp
);
840 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
844 timestamptztypmodin(PG_FUNCTION_ARGS
)
846 ArrayType
*ta
= PG_GETARG_ARRAYTYPE_P(0);
848 PG_RETURN_INT32(anytimestamp_typmodin(true, ta
));
852 timestamptztypmodout(PG_FUNCTION_ARGS
)
854 int32 typmod
= PG_GETARG_INT32(0);
856 PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod
));
860 /* timestamptz_scale()
861 * Adjust time type for specified scale factor.
862 * Used by PostgreSQL type system to stuff columns.
865 timestamptz_scale(PG_FUNCTION_ARGS
)
867 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
868 int32 typmod
= PG_GETARG_INT32(1);
873 AdjustTimestampForTypmod(&result
, typmod
, NULL
);
875 PG_RETURN_TIMESTAMPTZ(result
);
880 * Convert a string to internal form.
882 * External format(s):
883 * Uses the generic date/time parsing and decoding routines.
886 interval_in(PG_FUNCTION_ARGS
)
888 char *str
= PG_GETARG_CSTRING(0);
890 Oid typelem
= PG_GETARG_OID(1);
892 int32 typmod
= PG_GETARG_INT32(2);
893 Node
*escontext
= fcinfo
->context
;
901 char *field
[MAXDATEFIELDS
];
902 int ftype
[MAXDATEFIELDS
];
904 DateTimeErrorExtra extra
;
912 range
= INTERVAL_RANGE(typmod
);
914 range
= INTERVAL_FULL_RANGE
;
916 dterr
= ParseDateTime(str
, workbuf
, sizeof(workbuf
), field
,
917 ftype
, MAXDATEFIELDS
, &nf
);
919 dterr
= DecodeInterval(field
, ftype
, nf
, range
,
922 /* if those functions think it's a bad format, try ISO8601 style */
923 if (dterr
== DTERR_BAD_FORMAT
)
924 dterr
= DecodeISO8601Interval(str
,
929 if (dterr
== DTERR_FIELD_OVERFLOW
)
930 dterr
= DTERR_INTERVAL_OVERFLOW
;
931 DateTimeParseError(dterr
, &extra
, str
, "interval", escontext
);
935 result
= (Interval
*) palloc(sizeof(Interval
));
940 if (itmin2interval(itm_in
, result
) != 0)
941 ereturn(escontext
, (Datum
) 0,
942 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
943 errmsg("interval out of range")));
947 elog(ERROR
, "unexpected dtype %d while parsing interval \"%s\"",
951 AdjustIntervalForTypmod(result
, typmod
, escontext
);
953 PG_RETURN_INTERVAL_P(result
);
957 * Convert a time span to external form.
960 interval_out(PG_FUNCTION_ARGS
)
962 Interval
*span
= PG_GETARG_INTERVAL_P(0);
966 char buf
[MAXDATELEN
+ 1];
968 interval2itm(*span
, itm
);
969 EncodeInterval(itm
, IntervalStyle
, buf
);
971 result
= pstrdup(buf
);
972 PG_RETURN_CSTRING(result
);
976 * interval_recv - converts external binary format to interval
979 interval_recv(PG_FUNCTION_ARGS
)
981 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
984 Oid typelem
= PG_GETARG_OID(1);
986 int32 typmod
= PG_GETARG_INT32(2);
989 interval
= (Interval
*) palloc(sizeof(Interval
));
991 interval
->time
= pq_getmsgint64(buf
);
992 interval
->day
= pq_getmsgint(buf
, sizeof(interval
->day
));
993 interval
->month
= pq_getmsgint(buf
, sizeof(interval
->month
));
995 AdjustIntervalForTypmod(interval
, typmod
, NULL
);
997 PG_RETURN_INTERVAL_P(interval
);
1001 * interval_send - converts interval to binary format
1004 interval_send(PG_FUNCTION_ARGS
)
1006 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
1009 pq_begintypsend(&buf
);
1010 pq_sendint64(&buf
, interval
->time
);
1011 pq_sendint32(&buf
, interval
->day
);
1012 pq_sendint32(&buf
, interval
->month
);
1013 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
1017 * The interval typmod stores a "range" in its high 16 bits and a "precision"
1018 * in its low 16 bits. Both contribute to defining the resolution of the
1019 * type. Range addresses resolution granules larger than one second, and
1020 * precision specifies resolution below one second. This representation can
1021 * express all SQL standard resolutions, but we implement them all in terms of
1022 * truncating rightward from some position. Range is a bitmap of permitted
1023 * fields, but only the temporally-smallest such field is significant to our
1024 * calculations. Precision is a count of sub-second decimal places to retain.
1025 * Setting all bits (INTERVAL_FULL_PRECISION) gives the same truncation
1026 * semantics as choosing MAX_INTERVAL_PRECISION.
1029 intervaltypmodin(PG_FUNCTION_ARGS
)
1031 ArrayType
*ta
= PG_GETARG_ARRAYTYPE_P(0);
1036 tl
= ArrayGetIntegerTypmods(ta
, &n
);
1039 * tl[0] - interval range (fields bitmask) tl[1] - precision (optional)
1041 * Note we must validate tl[0] even though it's normally guaranteed
1042 * correct by the grammar --- consider SELECT 'foo'::"interval"(1000).
1048 case INTERVAL_MASK(YEAR
):
1049 case INTERVAL_MASK(MONTH
):
1050 case INTERVAL_MASK(DAY
):
1051 case INTERVAL_MASK(HOUR
):
1052 case INTERVAL_MASK(MINUTE
):
1053 case INTERVAL_MASK(SECOND
):
1054 case INTERVAL_MASK(YEAR
) | INTERVAL_MASK(MONTH
):
1055 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
):
1056 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
1057 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1058 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
1059 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1060 case INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1061 case INTERVAL_FULL_RANGE
:
1066 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1067 errmsg("invalid INTERVAL type modifier")));
1073 if (tl
[0] != INTERVAL_FULL_RANGE
)
1074 typmod
= INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION
, tl
[0]);
1082 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1083 errmsg("INTERVAL(%d) precision must not be negative",
1085 if (tl
[1] > MAX_INTERVAL_PRECISION
)
1088 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1089 errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
1090 tl
[1], MAX_INTERVAL_PRECISION
)));
1091 typmod
= INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION
, tl
[0]);
1094 typmod
= INTERVAL_TYPMOD(tl
[1], tl
[0]);
1099 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1100 errmsg("invalid INTERVAL type modifier")));
1101 typmod
= 0; /* keep compiler quiet */
1104 PG_RETURN_INT32(typmod
);
1108 intervaltypmodout(PG_FUNCTION_ARGS
)
1110 int32 typmod
= PG_GETARG_INT32(0);
1111 char *res
= (char *) palloc(64);
1114 const char *fieldstr
;
1119 PG_RETURN_CSTRING(res
);
1122 fields
= INTERVAL_RANGE(typmod
);
1123 precision
= INTERVAL_PRECISION(typmod
);
1127 case INTERVAL_MASK(YEAR
):
1130 case INTERVAL_MASK(MONTH
):
1131 fieldstr
= " month";
1133 case INTERVAL_MASK(DAY
):
1136 case INTERVAL_MASK(HOUR
):
1139 case INTERVAL_MASK(MINUTE
):
1140 fieldstr
= " minute";
1142 case INTERVAL_MASK(SECOND
):
1143 fieldstr
= " second";
1145 case INTERVAL_MASK(YEAR
) | INTERVAL_MASK(MONTH
):
1146 fieldstr
= " year to month";
1148 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
):
1149 fieldstr
= " day to hour";
1151 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
1152 fieldstr
= " day to minute";
1154 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1155 fieldstr
= " day to second";
1157 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
1158 fieldstr
= " hour to minute";
1160 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1161 fieldstr
= " hour to second";
1163 case INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1164 fieldstr
= " minute to second";
1166 case INTERVAL_FULL_RANGE
:
1170 elog(ERROR
, "invalid INTERVAL typmod: 0x%x", typmod
);
1175 if (precision
!= INTERVAL_FULL_PRECISION
)
1176 snprintf(res
, 64, "%s(%d)", fieldstr
, precision
);
1178 snprintf(res
, 64, "%s", fieldstr
);
1180 PG_RETURN_CSTRING(res
);
1184 * Given an interval typmod value, return a code for the least-significant
1185 * field that the typmod allows to be nonzero, for instance given
1186 * INTERVAL DAY TO HOUR we want to identify "hour".
1188 * The results should be ordered by field significance, which means
1189 * we can't use the dt.h macros YEAR etc, because for some odd reason
1190 * they aren't ordered that way. Instead, arbitrarily represent
1191 * SECOND = 0, MINUTE = 1, HOUR = 2, DAY = 3, MONTH = 4, YEAR = 5.
1194 intervaltypmodleastfield(int32 typmod
)
1197 return 0; /* SECOND */
1199 switch (INTERVAL_RANGE(typmod
))
1201 case INTERVAL_MASK(YEAR
):
1202 return 5; /* YEAR */
1203 case INTERVAL_MASK(MONTH
):
1204 return 4; /* MONTH */
1205 case INTERVAL_MASK(DAY
):
1207 case INTERVAL_MASK(HOUR
):
1208 return 2; /* HOUR */
1209 case INTERVAL_MASK(MINUTE
):
1210 return 1; /* MINUTE */
1211 case INTERVAL_MASK(SECOND
):
1212 return 0; /* SECOND */
1213 case INTERVAL_MASK(YEAR
) | INTERVAL_MASK(MONTH
):
1214 return 4; /* MONTH */
1215 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
):
1216 return 2; /* HOUR */
1217 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
1218 return 1; /* MINUTE */
1219 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1220 return 0; /* SECOND */
1221 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
1222 return 1; /* MINUTE */
1223 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1224 return 0; /* SECOND */
1225 case INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
1226 return 0; /* SECOND */
1227 case INTERVAL_FULL_RANGE
:
1228 return 0; /* SECOND */
1230 elog(ERROR
, "invalid INTERVAL typmod: 0x%x", typmod
);
1233 return 0; /* can't get here, but keep compiler quiet */
1238 * interval_support()
1240 * Planner support function for interval_scale().
1242 * Flatten superfluous calls to interval_scale(). The interval typmod is
1243 * complex to permit accepting and regurgitating all SQL standard variations.
1244 * For truncation purposes, it boils down to a single, simple granularity.
1247 interval_support(PG_FUNCTION_ARGS
)
1249 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
1252 if (IsA(rawreq
, SupportRequestSimplify
))
1254 SupportRequestSimplify
*req
= (SupportRequestSimplify
*) rawreq
;
1255 FuncExpr
*expr
= req
->fcall
;
1258 Assert(list_length(expr
->args
) >= 2);
1260 typmod
= (Node
*) lsecond(expr
->args
);
1262 if (IsA(typmod
, Const
) && !((Const
*) typmod
)->constisnull
)
1264 Node
*source
= (Node
*) linitial(expr
->args
);
1265 int32 new_typmod
= DatumGetInt32(((Const
*) typmod
)->constvalue
);
1272 int32 old_typmod
= exprTypmod(source
);
1273 int old_least_field
;
1274 int new_least_field
;
1278 old_least_field
= intervaltypmodleastfield(old_typmod
);
1279 new_least_field
= intervaltypmodleastfield(new_typmod
);
1281 old_precis
= INTERVAL_FULL_PRECISION
;
1283 old_precis
= INTERVAL_PRECISION(old_typmod
);
1284 new_precis
= INTERVAL_PRECISION(new_typmod
);
1287 * Cast is a no-op if least field stays the same or decreases
1288 * while precision stays the same or increases. But
1289 * precision, which is to say, sub-second precision, only
1290 * affects ranges that include SECOND.
1292 noop
= (new_least_field
<= old_least_field
) &&
1293 (old_least_field
> 0 /* SECOND */ ||
1294 new_precis
>= MAX_INTERVAL_PRECISION
||
1295 new_precis
>= old_precis
);
1298 ret
= relabel_to_typmod(source
, new_typmod
);
1302 PG_RETURN_POINTER(ret
);
1306 * Adjust interval type for specified fields.
1307 * Used by PostgreSQL type system to stuff columns.
1310 interval_scale(PG_FUNCTION_ARGS
)
1312 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
1313 int32 typmod
= PG_GETARG_INT32(1);
1316 result
= palloc(sizeof(Interval
));
1317 *result
= *interval
;
1319 AdjustIntervalForTypmod(result
, typmod
, NULL
);
1321 PG_RETURN_INTERVAL_P(result
);
1325 * Adjust interval for specified precision, in both YEAR to SECOND
1326 * range and sub-second precision.
1328 * Returns true on success, false on failure (if escontext points to an
1329 * ErrorSaveContext; otherwise errors are thrown).
1332 AdjustIntervalForTypmod(Interval
*interval
, int32 typmod
,
1335 static const int64 IntervalScales
[MAX_INTERVAL_PRECISION
+ 1] = {
1336 INT64CONST(1000000),
1345 static const int64 IntervalOffsets
[MAX_INTERVAL_PRECISION
+ 1] = {
1356 * Unspecified range and precision? Then not necessary to adjust. Setting
1357 * typmod to -1 is the convention for all data types.
1361 int range
= INTERVAL_RANGE(typmod
);
1362 int precision
= INTERVAL_PRECISION(typmod
);
1365 * Our interpretation of intervals with a limited set of fields is
1366 * that fields to the right of the last one specified are zeroed out,
1367 * but those to the left of it remain valid. Thus for example there
1368 * is no operational difference between INTERVAL YEAR TO MONTH and
1369 * INTERVAL MONTH. In some cases we could meaningfully enforce that
1370 * higher-order fields are zero; for example INTERVAL DAY could reject
1371 * nonzero "month" field. However that seems a bit pointless when we
1372 * can't do it consistently. (We cannot enforce a range limit on the
1373 * highest expected field, since we do not have any equivalent of
1374 * SQL's <interval leading field precision>.) If we ever decide to
1375 * revisit this, interval_support will likely require adjusting.
1377 * Note: before PG 8.4 we interpreted a limited set of fields as
1378 * actually causing a "modulo" operation on a given value, potentially
1379 * losing high-order as well as low-order information. But there is
1380 * no support for such behavior in the standard, and it seems fairly
1381 * undesirable on data consistency grounds anyway. Now we only
1382 * perform truncation or rounding of low-order fields.
1384 if (range
== INTERVAL_FULL_RANGE
)
1388 else if (range
== INTERVAL_MASK(YEAR
))
1390 interval
->month
= (interval
->month
/ MONTHS_PER_YEAR
) * MONTHS_PER_YEAR
;
1394 else if (range
== INTERVAL_MASK(MONTH
))
1400 else if (range
== (INTERVAL_MASK(YEAR
) | INTERVAL_MASK(MONTH
)))
1405 else if (range
== INTERVAL_MASK(DAY
))
1409 else if (range
== INTERVAL_MASK(HOUR
))
1411 interval
->time
= (interval
->time
/ USECS_PER_HOUR
) *
1414 else if (range
== INTERVAL_MASK(MINUTE
))
1416 interval
->time
= (interval
->time
/ USECS_PER_MINUTE
) *
1419 else if (range
== INTERVAL_MASK(SECOND
))
1421 /* fractional-second rounding will be dealt with below */
1424 else if (range
== (INTERVAL_MASK(DAY
) |
1425 INTERVAL_MASK(HOUR
)))
1427 interval
->time
= (interval
->time
/ USECS_PER_HOUR
) *
1431 else if (range
== (INTERVAL_MASK(DAY
) |
1432 INTERVAL_MASK(HOUR
) |
1433 INTERVAL_MASK(MINUTE
)))
1435 interval
->time
= (interval
->time
/ USECS_PER_MINUTE
) *
1439 else if (range
== (INTERVAL_MASK(DAY
) |
1440 INTERVAL_MASK(HOUR
) |
1441 INTERVAL_MASK(MINUTE
) |
1442 INTERVAL_MASK(SECOND
)))
1444 /* fractional-second rounding will be dealt with below */
1446 /* HOUR TO MINUTE */
1447 else if (range
== (INTERVAL_MASK(HOUR
) |
1448 INTERVAL_MASK(MINUTE
)))
1450 interval
->time
= (interval
->time
/ USECS_PER_MINUTE
) *
1453 /* HOUR TO SECOND */
1454 else if (range
== (INTERVAL_MASK(HOUR
) |
1455 INTERVAL_MASK(MINUTE
) |
1456 INTERVAL_MASK(SECOND
)))
1458 /* fractional-second rounding will be dealt with below */
1460 /* MINUTE TO SECOND */
1461 else if (range
== (INTERVAL_MASK(MINUTE
) |
1462 INTERVAL_MASK(SECOND
)))
1464 /* fractional-second rounding will be dealt with below */
1467 elog(ERROR
, "unrecognized interval typmod: %d", typmod
);
1469 /* Need to adjust sub-second precision? */
1470 if (precision
!= INTERVAL_FULL_PRECISION
)
1472 if (precision
< 0 || precision
> MAX_INTERVAL_PRECISION
)
1473 ereturn(escontext
, false,
1474 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1475 errmsg("interval(%d) precision must be between %d and %d",
1476 precision
, 0, MAX_INTERVAL_PRECISION
)));
1478 if (interval
->time
>= INT64CONST(0))
1480 interval
->time
= ((interval
->time
+
1481 IntervalOffsets
[precision
]) /
1482 IntervalScales
[precision
]) *
1483 IntervalScales
[precision
];
1487 interval
->time
= -(((-interval
->time
+
1488 IntervalOffsets
[precision
]) /
1489 IntervalScales
[precision
]) *
1490 IntervalScales
[precision
]);
1499 * make_interval - numeric Interval constructor
1502 make_interval(PG_FUNCTION_ARGS
)
1504 int32 years
= PG_GETARG_INT32(0);
1505 int32 months
= PG_GETARG_INT32(1);
1506 int32 weeks
= PG_GETARG_INT32(2);
1507 int32 days
= PG_GETARG_INT32(3);
1508 int32 hours
= PG_GETARG_INT32(4);
1509 int32 mins
= PG_GETARG_INT32(5);
1510 double secs
= PG_GETARG_FLOAT8(6);
1514 * Reject out-of-range inputs. We really ought to check the integer
1515 * inputs as well, but it's not entirely clear what limits to apply.
1517 if (isinf(secs
) || isnan(secs
))
1519 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
1520 errmsg("interval out of range")));
1522 result
= (Interval
*) palloc(sizeof(Interval
));
1523 result
->month
= years
* MONTHS_PER_YEAR
+ months
;
1524 result
->day
= weeks
* 7 + days
;
1526 secs
= rint(secs
* USECS_PER_SEC
);
1527 result
->time
= hours
* ((int64
) SECS_PER_HOUR
* USECS_PER_SEC
) +
1528 mins
* ((int64
) SECS_PER_MINUTE
* USECS_PER_SEC
) +
1531 PG_RETURN_INTERVAL_P(result
);
1534 /* EncodeSpecialTimestamp()
1535 * Convert reserved timestamp data type to string.
1538 EncodeSpecialTimestamp(Timestamp dt
, char *str
)
1540 if (TIMESTAMP_IS_NOBEGIN(dt
))
1542 else if (TIMESTAMP_IS_NOEND(dt
))
1544 else /* shouldn't happen */
1545 elog(ERROR
, "invalid argument for EncodeSpecialTimestamp");
1549 now(PG_FUNCTION_ARGS
)
1551 PG_RETURN_TIMESTAMPTZ(GetCurrentTransactionStartTimestamp());
1555 statement_timestamp(PG_FUNCTION_ARGS
)
1557 PG_RETURN_TIMESTAMPTZ(GetCurrentStatementStartTimestamp());
1561 clock_timestamp(PG_FUNCTION_ARGS
)
1563 PG_RETURN_TIMESTAMPTZ(GetCurrentTimestamp());
1567 pg_postmaster_start_time(PG_FUNCTION_ARGS
)
1569 PG_RETURN_TIMESTAMPTZ(PgStartTime
);
1573 pg_conf_load_time(PG_FUNCTION_ARGS
)
1575 PG_RETURN_TIMESTAMPTZ(PgReloadTime
);
1579 * GetCurrentTimestamp -- get the current operating system time
1581 * Result is in the form of a TimestampTz value, and is expressed to the
1582 * full precision of the gettimeofday() syscall
1585 GetCurrentTimestamp(void)
1590 gettimeofday(&tp
, NULL
);
1592 result
= (TimestampTz
) tp
.tv_sec
-
1593 ((POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
);
1594 result
= (result
* USECS_PER_SEC
) + tp
.tv_usec
;
1600 * current_timestamp -- implements CURRENT_TIMESTAMP, CURRENT_TIMESTAMP(n)
1603 current_timestamp(PG_FUNCTION_ARGS
)
1608 if (!PG_ARGISNULL(0))
1609 typmod
= anytimestamp_typmod_check(true, PG_GETARG_INT32(0));
1611 ts
= GetCurrentTransactionStartTimestamp();
1613 AdjustTimestampForTypmod(&ts
, typmod
, NULL
);
1614 return TimestampTzGetDatum(ts
);
1618 * sql_localtimestamp -- implements LOCALTIMESTAMP, LOCALTIMESTAMP(n)
1621 sql_localtimestamp(PG_FUNCTION_ARGS
)
1626 if (!PG_ARGISNULL(0))
1627 typmod
= anytimestamp_typmod_check(false, PG_GETARG_INT32(0));
1629 ts
= timestamptz2timestamp(GetCurrentTransactionStartTimestamp());
1631 AdjustTimestampForTypmod(&ts
, typmod
, NULL
);
1632 return TimestampGetDatum(ts
);
1637 * timeofday(*) -- returns the current time as a text.
1640 timeofday(PG_FUNCTION_ARGS
)
1647 gettimeofday(&tp
, NULL
);
1648 tt
= (pg_time_t
) tp
.tv_sec
;
1649 pg_strftime(templ
, sizeof(templ
), "%a %b %d %H:%M:%S.%%06d %Y %Z",
1650 pg_localtime(&tt
, session_timezone
));
1651 snprintf(buf
, sizeof(buf
), templ
, tp
.tv_usec
);
1653 PG_RETURN_TEXT_P(cstring_to_text(buf
));
1657 * TimestampDifference -- convert the difference between two timestamps
1658 * into integer seconds and microseconds
1660 * This is typically used to calculate a wait timeout for select(2),
1661 * which explains the otherwise-odd choice of output format.
1663 * Both inputs must be ordinary finite timestamps (in current usage,
1664 * they'll be results from GetCurrentTimestamp()).
1666 * We expect start_time <= stop_time. If not, we return zeros,
1667 * since then we're already past the previously determined stop_time.
1670 TimestampDifference(TimestampTz start_time
, TimestampTz stop_time
,
1671 long *secs
, int *microsecs
)
1673 TimestampTz diff
= stop_time
- start_time
;
1682 *secs
= (long) (diff
/ USECS_PER_SEC
);
1683 *microsecs
= (int) (diff
% USECS_PER_SEC
);
1688 * TimestampDifferenceMilliseconds -- convert the difference between two
1689 * timestamps into integer milliseconds
1691 * This is typically used to calculate a wait timeout for WaitLatch()
1692 * or a related function. The choice of "long" as the result type
1693 * is to harmonize with that; furthermore, we clamp the result to at most
1694 * INT_MAX milliseconds, because that's all that WaitLatch() allows.
1696 * We expect start_time <= stop_time. If not, we return zero,
1697 * since then we're already past the previously determined stop_time.
1699 * Subtracting finite and infinite timestamps works correctly, returning
1700 * zero or INT_MAX as appropriate.
1702 * Note we round up any fractional millisecond, since waiting for just
1703 * less than the intended timeout is undesirable.
1706 TimestampDifferenceMilliseconds(TimestampTz start_time
, TimestampTz stop_time
)
1710 /* Deal with zero or negative elapsed time quickly. */
1711 if (start_time
>= stop_time
)
1713 /* To not fail with timestamp infinities, we must detect overflow. */
1714 if (pg_sub_s64_overflow(stop_time
, start_time
, &diff
))
1715 return (long) INT_MAX
;
1716 if (diff
>= (INT_MAX
* INT64CONST(1000) - 999))
1717 return (long) INT_MAX
;
1719 return (long) ((diff
+ 999) / 1000);
1723 * TimestampDifferenceExceeds -- report whether the difference between two
1724 * timestamps is >= a threshold (expressed in milliseconds)
1726 * Both inputs must be ordinary finite timestamps (in current usage,
1727 * they'll be results from GetCurrentTimestamp()).
1730 TimestampDifferenceExceeds(TimestampTz start_time
,
1731 TimestampTz stop_time
,
1734 TimestampTz diff
= stop_time
- start_time
;
1736 return (diff
>= msec
* INT64CONST(1000));
1740 * Convert a time_t to TimestampTz.
1742 * We do not use time_t internally in Postgres, but this is provided for use
1743 * by functions that need to interpret, say, a stat(2) result.
1745 * To avoid having the function's ABI vary depending on the width of time_t,
1746 * we declare the argument as pg_time_t, which is cast-compatible with
1747 * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1748 * This detail should be invisible to callers, at least at source code level.
1751 time_t_to_timestamptz(pg_time_t tm
)
1755 result
= (TimestampTz
) tm
-
1756 ((POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
);
1757 result
*= USECS_PER_SEC
;
1763 * Convert a TimestampTz to time_t.
1765 * This too is just marginally useful, but some places need it.
1767 * To avoid having the function's ABI vary depending on the width of time_t,
1768 * we declare the result as pg_time_t, which is cast-compatible with
1769 * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1770 * This detail should be invisible to callers, at least at source code level.
1773 timestamptz_to_time_t(TimestampTz t
)
1777 result
= (pg_time_t
) (t
/ USECS_PER_SEC
+
1778 ((POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
));
1784 * Produce a C-string representation of a TimestampTz.
1786 * This is mostly for use in emitting messages. The primary difference
1787 * from timestamptz_out is that we force the output format to ISO. Note
1788 * also that the result is in a static buffer, not pstrdup'd.
1790 * See also pg_strftime.
1793 timestamptz_to_str(TimestampTz t
)
1795 static char buf
[MAXDATELEN
+ 1];
1802 if (TIMESTAMP_NOT_FINITE(t
))
1803 EncodeSpecialTimestamp(t
, buf
);
1804 else if (timestamp2tm(t
, &tz
, tm
, &fsec
, &tzn
, NULL
) == 0)
1805 EncodeDateTime(tm
, fsec
, true, tz
, tzn
, USE_ISO_DATES
, buf
);
1807 strlcpy(buf
, "(timestamp out of range)", sizeof(buf
));
1814 dt2time(Timestamp jd
, int *hour
, int *min
, int *sec
, fsec_t
*fsec
)
1820 *hour
= time
/ USECS_PER_HOUR
;
1821 time
-= (*hour
) * USECS_PER_HOUR
;
1822 *min
= time
/ USECS_PER_MINUTE
;
1823 time
-= (*min
) * USECS_PER_MINUTE
;
1824 *sec
= time
/ USECS_PER_SEC
;
1825 *fsec
= time
- (*sec
* USECS_PER_SEC
);
1830 * timestamp2tm() - Convert timestamp data type to POSIX time structure.
1832 * Note that year is _not_ 1900-based, but is an explicit full value.
1833 * Also, month is one-based, _not_ zero-based.
1836 * -1 on out of range
1838 * If attimezone is NULL, the global timezone setting will be used.
1841 timestamp2tm(Timestamp dt
, int *tzp
, struct pg_tm
*tm
, fsec_t
*fsec
, const char **tzn
, pg_tz
*attimezone
)
1847 /* Use session timezone if caller asks for default */
1848 if (attimezone
== NULL
)
1849 attimezone
= session_timezone
;
1852 TMODULO(time
, date
, USECS_PER_DAY
);
1854 if (time
< INT64CONST(0))
1856 time
+= USECS_PER_DAY
;
1860 /* add offset to go from J2000 back to standard Julian date */
1861 date
+= POSTGRES_EPOCH_JDATE
;
1863 /* Julian day routine does not work for negative Julian days */
1864 if (date
< 0 || date
> (Timestamp
) INT_MAX
)
1867 j2date((int) date
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1868 dt2time(time
, &tm
->tm_hour
, &tm
->tm_min
, &tm
->tm_sec
, fsec
);
1870 /* Done if no TZ conversion wanted */
1882 * If the time falls within the range of pg_time_t, use pg_localtime() to
1883 * rotate to the local time zone.
1885 * First, convert to an integral timestamp, avoiding possibly
1886 * platform-specific roundoff-in-wrong-direction errors, and adjust to
1887 * Unix epoch. Then see if we can convert to pg_time_t without loss. This
1888 * coding avoids hardwiring any assumptions about the width of pg_time_t,
1889 * so it should behave sanely on machines without int64.
1891 dt
= (dt
- *fsec
) / USECS_PER_SEC
+
1892 (POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
;
1893 utime
= (pg_time_t
) dt
;
1894 if ((Timestamp
) utime
== dt
)
1896 struct pg_tm
*tx
= pg_localtime(&utime
, attimezone
);
1898 tm
->tm_year
= tx
->tm_year
+ 1900;
1899 tm
->tm_mon
= tx
->tm_mon
+ 1;
1900 tm
->tm_mday
= tx
->tm_mday
;
1901 tm
->tm_hour
= tx
->tm_hour
;
1902 tm
->tm_min
= tx
->tm_min
;
1903 tm
->tm_sec
= tx
->tm_sec
;
1904 tm
->tm_isdst
= tx
->tm_isdst
;
1905 tm
->tm_gmtoff
= tx
->tm_gmtoff
;
1906 tm
->tm_zone
= tx
->tm_zone
;
1907 *tzp
= -tm
->tm_gmtoff
;
1914 * When out of range of pg_time_t, treat as GMT
1917 /* Mark this as *no* time zone available */
1930 * Convert a tm structure to a timestamp data type.
1931 * Note that year is _not_ 1900-based, but is an explicit full value.
1932 * Also, month is one-based, _not_ zero-based.
1934 * Returns -1 on failure (value out of range).
1937 tm2timestamp(struct pg_tm
*tm
, fsec_t fsec
, int *tzp
, Timestamp
*result
)
1942 /* Prevent overflow in Julian-day routines */
1943 if (!IS_VALID_JULIAN(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
))
1945 *result
= 0; /* keep compiler quiet */
1949 date
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) - POSTGRES_EPOCH_JDATE
;
1950 time
= time2t(tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
, fsec
);
1952 *result
= date
* USECS_PER_DAY
+ time
;
1953 /* check for major overflow */
1954 if ((*result
- time
) / USECS_PER_DAY
!= date
)
1956 *result
= 0; /* keep compiler quiet */
1959 /* check for just-barely overflow (okay except time-of-day wraps) */
1960 /* caution: we want to allow 1999-12-31 24:00:00 */
1961 if ((*result
< 0 && date
> 0) ||
1962 (*result
> 0 && date
< -1))
1964 *result
= 0; /* keep compiler quiet */
1968 *result
= dt2local(*result
, -(*tzp
));
1970 /* final range check catches just-out-of-range timestamps */
1971 if (!IS_VALID_TIMESTAMP(*result
))
1973 *result
= 0; /* keep compiler quiet */
1982 * Convert an Interval to a pg_itm structure.
1983 * Note: overflow is not possible, because the pg_itm fields are
1984 * wide enough for all possible conversion results.
1987 interval2itm(Interval span
, struct pg_itm
*itm
)
1992 itm
->tm_year
= span
.month
/ MONTHS_PER_YEAR
;
1993 itm
->tm_mon
= span
.month
% MONTHS_PER_YEAR
;
1994 itm
->tm_mday
= span
.day
;
1997 tfrac
= time
/ USECS_PER_HOUR
;
1998 time
-= tfrac
* USECS_PER_HOUR
;
1999 itm
->tm_hour
= tfrac
;
2000 tfrac
= time
/ USECS_PER_MINUTE
;
2001 time
-= tfrac
* USECS_PER_MINUTE
;
2002 itm
->tm_min
= (int) tfrac
;
2003 tfrac
= time
/ USECS_PER_SEC
;
2004 time
-= tfrac
* USECS_PER_SEC
;
2005 itm
->tm_sec
= (int) tfrac
;
2006 itm
->tm_usec
= (int) time
;
2010 * Convert a pg_itm structure to an Interval.
2011 * Returns 0 if OK, -1 on overflow.
2014 itm2interval(struct pg_itm
*itm
, Interval
*span
)
2016 int64 total_months
= (int64
) itm
->tm_year
* MONTHS_PER_YEAR
+ itm
->tm_mon
;
2018 if (total_months
> INT_MAX
|| total_months
< INT_MIN
)
2020 span
->month
= (int32
) total_months
;
2021 span
->day
= itm
->tm_mday
;
2022 if (pg_mul_s64_overflow(itm
->tm_hour
, USECS_PER_HOUR
,
2025 /* tm_min, tm_sec are 32 bits, so intermediate products can't overflow */
2026 if (pg_add_s64_overflow(span
->time
, itm
->tm_min
* USECS_PER_MINUTE
,
2029 if (pg_add_s64_overflow(span
->time
, itm
->tm_sec
* USECS_PER_SEC
,
2032 if (pg_add_s64_overflow(span
->time
, itm
->tm_usec
,
2039 * Convert a pg_itm_in structure to an Interval.
2040 * Returns 0 if OK, -1 on overflow.
2043 itmin2interval(struct pg_itm_in
*itm_in
, Interval
*span
)
2045 int64 total_months
= (int64
) itm_in
->tm_year
* MONTHS_PER_YEAR
+ itm_in
->tm_mon
;
2047 if (total_months
> INT_MAX
|| total_months
< INT_MIN
)
2049 span
->month
= (int32
) total_months
;
2050 span
->day
= itm_in
->tm_mday
;
2051 span
->time
= itm_in
->tm_usec
;
2056 time2t(const int hour
, const int min
, const int sec
, const fsec_t fsec
)
2058 return (((((hour
* MINS_PER_HOUR
) + min
) * SECS_PER_MINUTE
) + sec
) * USECS_PER_SEC
) + fsec
;
2062 dt2local(Timestamp dt
, int timezone
)
2064 dt
-= (timezone
* USECS_PER_SEC
);
2069 /*****************************************************************************
2071 *****************************************************************************/
2075 timestamp_finite(PG_FUNCTION_ARGS
)
2077 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
2079 PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp
));
2083 interval_finite(PG_FUNCTION_ARGS
)
2085 PG_RETURN_BOOL(true);
2089 /*----------------------------------------------------------
2090 * Relational operators for timestamp.
2091 *---------------------------------------------------------*/
2094 GetEpochTime(struct pg_tm
*tm
)
2097 pg_time_t epoch
= 0;
2099 t0
= pg_gmtime(&epoch
);
2102 elog(ERROR
, "could not convert epoch to timestamp: %m");
2104 tm
->tm_year
= t0
->tm_year
;
2105 tm
->tm_mon
= t0
->tm_mon
;
2106 tm
->tm_mday
= t0
->tm_mday
;
2107 tm
->tm_hour
= t0
->tm_hour
;
2108 tm
->tm_min
= t0
->tm_min
;
2109 tm
->tm_sec
= t0
->tm_sec
;
2111 tm
->tm_year
+= 1900;
2116 SetEpochTimestamp(void)
2123 /* we don't bother to test for failure ... */
2124 tm2timestamp(tm
, 0, NULL
, &dt
);
2127 } /* SetEpochTimestamp() */
2130 * We are currently sharing some code between timestamp and timestamptz.
2131 * The comparison functions are among them. - thomas 2001-09-25
2133 * timestamp_relop - is timestamp1 relop timestamp2
2136 timestamp_cmp_internal(Timestamp dt1
, Timestamp dt2
)
2138 return (dt1
< dt2
) ? -1 : ((dt1
> dt2
) ? 1 : 0);
2142 timestamp_eq(PG_FUNCTION_ARGS
)
2144 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2145 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2147 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) == 0);
2151 timestamp_ne(PG_FUNCTION_ARGS
)
2153 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2154 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2156 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) != 0);
2160 timestamp_lt(PG_FUNCTION_ARGS
)
2162 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2163 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2165 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) < 0);
2169 timestamp_gt(PG_FUNCTION_ARGS
)
2171 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2172 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2174 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) > 0);
2178 timestamp_le(PG_FUNCTION_ARGS
)
2180 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2181 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2183 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) <= 0);
2187 timestamp_ge(PG_FUNCTION_ARGS
)
2189 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2190 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2192 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) >= 0);
2196 timestamp_cmp(PG_FUNCTION_ARGS
)
2198 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2199 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2201 PG_RETURN_INT32(timestamp_cmp_internal(dt1
, dt2
));
2204 #if SIZEOF_DATUM < 8
2205 /* note: this is used for timestamptz also */
2207 timestamp_fastcmp(Datum x
, Datum y
, SortSupport ssup
)
2209 Timestamp a
= DatumGetTimestamp(x
);
2210 Timestamp b
= DatumGetTimestamp(y
);
2212 return timestamp_cmp_internal(a
, b
);
2217 timestamp_sortsupport(PG_FUNCTION_ARGS
)
2219 SortSupport ssup
= (SortSupport
) PG_GETARG_POINTER(0);
2221 #if SIZEOF_DATUM >= 8
2224 * If this build has pass-by-value timestamps, then we can use a standard
2225 * comparator function.
2227 ssup
->comparator
= ssup_datum_signed_cmp
;
2229 ssup
->comparator
= timestamp_fastcmp
;
2235 timestamp_hash(PG_FUNCTION_ARGS
)
2237 return hashint8(fcinfo
);
2241 timestamp_hash_extended(PG_FUNCTION_ARGS
)
2243 return hashint8extended(fcinfo
);
2247 * Cross-type comparison functions for timestamp vs timestamptz
2251 timestamp_cmp_timestamptz_internal(Timestamp timestampVal
, TimestampTz dt2
)
2256 dt1
= timestamp2timestamptz_opt_overflow(timestampVal
, &overflow
);
2259 /* dt1 is larger than any finite timestamp, but less than infinity */
2260 return TIMESTAMP_IS_NOEND(dt2
) ? -1 : +1;
2264 /* dt1 is less than any finite timestamp, but more than -infinity */
2265 return TIMESTAMP_IS_NOBEGIN(dt2
) ? +1 : -1;
2268 return timestamptz_cmp_internal(dt1
, dt2
);
2272 timestamp_eq_timestamptz(PG_FUNCTION_ARGS
)
2274 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
2275 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
2277 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt2
) == 0);
2281 timestamp_ne_timestamptz(PG_FUNCTION_ARGS
)
2283 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
2284 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
2286 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt2
) != 0);
2290 timestamp_lt_timestamptz(PG_FUNCTION_ARGS
)
2292 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
2293 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
2295 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt2
) < 0);
2299 timestamp_gt_timestamptz(PG_FUNCTION_ARGS
)
2301 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
2302 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
2304 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt2
) > 0);
2308 timestamp_le_timestamptz(PG_FUNCTION_ARGS
)
2310 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
2311 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
2313 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt2
) <= 0);
2317 timestamp_ge_timestamptz(PG_FUNCTION_ARGS
)
2319 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
2320 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
2322 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt2
) >= 0);
2326 timestamp_cmp_timestamptz(PG_FUNCTION_ARGS
)
2328 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
2329 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
2331 PG_RETURN_INT32(timestamp_cmp_timestamptz_internal(timestampVal
, dt2
));
2335 timestamptz_eq_timestamp(PG_FUNCTION_ARGS
)
2337 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
2338 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
2340 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt1
) == 0);
2344 timestamptz_ne_timestamp(PG_FUNCTION_ARGS
)
2346 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
2347 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
2349 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt1
) != 0);
2353 timestamptz_lt_timestamp(PG_FUNCTION_ARGS
)
2355 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
2356 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
2358 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt1
) > 0);
2362 timestamptz_gt_timestamp(PG_FUNCTION_ARGS
)
2364 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
2365 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
2367 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt1
) < 0);
2371 timestamptz_le_timestamp(PG_FUNCTION_ARGS
)
2373 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
2374 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
2376 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt1
) >= 0);
2380 timestamptz_ge_timestamp(PG_FUNCTION_ARGS
)
2382 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
2383 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
2385 PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal
, dt1
) <= 0);
2389 timestamptz_cmp_timestamp(PG_FUNCTION_ARGS
)
2391 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
2392 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
2394 PG_RETURN_INT32(-timestamp_cmp_timestamptz_internal(timestampVal
, dt1
));
2399 * interval_relop - is interval1 relop interval2
2401 * Interval comparison is based on converting interval values to a linear
2402 * representation expressed in the units of the time field (microseconds,
2403 * in the case of integer timestamps) with days assumed to be always 24 hours
2404 * and months assumed to be always 30 days. To avoid overflow, we need a
2405 * wider-than-int64 datatype for the linear representation, so use INT128.
2408 static inline INT128
2409 interval_cmp_value(const Interval
*interval
)
2415 * Combine the month and day fields into an integral number of days.
2416 * Because the inputs are int32, int64 arithmetic suffices here.
2418 days
= interval
->month
* INT64CONST(30);
2419 days
+= interval
->day
;
2421 /* Widen time field to 128 bits */
2422 span
= int64_to_int128(interval
->time
);
2424 /* Scale up days to microseconds, forming a 128-bit product */
2425 int128_add_int64_mul_int64(&span
, days
, USECS_PER_DAY
);
2431 interval_cmp_internal(const Interval
*interval1
, const Interval
*interval2
)
2433 INT128 span1
= interval_cmp_value(interval1
);
2434 INT128 span2
= interval_cmp_value(interval2
);
2436 return int128_compare(span1
, span2
);
2440 interval_eq(PG_FUNCTION_ARGS
)
2442 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2443 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2445 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) == 0);
2449 interval_ne(PG_FUNCTION_ARGS
)
2451 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2452 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2454 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) != 0);
2458 interval_lt(PG_FUNCTION_ARGS
)
2460 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2461 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2463 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) < 0);
2467 interval_gt(PG_FUNCTION_ARGS
)
2469 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2470 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2472 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) > 0);
2476 interval_le(PG_FUNCTION_ARGS
)
2478 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2479 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2481 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) <= 0);
2485 interval_ge(PG_FUNCTION_ARGS
)
2487 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2488 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2490 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) >= 0);
2494 interval_cmp(PG_FUNCTION_ARGS
)
2496 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2497 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2499 PG_RETURN_INT32(interval_cmp_internal(interval1
, interval2
));
2503 * Hashing for intervals
2505 * We must produce equal hashvals for values that interval_cmp_internal()
2506 * considers equal. So, compute the net span the same way it does,
2507 * and then hash that.
2510 interval_hash(PG_FUNCTION_ARGS
)
2512 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
2513 INT128 span
= interval_cmp_value(interval
);
2517 * Use only the least significant 64 bits for hashing. The upper 64 bits
2518 * seldom add any useful information, and besides we must do it like this
2519 * for compatibility with hashes calculated before use of INT128 was
2522 span64
= int128_to_int64(span
);
2524 return DirectFunctionCall1(hashint8
, Int64GetDatumFast(span64
));
2528 interval_hash_extended(PG_FUNCTION_ARGS
)
2530 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
2531 INT128 span
= interval_cmp_value(interval
);
2534 /* Same approach as interval_hash */
2535 span64
= int128_to_int64(span
);
2537 return DirectFunctionCall2(hashint8extended
, Int64GetDatumFast(span64
),
2538 PG_GETARG_DATUM(1));
2541 /* overlaps_timestamp() --- implements the SQL OVERLAPS operator.
2543 * Algorithm is per SQL spec. This is much harder than you'd think
2544 * because the spec requires us to deliver a non-null answer in some cases
2545 * where some of the inputs are null.
2548 overlaps_timestamp(PG_FUNCTION_ARGS
)
2551 * The arguments are Timestamps, but we leave them as generic Datums to
2552 * avoid unnecessary conversions between value and reference forms --- not
2553 * to mention possible dereferences of null pointers.
2555 Datum ts1
= PG_GETARG_DATUM(0);
2556 Datum te1
= PG_GETARG_DATUM(1);
2557 Datum ts2
= PG_GETARG_DATUM(2);
2558 Datum te2
= PG_GETARG_DATUM(3);
2559 bool ts1IsNull
= PG_ARGISNULL(0);
2560 bool te1IsNull
= PG_ARGISNULL(1);
2561 bool ts2IsNull
= PG_ARGISNULL(2);
2562 bool te2IsNull
= PG_ARGISNULL(3);
2564 #define TIMESTAMP_GT(t1,t2) \
2565 DatumGetBool(DirectFunctionCall2(timestamp_gt,t1,t2))
2566 #define TIMESTAMP_LT(t1,t2) \
2567 DatumGetBool(DirectFunctionCall2(timestamp_lt,t1,t2))
2570 * If both endpoints of interval 1 are null, the result is null (unknown).
2571 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2572 * take ts1 as the lesser endpoint.
2578 /* swap null for non-null */
2582 else if (!te1IsNull
)
2584 if (TIMESTAMP_GT(ts1
, te1
))
2593 /* Likewise for interval 2. */
2598 /* swap null for non-null */
2602 else if (!te2IsNull
)
2604 if (TIMESTAMP_GT(ts2
, te2
))
2614 * At this point neither ts1 nor ts2 is null, so we can consider three
2615 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2617 if (TIMESTAMP_GT(ts1
, ts2
))
2620 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2621 * in the presence of nulls it's not quite completely so.
2625 if (TIMESTAMP_LT(ts1
, te2
))
2626 PG_RETURN_BOOL(true);
2631 * If te1 is not null then we had ts1 <= te1 above, and we just found
2632 * ts1 >= te2, hence te1 >= te2.
2634 PG_RETURN_BOOL(false);
2636 else if (TIMESTAMP_LT(ts1
, ts2
))
2638 /* This case is ts2 < te1 OR te2 < te1 */
2641 if (TIMESTAMP_LT(ts2
, te1
))
2642 PG_RETURN_BOOL(true);
2647 * If te2 is not null then we had ts2 <= te2 above, and we just found
2648 * ts2 >= te1, hence te2 >= te1.
2650 PG_RETURN_BOOL(false);
2655 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2656 * rather silly way of saying "true if both are non-null, else null".
2658 if (te1IsNull
|| te2IsNull
)
2660 PG_RETURN_BOOL(true);
2668 /*----------------------------------------------------------
2669 * "Arithmetic" operators on date/times.
2670 *---------------------------------------------------------*/
2673 timestamp_smaller(PG_FUNCTION_ARGS
)
2675 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2676 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2679 /* use timestamp_cmp_internal to be sure this agrees with comparisons */
2680 if (timestamp_cmp_internal(dt1
, dt2
) < 0)
2684 PG_RETURN_TIMESTAMP(result
);
2688 timestamp_larger(PG_FUNCTION_ARGS
)
2690 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2691 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2694 if (timestamp_cmp_internal(dt1
, dt2
) > 0)
2698 PG_RETURN_TIMESTAMP(result
);
2703 timestamp_mi(PG_FUNCTION_ARGS
)
2705 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2706 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2709 result
= (Interval
*) palloc(sizeof(Interval
));
2711 if (TIMESTAMP_NOT_FINITE(dt1
) || TIMESTAMP_NOT_FINITE(dt2
))
2713 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2714 errmsg("cannot subtract infinite timestamps")));
2716 if (unlikely(pg_sub_s64_overflow(dt1
, dt2
, &result
->time
)))
2718 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2719 errmsg("interval out of range")));
2725 * This is wrong, but removing it breaks a lot of regression tests.
2728 * test=> SET timezone = 'EST5EDT';
2730 * test-> ('2005-10-30 13:22:00-05'::timestamptz -
2731 * test(> '2005-10-29 13:22:00-04'::timestamptz);
2737 * so adding that to the first timestamp gets:
2740 * test-> ('2005-10-29 13:22:00-04'::timestamptz +
2741 * test(> ('2005-10-30 13:22:00-05'::timestamptz -
2742 * test(> '2005-10-29 13:22:00-04'::timestamptz)) at time zone 'EST';
2744 * --------------------
2745 * 2005-10-30 14:22:00
2749 result
= DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours
,
2750 IntervalPGetDatum(result
)));
2752 PG_RETURN_INTERVAL_P(result
);
2756 * interval_justify_interval()
2758 * Adjust interval so 'month', 'day', and 'time' portions are within
2759 * customary bounds. Specifically:
2761 * 0 <= abs(time) < 24 hours
2762 * 0 <= abs(day) < 30 days
2764 * Also, the sign bit on all three fields is made equal, so either
2765 * all three fields are negative or all are positive.
2768 interval_justify_interval(PG_FUNCTION_ARGS
)
2770 Interval
*span
= PG_GETARG_INTERVAL_P(0);
2772 TimeOffset wholeday
;
2775 result
= (Interval
*) palloc(sizeof(Interval
));
2776 result
->month
= span
->month
;
2777 result
->day
= span
->day
;
2778 result
->time
= span
->time
;
2780 /* pre-justify days if it might prevent overflow */
2781 if ((result
->day
> 0 && result
->time
> 0) ||
2782 (result
->day
< 0 && result
->time
< 0))
2784 wholemonth
= result
->day
/ DAYS_PER_MONTH
;
2785 result
->day
-= wholemonth
* DAYS_PER_MONTH
;
2786 if (pg_add_s32_overflow(result
->month
, wholemonth
, &result
->month
))
2788 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2789 errmsg("interval out of range")));
2793 * Since TimeOffset is int64, abs(wholeday) can't exceed about 1.07e8. If
2794 * we pre-justified then abs(result->day) is less than DAYS_PER_MONTH, so
2795 * this addition can't overflow. If we didn't pre-justify, then day and
2796 * time are of different signs, so it still can't overflow.
2798 TMODULO(result
->time
, wholeday
, USECS_PER_DAY
);
2799 result
->day
+= wholeday
;
2801 wholemonth
= result
->day
/ DAYS_PER_MONTH
;
2802 result
->day
-= wholemonth
* DAYS_PER_MONTH
;
2803 if (pg_add_s32_overflow(result
->month
, wholemonth
, &result
->month
))
2805 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2806 errmsg("interval out of range")));
2808 if (result
->month
> 0 &&
2809 (result
->day
< 0 || (result
->day
== 0 && result
->time
< 0)))
2811 result
->day
+= DAYS_PER_MONTH
;
2814 else if (result
->month
< 0 &&
2815 (result
->day
> 0 || (result
->day
== 0 && result
->time
> 0)))
2817 result
->day
-= DAYS_PER_MONTH
;
2821 if (result
->day
> 0 && result
->time
< 0)
2823 result
->time
+= USECS_PER_DAY
;
2826 else if (result
->day
< 0 && result
->time
> 0)
2828 result
->time
-= USECS_PER_DAY
;
2832 PG_RETURN_INTERVAL_P(result
);
2836 * interval_justify_hours()
2838 * Adjust interval so 'time' contains less than a whole day, adding
2839 * the excess to 'day'. This is useful for
2840 * situations (such as non-TZ) where '1 day' = '24 hours' is valid,
2841 * e.g. interval subtraction and division.
2844 interval_justify_hours(PG_FUNCTION_ARGS
)
2846 Interval
*span
= PG_GETARG_INTERVAL_P(0);
2848 TimeOffset wholeday
;
2850 result
= (Interval
*) palloc(sizeof(Interval
));
2851 result
->month
= span
->month
;
2852 result
->day
= span
->day
;
2853 result
->time
= span
->time
;
2855 TMODULO(result
->time
, wholeday
, USECS_PER_DAY
);
2856 if (pg_add_s32_overflow(result
->day
, wholeday
, &result
->day
))
2858 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2859 errmsg("interval out of range")));
2861 if (result
->day
> 0 && result
->time
< 0)
2863 result
->time
+= USECS_PER_DAY
;
2866 else if (result
->day
< 0 && result
->time
> 0)
2868 result
->time
-= USECS_PER_DAY
;
2872 PG_RETURN_INTERVAL_P(result
);
2876 * interval_justify_days()
2878 * Adjust interval so 'day' contains less than 30 days, adding
2879 * the excess to 'month'.
2882 interval_justify_days(PG_FUNCTION_ARGS
)
2884 Interval
*span
= PG_GETARG_INTERVAL_P(0);
2888 result
= (Interval
*) palloc(sizeof(Interval
));
2889 result
->month
= span
->month
;
2890 result
->day
= span
->day
;
2891 result
->time
= span
->time
;
2893 wholemonth
= result
->day
/ DAYS_PER_MONTH
;
2894 result
->day
-= wholemonth
* DAYS_PER_MONTH
;
2895 if (pg_add_s32_overflow(result
->month
, wholemonth
, &result
->month
))
2897 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2898 errmsg("interval out of range")));
2900 if (result
->month
> 0 && result
->day
< 0)
2902 result
->day
+= DAYS_PER_MONTH
;
2905 else if (result
->month
< 0 && result
->day
> 0)
2907 result
->day
-= DAYS_PER_MONTH
;
2911 PG_RETURN_INTERVAL_P(result
);
2914 /* timestamp_pl_interval()
2915 * Add an interval to a timestamp data type.
2916 * Note that interval has provisions for qualitative year/month and day
2917 * units, so try to do the right thing with them.
2918 * To add a month, increment the month, and use the same day of month.
2919 * Then, if the next month has fewer days, set the day of month
2920 * to the last day of month.
2921 * To add a day, increment the mday, and use the same time of day.
2922 * Lastly, add in the "quantitative time".
2925 timestamp_pl_interval(PG_FUNCTION_ARGS
)
2927 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
2928 Interval
*span
= PG_GETARG_INTERVAL_P(1);
2931 if (TIMESTAMP_NOT_FINITE(timestamp
))
2935 if (span
->month
!= 0)
2941 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
2943 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2944 errmsg("timestamp out of range")));
2946 tm
->tm_mon
+= span
->month
;
2947 if (tm
->tm_mon
> MONTHS_PER_YEAR
)
2949 tm
->tm_year
+= (tm
->tm_mon
- 1) / MONTHS_PER_YEAR
;
2950 tm
->tm_mon
= ((tm
->tm_mon
- 1) % MONTHS_PER_YEAR
) + 1;
2952 else if (tm
->tm_mon
< 1)
2954 tm
->tm_year
+= tm
->tm_mon
/ MONTHS_PER_YEAR
- 1;
2955 tm
->tm_mon
= tm
->tm_mon
% MONTHS_PER_YEAR
+ MONTHS_PER_YEAR
;
2958 /* adjust for end of month boundary problems... */
2959 if (tm
->tm_mday
> day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1])
2960 tm
->tm_mday
= (day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1]);
2962 if (tm2timestamp(tm
, fsec
, NULL
, ×tamp
) != 0)
2964 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2965 errmsg("timestamp out of range")));
2975 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
2977 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2978 errmsg("timestamp out of range")));
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
);
2984 if (tm2timestamp(tm
, fsec
, NULL
, ×tamp
) != 0)
2986 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2987 errmsg("timestamp out of range")));
2990 timestamp
+= span
->time
;
2992 if (!IS_VALID_TIMESTAMP(timestamp
))
2994 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2995 errmsg("timestamp out of range")));
3000 PG_RETURN_TIMESTAMP(result
);
3004 timestamp_mi_interval(PG_FUNCTION_ARGS
)
3006 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
3007 Interval
*span
= PG_GETARG_INTERVAL_P(1);
3010 tspan
.month
= -span
->month
;
3011 tspan
.day
= -span
->day
;
3012 tspan
.time
= -span
->time
;
3014 return DirectFunctionCall2(timestamp_pl_interval
,
3015 TimestampGetDatum(timestamp
),
3016 PointerGetDatum(&tspan
));
3020 /* timestamptz_pl_interval()
3021 * Add an interval to a timestamp with time zone data type.
3022 * Note that interval has provisions for qualitative year/month
3023 * units, so try to do the right thing with them.
3024 * To add a month, increment the month, and use the same day of month.
3025 * Then, if the next month has fewer days, set the day of month
3026 * to the last day of month.
3027 * Lastly, add in the "quantitative time".
3030 timestamptz_pl_interval(PG_FUNCTION_ARGS
)
3032 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
3033 Interval
*span
= PG_GETARG_INTERVAL_P(1);
3037 if (TIMESTAMP_NOT_FINITE(timestamp
))
3041 if (span
->month
!= 0)
3047 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, NULL
, NULL
) != 0)
3049 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3050 errmsg("timestamp out of range")));
3052 tm
->tm_mon
+= span
->month
;
3053 if (tm
->tm_mon
> MONTHS_PER_YEAR
)
3055 tm
->tm_year
+= (tm
->tm_mon
- 1) / MONTHS_PER_YEAR
;
3056 tm
->tm_mon
= ((tm
->tm_mon
- 1) % MONTHS_PER_YEAR
) + 1;
3058 else if (tm
->tm_mon
< 1)
3060 tm
->tm_year
+= tm
->tm_mon
/ MONTHS_PER_YEAR
- 1;
3061 tm
->tm_mon
= tm
->tm_mon
% MONTHS_PER_YEAR
+ MONTHS_PER_YEAR
;
3064 /* adjust for end of month boundary problems... */
3065 if (tm
->tm_mday
> day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1])
3066 tm
->tm_mday
= (day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1]);
3068 tz
= DetermineTimeZoneOffset(tm
, session_timezone
);
3070 if (tm2timestamp(tm
, fsec
, &tz
, ×tamp
) != 0)
3072 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3073 errmsg("timestamp out of range")));
3083 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, NULL
, NULL
) != 0)
3085 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3086 errmsg("timestamp out of range")));
3088 /* Add days by converting to and from Julian */
3089 julian
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) + span
->day
;
3090 j2date(julian
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
3092 tz
= DetermineTimeZoneOffset(tm
, session_timezone
);
3094 if (tm2timestamp(tm
, fsec
, &tz
, ×tamp
) != 0)
3096 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3097 errmsg("timestamp out of range")));
3100 timestamp
+= span
->time
;
3102 if (!IS_VALID_TIMESTAMP(timestamp
))
3104 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3105 errmsg("timestamp out of range")));
3110 PG_RETURN_TIMESTAMP(result
);
3114 timestamptz_mi_interval(PG_FUNCTION_ARGS
)
3116 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
3117 Interval
*span
= PG_GETARG_INTERVAL_P(1);
3120 tspan
.month
= -span
->month
;
3121 tspan
.day
= -span
->day
;
3122 tspan
.time
= -span
->time
;
3124 return DirectFunctionCall2(timestamptz_pl_interval
,
3125 TimestampGetDatum(timestamp
),
3126 PointerGetDatum(&tspan
));
3131 interval_um(PG_FUNCTION_ARGS
)
3133 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
3136 result
= (Interval
*) palloc(sizeof(Interval
));
3138 result
->time
= -interval
->time
;
3139 /* overflow check copied from int4um */
3140 if (interval
->time
!= 0 && SAMESIGN(result
->time
, interval
->time
))
3142 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3143 errmsg("interval out of range")));
3144 result
->day
= -interval
->day
;
3145 if (interval
->day
!= 0 && SAMESIGN(result
->day
, interval
->day
))
3147 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3148 errmsg("interval out of range")));
3149 result
->month
= -interval
->month
;
3150 if (interval
->month
!= 0 && SAMESIGN(result
->month
, interval
->month
))
3152 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3153 errmsg("interval out of range")));
3155 PG_RETURN_INTERVAL_P(result
);
3160 interval_smaller(PG_FUNCTION_ARGS
)
3162 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
3163 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
3166 /* use interval_cmp_internal to be sure this agrees with comparisons */
3167 if (interval_cmp_internal(interval1
, interval2
) < 0)
3171 PG_RETURN_INTERVAL_P(result
);
3175 interval_larger(PG_FUNCTION_ARGS
)
3177 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
3178 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
3181 if (interval_cmp_internal(interval1
, interval2
) > 0)
3185 PG_RETURN_INTERVAL_P(result
);
3189 interval_pl(PG_FUNCTION_ARGS
)
3191 Interval
*span1
= PG_GETARG_INTERVAL_P(0);
3192 Interval
*span2
= PG_GETARG_INTERVAL_P(1);
3195 result
= (Interval
*) palloc(sizeof(Interval
));
3197 result
->month
= span1
->month
+ span2
->month
;
3198 /* overflow check copied from int4pl */
3199 if (SAMESIGN(span1
->month
, span2
->month
) &&
3200 !SAMESIGN(result
->month
, span1
->month
))
3202 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3203 errmsg("interval out of range")));
3205 result
->day
= span1
->day
+ span2
->day
;
3206 if (SAMESIGN(span1
->day
, span2
->day
) &&
3207 !SAMESIGN(result
->day
, span1
->day
))
3209 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3210 errmsg("interval out of range")));
3212 result
->time
= span1
->time
+ span2
->time
;
3213 if (SAMESIGN(span1
->time
, span2
->time
) &&
3214 !SAMESIGN(result
->time
, span1
->time
))
3216 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3217 errmsg("interval out of range")));
3219 PG_RETURN_INTERVAL_P(result
);
3223 interval_mi(PG_FUNCTION_ARGS
)
3225 Interval
*span1
= PG_GETARG_INTERVAL_P(0);
3226 Interval
*span2
= PG_GETARG_INTERVAL_P(1);
3229 result
= (Interval
*) palloc(sizeof(Interval
));
3231 result
->month
= span1
->month
- span2
->month
;
3232 /* overflow check copied from int4mi */
3233 if (!SAMESIGN(span1
->month
, span2
->month
) &&
3234 !SAMESIGN(result
->month
, span1
->month
))
3236 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3237 errmsg("interval out of range")));
3239 result
->day
= span1
->day
- span2
->day
;
3240 if (!SAMESIGN(span1
->day
, span2
->day
) &&
3241 !SAMESIGN(result
->day
, span1
->day
))
3243 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3244 errmsg("interval out of range")));
3246 result
->time
= span1
->time
- span2
->time
;
3247 if (!SAMESIGN(span1
->time
, span2
->time
) &&
3248 !SAMESIGN(result
->time
, span1
->time
))
3250 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3251 errmsg("interval out of range")));
3253 PG_RETURN_INTERVAL_P(result
);
3257 * There is no interval_abs(): it is unclear what value to return:
3258 * http://archives.postgresql.org/pgsql-general/2009-10/msg01031.php
3259 * http://archives.postgresql.org/pgsql-general/2009-11/msg00041.php
3263 interval_mul(PG_FUNCTION_ARGS
)
3265 Interval
*span
= PG_GETARG_INTERVAL_P(0);
3266 float8 factor
= PG_GETARG_FLOAT8(1);
3267 double month_remainder_days
,
3270 int32 orig_month
= span
->month
,
3271 orig_day
= span
->day
;
3274 result
= (Interval
*) palloc(sizeof(Interval
));
3276 result_double
= span
->month
* factor
;
3277 if (isnan(result_double
) ||
3278 result_double
> INT_MAX
|| result_double
< INT_MIN
)
3280 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3281 errmsg("interval out of range")));
3282 result
->month
= (int32
) result_double
;
3284 result_double
= span
->day
* factor
;
3285 if (isnan(result_double
) ||
3286 result_double
> INT_MAX
|| result_double
< INT_MIN
)
3288 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3289 errmsg("interval out of range")));
3290 result
->day
= (int32
) result_double
;
3293 * The above correctly handles the whole-number part of the month and day
3294 * products, but we have to do something with any fractional part
3295 * resulting when the factor is non-integral. We cascade the fractions
3296 * down to lower units using the conversion factors DAYS_PER_MONTH and
3297 * SECS_PER_DAY. Note we do NOT cascade up, since we are not forced to do
3298 * so by the representation. The user can choose to cascade up later,
3299 * using justify_hours and/or justify_days.
3303 * Fractional months full days into days.
3305 * Floating point calculation are inherently imprecise, so these
3306 * calculations are crafted to produce the most reliable result possible.
3307 * TSROUND() is needed to more accurately produce whole numbers where
3310 month_remainder_days
= (orig_month
* factor
- result
->month
) * DAYS_PER_MONTH
;
3311 month_remainder_days
= TSROUND(month_remainder_days
);
3312 sec_remainder
= (orig_day
* factor
- result
->day
+
3313 month_remainder_days
- (int) month_remainder_days
) * SECS_PER_DAY
;
3314 sec_remainder
= TSROUND(sec_remainder
);
3317 * Might have 24:00:00 hours due to rounding, or >24 hours because of time
3318 * cascade from months and days. It might still be >24 if the combination
3319 * of cascade and the seconds factor operation itself.
3321 if (fabs(sec_remainder
) >= SECS_PER_DAY
)
3323 result
->day
+= (int) (sec_remainder
/ SECS_PER_DAY
);
3324 sec_remainder
-= (int) (sec_remainder
/ SECS_PER_DAY
) * SECS_PER_DAY
;
3327 /* cascade units down */
3328 result
->day
+= (int32
) month_remainder_days
;
3329 result_double
= rint(span
->time
* factor
+ sec_remainder
* USECS_PER_SEC
);
3330 if (isnan(result_double
) || !FLOAT8_FITS_IN_INT64(result_double
))
3332 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3333 errmsg("interval out of range")));
3334 result
->time
= (int64
) result_double
;
3336 PG_RETURN_INTERVAL_P(result
);
3340 mul_d_interval(PG_FUNCTION_ARGS
)
3342 /* Args are float8 and Interval *, but leave them as generic Datum */
3343 Datum factor
= PG_GETARG_DATUM(0);
3344 Datum span
= PG_GETARG_DATUM(1);
3346 return DirectFunctionCall2(interval_mul
, span
, factor
);
3350 interval_div(PG_FUNCTION_ARGS
)
3352 Interval
*span
= PG_GETARG_INTERVAL_P(0);
3353 float8 factor
= PG_GETARG_FLOAT8(1);
3354 double month_remainder_days
,
3356 int32 orig_month
= span
->month
,
3357 orig_day
= span
->day
;
3360 result
= (Interval
*) palloc(sizeof(Interval
));
3364 (errcode(ERRCODE_DIVISION_BY_ZERO
),
3365 errmsg("division by zero")));
3367 result
->month
= (int32
) (span
->month
/ factor
);
3368 result
->day
= (int32
) (span
->day
/ factor
);
3371 * Fractional months full days into days. See comment in interval_mul().
3373 month_remainder_days
= (orig_month
/ factor
- result
->month
) * DAYS_PER_MONTH
;
3374 month_remainder_days
= TSROUND(month_remainder_days
);
3375 sec_remainder
= (orig_day
/ factor
- result
->day
+
3376 month_remainder_days
- (int) month_remainder_days
) * SECS_PER_DAY
;
3377 sec_remainder
= TSROUND(sec_remainder
);
3378 if (fabs(sec_remainder
) >= SECS_PER_DAY
)
3380 result
->day
+= (int) (sec_remainder
/ SECS_PER_DAY
);
3381 sec_remainder
-= (int) (sec_remainder
/ SECS_PER_DAY
) * SECS_PER_DAY
;
3384 /* cascade units down */
3385 result
->day
+= (int32
) month_remainder_days
;
3386 result
->time
= rint(span
->time
/ factor
+ sec_remainder
* USECS_PER_SEC
);
3388 PG_RETURN_INTERVAL_P(result
);
3393 * in_range support functions for timestamps and intervals.
3395 * Per SQL spec, we support these with interval as the offset type.
3396 * The spec's restriction that the offset not be negative is a bit hard to
3397 * decipher for intervals, but we choose to interpret it the same as our
3398 * interval comparison operators would.
3402 in_range_timestamptz_interval(PG_FUNCTION_ARGS
)
3404 TimestampTz val
= PG_GETARG_TIMESTAMPTZ(0);
3405 TimestampTz base
= PG_GETARG_TIMESTAMPTZ(1);
3406 Interval
*offset
= PG_GETARG_INTERVAL_P(2);
3407 bool sub
= PG_GETARG_BOOL(3);
3408 bool less
= PG_GETARG_BOOL(4);
3411 if (int128_compare(interval_cmp_value(offset
), int64_to_int128(0)) < 0)
3413 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE
),
3414 errmsg("invalid preceding or following size in window function")));
3416 /* We don't currently bother to avoid overflow hazards here */
3418 sum
= DatumGetTimestampTz(DirectFunctionCall2(timestamptz_mi_interval
,
3419 TimestampTzGetDatum(base
),
3420 IntervalPGetDatum(offset
)));
3422 sum
= DatumGetTimestampTz(DirectFunctionCall2(timestamptz_pl_interval
,
3423 TimestampTzGetDatum(base
),
3424 IntervalPGetDatum(offset
)));
3427 PG_RETURN_BOOL(val
<= sum
);
3429 PG_RETURN_BOOL(val
>= sum
);
3433 in_range_timestamp_interval(PG_FUNCTION_ARGS
)
3435 Timestamp val
= PG_GETARG_TIMESTAMP(0);
3436 Timestamp base
= PG_GETARG_TIMESTAMP(1);
3437 Interval
*offset
= PG_GETARG_INTERVAL_P(2);
3438 bool sub
= PG_GETARG_BOOL(3);
3439 bool less
= PG_GETARG_BOOL(4);
3442 if (int128_compare(interval_cmp_value(offset
), int64_to_int128(0)) < 0)
3444 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE
),
3445 errmsg("invalid preceding or following size in window function")));
3447 /* We don't currently bother to avoid overflow hazards here */
3449 sum
= DatumGetTimestamp(DirectFunctionCall2(timestamp_mi_interval
,
3450 TimestampGetDatum(base
),
3451 IntervalPGetDatum(offset
)));
3453 sum
= DatumGetTimestamp(DirectFunctionCall2(timestamp_pl_interval
,
3454 TimestampGetDatum(base
),
3455 IntervalPGetDatum(offset
)));
3458 PG_RETURN_BOOL(val
<= sum
);
3460 PG_RETURN_BOOL(val
>= sum
);
3464 in_range_interval_interval(PG_FUNCTION_ARGS
)
3466 Interval
*val
= PG_GETARG_INTERVAL_P(0);
3467 Interval
*base
= PG_GETARG_INTERVAL_P(1);
3468 Interval
*offset
= PG_GETARG_INTERVAL_P(2);
3469 bool sub
= PG_GETARG_BOOL(3);
3470 bool less
= PG_GETARG_BOOL(4);
3473 if (int128_compare(interval_cmp_value(offset
), int64_to_int128(0)) < 0)
3475 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE
),
3476 errmsg("invalid preceding or following size in window function")));
3478 /* We don't currently bother to avoid overflow hazards here */
3480 sum
= DatumGetIntervalP(DirectFunctionCall2(interval_mi
,
3481 IntervalPGetDatum(base
),
3482 IntervalPGetDatum(offset
)));
3484 sum
= DatumGetIntervalP(DirectFunctionCall2(interval_pl
,
3485 IntervalPGetDatum(base
),
3486 IntervalPGetDatum(offset
)));
3489 PG_RETURN_BOOL(interval_cmp_internal(val
, sum
) <= 0);
3491 PG_RETURN_BOOL(interval_cmp_internal(val
, sum
) >= 0);
3496 * interval_accum, interval_accum_inv, and interval_avg implement the
3497 * AVG(interval) aggregate.
3499 * The transition datatype for this aggregate is a 2-element array of
3500 * intervals, where the first is the running sum and the second contains
3501 * the number of values so far in its 'time' field. This is a bit ugly
3502 * but it beats inventing a specialized datatype for the purpose.
3506 interval_accum(PG_FUNCTION_ARGS
)
3508 ArrayType
*transarray
= PG_GETARG_ARRAYTYPE_P(0);
3509 Interval
*newval
= PG_GETARG_INTERVAL_P(1);
3517 deconstruct_array(transarray
,
3518 INTERVALOID
, sizeof(Interval
), false, TYPALIGN_DOUBLE
,
3519 &transdatums
, NULL
, &ndatums
);
3521 elog(ERROR
, "expected 2-element interval array");
3523 sumX
= *(DatumGetIntervalP(transdatums
[0]));
3524 N
= *(DatumGetIntervalP(transdatums
[1]));
3526 newsum
= DatumGetIntervalP(DirectFunctionCall2(interval_pl
,
3527 IntervalPGetDatum(&sumX
),
3528 IntervalPGetDatum(newval
)));
3531 transdatums
[0] = IntervalPGetDatum(newsum
);
3532 transdatums
[1] = IntervalPGetDatum(&N
);
3534 result
= construct_array(transdatums
, 2,
3535 INTERVALOID
, sizeof(Interval
), false, TYPALIGN_DOUBLE
);
3537 PG_RETURN_ARRAYTYPE_P(result
);
3541 interval_combine(PG_FUNCTION_ARGS
)
3543 ArrayType
*transarray1
= PG_GETARG_ARRAYTYPE_P(0);
3544 ArrayType
*transarray2
= PG_GETARG_ARRAYTYPE_P(1);
3545 Datum
*transdatums1
;
3546 Datum
*transdatums2
;
3557 deconstruct_array(transarray1
,
3558 INTERVALOID
, sizeof(Interval
), false, TYPALIGN_DOUBLE
,
3559 &transdatums1
, NULL
, &ndatums1
);
3561 elog(ERROR
, "expected 2-element interval array");
3563 sum1
= *(DatumGetIntervalP(transdatums1
[0]));
3564 N1
= *(DatumGetIntervalP(transdatums1
[1]));
3566 deconstruct_array(transarray2
,
3567 INTERVALOID
, sizeof(Interval
), false, TYPALIGN_DOUBLE
,
3568 &transdatums2
, NULL
, &ndatums2
);
3570 elog(ERROR
, "expected 2-element interval array");
3572 sum2
= *(DatumGetIntervalP(transdatums2
[0]));
3573 N2
= *(DatumGetIntervalP(transdatums2
[1]));
3575 newsum
= DatumGetIntervalP(DirectFunctionCall2(interval_pl
,
3576 IntervalPGetDatum(&sum1
),
3577 IntervalPGetDatum(&sum2
)));
3580 transdatums1
[0] = IntervalPGetDatum(newsum
);
3581 transdatums1
[1] = IntervalPGetDatum(&N1
);
3583 result
= construct_array(transdatums1
, 2,
3584 INTERVALOID
, sizeof(Interval
), false, TYPALIGN_DOUBLE
);
3586 PG_RETURN_ARRAYTYPE_P(result
);
3590 interval_accum_inv(PG_FUNCTION_ARGS
)
3592 ArrayType
*transarray
= PG_GETARG_ARRAYTYPE_P(0);
3593 Interval
*newval
= PG_GETARG_INTERVAL_P(1);
3601 deconstruct_array(transarray
,
3602 INTERVALOID
, sizeof(Interval
), false, TYPALIGN_DOUBLE
,
3603 &transdatums
, NULL
, &ndatums
);
3605 elog(ERROR
, "expected 2-element interval array");
3607 sumX
= *(DatumGetIntervalP(transdatums
[0]));
3608 N
= *(DatumGetIntervalP(transdatums
[1]));
3610 newsum
= DatumGetIntervalP(DirectFunctionCall2(interval_mi
,
3611 IntervalPGetDatum(&sumX
),
3612 IntervalPGetDatum(newval
)));
3615 transdatums
[0] = IntervalPGetDatum(newsum
);
3616 transdatums
[1] = IntervalPGetDatum(&N
);
3618 result
= construct_array(transdatums
, 2,
3619 INTERVALOID
, sizeof(Interval
), false, TYPALIGN_DOUBLE
);
3621 PG_RETURN_ARRAYTYPE_P(result
);
3625 interval_avg(PG_FUNCTION_ARGS
)
3627 ArrayType
*transarray
= PG_GETARG_ARRAYTYPE_P(0);
3633 deconstruct_array(transarray
,
3634 INTERVALOID
, sizeof(Interval
), false, TYPALIGN_DOUBLE
,
3635 &transdatums
, NULL
, &ndatums
);
3637 elog(ERROR
, "expected 2-element interval array");
3639 sumX
= *(DatumGetIntervalP(transdatums
[0]));
3640 N
= *(DatumGetIntervalP(transdatums
[1]));
3642 /* SQL defines AVG of no values to be NULL */
3646 return DirectFunctionCall2(interval_div
,
3647 IntervalPGetDatum(&sumX
),
3648 Float8GetDatum((double) N
.time
));
3653 * Calculate time difference while retaining year/month fields.
3654 * Note that this does not result in an accurate absolute time span
3655 * since year and month are out of context once the arithmetic
3659 timestamp_age(PG_FUNCTION_ARGS
)
3661 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
3662 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
3673 result
= (Interval
*) palloc(sizeof(Interval
));
3675 if (timestamp2tm(dt1
, NULL
, tm1
, &fsec1
, NULL
, NULL
) == 0 &&
3676 timestamp2tm(dt2
, NULL
, tm2
, &fsec2
, NULL
, NULL
) == 0)
3678 /* form the symbolic difference */
3679 tm
->tm_usec
= fsec1
- fsec2
;
3680 tm
->tm_sec
= tm1
->tm_sec
- tm2
->tm_sec
;
3681 tm
->tm_min
= tm1
->tm_min
- tm2
->tm_min
;
3682 tm
->tm_hour
= tm1
->tm_hour
- tm2
->tm_hour
;
3683 tm
->tm_mday
= tm1
->tm_mday
- tm2
->tm_mday
;
3684 tm
->tm_mon
= tm1
->tm_mon
- tm2
->tm_mon
;
3685 tm
->tm_year
= tm1
->tm_year
- tm2
->tm_year
;
3687 /* flip sign if necessary... */
3690 tm
->tm_usec
= -tm
->tm_usec
;
3691 tm
->tm_sec
= -tm
->tm_sec
;
3692 tm
->tm_min
= -tm
->tm_min
;
3693 tm
->tm_hour
= -tm
->tm_hour
;
3694 tm
->tm_mday
= -tm
->tm_mday
;
3695 tm
->tm_mon
= -tm
->tm_mon
;
3696 tm
->tm_year
= -tm
->tm_year
;
3699 /* propagate any negative fields into the next higher field */
3700 while (tm
->tm_usec
< 0)
3702 tm
->tm_usec
+= USECS_PER_SEC
;
3706 while (tm
->tm_sec
< 0)
3708 tm
->tm_sec
+= SECS_PER_MINUTE
;
3712 while (tm
->tm_min
< 0)
3714 tm
->tm_min
+= MINS_PER_HOUR
;
3718 while (tm
->tm_hour
< 0)
3720 tm
->tm_hour
+= HOURS_PER_DAY
;
3724 while (tm
->tm_mday
< 0)
3728 tm
->tm_mday
+= day_tab
[isleap(tm1
->tm_year
)][tm1
->tm_mon
- 1];
3733 tm
->tm_mday
+= day_tab
[isleap(tm2
->tm_year
)][tm2
->tm_mon
- 1];
3738 while (tm
->tm_mon
< 0)
3740 tm
->tm_mon
+= MONTHS_PER_YEAR
;
3744 /* recover sign if necessary... */
3747 tm
->tm_usec
= -tm
->tm_usec
;
3748 tm
->tm_sec
= -tm
->tm_sec
;
3749 tm
->tm_min
= -tm
->tm_min
;
3750 tm
->tm_hour
= -tm
->tm_hour
;
3751 tm
->tm_mday
= -tm
->tm_mday
;
3752 tm
->tm_mon
= -tm
->tm_mon
;
3753 tm
->tm_year
= -tm
->tm_year
;
3756 if (itm2interval(tm
, result
) != 0)
3758 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3759 errmsg("interval out of range")));
3763 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3764 errmsg("timestamp out of range")));
3766 PG_RETURN_INTERVAL_P(result
);
3770 /* timestamptz_age()
3771 * Calculate time difference while retaining year/month fields.
3772 * Note that this does not result in an accurate absolute time span
3773 * since year and month are out of context once the arithmetic
3777 timestamptz_age(PG_FUNCTION_ARGS
)
3779 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
3780 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
3793 result
= (Interval
*) palloc(sizeof(Interval
));
3795 if (timestamp2tm(dt1
, &tz1
, tm1
, &fsec1
, NULL
, NULL
) == 0 &&
3796 timestamp2tm(dt2
, &tz2
, tm2
, &fsec2
, NULL
, NULL
) == 0)
3798 /* form the symbolic difference */
3799 tm
->tm_usec
= fsec1
- fsec2
;
3800 tm
->tm_sec
= tm1
->tm_sec
- tm2
->tm_sec
;
3801 tm
->tm_min
= tm1
->tm_min
- tm2
->tm_min
;
3802 tm
->tm_hour
= tm1
->tm_hour
- tm2
->tm_hour
;
3803 tm
->tm_mday
= tm1
->tm_mday
- tm2
->tm_mday
;
3804 tm
->tm_mon
= tm1
->tm_mon
- tm2
->tm_mon
;
3805 tm
->tm_year
= tm1
->tm_year
- tm2
->tm_year
;
3807 /* flip sign if necessary... */
3810 tm
->tm_usec
= -tm
->tm_usec
;
3811 tm
->tm_sec
= -tm
->tm_sec
;
3812 tm
->tm_min
= -tm
->tm_min
;
3813 tm
->tm_hour
= -tm
->tm_hour
;
3814 tm
->tm_mday
= -tm
->tm_mday
;
3815 tm
->tm_mon
= -tm
->tm_mon
;
3816 tm
->tm_year
= -tm
->tm_year
;
3819 /* propagate any negative fields into the next higher field */
3820 while (tm
->tm_usec
< 0)
3822 tm
->tm_usec
+= USECS_PER_SEC
;
3826 while (tm
->tm_sec
< 0)
3828 tm
->tm_sec
+= SECS_PER_MINUTE
;
3832 while (tm
->tm_min
< 0)
3834 tm
->tm_min
+= MINS_PER_HOUR
;
3838 while (tm
->tm_hour
< 0)
3840 tm
->tm_hour
+= HOURS_PER_DAY
;
3844 while (tm
->tm_mday
< 0)
3848 tm
->tm_mday
+= day_tab
[isleap(tm1
->tm_year
)][tm1
->tm_mon
- 1];
3853 tm
->tm_mday
+= day_tab
[isleap(tm2
->tm_year
)][tm2
->tm_mon
- 1];
3858 while (tm
->tm_mon
< 0)
3860 tm
->tm_mon
+= MONTHS_PER_YEAR
;
3865 * Note: we deliberately ignore any difference between tz1 and tz2.
3868 /* recover sign if necessary... */
3871 tm
->tm_usec
= -tm
->tm_usec
;
3872 tm
->tm_sec
= -tm
->tm_sec
;
3873 tm
->tm_min
= -tm
->tm_min
;
3874 tm
->tm_hour
= -tm
->tm_hour
;
3875 tm
->tm_mday
= -tm
->tm_mday
;
3876 tm
->tm_mon
= -tm
->tm_mon
;
3877 tm
->tm_year
= -tm
->tm_year
;
3880 if (itm2interval(tm
, result
) != 0)
3882 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3883 errmsg("interval out of range")));
3887 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3888 errmsg("timestamp out of range")));
3890 PG_RETURN_INTERVAL_P(result
);
3894 /*----------------------------------------------------------
3895 * Conversion operators.
3896 *---------------------------------------------------------*/
3900 * Bin timestamp into specified interval.
3903 timestamp_bin(PG_FUNCTION_ARGS
)
3905 Interval
*stride
= PG_GETARG_INTERVAL_P(0);
3906 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
3907 Timestamp origin
= PG_GETARG_TIMESTAMP(2);
3913 if (TIMESTAMP_NOT_FINITE(timestamp
))
3914 PG_RETURN_TIMESTAMP(timestamp
);
3916 if (TIMESTAMP_NOT_FINITE(origin
))
3918 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3919 errmsg("origin out of range")));
3921 if (stride
->month
!= 0)
3923 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3924 errmsg("timestamps cannot be binned into intervals containing months or years")));
3926 stride_usecs
= stride
->day
* USECS_PER_DAY
+ stride
->time
;
3928 if (stride_usecs
<= 0)
3930 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3931 errmsg("stride must be greater than zero")));
3933 tm_diff
= timestamp
- origin
;
3934 tm_delta
= tm_diff
- tm_diff
% stride_usecs
;
3937 * Make sure the returned timestamp is at the start of the bin, even if
3938 * the origin is in the future.
3940 if (origin
> timestamp
&& stride_usecs
> 1)
3941 tm_delta
-= stride_usecs
;
3943 result
= origin
+ tm_delta
;
3945 PG_RETURN_TIMESTAMP(result
);
3948 /* timestamp_trunc()
3949 * Truncate timestamp to specified units.
3952 timestamp_trunc(PG_FUNCTION_ARGS
)
3954 text
*units
= PG_GETARG_TEXT_PP(0);
3955 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
3964 if (TIMESTAMP_NOT_FINITE(timestamp
))
3965 PG_RETURN_TIMESTAMP(timestamp
);
3967 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
3968 VARSIZE_ANY_EXHDR(units
),
3971 type
= DecodeUnits(0, lowunits
, &val
);
3975 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
3977 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3978 errmsg("timestamp out of range")));
3986 woy
= date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3989 * If it is week 52/53 and the month is January, then the
3990 * week must belong to the previous year. Also, some
3991 * December dates belong to the next year.
3993 if (woy
>= 52 && tm
->tm_mon
== 1)
3995 if (woy
<= 1 && tm
->tm_mon
== MONTHS_PER_YEAR
)
3997 isoweek2date(woy
, &(tm
->tm_year
), &(tm
->tm_mon
), &(tm
->tm_mday
));
4004 case DTK_MILLENNIUM
:
4005 /* see comments in timestamptz_trunc */
4006 if (tm
->tm_year
> 0)
4007 tm
->tm_year
= ((tm
->tm_year
+ 999) / 1000) * 1000 - 999;
4009 tm
->tm_year
= -((999 - (tm
->tm_year
- 1)) / 1000) * 1000 + 1;
4012 /* see comments in timestamptz_trunc */
4013 if (tm
->tm_year
> 0)
4014 tm
->tm_year
= ((tm
->tm_year
+ 99) / 100) * 100 - 99;
4016 tm
->tm_year
= -((99 - (tm
->tm_year
- 1)) / 100) * 100 + 1;
4019 /* see comments in timestamptz_trunc */
4020 if (val
!= DTK_MILLENNIUM
&& val
!= DTK_CENTURY
)
4022 if (tm
->tm_year
> 0)
4023 tm
->tm_year
= (tm
->tm_year
/ 10) * 10;
4025 tm
->tm_year
= -((8 - (tm
->tm_year
- 1)) / 10) * 10;
4032 tm
->tm_mon
= (3 * ((tm
->tm_mon
- 1) / 3)) + 1;
4051 fsec
= (fsec
/ 1000) * 1000;
4059 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4060 errmsg("unit \"%s\" not supported for type %s",
4061 lowunits
, format_type_be(TIMESTAMPOID
))));
4065 if (tm2timestamp(tm
, fsec
, NULL
, &result
) != 0)
4067 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4068 errmsg("timestamp out of range")));
4073 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4074 errmsg("unit \"%s\" not recognized for type %s",
4075 lowunits
, format_type_be(TIMESTAMPOID
))));
4079 PG_RETURN_TIMESTAMP(result
);
4082 /* timestamptz_bin()
4083 * Bin timestamptz into specified interval using specified origin.
4086 timestamptz_bin(PG_FUNCTION_ARGS
)
4088 Interval
*stride
= PG_GETARG_INTERVAL_P(0);
4089 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
4090 TimestampTz origin
= PG_GETARG_TIMESTAMPTZ(2);
4096 if (TIMESTAMP_NOT_FINITE(timestamp
))
4097 PG_RETURN_TIMESTAMPTZ(timestamp
);
4099 if (TIMESTAMP_NOT_FINITE(origin
))
4101 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4102 errmsg("origin out of range")));
4104 if (stride
->month
!= 0)
4106 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4107 errmsg("timestamps cannot be binned into intervals containing months or years")));
4109 stride_usecs
= stride
->day
* USECS_PER_DAY
+ stride
->time
;
4111 if (stride_usecs
<= 0)
4113 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4114 errmsg("stride must be greater than zero")));
4116 tm_diff
= timestamp
- origin
;
4117 tm_delta
= tm_diff
- tm_diff
% stride_usecs
;
4120 * Make sure the returned timestamp is at the start of the bin, even if
4121 * the origin is in the future.
4123 if (origin
> timestamp
&& stride_usecs
> 1)
4124 tm_delta
-= stride_usecs
;
4126 result
= origin
+ tm_delta
;
4128 PG_RETURN_TIMESTAMPTZ(result
);
4132 * Common code for timestamptz_trunc() and timestamptz_trunc_zone().
4134 * tzp identifies the zone to truncate with respect to. We assume
4135 * infinite timestamps have already been rejected.
4138 timestamptz_trunc_internal(text
*units
, TimestampTz timestamp
, pg_tz
*tzp
)
4144 bool redotz
= false;
4150 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
4151 VARSIZE_ANY_EXHDR(units
),
4154 type
= DecodeUnits(0, lowunits
, &val
);
4158 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, NULL
, tzp
) != 0)
4160 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4161 errmsg("timestamp out of range")));
4169 woy
= date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
4172 * If it is week 52/53 and the month is January, then the
4173 * week must belong to the previous year. Also, some
4174 * December dates belong to the next year.
4176 if (woy
>= 52 && tm
->tm_mon
== 1)
4178 if (woy
<= 1 && tm
->tm_mon
== MONTHS_PER_YEAR
)
4180 isoweek2date(woy
, &(tm
->tm_year
), &(tm
->tm_mon
), &(tm
->tm_mday
));
4188 /* one may consider DTK_THOUSAND and DTK_HUNDRED... */
4189 case DTK_MILLENNIUM
:
4192 * truncating to the millennium? what is this supposed to
4193 * mean? let us put the first year of the millennium... i.e.
4194 * -1000, 1, 1001, 2001...
4196 if (tm
->tm_year
> 0)
4197 tm
->tm_year
= ((tm
->tm_year
+ 999) / 1000) * 1000 - 999;
4199 tm
->tm_year
= -((999 - (tm
->tm_year
- 1)) / 1000) * 1000 + 1;
4202 /* truncating to the century? as above: -100, 1, 101... */
4203 if (tm
->tm_year
> 0)
4204 tm
->tm_year
= ((tm
->tm_year
+ 99) / 100) * 100 - 99;
4206 tm
->tm_year
= -((99 - (tm
->tm_year
- 1)) / 100) * 100 + 1;
4211 * truncating to the decade? first year of the decade. must
4212 * not be applied if year was truncated before!
4214 if (val
!= DTK_MILLENNIUM
&& val
!= DTK_CENTURY
)
4216 if (tm
->tm_year
> 0)
4217 tm
->tm_year
= (tm
->tm_year
/ 10) * 10;
4219 tm
->tm_year
= -((8 - (tm
->tm_year
- 1)) / 10) * 10;
4226 tm
->tm_mon
= (3 * ((tm
->tm_mon
- 1) / 3)) + 1;
4233 redotz
= true; /* for all cases >= DAY */
4245 fsec
= (fsec
/ 1000) * 1000;
4252 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4253 errmsg("unit \"%s\" not supported for type %s",
4254 lowunits
, format_type_be(TIMESTAMPTZOID
))));
4259 tz
= DetermineTimeZoneOffset(tm
, tzp
);
4261 if (tm2timestamp(tm
, fsec
, &tz
, &result
) != 0)
4263 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4264 errmsg("timestamp out of range")));
4269 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4270 errmsg("unit \"%s\" not recognized for type %s",
4271 lowunits
, format_type_be(TIMESTAMPTZOID
))));
4278 /* timestamptz_trunc()
4279 * Truncate timestamptz to specified units in session timezone.
4282 timestamptz_trunc(PG_FUNCTION_ARGS
)
4284 text
*units
= PG_GETARG_TEXT_PP(0);
4285 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
4288 if (TIMESTAMP_NOT_FINITE(timestamp
))
4289 PG_RETURN_TIMESTAMPTZ(timestamp
);
4291 result
= timestamptz_trunc_internal(units
, timestamp
, session_timezone
);
4293 PG_RETURN_TIMESTAMPTZ(result
);
4296 /* timestamptz_trunc_zone()
4297 * Truncate timestamptz to specified units in specified timezone.
4300 timestamptz_trunc_zone(PG_FUNCTION_ARGS
)
4302 text
*units
= PG_GETARG_TEXT_PP(0);
4303 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
4304 text
*zone
= PG_GETARG_TEXT_PP(2);
4306 char tzname
[TZ_STRLEN_MAX
+ 1];
4312 DateTimeErrorExtra extra
;
4315 * timestamptz_zone() doesn't look up the zone for infinite inputs, so we
4316 * don't do so here either.
4318 if (TIMESTAMP_NOT_FINITE(timestamp
))
4319 PG_RETURN_TIMESTAMP(timestamp
);
4322 * Look up the requested timezone (see notes in timestamptz_zone()).
4324 text_to_cstring_buffer(zone
, tzname
, sizeof(tzname
));
4326 /* DecodeTimezoneAbbrev requires lowercase input */
4327 lowzone
= downcase_truncate_identifier(tzname
,
4331 dterr
= DecodeTimezoneAbbrev(0, lowzone
, &type
, &val
, &tzp
, &extra
);
4333 DateTimeParseError(dterr
, &extra
, NULL
, NULL
, NULL
);
4335 if (type
== TZ
|| type
== DTZ
)
4337 /* fixed-offset abbreviation, get a pg_tz descriptor for that */
4338 tzp
= pg_tzset_offset(-val
);
4340 else if (type
== DYNTZ
)
4342 /* dynamic-offset abbreviation, use its referenced timezone */
4346 /* try it as a full zone name */
4347 tzp
= pg_tzset(tzname
);
4350 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4351 errmsg("time zone \"%s\" not recognized", tzname
)));
4354 result
= timestamptz_trunc_internal(units
, timestamp
, tzp
);
4356 PG_RETURN_TIMESTAMPTZ(result
);
4360 * Extract specified field from interval.
4363 interval_trunc(PG_FUNCTION_ARGS
)
4365 text
*units
= PG_GETARG_TEXT_PP(0);
4366 Interval
*interval
= PG_GETARG_INTERVAL_P(1);
4374 result
= (Interval
*) palloc(sizeof(Interval
));
4376 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
4377 VARSIZE_ANY_EXHDR(units
),
4380 type
= DecodeUnits(0, lowunits
, &val
);
4384 interval2itm(*interval
, tm
);
4387 case DTK_MILLENNIUM
:
4388 /* caution: C division may have negative remainder */
4389 tm
->tm_year
= (tm
->tm_year
/ 1000) * 1000;
4392 /* caution: C division may have negative remainder */
4393 tm
->tm_year
= (tm
->tm_year
/ 100) * 100;
4396 /* caution: C division may have negative remainder */
4397 tm
->tm_year
= (tm
->tm_year
/ 10) * 10;
4403 tm
->tm_mon
= 3 * (tm
->tm_mon
/ 3);
4421 tm
->tm_usec
= (tm
->tm_usec
/ 1000) * 1000;
4428 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4429 errmsg("unit \"%s\" not supported for type %s",
4430 lowunits
, format_type_be(INTERVALOID
)),
4431 (val
== DTK_WEEK
) ? errdetail("Months usually have fractional weeks.") : 0));
4434 if (itm2interval(tm
, result
) != 0)
4436 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4437 errmsg("interval out of range")));
4442 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4443 errmsg("unit \"%s\" not recognized for type %s",
4444 lowunits
, format_type_be(INTERVALOID
))));
4447 PG_RETURN_INTERVAL_P(result
);
4452 * Return the Julian day which corresponds to the first day (Monday) of the given ISO 8601 year and week.
4453 * Julian days are used to convert between ISO week dates and Gregorian dates.
4456 isoweek2j(int year
, int week
)
4461 /* fourth day of current year */
4462 day4
= date2j(year
, 1, 4);
4464 /* day0 == offset to first day of week (Monday) */
4465 day0
= j2day(day4
- 1);
4467 return ((week
- 1) * 7) + (day4
- day0
);
4471 * Convert ISO week of year number to date.
4472 * The year field must be specified with the ISO year!
4476 isoweek2date(int woy
, int *year
, int *mon
, int *mday
)
4478 j2date(isoweek2j(*year
, woy
), year
, mon
, mday
);
4481 /* isoweekdate2date()
4483 * Convert an ISO 8601 week date (ISO year, ISO week) into a Gregorian date.
4484 * Gregorian day of week sent so weekday strings can be supplied.
4485 * Populates year, mon, and mday with the correct Gregorian values.
4486 * year must be passed in as the ISO year.
4489 isoweekdate2date(int isoweek
, int wday
, int *year
, int *mon
, int *mday
)
4493 jday
= isoweek2j(*year
, isoweek
);
4494 /* convert Gregorian week start (Sunday=1) to ISO week start (Monday=1) */
4499 j2date(jday
, year
, mon
, mday
);
4504 * Returns ISO week number of year.
4507 date2isoweek(int year
, int mon
, int mday
)
4515 dayn
= date2j(year
, mon
, mday
);
4517 /* fourth day of current year */
4518 day4
= date2j(year
, 1, 4);
4520 /* day0 == offset to first day of week (Monday) */
4521 day0
= j2day(day4
- 1);
4524 * We need the first week containing a Thursday, otherwise this day falls
4525 * into the previous year for purposes of counting weeks
4527 if (dayn
< day4
- day0
)
4529 day4
= date2j(year
- 1, 1, 4);
4531 /* day0 == offset to first day of week (Monday) */
4532 day0
= j2day(day4
- 1);
4535 result
= (dayn
- (day4
- day0
)) / 7 + 1;
4538 * Sometimes the last few days in a year will fall into the first week of
4539 * the next year, so check for this.
4543 day4
= date2j(year
+ 1, 1, 4);
4545 /* day0 == offset to first day of week (Monday) */
4546 day0
= j2day(day4
- 1);
4548 if (dayn
>= day4
- day0
)
4549 result
= (dayn
- (day4
- day0
)) / 7 + 1;
4552 return (int) result
;
4558 * Returns ISO 8601 year number.
4559 * Note: zero or negative results follow the year-zero-exists convention.
4562 date2isoyear(int year
, int mon
, int mday
)
4570 dayn
= date2j(year
, mon
, mday
);
4572 /* fourth day of current year */
4573 day4
= date2j(year
, 1, 4);
4575 /* day0 == offset to first day of week (Monday) */
4576 day0
= j2day(day4
- 1);
4579 * We need the first week containing a Thursday, otherwise this day falls
4580 * into the previous year for purposes of counting weeks
4582 if (dayn
< day4
- day0
)
4584 day4
= date2j(year
- 1, 1, 4);
4586 /* day0 == offset to first day of week (Monday) */
4587 day0
= j2day(day4
- 1);
4592 result
= (dayn
- (day4
- day0
)) / 7 + 1;
4595 * Sometimes the last few days in a year will fall into the first week of
4596 * the next year, so check for this.
4600 day4
= date2j(year
+ 1, 1, 4);
4602 /* day0 == offset to first day of week (Monday) */
4603 day0
= j2day(day4
- 1);
4605 if (dayn
>= day4
- day0
)
4613 /* date2isoyearday()
4615 * Returns the ISO 8601 day-of-year, given a Gregorian year, month and day.
4616 * Possible return values are 1 through 371 (364 in non-leap years).
4619 date2isoyearday(int year
, int mon
, int mday
)
4621 return date2j(year
, mon
, mday
) - isoweek2j(date2isoyear(year
, mon
, mday
), 1) + 1;
4625 * NonFiniteTimestampTzPart
4627 * Used by timestamp_part and timestamptz_part when extracting from infinite
4628 * timestamp[tz]. Returns +/-Infinity if that is the appropriate result,
4629 * otherwise returns zero (which should be taken as meaning to return NULL).
4631 * Errors thrown here for invalid units should exactly match those that
4632 * would be thrown in the calling functions, else there will be unexpected
4633 * discrepancies between finite- and infinite-input cases.
4636 NonFiniteTimestampTzPart(int type
, int unit
, char *lowunits
,
4637 bool isNegative
, bool isTz
)
4639 if ((type
!= UNITS
) && (type
!= RESERV
))
4641 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4642 errmsg("unit \"%s\" not recognized for type %s",
4644 format_type_be(isTz
? TIMESTAMPTZOID
: TIMESTAMPOID
))));
4648 /* Oscillating units */
4666 /* Monotonically-increasing units */
4670 case DTK_MILLENNIUM
:
4675 return -get_float8_infinity();
4677 return get_float8_infinity();
4681 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4682 errmsg("unit \"%s\" not supported for type %s",
4684 format_type_be(isTz
? TIMESTAMPTZOID
: TIMESTAMPOID
))));
4685 return 0.0; /* keep compiler quiet */
4689 /* timestamp_part() and extract_timestamp()
4690 * Extract specified field from timestamp.
4693 timestamp_part_common(PG_FUNCTION_ARGS
, bool retnumeric
)
4695 text
*units
= PG_GETARG_TEXT_PP(0);
4696 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
4706 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
4707 VARSIZE_ANY_EXHDR(units
),
4710 type
= DecodeUnits(0, lowunits
, &val
);
4711 if (type
== UNKNOWN_FIELD
)
4712 type
= DecodeSpecial(0, lowunits
, &val
);
4714 if (TIMESTAMP_NOT_FINITE(timestamp
))
4716 double r
= NonFiniteTimestampTzPart(type
, val
, lowunits
,
4717 TIMESTAMP_IS_NOBEGIN(timestamp
),
4725 return DirectFunctionCall3(numeric_in
,
4726 CStringGetDatum("-Infinity"),
4727 ObjectIdGetDatum(InvalidOid
),
4730 return DirectFunctionCall3(numeric_in
,
4731 CStringGetDatum("Infinity"),
4732 ObjectIdGetDatum(InvalidOid
),
4736 PG_RETURN_FLOAT8(r
);
4744 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
4746 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4747 errmsg("timestamp out of range")));
4752 intresult
= tm
->tm_sec
* INT64CONST(1000000) + fsec
;
4758 * tm->tm_sec * 1000 + fsec / 1000
4759 * = (tm->tm_sec * 1'000'000 + fsec) / 1000
4761 PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm
->tm_sec
* INT64CONST(1000000) + fsec
, 3));
4763 PG_RETURN_FLOAT8(tm
->tm_sec
* 1000.0 + fsec
/ 1000.0);
4769 * tm->tm_sec + fsec / 1'000'000
4770 * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
4772 PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm
->tm_sec
* INT64CONST(1000000) + fsec
, 6));
4774 PG_RETURN_FLOAT8(tm
->tm_sec
+ fsec
/ 1000000.0);
4778 intresult
= tm
->tm_min
;
4782 intresult
= tm
->tm_hour
;
4786 intresult
= tm
->tm_mday
;
4790 intresult
= tm
->tm_mon
;
4794 intresult
= (tm
->tm_mon
- 1) / 3 + 1;
4798 intresult
= date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
4802 if (tm
->tm_year
> 0)
4803 intresult
= tm
->tm_year
;
4805 /* there is no year 0, just 1 BC and 1 AD */
4806 intresult
= tm
->tm_year
- 1;
4812 * what is a decade wrt dates? let us assume that decade 199
4813 * is 1990 thru 1999... decade 0 starts on year 1 BC, and -1
4814 * is 11 BC thru 2 BC...
4816 if (tm
->tm_year
>= 0)
4817 intresult
= tm
->tm_year
/ 10;
4819 intresult
= -((8 - (tm
->tm_year
- 1)) / 10);
4825 * centuries AD, c>0: year in [ (c-1)* 100 + 1 : c*100 ]
4826 * centuries BC, c<0: year in [ c*100 : (c+1) * 100 - 1]
4827 * there is no number 0 century.
4830 if (tm
->tm_year
> 0)
4831 intresult
= (tm
->tm_year
+ 99) / 100;
4833 /* caution: C division may have negative remainder */
4834 intresult
= -((99 - (tm
->tm_year
- 1)) / 100);
4837 case DTK_MILLENNIUM
:
4838 /* see comments above. */
4839 if (tm
->tm_year
> 0)
4840 intresult
= (tm
->tm_year
+ 999) / 1000;
4842 intresult
= -((999 - (tm
->tm_year
- 1)) / 1000);
4847 PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
)),
4848 numeric_div_opt_error(int64_to_numeric(((((tm
->tm_hour
* MINS_PER_HOUR
) + tm
->tm_min
) * SECS_PER_MINUTE
) + tm
->tm_sec
) * INT64CONST(1000000) + fsec
),
4849 int64_to_numeric(SECS_PER_DAY
* INT64CONST(1000000)),
4853 PG_RETURN_FLOAT8(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) +
4854 ((((tm
->tm_hour
* MINS_PER_HOUR
) + tm
->tm_min
) * SECS_PER_MINUTE
) +
4855 tm
->tm_sec
+ (fsec
/ 1000000.0)) / (double) SECS_PER_DAY
);
4859 intresult
= date2isoyear(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
4860 /* Adjust BC years */
4867 intresult
= j2day(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
));
4868 if (val
== DTK_ISODOW
&& intresult
== 0)
4873 intresult
= (date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
)
4874 - date2j(tm
->tm_year
, 1, 1) + 1);
4882 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4883 errmsg("unit \"%s\" not supported for type %s",
4884 lowunits
, format_type_be(TIMESTAMPOID
))));
4888 else if (type
== RESERV
)
4893 epoch
= SetEpochTimestamp();
4894 /* (timestamp - epoch) / 1000000 */
4899 if (timestamp
< (PG_INT64_MAX
+ epoch
))
4900 result
= int64_div_fast_to_numeric(timestamp
- epoch
, 6);
4903 result
= numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp
),
4904 int64_to_numeric(epoch
),
4906 int64_to_numeric(1000000),
4908 result
= DatumGetNumeric(DirectFunctionCall2(numeric_round
,
4909 NumericGetDatum(result
),
4912 PG_RETURN_NUMERIC(result
);
4918 /* try to avoid precision loss in subtraction */
4919 if (timestamp
< (PG_INT64_MAX
+ epoch
))
4920 result
= (timestamp
- epoch
) / 1000000.0;
4922 result
= ((float8
) timestamp
- epoch
) / 1000000.0;
4923 PG_RETURN_FLOAT8(result
);
4929 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4930 errmsg("unit \"%s\" not supported for type %s",
4931 lowunits
, format_type_be(TIMESTAMPOID
))));
4938 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4939 errmsg("unit \"%s\" not recognized for type %s",
4940 lowunits
, format_type_be(TIMESTAMPOID
))));
4945 PG_RETURN_NUMERIC(int64_to_numeric(intresult
));
4947 PG_RETURN_FLOAT8(intresult
);
4951 timestamp_part(PG_FUNCTION_ARGS
)
4953 return timestamp_part_common(fcinfo
, false);
4957 extract_timestamp(PG_FUNCTION_ARGS
)
4959 return timestamp_part_common(fcinfo
, true);
4962 /* timestamptz_part() and extract_timestamptz()
4963 * Extract specified field from timestamp with time zone.
4966 timestamptz_part_common(PG_FUNCTION_ARGS
, bool retnumeric
)
4968 text
*units
= PG_GETARG_TEXT_PP(0);
4969 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
4980 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
4981 VARSIZE_ANY_EXHDR(units
),
4984 type
= DecodeUnits(0, lowunits
, &val
);
4985 if (type
== UNKNOWN_FIELD
)
4986 type
= DecodeSpecial(0, lowunits
, &val
);
4988 if (TIMESTAMP_NOT_FINITE(timestamp
))
4990 double r
= NonFiniteTimestampTzPart(type
, val
, lowunits
,
4991 TIMESTAMP_IS_NOBEGIN(timestamp
),
4999 return DirectFunctionCall3(numeric_in
,
5000 CStringGetDatum("-Infinity"),
5001 ObjectIdGetDatum(InvalidOid
),
5004 return DirectFunctionCall3(numeric_in
,
5005 CStringGetDatum("Infinity"),
5006 ObjectIdGetDatum(InvalidOid
),
5010 PG_RETURN_FLOAT8(r
);
5018 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, NULL
, NULL
) != 0)
5020 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5021 errmsg("timestamp out of range")));
5030 intresult
= (-tz
/ SECS_PER_MINUTE
) % MINS_PER_HOUR
;
5034 intresult
= -tz
/ SECS_PER_HOUR
;
5038 intresult
= tm
->tm_sec
* INT64CONST(1000000) + fsec
;
5044 * tm->tm_sec * 1000 + fsec / 1000
5045 * = (tm->tm_sec * 1'000'000 + fsec) / 1000
5047 PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm
->tm_sec
* INT64CONST(1000000) + fsec
, 3));
5049 PG_RETURN_FLOAT8(tm
->tm_sec
* 1000.0 + fsec
/ 1000.0);
5055 * tm->tm_sec + fsec / 1'000'000
5056 * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
5058 PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm
->tm_sec
* INT64CONST(1000000) + fsec
, 6));
5060 PG_RETURN_FLOAT8(tm
->tm_sec
+ fsec
/ 1000000.0);
5064 intresult
= tm
->tm_min
;
5068 intresult
= tm
->tm_hour
;
5072 intresult
= tm
->tm_mday
;
5076 intresult
= tm
->tm_mon
;
5080 intresult
= (tm
->tm_mon
- 1) / 3 + 1;
5084 intresult
= date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
5088 if (tm
->tm_year
> 0)
5089 intresult
= tm
->tm_year
;
5091 /* there is no year 0, just 1 BC and 1 AD */
5092 intresult
= tm
->tm_year
- 1;
5096 /* see comments in timestamp_part */
5097 if (tm
->tm_year
> 0)
5098 intresult
= tm
->tm_year
/ 10;
5100 intresult
= -((8 - (tm
->tm_year
- 1)) / 10);
5104 /* see comments in timestamp_part */
5105 if (tm
->tm_year
> 0)
5106 intresult
= (tm
->tm_year
+ 99) / 100;
5108 intresult
= -((99 - (tm
->tm_year
- 1)) / 100);
5111 case DTK_MILLENNIUM
:
5112 /* see comments in timestamp_part */
5113 if (tm
->tm_year
> 0)
5114 intresult
= (tm
->tm_year
+ 999) / 1000;
5116 intresult
= -((999 - (tm
->tm_year
- 1)) / 1000);
5121 PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
)),
5122 numeric_div_opt_error(int64_to_numeric(((((tm
->tm_hour
* MINS_PER_HOUR
) + tm
->tm_min
) * SECS_PER_MINUTE
) + tm
->tm_sec
) * INT64CONST(1000000) + fsec
),
5123 int64_to_numeric(SECS_PER_DAY
* INT64CONST(1000000)),
5127 PG_RETURN_FLOAT8(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) +
5128 ((((tm
->tm_hour
* MINS_PER_HOUR
) + tm
->tm_min
) * SECS_PER_MINUTE
) +
5129 tm
->tm_sec
+ (fsec
/ 1000000.0)) / (double) SECS_PER_DAY
);
5133 intresult
= date2isoyear(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
5134 /* Adjust BC years */
5141 intresult
= j2day(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
));
5142 if (val
== DTK_ISODOW
&& intresult
== 0)
5147 intresult
= (date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
)
5148 - date2j(tm
->tm_year
, 1, 1) + 1);
5153 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5154 errmsg("unit \"%s\" not supported for type %s",
5155 lowunits
, format_type_be(TIMESTAMPTZOID
))));
5159 else if (type
== RESERV
)
5164 epoch
= SetEpochTimestamp();
5165 /* (timestamp - epoch) / 1000000 */
5170 if (timestamp
< (PG_INT64_MAX
+ epoch
))
5171 result
= int64_div_fast_to_numeric(timestamp
- epoch
, 6);
5174 result
= numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp
),
5175 int64_to_numeric(epoch
),
5177 int64_to_numeric(1000000),
5179 result
= DatumGetNumeric(DirectFunctionCall2(numeric_round
,
5180 NumericGetDatum(result
),
5183 PG_RETURN_NUMERIC(result
);
5189 /* try to avoid precision loss in subtraction */
5190 if (timestamp
< (PG_INT64_MAX
+ epoch
))
5191 result
= (timestamp
- epoch
) / 1000000.0;
5193 result
= ((float8
) timestamp
- epoch
) / 1000000.0;
5194 PG_RETURN_FLOAT8(result
);
5200 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5201 errmsg("unit \"%s\" not supported for type %s",
5202 lowunits
, format_type_be(TIMESTAMPTZOID
))));
5209 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
5210 errmsg("unit \"%s\" not recognized for type %s",
5211 lowunits
, format_type_be(TIMESTAMPTZOID
))));
5217 PG_RETURN_NUMERIC(int64_to_numeric(intresult
));
5219 PG_RETURN_FLOAT8(intresult
);
5223 timestamptz_part(PG_FUNCTION_ARGS
)
5225 return timestamptz_part_common(fcinfo
, false);
5229 extract_timestamptz(PG_FUNCTION_ARGS
)
5231 return timestamptz_part_common(fcinfo
, true);
5235 /* interval_part() and extract_interval()
5236 * Extract specified field from interval.
5239 interval_part_common(PG_FUNCTION_ARGS
, bool retnumeric
)
5241 text
*units
= PG_GETARG_TEXT_PP(0);
5242 Interval
*interval
= PG_GETARG_INTERVAL_P(1);
5250 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
5251 VARSIZE_ANY_EXHDR(units
),
5254 type
= DecodeUnits(0, lowunits
, &val
);
5255 if (type
== UNKNOWN_FIELD
)
5256 type
= DecodeSpecial(0, lowunits
, &val
);
5260 interval2itm(*interval
, tm
);
5264 intresult
= tm
->tm_sec
* INT64CONST(1000000) + tm
->tm_usec
;
5270 * tm->tm_sec * 1000 + fsec / 1000
5271 * = (tm->tm_sec * 1'000'000 + fsec) / 1000
5273 PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm
->tm_sec
* INT64CONST(1000000) + tm
->tm_usec
, 3));
5275 PG_RETURN_FLOAT8(tm
->tm_sec
* 1000.0 + tm
->tm_usec
/ 1000.0);
5281 * tm->tm_sec + fsec / 1'000'000
5282 * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
5284 PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm
->tm_sec
* INT64CONST(1000000) + tm
->tm_usec
, 6));
5286 PG_RETURN_FLOAT8(tm
->tm_sec
+ tm
->tm_usec
/ 1000000.0);
5290 intresult
= tm
->tm_min
;
5294 intresult
= tm
->tm_hour
;
5298 intresult
= tm
->tm_mday
;
5302 intresult
= tm
->tm_mon
;
5306 intresult
= (tm
->tm_mon
/ 3) + 1;
5310 intresult
= tm
->tm_year
;
5314 /* caution: C division may have negative remainder */
5315 intresult
= tm
->tm_year
/ 10;
5319 /* caution: C division may have negative remainder */
5320 intresult
= tm
->tm_year
/ 100;
5323 case DTK_MILLENNIUM
:
5324 /* caution: C division may have negative remainder */
5325 intresult
= tm
->tm_year
/ 1000;
5330 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
5331 errmsg("unit \"%s\" not supported for type %s",
5332 lowunits
, format_type_be(INTERVALOID
))));
5336 else if (type
== RESERV
&& val
== DTK_EPOCH
)
5341 int64 secs_from_day_month
;
5345 * To do this calculation in integer arithmetic even though
5346 * DAYS_PER_YEAR is fractional, multiply everything by 4 and then
5347 * divide by 4 again at the end. This relies on DAYS_PER_YEAR
5348 * being a multiple of 0.25 and on SECS_PER_DAY being a multiple
5351 secs_from_day_month
= ((int64
) (4 * DAYS_PER_YEAR
) * (interval
->month
/ MONTHS_PER_YEAR
) +
5352 (int64
) (4 * DAYS_PER_MONTH
) * (interval
->month
% MONTHS_PER_YEAR
) +
5353 (int64
) 4 * interval
->day
) * (SECS_PER_DAY
/ 4);
5356 * result = secs_from_day_month + interval->time / 1'000'000
5357 * = (secs_from_day_month * 1'000'000 + interval->time) / 1'000'000
5361 * Try the computation inside int64; if it overflows, do it in
5362 * numeric (slower). This overflow happens around 10^9 days, so
5363 * not common in practice.
5365 if (!pg_mul_s64_overflow(secs_from_day_month
, 1000000, &val
) &&
5366 !pg_add_s64_overflow(val
, interval
->time
, &val
))
5367 result
= int64_div_fast_to_numeric(val
, 6);
5370 numeric_add_opt_error(int64_div_fast_to_numeric(interval
->time
, 6),
5371 int64_to_numeric(secs_from_day_month
),
5374 PG_RETURN_NUMERIC(result
);
5380 result
= interval
->time
/ 1000000.0;
5381 result
+= ((double) DAYS_PER_YEAR
* SECS_PER_DAY
) * (interval
->month
/ MONTHS_PER_YEAR
);
5382 result
+= ((double) DAYS_PER_MONTH
* SECS_PER_DAY
) * (interval
->month
% MONTHS_PER_YEAR
);
5383 result
+= ((double) SECS_PER_DAY
) * interval
->day
;
5385 PG_RETURN_FLOAT8(result
);
5391 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
5392 errmsg("unit \"%s\" not recognized for type %s",
5393 lowunits
, format_type_be(INTERVALOID
))));
5398 PG_RETURN_NUMERIC(int64_to_numeric(intresult
));
5400 PG_RETURN_FLOAT8(intresult
);
5404 interval_part(PG_FUNCTION_ARGS
)
5406 return interval_part_common(fcinfo
, false);
5410 extract_interval(PG_FUNCTION_ARGS
)
5412 return interval_part_common(fcinfo
, true);
5417 * Encode timestamp type with specified time zone.
5418 * This function is just timestamp2timestamptz() except instead of
5419 * shifting to the global timezone, we shift to the specified timezone.
5420 * This is different from the other AT TIME ZONE cases because instead
5421 * of shifting _to_ a new time zone, it sets the time to _be_ the
5422 * specified timezone.
5425 timestamp_zone(PG_FUNCTION_ARGS
)
5427 text
*zone
= PG_GETARG_TEXT_PP(0);
5428 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
5431 char tzname
[TZ_STRLEN_MAX
+ 1];
5437 DateTimeErrorExtra extra
;
5441 if (TIMESTAMP_NOT_FINITE(timestamp
))
5442 PG_RETURN_TIMESTAMPTZ(timestamp
);
5445 * Look up the requested timezone. First we look in the timezone
5446 * abbreviation table (to handle cases like "EST"), and if that fails, we
5447 * look in the timezone database (to handle cases like
5448 * "America/New_York"). (This matches the order in which timestamp input
5449 * checks the cases; it's important because the timezone database unwisely
5450 * uses a few zone names that are identical to offset abbreviations.)
5452 text_to_cstring_buffer(zone
, tzname
, sizeof(tzname
));
5454 /* DecodeTimezoneAbbrev requires lowercase input */
5455 lowzone
= downcase_truncate_identifier(tzname
,
5459 dterr
= DecodeTimezoneAbbrev(0, lowzone
, &type
, &val
, &tzp
, &extra
);
5461 DateTimeParseError(dterr
, &extra
, NULL
, NULL
, NULL
);
5463 if (type
== TZ
|| type
== DTZ
)
5465 /* fixed-offset abbreviation */
5467 result
= dt2local(timestamp
, tz
);
5469 else if (type
== DYNTZ
)
5471 /* dynamic-offset abbreviation, resolve using specified time */
5472 if (timestamp2tm(timestamp
, NULL
, &tm
, &fsec
, NULL
, tzp
) != 0)
5474 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5475 errmsg("timestamp out of range")));
5476 tz
= -DetermineTimeZoneAbbrevOffset(&tm
, tzname
, tzp
);
5477 result
= dt2local(timestamp
, tz
);
5481 /* try it as a full zone name */
5482 tzp
= pg_tzset(tzname
);
5485 /* Apply the timezone change */
5486 if (timestamp2tm(timestamp
, NULL
, &tm
, &fsec
, NULL
, tzp
) != 0)
5488 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5489 errmsg("timestamp out of range")));
5490 tz
= DetermineTimeZoneOffset(&tm
, tzp
);
5491 if (tm2timestamp(&tm
, fsec
, &tz
, &result
) != 0)
5493 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5494 errmsg("timestamp out of range")));
5499 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
5500 errmsg("time zone \"%s\" not recognized", tzname
)));
5501 result
= 0; /* keep compiler quiet */
5505 if (!IS_VALID_TIMESTAMP(result
))
5507 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5508 errmsg("timestamp out of range")));
5510 PG_RETURN_TIMESTAMPTZ(result
);
5513 /* timestamp_izone()
5514 * Encode timestamp type with specified time interval as time zone.
5517 timestamp_izone(PG_FUNCTION_ARGS
)
5519 Interval
*zone
= PG_GETARG_INTERVAL_P(0);
5520 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
5524 if (TIMESTAMP_NOT_FINITE(timestamp
))
5525 PG_RETURN_TIMESTAMPTZ(timestamp
);
5527 if (zone
->month
!= 0 || zone
->day
!= 0)
5529 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
5530 errmsg("interval time zone \"%s\" must not include months or days",
5531 DatumGetCString(DirectFunctionCall1(interval_out
,
5532 PointerGetDatum(zone
))))));
5534 tz
= zone
->time
/ USECS_PER_SEC
;
5536 result
= dt2local(timestamp
, tz
);
5538 if (!IS_VALID_TIMESTAMP(result
))
5540 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5541 errmsg("timestamp out of range")));
5543 PG_RETURN_TIMESTAMPTZ(result
);
5544 } /* timestamp_izone() */
5546 /* TimestampTimestampTzRequiresRewrite()
5548 * Returns false if the TimeZone GUC setting causes timestamp_timestamptz and
5549 * timestamptz_timestamp to be no-ops, where the return value has the same
5550 * bits as the argument. Since project convention is to assume a GUC changes
5551 * no more often than STABLE functions change, the answer is valid that long.
5554 TimestampTimestampTzRequiresRewrite(void)
5558 if (pg_get_timezone_offset(session_timezone
, &offset
) && offset
== 0)
5563 /* timestamp_timestamptz()
5564 * Convert local timestamp to timestamp at GMT
5567 timestamp_timestamptz(PG_FUNCTION_ARGS
)
5569 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
5571 PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp
));
5575 * Convert timestamp to timestamp with time zone.
5577 * On successful conversion, *overflow is set to zero if it's not NULL.
5579 * If the timestamp is finite but out of the valid range for timestamptz, then:
5580 * if overflow is NULL, we throw an out-of-range error.
5581 * if overflow is not NULL, we store +1 or -1 there to indicate the sign
5582 * of the overflow, and return the appropriate timestamptz infinity.
5585 timestamp2timestamptz_opt_overflow(Timestamp timestamp
, int *overflow
)
5596 if (TIMESTAMP_NOT_FINITE(timestamp
))
5599 /* We don't expect this to fail, but check it pro forma */
5600 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) == 0)
5602 tz
= DetermineTimeZoneOffset(tm
, session_timezone
);
5604 result
= dt2local(timestamp
, -tz
);
5606 if (IS_VALID_TIMESTAMP(result
))
5612 if (result
< MIN_TIMESTAMP
)
5615 TIMESTAMP_NOBEGIN(result
);
5620 TIMESTAMP_NOEND(result
);
5627 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5628 errmsg("timestamp out of range")));
5634 * Promote timestamp to timestamptz, throwing error for overflow.
5637 timestamp2timestamptz(Timestamp timestamp
)
5639 return timestamp2timestamptz_opt_overflow(timestamp
, NULL
);
5642 /* timestamptz_timestamp()
5643 * Convert timestamp at GMT to local timestamp
5646 timestamptz_timestamp(PG_FUNCTION_ARGS
)
5648 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
5650 PG_RETURN_TIMESTAMP(timestamptz2timestamp(timestamp
));
5654 timestamptz2timestamp(TimestampTz timestamp
)
5662 if (TIMESTAMP_NOT_FINITE(timestamp
))
5666 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, NULL
, NULL
) != 0)
5668 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5669 errmsg("timestamp out of range")));
5670 if (tm2timestamp(tm
, fsec
, NULL
, &result
) != 0)
5672 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5673 errmsg("timestamp out of range")));
5678 /* timestamptz_zone()
5679 * Evaluate timestamp with time zone type at the specified time zone.
5680 * Returns a timestamp without time zone.
5683 timestamptz_zone(PG_FUNCTION_ARGS
)
5685 text
*zone
= PG_GETARG_TEXT_PP(0);
5686 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
5689 char tzname
[TZ_STRLEN_MAX
+ 1];
5695 DateTimeErrorExtra extra
;
5697 if (TIMESTAMP_NOT_FINITE(timestamp
))
5698 PG_RETURN_TIMESTAMP(timestamp
);
5701 * Look up the requested timezone. First we look in the timezone
5702 * abbreviation table (to handle cases like "EST"), and if that fails, we
5703 * look in the timezone database (to handle cases like
5704 * "America/New_York"). (This matches the order in which timestamp input
5705 * checks the cases; it's important because the timezone database unwisely
5706 * uses a few zone names that are identical to offset abbreviations.)
5708 text_to_cstring_buffer(zone
, tzname
, sizeof(tzname
));
5710 /* DecodeTimezoneAbbrev requires lowercase input */
5711 lowzone
= downcase_truncate_identifier(tzname
,
5715 dterr
= DecodeTimezoneAbbrev(0, lowzone
, &type
, &val
, &tzp
, &extra
);
5717 DateTimeParseError(dterr
, &extra
, NULL
, NULL
, NULL
);
5719 if (type
== TZ
|| type
== DTZ
)
5721 /* fixed-offset abbreviation */
5723 result
= dt2local(timestamp
, tz
);
5725 else if (type
== DYNTZ
)
5727 /* dynamic-offset abbreviation, resolve using specified time */
5730 tz
= DetermineTimeZoneAbbrevOffsetTS(timestamp
, tzname
, tzp
, &isdst
);
5731 result
= dt2local(timestamp
, tz
);
5735 /* try it as a full zone name */
5736 tzp
= pg_tzset(tzname
);
5739 /* Apply the timezone change */
5743 if (timestamp2tm(timestamp
, &tz
, &tm
, &fsec
, NULL
, tzp
) != 0)
5745 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5746 errmsg("timestamp out of range")));
5747 if (tm2timestamp(&tm
, fsec
, NULL
, &result
) != 0)
5749 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5750 errmsg("timestamp out of range")));
5755 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
5756 errmsg("time zone \"%s\" not recognized", tzname
)));
5757 result
= 0; /* keep compiler quiet */
5761 if (!IS_VALID_TIMESTAMP(result
))
5763 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5764 errmsg("timestamp out of range")));
5766 PG_RETURN_TIMESTAMP(result
);
5769 /* timestamptz_izone()
5770 * Encode timestamp with time zone type with specified time interval as time zone.
5771 * Returns a timestamp without time zone.
5774 timestamptz_izone(PG_FUNCTION_ARGS
)
5776 Interval
*zone
= PG_GETARG_INTERVAL_P(0);
5777 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
5781 if (TIMESTAMP_NOT_FINITE(timestamp
))
5782 PG_RETURN_TIMESTAMP(timestamp
);
5784 if (zone
->month
!= 0 || zone
->day
!= 0)
5786 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
5787 errmsg("interval time zone \"%s\" must not include months or days",
5788 DatumGetCString(DirectFunctionCall1(interval_out
,
5789 PointerGetDatum(zone
))))));
5791 tz
= -(zone
->time
/ USECS_PER_SEC
);
5793 result
= dt2local(timestamp
, tz
);
5795 if (!IS_VALID_TIMESTAMP(result
))
5797 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
5798 errmsg("timestamp out of range")));
5800 PG_RETURN_TIMESTAMP(result
);
5803 /* generate_series_timestamp()
5804 * Generate the set of timestamps from start to finish by step
5807 generate_series_timestamp(PG_FUNCTION_ARGS
)
5809 FuncCallContext
*funcctx
;
5810 generate_series_timestamp_fctx
*fctx
;
5813 /* stuff done only on the first call of the function */
5814 if (SRF_IS_FIRSTCALL())
5816 Timestamp start
= PG_GETARG_TIMESTAMP(0);
5817 Timestamp finish
= PG_GETARG_TIMESTAMP(1);
5818 Interval
*step
= PG_GETARG_INTERVAL_P(2);
5819 MemoryContext oldcontext
;
5820 const Interval interval_zero
= {0};
5822 /* create a function context for cross-call persistence */
5823 funcctx
= SRF_FIRSTCALL_INIT();
5826 * switch to memory context appropriate for multiple function calls
5828 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
5830 /* allocate memory for user context */
5831 fctx
= (generate_series_timestamp_fctx
*)
5832 palloc(sizeof(generate_series_timestamp_fctx
));
5835 * Use fctx to keep state from call to call. Seed current with the
5836 * original start value
5838 fctx
->current
= start
;
5839 fctx
->finish
= finish
;
5842 /* Determine sign of the interval */
5843 fctx
->step_sign
= interval_cmp_internal(&fctx
->step
, &interval_zero
);
5845 if (fctx
->step_sign
== 0)
5847 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
5848 errmsg("step size cannot equal zero")));
5850 funcctx
->user_fctx
= fctx
;
5851 MemoryContextSwitchTo(oldcontext
);
5854 /* stuff done on every call of the function */
5855 funcctx
= SRF_PERCALL_SETUP();
5858 * get the saved state and use current as the result for this iteration
5860 fctx
= funcctx
->user_fctx
;
5861 result
= fctx
->current
;
5863 if (fctx
->step_sign
> 0 ?
5864 timestamp_cmp_internal(result
, fctx
->finish
) <= 0 :
5865 timestamp_cmp_internal(result
, fctx
->finish
) >= 0)
5867 /* increment current in preparation for next iteration */
5868 fctx
->current
= DatumGetTimestamp(DirectFunctionCall2(timestamp_pl_interval
,
5869 TimestampGetDatum(fctx
->current
),
5870 PointerGetDatum(&fctx
->step
)));
5872 /* do when there is more left to send */
5873 SRF_RETURN_NEXT(funcctx
, TimestampGetDatum(result
));
5877 /* do when there is no more left */
5878 SRF_RETURN_DONE(funcctx
);
5882 /* generate_series_timestamptz()
5883 * Generate the set of timestamps from start to finish by step
5886 generate_series_timestamptz(PG_FUNCTION_ARGS
)
5888 FuncCallContext
*funcctx
;
5889 generate_series_timestamptz_fctx
*fctx
;
5892 /* stuff done only on the first call of the function */
5893 if (SRF_IS_FIRSTCALL())
5895 TimestampTz start
= PG_GETARG_TIMESTAMPTZ(0);
5896 TimestampTz finish
= PG_GETARG_TIMESTAMPTZ(1);
5897 Interval
*step
= PG_GETARG_INTERVAL_P(2);
5898 MemoryContext oldcontext
;
5899 const Interval interval_zero
= {0};
5901 /* create a function context for cross-call persistence */
5902 funcctx
= SRF_FIRSTCALL_INIT();
5905 * switch to memory context appropriate for multiple function calls
5907 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
5909 /* allocate memory for user context */
5910 fctx
= (generate_series_timestamptz_fctx
*)
5911 palloc(sizeof(generate_series_timestamptz_fctx
));
5914 * Use fctx to keep state from call to call. Seed current with the
5915 * original start value
5917 fctx
->current
= start
;
5918 fctx
->finish
= finish
;
5921 /* Determine sign of the interval */
5922 fctx
->step_sign
= interval_cmp_internal(&fctx
->step
, &interval_zero
);
5924 if (fctx
->step_sign
== 0)
5926 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
5927 errmsg("step size cannot equal zero")));
5929 funcctx
->user_fctx
= fctx
;
5930 MemoryContextSwitchTo(oldcontext
);
5933 /* stuff done on every call of the function */
5934 funcctx
= SRF_PERCALL_SETUP();
5937 * get the saved state and use current as the result for this iteration
5939 fctx
= funcctx
->user_fctx
;
5940 result
= fctx
->current
;
5942 if (fctx
->step_sign
> 0 ?
5943 timestamp_cmp_internal(result
, fctx
->finish
) <= 0 :
5944 timestamp_cmp_internal(result
, fctx
->finish
) >= 0)
5946 /* increment current in preparation for next iteration */
5947 fctx
->current
= DatumGetTimestampTz(DirectFunctionCall2(timestamptz_pl_interval
,
5948 TimestampTzGetDatum(fctx
->current
),
5949 PointerGetDatum(&fctx
->step
)));
5951 /* do when there is more left to send */
5952 SRF_RETURN_NEXT(funcctx
, TimestampTzGetDatum(result
));
5956 /* do when there is no more left */
5957 SRF_RETURN_DONE(funcctx
);