1 /*-------------------------------------------------------------------------
4 * Functions for the built-in SQL92 types "timestamp" and "interval".
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
13 *-------------------------------------------------------------------------
24 #include "access/hash.h"
25 #include "access/xact.h"
26 #include "catalog/pg_type.h"
28 #include "libpq/pqformat.h"
29 #include "miscadmin.h"
30 #include "parser/scansup.h"
31 #include "utils/array.h"
32 #include "utils/builtins.h"
33 #include "utils/datetime.h"
36 * gcc's -ffast-math switch breaks routines that expect exact results from
37 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
40 #error -ffast-math is known to break this code
44 /* Set at postmaster start */
45 TimestampTz PgStartTime
;
47 /* Set at configuration reload */
48 TimestampTz PgReloadTime
;
56 } generate_series_timestamp_fctx
;
64 } generate_series_timestamptz_fctx
;
67 static TimeOffset
time2t(const int hour
, const int min
, const int sec
, const fsec_t fsec
);
68 static void EncodeSpecialTimestamp(Timestamp dt
, char *str
);
69 static Timestamp
dt2local(Timestamp dt
, int timezone
);
70 static void AdjustTimestampForTypmod(Timestamp
*time
, int32 typmod
);
71 static void AdjustIntervalForTypmod(Interval
*interval
, int32 typmod
);
72 static TimestampTz
timestamp2timestamptz(Timestamp timestamp
);
75 /* common code for timestamptypmodin and timestamptztypmodin */
77 anytimestamp_typmodin(bool istz
, ArrayType
*ta
)
83 tl
= ArrayGetIntegerTypmods(ta
, &n
);
86 * we're not too tense about good error message here because grammar
87 * shouldn't allow wrong number of modifiers for TIMESTAMP
91 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
92 errmsg("invalid type modifier")));
96 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
97 errmsg("TIMESTAMP(%d)%s precision must not be negative",
98 *tl
, (istz
? " WITH TIME ZONE" : ""))));
99 if (*tl
> MAX_TIMESTAMP_PRECISION
)
102 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
103 errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d",
104 *tl
, (istz
? " WITH TIME ZONE" : ""),
105 MAX_TIMESTAMP_PRECISION
)));
106 typmod
= MAX_TIMESTAMP_PRECISION
;
114 /* common code for timestamptypmodout and timestamptztypmodout */
116 anytimestamp_typmodout(bool istz
, int32 typmod
)
118 char *res
= (char *) palloc(64);
119 const char *tz
= istz
? " with time zone" : " without time zone";
122 snprintf(res
, 64, "(%d)%s", (int) typmod
, tz
);
124 snprintf(res
, 64, "%s", tz
);
130 /*****************************************************************************
131 * USER I/O ROUTINES *
132 *****************************************************************************/
135 * Convert a string to internal form.
138 timestamp_in(PG_FUNCTION_ARGS
)
140 char *str
= PG_GETARG_CSTRING(0);
143 Oid typelem
= PG_GETARG_OID(1);
145 int32 typmod
= PG_GETARG_INT32(2);
154 char *field
[MAXDATEFIELDS
];
155 int ftype
[MAXDATEFIELDS
];
156 char workbuf
[MAXDATELEN
+ MAXDATEFIELDS
];
158 dterr
= ParseDateTime(str
, workbuf
, sizeof(workbuf
),
159 field
, ftype
, MAXDATEFIELDS
, &nf
);
161 dterr
= DecodeDateTime(field
, ftype
, nf
, &dtype
, tm
, &fsec
, &tz
);
163 DateTimeParseError(dterr
, str
, "timestamp");
168 if (tm2timestamp(tm
, fsec
, NULL
, &result
) != 0)
170 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
171 errmsg("timestamp out of range: \"%s\"", str
)));
175 result
= SetEpochTimestamp();
179 TIMESTAMP_NOEND(result
);
183 TIMESTAMP_NOBEGIN(result
);
188 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
189 errmsg("date/time value \"%s\" is no longer supported", str
)));
191 TIMESTAMP_NOEND(result
);
195 elog(ERROR
, "unexpected dtype %d while parsing timestamp \"%s\"",
197 TIMESTAMP_NOEND(result
);
200 AdjustTimestampForTypmod(&result
, typmod
);
202 PG_RETURN_TIMESTAMP(result
);
206 * Convert a timestamp to external form.
209 timestamp_out(PG_FUNCTION_ARGS
)
211 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
217 char buf
[MAXDATELEN
+ 1];
219 if (TIMESTAMP_NOT_FINITE(timestamp
))
220 EncodeSpecialTimestamp(timestamp
, buf
);
221 else if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) == 0)
222 EncodeDateTime(tm
, fsec
, NULL
, &tzn
, DateStyle
, buf
);
225 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
226 errmsg("timestamp out of range")));
228 result
= pstrdup(buf
);
229 PG_RETURN_CSTRING(result
);
233 * timestamp_recv - converts external binary format to timestamp
235 * We make no attempt to provide compatibility between int and float
236 * timestamp representations ...
239 timestamp_recv(PG_FUNCTION_ARGS
)
241 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
244 Oid typelem
= PG_GETARG_OID(1);
246 int32 typmod
= PG_GETARG_INT32(2);
252 #ifdef HAVE_INT64_TIMESTAMP
253 timestamp
= (Timestamp
) pq_getmsgint64(buf
);
255 timestamp
= (Timestamp
) pq_getmsgfloat8(buf
);
258 /* rangecheck: see if timestamp_out would like it */
259 if (TIMESTAMP_NOT_FINITE(timestamp
))
261 else if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
263 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
264 errmsg("timestamp out of range")));
266 AdjustTimestampForTypmod(×tamp
, typmod
);
268 PG_RETURN_TIMESTAMP(timestamp
);
272 * timestamp_send - converts timestamp to binary format
275 timestamp_send(PG_FUNCTION_ARGS
)
277 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
280 pq_begintypsend(&buf
);
281 #ifdef HAVE_INT64_TIMESTAMP
282 pq_sendint64(&buf
, timestamp
);
284 pq_sendfloat8(&buf
, timestamp
);
286 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
290 timestamptypmodin(PG_FUNCTION_ARGS
)
292 ArrayType
*ta
= PG_GETARG_ARRAYTYPE_P(0);
294 PG_RETURN_INT32(anytimestamp_typmodin(false, ta
));
298 timestamptypmodout(PG_FUNCTION_ARGS
)
300 int32 typmod
= PG_GETARG_INT32(0);
302 PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod
));
307 * Adjust time type for specified scale factor.
308 * Used by PostgreSQL type system to stuff columns.
311 timestamp_scale(PG_FUNCTION_ARGS
)
313 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
314 int32 typmod
= PG_GETARG_INT32(1);
319 AdjustTimestampForTypmod(&result
, typmod
);
321 PG_RETURN_TIMESTAMP(result
);
325 AdjustTimestampForTypmod(Timestamp
*time
, int32 typmod
)
327 #ifdef HAVE_INT64_TIMESTAMP
328 static const int64 TimestampScales
[MAX_TIMESTAMP_PRECISION
+ 1] = {
338 static const int64 TimestampOffsets
[MAX_TIMESTAMP_PRECISION
+ 1] = {
348 static const double TimestampScales
[MAX_TIMESTAMP_PRECISION
+ 1] = {
359 if (!TIMESTAMP_NOT_FINITE(*time
)
360 && (typmod
!= -1) && (typmod
!= MAX_TIMESTAMP_PRECISION
))
362 if (typmod
< 0 || typmod
> MAX_TIMESTAMP_PRECISION
)
364 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
365 errmsg("timestamp(%d) precision must be between %d and %d",
366 typmod
, 0, MAX_TIMESTAMP_PRECISION
)));
369 * Note: this round-to-nearest code is not completely consistent about
370 * rounding values that are exactly halfway between integral values.
371 * On most platforms, rint() will implement round-to-nearest-even, but
372 * the integer code always rounds up (away from zero). Is it worth
373 * trying to be consistent?
375 #ifdef HAVE_INT64_TIMESTAMP
376 if (*time
>= INT64CONST(0))
378 *time
= ((*time
+ TimestampOffsets
[typmod
]) / TimestampScales
[typmod
]) *
379 TimestampScales
[typmod
];
383 *time
= -((((-*time
) + TimestampOffsets
[typmod
]) / TimestampScales
[typmod
])
384 * TimestampScales
[typmod
]);
387 *time
= rint((double) *time
* TimestampScales
[typmod
]) / TimestampScales
[typmod
];
394 * Convert a string to internal form.
397 timestamptz_in(PG_FUNCTION_ARGS
)
399 char *str
= PG_GETARG_CSTRING(0);
402 Oid typelem
= PG_GETARG_OID(1);
404 int32 typmod
= PG_GETARG_INT32(2);
413 char *field
[MAXDATEFIELDS
];
414 int ftype
[MAXDATEFIELDS
];
415 char workbuf
[MAXDATELEN
+ MAXDATEFIELDS
];
417 dterr
= ParseDateTime(str
, workbuf
, sizeof(workbuf
),
418 field
, ftype
, MAXDATEFIELDS
, &nf
);
420 dterr
= DecodeDateTime(field
, ftype
, nf
, &dtype
, tm
, &fsec
, &tz
);
422 DateTimeParseError(dterr
, str
, "timestamp with time zone");
427 if (tm2timestamp(tm
, fsec
, &tz
, &result
) != 0)
429 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
430 errmsg("timestamp out of range: \"%s\"", str
)));
434 result
= SetEpochTimestamp();
438 TIMESTAMP_NOEND(result
);
442 TIMESTAMP_NOBEGIN(result
);
447 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
448 errmsg("date/time value \"%s\" is no longer supported", str
)));
450 TIMESTAMP_NOEND(result
);
454 elog(ERROR
, "unexpected dtype %d while parsing timestamptz \"%s\"",
456 TIMESTAMP_NOEND(result
);
459 AdjustTimestampForTypmod(&result
, typmod
);
461 PG_RETURN_TIMESTAMPTZ(result
);
465 * Convert a timestamp to external form.
468 timestamptz_out(PG_FUNCTION_ARGS
)
470 TimestampTz dt
= PG_GETARG_TIMESTAMPTZ(0);
477 char buf
[MAXDATELEN
+ 1];
479 if (TIMESTAMP_NOT_FINITE(dt
))
480 EncodeSpecialTimestamp(dt
, buf
);
481 else if (timestamp2tm(dt
, &tz
, tm
, &fsec
, &tzn
, NULL
) == 0)
482 EncodeDateTime(tm
, fsec
, &tz
, &tzn
, DateStyle
, buf
);
485 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
486 errmsg("timestamp out of range")));
488 result
= pstrdup(buf
);
489 PG_RETURN_CSTRING(result
);
493 * timestamptz_recv - converts external binary format to timestamptz
495 * We make no attempt to provide compatibility between int and float
496 * timestamp representations ...
499 timestamptz_recv(PG_FUNCTION_ARGS
)
501 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
504 Oid typelem
= PG_GETARG_OID(1);
506 int32 typmod
= PG_GETARG_INT32(2);
507 TimestampTz timestamp
;
514 #ifdef HAVE_INT64_TIMESTAMP
515 timestamp
= (TimestampTz
) pq_getmsgint64(buf
);
517 timestamp
= (TimestampTz
) pq_getmsgfloat8(buf
);
520 /* rangecheck: see if timestamptz_out would like it */
521 if (TIMESTAMP_NOT_FINITE(timestamp
))
523 else if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, &tzn
, NULL
) != 0)
525 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
526 errmsg("timestamp out of range")));
528 AdjustTimestampForTypmod(×tamp
, typmod
);
530 PG_RETURN_TIMESTAMPTZ(timestamp
);
534 * timestamptz_send - converts timestamptz to binary format
537 timestamptz_send(PG_FUNCTION_ARGS
)
539 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
542 pq_begintypsend(&buf
);
543 #ifdef HAVE_INT64_TIMESTAMP
544 pq_sendint64(&buf
, timestamp
);
546 pq_sendfloat8(&buf
, timestamp
);
548 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
552 timestamptztypmodin(PG_FUNCTION_ARGS
)
554 ArrayType
*ta
= PG_GETARG_ARRAYTYPE_P(0);
556 PG_RETURN_INT32(anytimestamp_typmodin(true, ta
));
560 timestamptztypmodout(PG_FUNCTION_ARGS
)
562 int32 typmod
= PG_GETARG_INT32(0);
564 PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod
));
568 /* timestamptz_scale()
569 * Adjust time type for specified scale factor.
570 * Used by PostgreSQL type system to stuff columns.
573 timestamptz_scale(PG_FUNCTION_ARGS
)
575 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
576 int32 typmod
= PG_GETARG_INT32(1);
581 AdjustTimestampForTypmod(&result
, typmod
);
583 PG_RETURN_TIMESTAMPTZ(result
);
588 * Convert a string to internal form.
590 * External format(s):
591 * Uses the generic date/time parsing and decoding routines.
594 interval_in(PG_FUNCTION_ARGS
)
596 char *str
= PG_GETARG_CSTRING(0);
599 Oid typelem
= PG_GETARG_OID(1);
601 int32 typmod
= PG_GETARG_INT32(2);
610 char *field
[MAXDATEFIELDS
];
611 int ftype
[MAXDATEFIELDS
];
623 range
= INTERVAL_RANGE(typmod
);
625 range
= INTERVAL_FULL_RANGE
;
627 dterr
= ParseDateTime(str
, workbuf
, sizeof(workbuf
), field
,
628 ftype
, MAXDATEFIELDS
, &nf
);
630 dterr
= DecodeInterval(field
, ftype
, nf
, range
,
633 /* if those functions think it's a bad format, try ISO8601 style */
634 if (dterr
== DTERR_BAD_FORMAT
)
635 dterr
= DecodeISO8601Interval(str
,
640 if (dterr
== DTERR_FIELD_OVERFLOW
)
641 dterr
= DTERR_INTERVAL_OVERFLOW
;
642 DateTimeParseError(dterr
, str
, "interval");
645 result
= (Interval
*) palloc(sizeof(Interval
));
650 if (tm2interval(tm
, fsec
, result
) != 0)
652 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
653 errmsg("interval out of range")));
658 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
659 errmsg("date/time value \"%s\" is no longer supported", str
)));
663 elog(ERROR
, "unexpected dtype %d while parsing interval \"%s\"",
667 AdjustIntervalForTypmod(result
, typmod
);
669 PG_RETURN_INTERVAL_P(result
);
673 * Convert a time span to external form.
676 interval_out(PG_FUNCTION_ARGS
)
678 Interval
*span
= PG_GETARG_INTERVAL_P(0);
683 char buf
[MAXDATELEN
+ 1];
685 if (interval2tm(*span
, tm
, &fsec
) != 0)
686 elog(ERROR
, "could not convert interval to tm");
688 EncodeInterval(tm
, fsec
, IntervalStyle
, buf
);
690 result
= pstrdup(buf
);
691 PG_RETURN_CSTRING(result
);
695 * interval_recv - converts external binary format to interval
698 interval_recv(PG_FUNCTION_ARGS
)
700 StringInfo buf
= (StringInfo
) PG_GETARG_POINTER(0);
703 Oid typelem
= PG_GETARG_OID(1);
705 int32 typmod
= PG_GETARG_INT32(2);
708 interval
= (Interval
*) palloc(sizeof(Interval
));
710 #ifdef HAVE_INT64_TIMESTAMP
711 interval
->time
= pq_getmsgint64(buf
);
713 interval
->time
= pq_getmsgfloat8(buf
);
715 interval
->day
= pq_getmsgint(buf
, sizeof(interval
->day
));
716 interval
->month
= pq_getmsgint(buf
, sizeof(interval
->month
));
718 AdjustIntervalForTypmod(interval
, typmod
);
720 PG_RETURN_INTERVAL_P(interval
);
724 * interval_send - converts interval to binary format
727 interval_send(PG_FUNCTION_ARGS
)
729 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
732 pq_begintypsend(&buf
);
733 #ifdef HAVE_INT64_TIMESTAMP
734 pq_sendint64(&buf
, interval
->time
);
736 pq_sendfloat8(&buf
, interval
->time
);
738 pq_sendint(&buf
, interval
->day
, sizeof(interval
->day
));
739 pq_sendint(&buf
, interval
->month
, sizeof(interval
->month
));
740 PG_RETURN_BYTEA_P(pq_endtypsend(&buf
));
744 intervaltypmodin(PG_FUNCTION_ARGS
)
746 ArrayType
*ta
= PG_GETARG_ARRAYTYPE_P(0);
751 tl
= ArrayGetIntegerTypmods(ta
, &n
);
754 * tl[0] - interval range (fields bitmask) tl[1] - precision (optional)
756 * Note we must validate tl[0] even though it's normally guaranteed
757 * correct by the grammar --- consider SELECT 'foo'::"interval"(1000).
763 case INTERVAL_MASK(YEAR
):
764 case INTERVAL_MASK(MONTH
):
765 case INTERVAL_MASK(DAY
):
766 case INTERVAL_MASK(HOUR
):
767 case INTERVAL_MASK(MINUTE
):
768 case INTERVAL_MASK(SECOND
):
769 case INTERVAL_MASK(YEAR
) | INTERVAL_MASK(MONTH
):
770 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
):
771 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
772 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
773 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
774 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
775 case INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
776 case INTERVAL_FULL_RANGE
:
781 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
782 errmsg("invalid INTERVAL type modifier")));
788 if (tl
[0] != INTERVAL_FULL_RANGE
)
789 typmod
= INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION
, tl
[0]);
797 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
798 errmsg("INTERVAL(%d) precision must not be negative",
800 if (tl
[1] > MAX_INTERVAL_PRECISION
)
803 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
804 errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
805 tl
[1], MAX_INTERVAL_PRECISION
)));
806 typmod
= INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION
, tl
[0]);
809 typmod
= INTERVAL_TYPMOD(tl
[1], tl
[0]);
814 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
815 errmsg("invalid INTERVAL type modifier")));
816 typmod
= 0; /* keep compiler quiet */
819 PG_RETURN_INT32(typmod
);
823 intervaltypmodout(PG_FUNCTION_ARGS
)
825 int32 typmod
= PG_GETARG_INT32(0);
826 char *res
= (char *) palloc(64);
829 const char *fieldstr
;
834 PG_RETURN_CSTRING(res
);
837 fields
= INTERVAL_RANGE(typmod
);
838 precision
= INTERVAL_PRECISION(typmod
);
842 case INTERVAL_MASK(YEAR
):
845 case INTERVAL_MASK(MONTH
):
848 case INTERVAL_MASK(DAY
):
851 case INTERVAL_MASK(HOUR
):
854 case INTERVAL_MASK(MINUTE
):
855 fieldstr
= " minute";
857 case INTERVAL_MASK(SECOND
):
858 fieldstr
= " second";
860 case INTERVAL_MASK(YEAR
) | INTERVAL_MASK(MONTH
):
861 fieldstr
= " year to month";
863 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
):
864 fieldstr
= " day to hour";
866 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
867 fieldstr
= " day to minute";
869 case INTERVAL_MASK(DAY
) | INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
870 fieldstr
= " day to second";
872 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
):
873 fieldstr
= " hour to minute";
875 case INTERVAL_MASK(HOUR
) | INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
876 fieldstr
= " hour to second";
878 case INTERVAL_MASK(MINUTE
) | INTERVAL_MASK(SECOND
):
879 fieldstr
= " minute to second";
881 case INTERVAL_FULL_RANGE
:
885 elog(ERROR
, "invalid INTERVAL typmod: 0x%x", typmod
);
890 if (precision
!= INTERVAL_FULL_PRECISION
)
891 snprintf(res
, 64, "%s(%d)", fieldstr
, precision
);
893 snprintf(res
, 64, "%s", fieldstr
);
895 PG_RETURN_CSTRING(res
);
900 * Adjust interval type for specified fields.
901 * Used by PostgreSQL type system to stuff columns.
904 interval_scale(PG_FUNCTION_ARGS
)
906 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
907 int32 typmod
= PG_GETARG_INT32(1);
910 result
= palloc(sizeof(Interval
));
913 AdjustIntervalForTypmod(result
, typmod
);
915 PG_RETURN_INTERVAL_P(result
);
919 * Adjust interval for specified precision, in both YEAR to SECOND
920 * range and sub-second precision.
923 AdjustIntervalForTypmod(Interval
*interval
, int32 typmod
)
925 #ifdef HAVE_INT64_TIMESTAMP
926 static const int64 IntervalScales
[MAX_INTERVAL_PRECISION
+ 1] = {
936 static const int64 IntervalOffsets
[MAX_INTERVAL_PRECISION
+ 1] = {
946 static const double IntervalScales
[MAX_INTERVAL_PRECISION
+ 1] = {
958 * Unspecified range and precision? Then not necessary to adjust. Setting
959 * typmod to -1 is the convention for all data types.
963 int range
= INTERVAL_RANGE(typmod
);
964 int precision
= INTERVAL_PRECISION(typmod
);
967 * Our interpretation of intervals with a limited set of fields is
968 * that fields to the right of the last one specified are zeroed out,
969 * but those to the left of it remain valid. Thus for example there
970 * is no operational difference between INTERVAL YEAR TO MONTH and
971 * INTERVAL MONTH. In some cases we could meaningfully enforce that
972 * higher-order fields are zero; for example INTERVAL DAY could reject
973 * nonzero "month" field. However that seems a bit pointless when we
974 * can't do it consistently. (We cannot enforce a range limit on the
975 * highest expected field, since we do not have any equivalent of
976 * SQL's <interval leading field precision>.)
978 * Note: before PG 8.4 we interpreted a limited set of fields as
979 * actually causing a "modulo" operation on a given value, potentially
980 * losing high-order as well as low-order information. But there is
981 * no support for such behavior in the standard, and it seems fairly
982 * undesirable on data consistency grounds anyway. Now we only
983 * perform truncation or rounding of low-order fields.
985 if (range
== INTERVAL_FULL_RANGE
)
989 else if (range
== INTERVAL_MASK(YEAR
))
991 interval
->month
= (interval
->month
/ MONTHS_PER_YEAR
) * MONTHS_PER_YEAR
;
995 else if (range
== INTERVAL_MASK(MONTH
))
1001 else if (range
== (INTERVAL_MASK(YEAR
) | INTERVAL_MASK(MONTH
)))
1006 else if (range
== INTERVAL_MASK(DAY
))
1010 else if (range
== INTERVAL_MASK(HOUR
))
1012 #ifdef HAVE_INT64_TIMESTAMP
1013 interval
->time
= (interval
->time
/ USECS_PER_HOUR
) *
1016 interval
->time
= ((int) (interval
->time
/ SECS_PER_HOUR
)) * (double) SECS_PER_HOUR
;
1019 else if (range
== INTERVAL_MASK(MINUTE
))
1021 #ifdef HAVE_INT64_TIMESTAMP
1022 interval
->time
= (interval
->time
/ USECS_PER_MINUTE
) *
1025 interval
->time
= ((int) (interval
->time
/ SECS_PER_MINUTE
)) * (double) SECS_PER_MINUTE
;
1028 else if (range
== INTERVAL_MASK(SECOND
))
1030 /* fractional-second rounding will be dealt with below */
1033 else if (range
== (INTERVAL_MASK(DAY
) |
1034 INTERVAL_MASK(HOUR
)))
1036 #ifdef HAVE_INT64_TIMESTAMP
1037 interval
->time
= (interval
->time
/ USECS_PER_HOUR
) *
1040 interval
->time
= ((int) (interval
->time
/ SECS_PER_HOUR
)) * (double) SECS_PER_HOUR
;
1044 else if (range
== (INTERVAL_MASK(DAY
) |
1045 INTERVAL_MASK(HOUR
) |
1046 INTERVAL_MASK(MINUTE
)))
1048 #ifdef HAVE_INT64_TIMESTAMP
1049 interval
->time
= (interval
->time
/ USECS_PER_MINUTE
) *
1052 interval
->time
= ((int) (interval
->time
/ SECS_PER_MINUTE
)) * (double) SECS_PER_MINUTE
;
1056 else if (range
== (INTERVAL_MASK(DAY
) |
1057 INTERVAL_MASK(HOUR
) |
1058 INTERVAL_MASK(MINUTE
) |
1059 INTERVAL_MASK(SECOND
)))
1061 /* fractional-second rounding will be dealt with below */
1063 /* HOUR TO MINUTE */
1064 else if (range
== (INTERVAL_MASK(HOUR
) |
1065 INTERVAL_MASK(MINUTE
)))
1067 #ifdef HAVE_INT64_TIMESTAMP
1068 interval
->time
= (interval
->time
/ USECS_PER_MINUTE
) *
1071 interval
->time
= ((int) (interval
->time
/ SECS_PER_MINUTE
)) * (double) SECS_PER_MINUTE
;
1074 /* HOUR TO SECOND */
1075 else if (range
== (INTERVAL_MASK(HOUR
) |
1076 INTERVAL_MASK(MINUTE
) |
1077 INTERVAL_MASK(SECOND
)))
1079 /* fractional-second rounding will be dealt with below */
1081 /* MINUTE TO SECOND */
1082 else if (range
== (INTERVAL_MASK(MINUTE
) |
1083 INTERVAL_MASK(SECOND
)))
1085 /* fractional-second rounding will be dealt with below */
1088 elog(ERROR
, "unrecognized interval typmod: %d", typmod
);
1090 /* Need to adjust subsecond precision? */
1091 if (precision
!= INTERVAL_FULL_PRECISION
)
1093 if (precision
< 0 || precision
> MAX_INTERVAL_PRECISION
)
1095 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1096 errmsg("interval(%d) precision must be between %d and %d",
1097 precision
, 0, MAX_INTERVAL_PRECISION
)));
1100 * Note: this round-to-nearest code is not completely consistent
1101 * about rounding values that are exactly halfway between integral
1102 * values. On most platforms, rint() will implement
1103 * round-to-nearest-even, but the integer code always rounds up
1104 * (away from zero). Is it worth trying to be consistent?
1106 #ifdef HAVE_INT64_TIMESTAMP
1107 if (interval
->time
>= INT64CONST(0))
1109 interval
->time
= ((interval
->time
+
1110 IntervalOffsets
[precision
]) /
1111 IntervalScales
[precision
]) *
1112 IntervalScales
[precision
];
1116 interval
->time
= -(((-interval
->time
+
1117 IntervalOffsets
[precision
]) /
1118 IntervalScales
[precision
]) *
1119 IntervalScales
[precision
]);
1122 interval
->time
= rint(((double) interval
->time
) *
1123 IntervalScales
[precision
]) /
1124 IntervalScales
[precision
];
1131 /* EncodeSpecialTimestamp()
1132 * Convert reserved timestamp data type to string.
1135 EncodeSpecialTimestamp(Timestamp dt
, char *str
)
1137 if (TIMESTAMP_IS_NOBEGIN(dt
))
1139 else if (TIMESTAMP_IS_NOEND(dt
))
1141 else /* shouldn't happen */
1142 elog(ERROR
, "invalid argument for EncodeSpecialTimestamp");
1146 now(PG_FUNCTION_ARGS
)
1148 PG_RETURN_TIMESTAMPTZ(GetCurrentTransactionStartTimestamp());
1152 statement_timestamp(PG_FUNCTION_ARGS
)
1154 PG_RETURN_TIMESTAMPTZ(GetCurrentStatementStartTimestamp());
1158 clock_timestamp(PG_FUNCTION_ARGS
)
1160 PG_RETURN_TIMESTAMPTZ(GetCurrentTimestamp());
1164 pg_postmaster_start_time(PG_FUNCTION_ARGS
)
1166 PG_RETURN_TIMESTAMPTZ(PgStartTime
);
1170 pg_conf_load_time(PG_FUNCTION_ARGS
)
1172 PG_RETURN_TIMESTAMPTZ(PgReloadTime
);
1176 * GetCurrentTimestamp -- get the current operating system time
1178 * Result is in the form of a TimestampTz value, and is expressed to the
1179 * full precision of the gettimeofday() syscall
1182 GetCurrentTimestamp(void)
1187 gettimeofday(&tp
, NULL
);
1189 result
= (TimestampTz
) tp
.tv_sec
-
1190 ((POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
);
1192 #ifdef HAVE_INT64_TIMESTAMP
1193 result
= (result
* USECS_PER_SEC
) + tp
.tv_usec
;
1195 result
= result
+ (tp
.tv_usec
/ 1000000.0);
1202 * TimestampDifference -- convert the difference between two timestamps
1203 * into integer seconds and microseconds
1205 * Both inputs must be ordinary finite timestamps (in current usage,
1206 * they'll be results from GetCurrentTimestamp()).
1208 * We expect start_time <= stop_time. If not, we return zeroes; for current
1209 * callers there is no need to be tense about which way division rounds on
1213 TimestampDifference(TimestampTz start_time
, TimestampTz stop_time
,
1214 long *secs
, int *microsecs
)
1216 TimestampTz diff
= stop_time
- start_time
;
1225 #ifdef HAVE_INT64_TIMESTAMP
1226 *secs
= (long) (diff
/ USECS_PER_SEC
);
1227 *microsecs
= (int) (diff
% USECS_PER_SEC
);
1229 *secs
= (long) diff
;
1230 *microsecs
= (int) ((diff
- *secs
) * 1000000.0);
1236 * TimestampDifferenceExceeds -- report whether the difference between two
1237 * timestamps is >= a threshold (expressed in milliseconds)
1239 * Both inputs must be ordinary finite timestamps (in current usage,
1240 * they'll be results from GetCurrentTimestamp()).
1243 TimestampDifferenceExceeds(TimestampTz start_time
,
1244 TimestampTz stop_time
,
1247 TimestampTz diff
= stop_time
- start_time
;
1249 #ifdef HAVE_INT64_TIMESTAMP
1250 return (diff
>= msec
* INT64CONST(1000));
1252 return (diff
* 1000.0 >= msec
);
1257 * Convert a time_t to TimestampTz.
1259 * We do not use time_t internally in Postgres, but this is provided for use
1260 * by functions that need to interpret, say, a stat(2) result.
1262 * To avoid having the function's ABI vary depending on the width of time_t,
1263 * we declare the argument as pg_time_t, which is cast-compatible with
1264 * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1265 * This detail should be invisible to callers, at least at source code level.
1268 time_t_to_timestamptz(pg_time_t tm
)
1272 result
= (TimestampTz
) tm
-
1273 ((POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
);
1275 #ifdef HAVE_INT64_TIMESTAMP
1276 result
*= USECS_PER_SEC
;
1283 * Convert a TimestampTz to time_t.
1285 * This too is just marginally useful, but some places need it.
1287 * To avoid having the function's ABI vary depending on the width of time_t,
1288 * we declare the result as pg_time_t, which is cast-compatible with
1289 * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1290 * This detail should be invisible to callers, at least at source code level.
1293 timestamptz_to_time_t(TimestampTz t
)
1297 #ifdef HAVE_INT64_TIMESTAMP
1298 result
= (pg_time_t
) (t
/ USECS_PER_SEC
+
1299 ((POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
));
1301 result
= (pg_time_t
) (t
+
1302 ((POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
));
1309 * Produce a C-string representation of a TimestampTz.
1311 * This is mostly for use in emitting messages. The primary difference
1312 * from timestamptz_out is that we force the output format to ISO. Note
1313 * also that the result is in a static buffer, not pstrdup'd.
1316 timestamptz_to_str(TimestampTz t
)
1318 static char buf
[MAXDATELEN
+ 1];
1325 if (TIMESTAMP_NOT_FINITE(t
))
1326 EncodeSpecialTimestamp(t
, buf
);
1327 else if (timestamp2tm(t
, &tz
, tm
, &fsec
, &tzn
, NULL
) == 0)
1328 EncodeDateTime(tm
, fsec
, &tz
, &tzn
, USE_ISO_DATES
, buf
);
1330 strlcpy(buf
, "(timestamp out of range)", sizeof(buf
));
1337 dt2time(Timestamp jd
, int *hour
, int *min
, int *sec
, fsec_t
*fsec
)
1343 #ifdef HAVE_INT64_TIMESTAMP
1344 *hour
= time
/ USECS_PER_HOUR
;
1345 time
-= (*hour
) * USECS_PER_HOUR
;
1346 *min
= time
/ USECS_PER_MINUTE
;
1347 time
-= (*min
) * USECS_PER_MINUTE
;
1348 *sec
= time
/ USECS_PER_SEC
;
1349 *fsec
= time
- (*sec
* USECS_PER_SEC
);
1351 *hour
= time
/ SECS_PER_HOUR
;
1352 time
-= (*hour
) * SECS_PER_HOUR
;
1353 *min
= time
/ SECS_PER_MINUTE
;
1354 time
-= (*min
) * SECS_PER_MINUTE
;
1356 *fsec
= time
- *sec
;
1362 * timestamp2tm() - Convert timestamp data type to POSIX time structure.
1364 * Note that year is _not_ 1900-based, but is an explicit full value.
1365 * Also, month is one-based, _not_ zero-based.
1368 * -1 on out of range
1370 * If attimezone is NULL, the global timezone (including possibly brute forced
1371 * timezone) will be used.
1374 timestamp2tm(Timestamp dt
, int *tzp
, struct pg_tm
* tm
, fsec_t
*fsec
, char **tzn
, pg_tz
*attimezone
)
1381 * If HasCTZSet is true then we have a brute force time zone specified. Go
1382 * ahead and rotate to the local time zone since we will later bypass any
1383 * calls which adjust the tm fields.
1385 if (attimezone
== NULL
&& HasCTZSet
&& tzp
!= NULL
)
1387 #ifdef HAVE_INT64_TIMESTAMP
1388 dt
-= CTimeZone
* USECS_PER_SEC
;
1394 #ifdef HAVE_INT64_TIMESTAMP
1396 TMODULO(time
, date
, USECS_PER_DAY
);
1398 if (time
< INT64CONST(0))
1400 time
+= USECS_PER_DAY
;
1404 /* add offset to go from J2000 back to standard Julian date */
1405 date
+= POSTGRES_EPOCH_JDATE
;
1407 /* Julian day routine does not work for negative Julian days */
1408 if (date
< 0 || date
> (Timestamp
) INT_MAX
)
1411 j2date((int) date
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1412 dt2time(time
, &tm
->tm_hour
, &tm
->tm_min
, &tm
->tm_sec
, fsec
);
1415 TMODULO(time
, date
, (double) SECS_PER_DAY
);
1419 time
+= SECS_PER_DAY
;
1423 /* add offset to go from J2000 back to standard Julian date */
1424 date
+= POSTGRES_EPOCH_JDATE
;
1427 /* Julian day routine does not work for negative Julian days */
1428 if (date
< 0 || date
> (Timestamp
) INT_MAX
)
1431 j2date((int) date
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1433 dt2time(time
, &tm
->tm_hour
, &tm
->tm_min
, &tm
->tm_sec
, fsec
);
1435 *fsec
= TSROUND(*fsec
);
1436 /* roundoff may need to propagate to higher-order fields */
1440 if (time
>= (double) SECS_PER_DAY
)
1450 /* Done if no TZ conversion wanted */
1462 * We have a brute force time zone per SQL99? Then use it without change
1463 * since we have already rotated to the time zone.
1465 if (attimezone
== NULL
&& HasCTZSet
)
1469 tm
->tm_gmtoff
= CTimeZone
;
1477 * If the time falls within the range of pg_time_t, use pg_localtime() to
1478 * rotate to the local time zone.
1480 * First, convert to an integral timestamp, avoiding possibly
1481 * platform-specific roundoff-in-wrong-direction errors, and adjust to
1482 * Unix epoch. Then see if we can convert to pg_time_t without loss. This
1483 * coding avoids hardwiring any assumptions about the width of pg_time_t,
1484 * so it should behave sanely on machines without int64.
1486 #ifdef HAVE_INT64_TIMESTAMP
1487 dt
= (dt
- *fsec
) / USECS_PER_SEC
+
1488 (POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
;
1490 dt
= rint(dt
- *fsec
+
1491 (POSTGRES_EPOCH_JDATE
- UNIX_EPOCH_JDATE
) * SECS_PER_DAY
);
1493 utime
= (pg_time_t
) dt
;
1494 if ((Timestamp
) utime
== dt
)
1496 struct pg_tm
*tx
= pg_localtime(&utime
,
1497 attimezone
? attimezone
: session_timezone
);
1499 tm
->tm_year
= tx
->tm_year
+ 1900;
1500 tm
->tm_mon
= tx
->tm_mon
+ 1;
1501 tm
->tm_mday
= tx
->tm_mday
;
1502 tm
->tm_hour
= tx
->tm_hour
;
1503 tm
->tm_min
= tx
->tm_min
;
1504 tm
->tm_sec
= tx
->tm_sec
;
1505 tm
->tm_isdst
= tx
->tm_isdst
;
1506 tm
->tm_gmtoff
= tx
->tm_gmtoff
;
1507 tm
->tm_zone
= tx
->tm_zone
;
1508 *tzp
= -tm
->tm_gmtoff
;
1510 *tzn
= (char *) tm
->tm_zone
;
1515 * When out of range of pg_time_t, treat as GMT
1518 /* Mark this as *no* time zone available */
1531 * Convert a tm structure to a timestamp data type.
1532 * Note that year is _not_ 1900-based, but is an explicit full value.
1533 * Also, month is one-based, _not_ zero-based.
1535 * Returns -1 on failure (value out of range).
1538 tm2timestamp(struct pg_tm
* tm
, fsec_t fsec
, int *tzp
, Timestamp
*result
)
1543 /* Julian day routines are not correct for negative Julian days */
1544 if (!IS_VALID_JULIAN(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
))
1546 *result
= 0; /* keep compiler quiet */
1550 date
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) - POSTGRES_EPOCH_JDATE
;
1551 time
= time2t(tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
, fsec
);
1553 #ifdef HAVE_INT64_TIMESTAMP
1554 *result
= date
* USECS_PER_DAY
+ time
;
1555 /* check for major overflow */
1556 if ((*result
- time
) / USECS_PER_DAY
!= date
)
1558 *result
= 0; /* keep compiler quiet */
1561 /* check for just-barely overflow (okay except time-of-day wraps) */
1562 if ((*result
< 0 && date
>= 0) ||
1563 (*result
>= 0 && date
< 0))
1565 *result
= 0; /* keep compiler quiet */
1569 *result
= date
* SECS_PER_DAY
+ time
;
1572 *result
= dt2local(*result
, -(*tzp
));
1579 * Convert a interval data type to a tm structure.
1582 interval2tm(Interval span
, struct pg_tm
* tm
, fsec_t
*fsec
)
1587 tm
->tm_year
= span
.month
/ MONTHS_PER_YEAR
;
1588 tm
->tm_mon
= span
.month
% MONTHS_PER_YEAR
;
1589 tm
->tm_mday
= span
.day
;
1592 #ifdef HAVE_INT64_TIMESTAMP
1593 tfrac
= time
/ USECS_PER_HOUR
;
1594 time
-= tfrac
* USECS_PER_HOUR
;
1595 tm
->tm_hour
= tfrac
; /* could overflow ... */
1596 tfrac
= time
/ USECS_PER_MINUTE
;
1597 time
-= tfrac
* USECS_PER_MINUTE
;
1599 tfrac
= time
/ USECS_PER_SEC
;
1600 *fsec
= time
- (tfrac
* USECS_PER_SEC
);
1604 TMODULO(time
, tfrac
, (double) SECS_PER_HOUR
);
1605 tm
->tm_hour
= tfrac
; /* could overflow ... */
1606 TMODULO(time
, tfrac
, (double) SECS_PER_MINUTE
);
1608 TMODULO(time
, tfrac
, 1.0);
1610 time
= TSROUND(time
);
1611 /* roundoff may need to propagate to higher-order fields */
1614 time
= ceil(span
.time
);
1624 tm2interval(struct pg_tm
* tm
, fsec_t fsec
, Interval
*span
)
1626 span
->month
= tm
->tm_year
* MONTHS_PER_YEAR
+ tm
->tm_mon
;
1627 span
->day
= tm
->tm_mday
;
1628 #ifdef HAVE_INT64_TIMESTAMP
1629 span
->time
= (((((tm
->tm_hour
* INT64CONST(60)) +
1630 tm
->tm_min
) * INT64CONST(60)) +
1631 tm
->tm_sec
) * USECS_PER_SEC
) + fsec
;
1633 span
->time
= (((tm
->tm_hour
* (double) MINS_PER_HOUR
) +
1634 tm
->tm_min
) * (double) SECS_PER_MINUTE
) +
1642 time2t(const int hour
, const int min
, const int sec
, const fsec_t fsec
)
1644 #ifdef HAVE_INT64_TIMESTAMP
1645 return (((((hour
* MINS_PER_HOUR
) + min
) * SECS_PER_MINUTE
) + sec
) * USECS_PER_SEC
) + fsec
;
1647 return (((hour
* MINS_PER_HOUR
) + min
) * SECS_PER_MINUTE
) + sec
+ fsec
;
1652 dt2local(Timestamp dt
, int tz
)
1654 #ifdef HAVE_INT64_TIMESTAMP
1655 dt
-= (tz
* USECS_PER_SEC
);
1663 /*****************************************************************************
1665 *****************************************************************************/
1669 timestamp_finite(PG_FUNCTION_ARGS
)
1671 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
1673 PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp
));
1677 interval_finite(PG_FUNCTION_ARGS
)
1679 PG_RETURN_BOOL(true);
1683 /*----------------------------------------------------------
1684 * Relational operators for timestamp.
1685 *---------------------------------------------------------*/
1688 GetEpochTime(struct pg_tm
* tm
)
1691 pg_time_t epoch
= 0;
1693 t0
= pg_gmtime(&epoch
);
1695 tm
->tm_year
= t0
->tm_year
;
1696 tm
->tm_mon
= t0
->tm_mon
;
1697 tm
->tm_mday
= t0
->tm_mday
;
1698 tm
->tm_hour
= t0
->tm_hour
;
1699 tm
->tm_min
= t0
->tm_min
;
1700 tm
->tm_sec
= t0
->tm_sec
;
1702 tm
->tm_year
+= 1900;
1707 SetEpochTimestamp(void)
1714 /* we don't bother to test for failure ... */
1715 tm2timestamp(tm
, 0, NULL
, &dt
);
1718 } /* SetEpochTimestamp() */
1721 * We are currently sharing some code between timestamp and timestamptz.
1722 * The comparison functions are among them. - thomas 2001-09-25
1724 * timestamp_relop - is timestamp1 relop timestamp2
1726 * collate invalid timestamp at the end
1729 timestamp_cmp_internal(Timestamp dt1
, Timestamp dt2
)
1731 #ifdef HAVE_INT64_TIMESTAMP
1732 return (dt1
< dt2
) ? -1 : ((dt1
> dt2
) ? 1 : 0);
1736 * When using float representation, we have to be wary of NaNs.
1738 * We consider all NANs to be equal and larger than any non-NAN. This is
1739 * somewhat arbitrary; the important thing is to have a consistent sort
1745 return 0; /* NAN = NAN */
1747 return 1; /* NAN > non-NAN */
1749 else if (isnan(dt2
))
1751 return -1; /* non-NAN < NAN */
1766 timestamp_eq(PG_FUNCTION_ARGS
)
1768 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
1769 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
1771 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) == 0);
1775 timestamp_ne(PG_FUNCTION_ARGS
)
1777 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
1778 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
1780 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) != 0);
1784 timestamp_lt(PG_FUNCTION_ARGS
)
1786 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
1787 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
1789 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) < 0);
1793 timestamp_gt(PG_FUNCTION_ARGS
)
1795 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
1796 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
1798 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) > 0);
1802 timestamp_le(PG_FUNCTION_ARGS
)
1804 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
1805 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
1807 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) <= 0);
1811 timestamp_ge(PG_FUNCTION_ARGS
)
1813 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
1814 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
1816 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) >= 0);
1820 timestamp_cmp(PG_FUNCTION_ARGS
)
1822 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
1823 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
1825 PG_RETURN_INT32(timestamp_cmp_internal(dt1
, dt2
));
1829 timestamp_hash(PG_FUNCTION_ARGS
)
1831 /* We can use either hashint8 or hashfloat8 directly */
1832 #ifdef HAVE_INT64_TIMESTAMP
1833 return hashint8(fcinfo
);
1835 return hashfloat8(fcinfo
);
1841 * Crosstype comparison functions for timestamp vs timestamptz
1845 timestamp_eq_timestamptz(PG_FUNCTION_ARGS
)
1847 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
1848 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
1851 dt1
= timestamp2timestamptz(timestampVal
);
1853 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) == 0);
1857 timestamp_ne_timestamptz(PG_FUNCTION_ARGS
)
1859 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
1860 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
1863 dt1
= timestamp2timestamptz(timestampVal
);
1865 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) != 0);
1869 timestamp_lt_timestamptz(PG_FUNCTION_ARGS
)
1871 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
1872 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
1875 dt1
= timestamp2timestamptz(timestampVal
);
1877 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) < 0);
1881 timestamp_gt_timestamptz(PG_FUNCTION_ARGS
)
1883 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
1884 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
1887 dt1
= timestamp2timestamptz(timestampVal
);
1889 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) > 0);
1893 timestamp_le_timestamptz(PG_FUNCTION_ARGS
)
1895 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
1896 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
1899 dt1
= timestamp2timestamptz(timestampVal
);
1901 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) <= 0);
1905 timestamp_ge_timestamptz(PG_FUNCTION_ARGS
)
1907 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
1908 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
1911 dt1
= timestamp2timestamptz(timestampVal
);
1913 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) >= 0);
1917 timestamp_cmp_timestamptz(PG_FUNCTION_ARGS
)
1919 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(0);
1920 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
1923 dt1
= timestamp2timestamptz(timestampVal
);
1925 PG_RETURN_INT32(timestamp_cmp_internal(dt1
, dt2
));
1929 timestamptz_eq_timestamp(PG_FUNCTION_ARGS
)
1931 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
1932 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
1935 dt2
= timestamp2timestamptz(timestampVal
);
1937 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) == 0);
1941 timestamptz_ne_timestamp(PG_FUNCTION_ARGS
)
1943 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
1944 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
1947 dt2
= timestamp2timestamptz(timestampVal
);
1949 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) != 0);
1953 timestamptz_lt_timestamp(PG_FUNCTION_ARGS
)
1955 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
1956 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
1959 dt2
= timestamp2timestamptz(timestampVal
);
1961 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) < 0);
1965 timestamptz_gt_timestamp(PG_FUNCTION_ARGS
)
1967 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
1968 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
1971 dt2
= timestamp2timestamptz(timestampVal
);
1973 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) > 0);
1977 timestamptz_le_timestamp(PG_FUNCTION_ARGS
)
1979 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
1980 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
1983 dt2
= timestamp2timestamptz(timestampVal
);
1985 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) <= 0);
1989 timestamptz_ge_timestamp(PG_FUNCTION_ARGS
)
1991 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
1992 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
1995 dt2
= timestamp2timestamptz(timestampVal
);
1997 PG_RETURN_BOOL(timestamp_cmp_internal(dt1
, dt2
) >= 0);
2001 timestamptz_cmp_timestamp(PG_FUNCTION_ARGS
)
2003 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
2004 Timestamp timestampVal
= PG_GETARG_TIMESTAMP(1);
2007 dt2
= timestamp2timestamptz(timestampVal
);
2009 PG_RETURN_INT32(timestamp_cmp_internal(dt1
, dt2
));
2014 * interval_relop - is interval1 relop interval2
2016 * collate invalid interval at the end
2018 static inline TimeOffset
2019 interval_cmp_value(const Interval
*interval
)
2023 span
= interval
->time
;
2025 #ifdef HAVE_INT64_TIMESTAMP
2026 span
+= interval
->month
* INT64CONST(30) * USECS_PER_DAY
;
2027 span
+= interval
->day
* INT64CONST(24) * USECS_PER_HOUR
;
2029 span
+= interval
->month
* ((double) DAYS_PER_MONTH
* SECS_PER_DAY
);
2030 span
+= interval
->day
* ((double) HOURS_PER_DAY
* SECS_PER_HOUR
);
2037 interval_cmp_internal(Interval
*interval1
, Interval
*interval2
)
2039 TimeOffset span1
= interval_cmp_value(interval1
);
2040 TimeOffset span2
= interval_cmp_value(interval2
);
2042 return ((span1
< span2
) ? -1 : (span1
> span2
) ? 1 : 0);
2046 interval_eq(PG_FUNCTION_ARGS
)
2048 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2049 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2051 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) == 0);
2055 interval_ne(PG_FUNCTION_ARGS
)
2057 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2058 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2060 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) != 0);
2064 interval_lt(PG_FUNCTION_ARGS
)
2066 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2067 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2069 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) < 0);
2073 interval_gt(PG_FUNCTION_ARGS
)
2075 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2076 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2078 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) > 0);
2082 interval_le(PG_FUNCTION_ARGS
)
2084 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2085 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2087 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) <= 0);
2091 interval_ge(PG_FUNCTION_ARGS
)
2093 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2094 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2096 PG_RETURN_BOOL(interval_cmp_internal(interval1
, interval2
) >= 0);
2100 interval_cmp(PG_FUNCTION_ARGS
)
2102 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2103 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2105 PG_RETURN_INT32(interval_cmp_internal(interval1
, interval2
));
2109 * Hashing for intervals
2111 * We must produce equal hashvals for values that interval_cmp_internal()
2112 * considers equal. So, compute the net span the same way it does,
2113 * and then hash that, using either int64 or float8 hashing.
2116 interval_hash(PG_FUNCTION_ARGS
)
2118 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
2119 TimeOffset span
= interval_cmp_value(interval
);
2121 #ifdef HAVE_INT64_TIMESTAMP
2122 return DirectFunctionCall1(hashint8
, Int64GetDatumFast(span
));
2124 return DirectFunctionCall1(hashfloat8
, Float8GetDatumFast(span
));
2128 /* overlaps_timestamp() --- implements the SQL92 OVERLAPS operator.
2130 * Algorithm is per SQL92 spec. This is much harder than you'd think
2131 * because the spec requires us to deliver a non-null answer in some cases
2132 * where some of the inputs are null.
2135 overlaps_timestamp(PG_FUNCTION_ARGS
)
2138 * The arguments are Timestamps, but we leave them as generic Datums to
2139 * avoid unnecessary conversions between value and reference forms --- not
2140 * to mention possible dereferences of null pointers.
2142 Datum ts1
= PG_GETARG_DATUM(0);
2143 Datum te1
= PG_GETARG_DATUM(1);
2144 Datum ts2
= PG_GETARG_DATUM(2);
2145 Datum te2
= PG_GETARG_DATUM(3);
2146 bool ts1IsNull
= PG_ARGISNULL(0);
2147 bool te1IsNull
= PG_ARGISNULL(1);
2148 bool ts2IsNull
= PG_ARGISNULL(2);
2149 bool te2IsNull
= PG_ARGISNULL(3);
2151 #define TIMESTAMP_GT(t1,t2) \
2152 DatumGetBool(DirectFunctionCall2(timestamp_gt,t1,t2))
2153 #define TIMESTAMP_LT(t1,t2) \
2154 DatumGetBool(DirectFunctionCall2(timestamp_lt,t1,t2))
2157 * If both endpoints of interval 1 are null, the result is null (unknown).
2158 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2159 * take ts1 as the lesser endpoint.
2165 /* swap null for non-null */
2169 else if (!te1IsNull
)
2171 if (TIMESTAMP_GT(ts1
, te1
))
2180 /* Likewise for interval 2. */
2185 /* swap null for non-null */
2189 else if (!te2IsNull
)
2191 if (TIMESTAMP_GT(ts2
, te2
))
2201 * At this point neither ts1 nor ts2 is null, so we can consider three
2202 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2204 if (TIMESTAMP_GT(ts1
, ts2
))
2207 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2208 * in the presence of nulls it's not quite completely so.
2212 if (TIMESTAMP_LT(ts1
, te2
))
2213 PG_RETURN_BOOL(true);
2218 * If te1 is not null then we had ts1 <= te1 above, and we just found
2219 * ts1 >= te2, hence te1 >= te2.
2221 PG_RETURN_BOOL(false);
2223 else if (TIMESTAMP_LT(ts1
, ts2
))
2225 /* This case is ts2 < te1 OR te2 < te1 */
2228 if (TIMESTAMP_LT(ts2
, te1
))
2229 PG_RETURN_BOOL(true);
2234 * If te2 is not null then we had ts2 <= te2 above, and we just found
2235 * ts2 >= te1, hence te2 >= te1.
2237 PG_RETURN_BOOL(false);
2242 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2243 * rather silly way of saying "true if both are nonnull, else null".
2245 if (te1IsNull
|| te2IsNull
)
2247 PG_RETURN_BOOL(true);
2255 /*----------------------------------------------------------
2256 * "Arithmetic" operators on date/times.
2257 *---------------------------------------------------------*/
2260 timestamp_smaller(PG_FUNCTION_ARGS
)
2262 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2263 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2266 /* use timestamp_cmp_internal to be sure this agrees with comparisons */
2267 if (timestamp_cmp_internal(dt1
, dt2
) < 0)
2271 PG_RETURN_TIMESTAMP(result
);
2275 timestamp_larger(PG_FUNCTION_ARGS
)
2277 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2278 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2281 if (timestamp_cmp_internal(dt1
, dt2
) > 0)
2285 PG_RETURN_TIMESTAMP(result
);
2290 timestamp_mi(PG_FUNCTION_ARGS
)
2292 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2293 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
2296 result
= (Interval
*) palloc(sizeof(Interval
));
2298 if (TIMESTAMP_NOT_FINITE(dt1
) || TIMESTAMP_NOT_FINITE(dt2
))
2300 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2301 errmsg("cannot subtract infinite timestamps")));
2303 result
->time
= dt1
- dt2
;
2309 * This is wrong, but removing it breaks a lot of regression tests.
2312 * test=> SET timezone = 'EST5EDT';
2314 * test-> ('2005-10-30 13:22:00-05'::timestamptz -
2315 * test(> '2005-10-29 13:22:00-04'::timestamptz);
2321 * so adding that to the first timestamp gets:
2324 * test-> ('2005-10-29 13:22:00-04'::timestamptz +
2325 * test(> ('2005-10-30 13:22:00-05'::timestamptz -
2326 * test(> '2005-10-29 13:22:00-04'::timestamptz)) at time zone 'EST';
2328 * --------------------
2329 * 2005-10-30 14:22:00
2333 result
= DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours
,
2334 IntervalPGetDatum(result
)));
2336 PG_RETURN_INTERVAL_P(result
);
2340 * interval_justify_interval()
2342 * Adjust interval so 'month', 'day', and 'time' portions are within
2343 * customary bounds. Specifically:
2345 * 0 <= abs(time) < 24 hours
2346 * 0 <= abs(day) < 30 days
2348 * Also, the sign bit on all three fields is made equal, so either
2349 * all three fields are negative or all are positive.
2352 interval_justify_interval(PG_FUNCTION_ARGS
)
2354 Interval
*span
= PG_GETARG_INTERVAL_P(0);
2356 TimeOffset wholeday
;
2359 result
= (Interval
*) palloc(sizeof(Interval
));
2360 result
->month
= span
->month
;
2361 result
->day
= span
->day
;
2362 result
->time
= span
->time
;
2364 #ifdef HAVE_INT64_TIMESTAMP
2365 TMODULO(result
->time
, wholeday
, USECS_PER_DAY
);
2367 TMODULO(result
->time
, wholeday
, (double) SECS_PER_DAY
);
2369 result
->day
+= wholeday
; /* could overflow... */
2371 wholemonth
= result
->day
/ DAYS_PER_MONTH
;
2372 result
->day
-= wholemonth
* DAYS_PER_MONTH
;
2373 result
->month
+= wholemonth
;
2375 if (result
->month
> 0 &&
2376 (result
->day
< 0 || (result
->day
== 0 && result
->time
< 0)))
2378 result
->day
+= DAYS_PER_MONTH
;
2381 else if (result
->month
< 0 &&
2382 (result
->day
> 0 || (result
->day
== 0 && result
->time
> 0)))
2384 result
->day
-= DAYS_PER_MONTH
;
2388 if (result
->day
> 0 && result
->time
< 0)
2390 #ifdef HAVE_INT64_TIMESTAMP
2391 result
->time
+= USECS_PER_DAY
;
2393 result
->time
+= (double) SECS_PER_DAY
;
2397 else if (result
->day
< 0 && result
->time
> 0)
2399 #ifdef HAVE_INT64_TIMESTAMP
2400 result
->time
-= USECS_PER_DAY
;
2402 result
->time
-= (double) SECS_PER_DAY
;
2407 PG_RETURN_INTERVAL_P(result
);
2411 * interval_justify_hours()
2413 * Adjust interval so 'time' contains less than a whole day, adding
2414 * the excess to 'day'. This is useful for
2415 * situations (such as non-TZ) where '1 day' = '24 hours' is valid,
2416 * e.g. interval subtraction and division.
2419 interval_justify_hours(PG_FUNCTION_ARGS
)
2421 Interval
*span
= PG_GETARG_INTERVAL_P(0);
2423 TimeOffset wholeday
;
2425 result
= (Interval
*) palloc(sizeof(Interval
));
2426 result
->month
= span
->month
;
2427 result
->day
= span
->day
;
2428 result
->time
= span
->time
;
2430 #ifdef HAVE_INT64_TIMESTAMP
2431 TMODULO(result
->time
, wholeday
, USECS_PER_DAY
);
2433 TMODULO(result
->time
, wholeday
, (double) SECS_PER_DAY
);
2435 result
->day
+= wholeday
; /* could overflow... */
2437 if (result
->day
> 0 && result
->time
< 0)
2439 #ifdef HAVE_INT64_TIMESTAMP
2440 result
->time
+= USECS_PER_DAY
;
2442 result
->time
+= (double) SECS_PER_DAY
;
2446 else if (result
->day
< 0 && result
->time
> 0)
2448 #ifdef HAVE_INT64_TIMESTAMP
2449 result
->time
-= USECS_PER_DAY
;
2451 result
->time
-= (double) SECS_PER_DAY
;
2456 PG_RETURN_INTERVAL_P(result
);
2460 * interval_justify_days()
2462 * Adjust interval so 'day' contains less than 30 days, adding
2463 * the excess to 'month'.
2466 interval_justify_days(PG_FUNCTION_ARGS
)
2468 Interval
*span
= PG_GETARG_INTERVAL_P(0);
2472 result
= (Interval
*) palloc(sizeof(Interval
));
2473 result
->month
= span
->month
;
2474 result
->day
= span
->day
;
2475 result
->time
= span
->time
;
2477 wholemonth
= result
->day
/ DAYS_PER_MONTH
;
2478 result
->day
-= wholemonth
* DAYS_PER_MONTH
;
2479 result
->month
+= wholemonth
;
2481 if (result
->month
> 0 && result
->day
< 0)
2483 result
->day
+= DAYS_PER_MONTH
;
2486 else if (result
->month
< 0 && result
->day
> 0)
2488 result
->day
-= DAYS_PER_MONTH
;
2492 PG_RETURN_INTERVAL_P(result
);
2495 /* timestamp_pl_interval()
2496 * Add a interval to a timestamp data type.
2497 * Note that interval has provisions for qualitative year/month and day
2498 * units, so try to do the right thing with them.
2499 * To add a month, increment the month, and use the same day of month.
2500 * Then, if the next month has fewer days, set the day of month
2501 * to the last day of month.
2502 * To add a day, increment the mday, and use the same time of day.
2503 * Lastly, add in the "quantitative time".
2506 timestamp_pl_interval(PG_FUNCTION_ARGS
)
2508 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
2509 Interval
*span
= PG_GETARG_INTERVAL_P(1);
2512 if (TIMESTAMP_NOT_FINITE(timestamp
))
2516 if (span
->month
!= 0)
2522 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
2524 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2525 errmsg("timestamp out of range")));
2527 tm
->tm_mon
+= span
->month
;
2528 if (tm
->tm_mon
> MONTHS_PER_YEAR
)
2530 tm
->tm_year
+= (tm
->tm_mon
- 1) / MONTHS_PER_YEAR
;
2531 tm
->tm_mon
= ((tm
->tm_mon
- 1) % MONTHS_PER_YEAR
) + 1;
2533 else if (tm
->tm_mon
< 1)
2535 tm
->tm_year
+= tm
->tm_mon
/ MONTHS_PER_YEAR
- 1;
2536 tm
->tm_mon
= tm
->tm_mon
% MONTHS_PER_YEAR
+ MONTHS_PER_YEAR
;
2539 /* adjust for end of month boundary problems... */
2540 if (tm
->tm_mday
> day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1])
2541 tm
->tm_mday
= (day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1]);
2543 if (tm2timestamp(tm
, fsec
, NULL
, ×tamp
) != 0)
2545 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2546 errmsg("timestamp out of range")));
2556 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
2558 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2559 errmsg("timestamp out of range")));
2561 /* Add days by converting to and from julian */
2562 julian
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) + span
->day
;
2563 j2date(julian
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
2565 if (tm2timestamp(tm
, fsec
, NULL
, ×tamp
) != 0)
2567 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2568 errmsg("timestamp out of range")));
2571 timestamp
+= span
->time
;
2575 PG_RETURN_TIMESTAMP(result
);
2579 timestamp_mi_interval(PG_FUNCTION_ARGS
)
2581 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
2582 Interval
*span
= PG_GETARG_INTERVAL_P(1);
2585 tspan
.month
= -span
->month
;
2586 tspan
.day
= -span
->day
;
2587 tspan
.time
= -span
->time
;
2589 return DirectFunctionCall2(timestamp_pl_interval
,
2590 TimestampGetDatum(timestamp
),
2591 PointerGetDatum(&tspan
));
2595 /* timestamptz_pl_interval()
2596 * Add a interval to a timestamp with time zone data type.
2597 * Note that interval has provisions for qualitative year/month
2598 * units, so try to do the right thing with them.
2599 * To add a month, increment the month, and use the same day of month.
2600 * Then, if the next month has fewer days, set the day of month
2601 * to the last day of month.
2602 * Lastly, add in the "quantitative time".
2605 timestamptz_pl_interval(PG_FUNCTION_ARGS
)
2607 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
2608 Interval
*span
= PG_GETARG_INTERVAL_P(1);
2613 if (TIMESTAMP_NOT_FINITE(timestamp
))
2617 if (span
->month
!= 0)
2623 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, &tzn
, NULL
) != 0)
2625 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2626 errmsg("timestamp out of range")));
2628 tm
->tm_mon
+= span
->month
;
2629 if (tm
->tm_mon
> MONTHS_PER_YEAR
)
2631 tm
->tm_year
+= (tm
->tm_mon
- 1) / MONTHS_PER_YEAR
;
2632 tm
->tm_mon
= ((tm
->tm_mon
- 1) % MONTHS_PER_YEAR
) + 1;
2634 else if (tm
->tm_mon
< 1)
2636 tm
->tm_year
+= tm
->tm_mon
/ MONTHS_PER_YEAR
- 1;
2637 tm
->tm_mon
= tm
->tm_mon
% MONTHS_PER_YEAR
+ MONTHS_PER_YEAR
;
2640 /* adjust for end of month boundary problems... */
2641 if (tm
->tm_mday
> day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1])
2642 tm
->tm_mday
= (day_tab
[isleap(tm
->tm_year
)][tm
->tm_mon
- 1]);
2644 tz
= DetermineTimeZoneOffset(tm
, session_timezone
);
2646 if (tm2timestamp(tm
, fsec
, &tz
, ×tamp
) != 0)
2648 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2649 errmsg("timestamp out of range")));
2659 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, &tzn
, NULL
) != 0)
2661 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2662 errmsg("timestamp out of range")));
2664 /* Add days by converting to and from julian */
2665 julian
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
) + span
->day
;
2666 j2date(julian
, &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
2668 tz
= DetermineTimeZoneOffset(tm
, session_timezone
);
2670 if (tm2timestamp(tm
, fsec
, &tz
, ×tamp
) != 0)
2672 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
2673 errmsg("timestamp out of range")));
2676 timestamp
+= span
->time
;
2680 PG_RETURN_TIMESTAMP(result
);
2684 timestamptz_mi_interval(PG_FUNCTION_ARGS
)
2686 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
2687 Interval
*span
= PG_GETARG_INTERVAL_P(1);
2690 tspan
.month
= -span
->month
;
2691 tspan
.day
= -span
->day
;
2692 tspan
.time
= -span
->time
;
2694 return DirectFunctionCall2(timestamptz_pl_interval
,
2695 TimestampGetDatum(timestamp
),
2696 PointerGetDatum(&tspan
));
2701 interval_um(PG_FUNCTION_ARGS
)
2703 Interval
*interval
= PG_GETARG_INTERVAL_P(0);
2706 result
= (Interval
*) palloc(sizeof(Interval
));
2708 result
->time
= -interval
->time
;
2709 result
->day
= -interval
->day
;
2710 result
->month
= -interval
->month
;
2712 PG_RETURN_INTERVAL_P(result
);
2717 interval_smaller(PG_FUNCTION_ARGS
)
2719 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2720 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2723 /* use interval_cmp_internal to be sure this agrees with comparisons */
2724 if (interval_cmp_internal(interval1
, interval2
) < 0)
2728 PG_RETURN_INTERVAL_P(result
);
2732 interval_larger(PG_FUNCTION_ARGS
)
2734 Interval
*interval1
= PG_GETARG_INTERVAL_P(0);
2735 Interval
*interval2
= PG_GETARG_INTERVAL_P(1);
2738 if (interval_cmp_internal(interval1
, interval2
) > 0)
2742 PG_RETURN_INTERVAL_P(result
);
2746 interval_pl(PG_FUNCTION_ARGS
)
2748 Interval
*span1
= PG_GETARG_INTERVAL_P(0);
2749 Interval
*span2
= PG_GETARG_INTERVAL_P(1);
2752 result
= (Interval
*) palloc(sizeof(Interval
));
2754 result
->month
= span1
->month
+ span2
->month
;
2755 result
->day
= span1
->day
+ span2
->day
;
2756 result
->time
= span1
->time
+ span2
->time
;
2758 PG_RETURN_INTERVAL_P(result
);
2762 interval_mi(PG_FUNCTION_ARGS
)
2764 Interval
*span1
= PG_GETARG_INTERVAL_P(0);
2765 Interval
*span2
= PG_GETARG_INTERVAL_P(1);
2768 result
= (Interval
*) palloc(sizeof(Interval
));
2770 result
->month
= span1
->month
- span2
->month
;
2771 result
->day
= span1
->day
- span2
->day
;
2772 result
->time
= span1
->time
- span2
->time
;
2774 PG_RETURN_INTERVAL_P(result
);
2778 interval_mul(PG_FUNCTION_ARGS
)
2780 Interval
*span
= PG_GETARG_INTERVAL_P(0);
2781 float8 factor
= PG_GETARG_FLOAT8(1);
2782 double month_remainder_days
,
2784 int32 orig_month
= span
->month
,
2785 orig_day
= span
->day
;
2788 result
= (Interval
*) palloc(sizeof(Interval
));
2790 result
->month
= (int32
) (span
->month
* factor
);
2791 result
->day
= (int32
) (span
->day
* factor
);
2794 * The above correctly handles the whole-number part of the month and day
2795 * products, but we have to do something with any fractional part
2796 * resulting when the factor is nonintegral. We cascade the fractions
2797 * down to lower units using the conversion factors DAYS_PER_MONTH and
2798 * SECS_PER_DAY. Note we do NOT cascade up, since we are not forced to do
2799 * so by the representation. The user can choose to cascade up later,
2800 * using justify_hours and/or justify_days.
2804 * Fractional months full days into days.
2806 * Floating point calculation are inherently inprecise, so these
2807 * calculations are crafted to produce the most reliable result possible.
2808 * TSROUND() is needed to more accurately produce whole numbers where
2811 month_remainder_days
= (orig_month
* factor
- result
->month
) * DAYS_PER_MONTH
;
2812 month_remainder_days
= TSROUND(month_remainder_days
);
2813 sec_remainder
= (orig_day
* factor
- result
->day
+
2814 month_remainder_days
- (int) month_remainder_days
) * SECS_PER_DAY
;
2815 sec_remainder
= TSROUND(sec_remainder
);
2818 * Might have 24:00:00 hours due to rounding, or >24 hours because of time
2819 * cascade from months and days. It might still be >24 if the combination
2820 * of cascade and the seconds factor operation itself.
2822 if (Abs(sec_remainder
) >= SECS_PER_DAY
)
2824 result
->day
+= (int) (sec_remainder
/ SECS_PER_DAY
);
2825 sec_remainder
-= (int) (sec_remainder
/ SECS_PER_DAY
) * SECS_PER_DAY
;
2828 /* cascade units down */
2829 result
->day
+= (int32
) month_remainder_days
;
2830 #ifdef HAVE_INT64_TIMESTAMP
2831 result
->time
= rint(span
->time
* factor
+ sec_remainder
* USECS_PER_SEC
);
2833 result
->time
= span
->time
* factor
+ sec_remainder
;
2836 PG_RETURN_INTERVAL_P(result
);
2840 mul_d_interval(PG_FUNCTION_ARGS
)
2842 /* Args are float8 and Interval *, but leave them as generic Datum */
2843 Datum factor
= PG_GETARG_DATUM(0);
2844 Datum span
= PG_GETARG_DATUM(1);
2846 return DirectFunctionCall2(interval_mul
, span
, factor
);
2850 interval_div(PG_FUNCTION_ARGS
)
2852 Interval
*span
= PG_GETARG_INTERVAL_P(0);
2853 float8 factor
= PG_GETARG_FLOAT8(1);
2854 double month_remainder_days
,
2856 int32 orig_month
= span
->month
,
2857 orig_day
= span
->day
;
2860 result
= (Interval
*) palloc(sizeof(Interval
));
2864 (errcode(ERRCODE_DIVISION_BY_ZERO
),
2865 errmsg("division by zero")));
2867 result
->month
= (int32
) (span
->month
/ factor
);
2868 result
->day
= (int32
) (span
->day
/ factor
);
2871 * Fractional months full days into days. See comment in interval_mul().
2873 month_remainder_days
= (orig_month
/ factor
- result
->month
) * DAYS_PER_MONTH
;
2874 month_remainder_days
= TSROUND(month_remainder_days
);
2875 sec_remainder
= (orig_day
/ factor
- result
->day
+
2876 month_remainder_days
- (int) month_remainder_days
) * SECS_PER_DAY
;
2877 sec_remainder
= TSROUND(sec_remainder
);
2878 if (Abs(sec_remainder
) >= SECS_PER_DAY
)
2880 result
->day
+= (int) (sec_remainder
/ SECS_PER_DAY
);
2881 sec_remainder
-= (int) (sec_remainder
/ SECS_PER_DAY
) * SECS_PER_DAY
;
2884 /* cascade units down */
2885 result
->day
+= (int32
) month_remainder_days
;
2886 #ifdef HAVE_INT64_TIMESTAMP
2887 result
->time
= rint(span
->time
/ factor
+ sec_remainder
* USECS_PER_SEC
);
2889 /* See TSROUND comment in interval_mul(). */
2890 result
->time
= span
->time
/ factor
+ sec_remainder
;
2893 PG_RETURN_INTERVAL_P(result
);
2897 * interval_accum and interval_avg implement the AVG(interval) aggregate.
2899 * The transition datatype for this aggregate is a 2-element array of
2900 * intervals, where the first is the running sum and the second contains
2901 * the number of values so far in its 'time' field. This is a bit ugly
2902 * but it beats inventing a specialized datatype for the purpose.
2906 interval_accum(PG_FUNCTION_ARGS
)
2908 ArrayType
*transarray
= PG_GETARG_ARRAYTYPE_P(0);
2909 Interval
*newval
= PG_GETARG_INTERVAL_P(1);
2917 deconstruct_array(transarray
,
2918 INTERVALOID
, sizeof(Interval
), false, 'd',
2919 &transdatums
, NULL
, &ndatums
);
2921 elog(ERROR
, "expected 2-element interval array");
2924 * XXX memcpy, instead of just extracting a pointer, to work around buggy
2925 * array code: it won't ensure proper alignment of Interval objects on
2926 * machines where double requires 8-byte alignment. That should be fixed,
2927 * but in the meantime...
2929 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
2930 * compilers optimize into double-aligned load/store anyway.
2932 memcpy((void *) &sumX
, DatumGetPointer(transdatums
[0]), sizeof(Interval
));
2933 memcpy((void *) &N
, DatumGetPointer(transdatums
[1]), sizeof(Interval
));
2935 newsum
= DatumGetIntervalP(DirectFunctionCall2(interval_pl
,
2936 IntervalPGetDatum(&sumX
),
2937 IntervalPGetDatum(newval
)));
2940 transdatums
[0] = IntervalPGetDatum(newsum
);
2941 transdatums
[1] = IntervalPGetDatum(&N
);
2943 result
= construct_array(transdatums
, 2,
2944 INTERVALOID
, sizeof(Interval
), false, 'd');
2946 PG_RETURN_ARRAYTYPE_P(result
);
2950 interval_avg(PG_FUNCTION_ARGS
)
2952 ArrayType
*transarray
= PG_GETARG_ARRAYTYPE_P(0);
2958 deconstruct_array(transarray
,
2959 INTERVALOID
, sizeof(Interval
), false, 'd',
2960 &transdatums
, NULL
, &ndatums
);
2962 elog(ERROR
, "expected 2-element interval array");
2965 * XXX memcpy, instead of just extracting a pointer, to work around buggy
2966 * array code: it won't ensure proper alignment of Interval objects on
2967 * machines where double requires 8-byte alignment. That should be fixed,
2968 * but in the meantime...
2970 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
2971 * compilers optimize into double-aligned load/store anyway.
2973 memcpy((void *) &sumX
, DatumGetPointer(transdatums
[0]), sizeof(Interval
));
2974 memcpy((void *) &N
, DatumGetPointer(transdatums
[1]), sizeof(Interval
));
2976 /* SQL92 defines AVG of no values to be NULL */
2980 return DirectFunctionCall2(interval_div
,
2981 IntervalPGetDatum(&sumX
),
2982 Float8GetDatum(N
.time
));
2987 * Calculate time difference while retaining year/month fields.
2988 * Note that this does not result in an accurate absolute time span
2989 * since year and month are out of context once the arithmetic
2993 timestamp_age(PG_FUNCTION_ARGS
)
2995 Timestamp dt1
= PG_GETARG_TIMESTAMP(0);
2996 Timestamp dt2
= PG_GETARG_TIMESTAMP(1);
3008 result
= (Interval
*) palloc(sizeof(Interval
));
3010 if (timestamp2tm(dt1
, NULL
, tm1
, &fsec1
, NULL
, NULL
) == 0 &&
3011 timestamp2tm(dt2
, NULL
, tm2
, &fsec2
, NULL
, NULL
) == 0)
3013 /* form the symbolic difference */
3014 fsec
= fsec1
- fsec2
;
3015 tm
->tm_sec
= tm1
->tm_sec
- tm2
->tm_sec
;
3016 tm
->tm_min
= tm1
->tm_min
- tm2
->tm_min
;
3017 tm
->tm_hour
= tm1
->tm_hour
- tm2
->tm_hour
;
3018 tm
->tm_mday
= tm1
->tm_mday
- tm2
->tm_mday
;
3019 tm
->tm_mon
= tm1
->tm_mon
- tm2
->tm_mon
;
3020 tm
->tm_year
= tm1
->tm_year
- tm2
->tm_year
;
3022 /* flip sign if necessary... */
3026 tm
->tm_sec
= -tm
->tm_sec
;
3027 tm
->tm_min
= -tm
->tm_min
;
3028 tm
->tm_hour
= -tm
->tm_hour
;
3029 tm
->tm_mday
= -tm
->tm_mday
;
3030 tm
->tm_mon
= -tm
->tm_mon
;
3031 tm
->tm_year
= -tm
->tm_year
;
3034 /* propagate any negative fields into the next higher field */
3037 #ifdef HAVE_INT64_TIMESTAMP
3038 fsec
+= USECS_PER_SEC
;
3045 while (tm
->tm_sec
< 0)
3047 tm
->tm_sec
+= SECS_PER_MINUTE
;
3051 while (tm
->tm_min
< 0)
3053 tm
->tm_min
+= MINS_PER_HOUR
;
3057 while (tm
->tm_hour
< 0)
3059 tm
->tm_hour
+= HOURS_PER_DAY
;
3063 while (tm
->tm_mday
< 0)
3067 tm
->tm_mday
+= day_tab
[isleap(tm1
->tm_year
)][tm1
->tm_mon
- 1];
3072 tm
->tm_mday
+= day_tab
[isleap(tm2
->tm_year
)][tm2
->tm_mon
- 1];
3077 while (tm
->tm_mon
< 0)
3079 tm
->tm_mon
+= MONTHS_PER_YEAR
;
3083 /* recover sign if necessary... */
3087 tm
->tm_sec
= -tm
->tm_sec
;
3088 tm
->tm_min
= -tm
->tm_min
;
3089 tm
->tm_hour
= -tm
->tm_hour
;
3090 tm
->tm_mday
= -tm
->tm_mday
;
3091 tm
->tm_mon
= -tm
->tm_mon
;
3092 tm
->tm_year
= -tm
->tm_year
;
3095 if (tm2interval(tm
, fsec
, result
) != 0)
3097 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3098 errmsg("interval out of range")));
3102 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3103 errmsg("timestamp out of range")));
3105 PG_RETURN_INTERVAL_P(result
);
3109 /* timestamptz_age()
3110 * Calculate time difference while retaining year/month fields.
3111 * Note that this does not result in an accurate absolute time span
3112 * since year and month are out of context once the arithmetic
3116 timestamptz_age(PG_FUNCTION_ARGS
)
3118 TimestampTz dt1
= PG_GETARG_TIMESTAMPTZ(0);
3119 TimestampTz dt2
= PG_GETARG_TIMESTAMPTZ(1);
3134 result
= (Interval
*) palloc(sizeof(Interval
));
3136 if (timestamp2tm(dt1
, &tz1
, tm1
, &fsec1
, &tzn
, NULL
) == 0 &&
3137 timestamp2tm(dt2
, &tz2
, tm2
, &fsec2
, &tzn
, NULL
) == 0)
3139 /* form the symbolic difference */
3140 fsec
= fsec1
- fsec2
;
3141 tm
->tm_sec
= tm1
->tm_sec
- tm2
->tm_sec
;
3142 tm
->tm_min
= tm1
->tm_min
- tm2
->tm_min
;
3143 tm
->tm_hour
= tm1
->tm_hour
- tm2
->tm_hour
;
3144 tm
->tm_mday
= tm1
->tm_mday
- tm2
->tm_mday
;
3145 tm
->tm_mon
= tm1
->tm_mon
- tm2
->tm_mon
;
3146 tm
->tm_year
= tm1
->tm_year
- tm2
->tm_year
;
3148 /* flip sign if necessary... */
3152 tm
->tm_sec
= -tm
->tm_sec
;
3153 tm
->tm_min
= -tm
->tm_min
;
3154 tm
->tm_hour
= -tm
->tm_hour
;
3155 tm
->tm_mday
= -tm
->tm_mday
;
3156 tm
->tm_mon
= -tm
->tm_mon
;
3157 tm
->tm_year
= -tm
->tm_year
;
3160 /* propagate any negative fields into the next higher field */
3163 #ifdef HAVE_INT64_TIMESTAMP
3164 fsec
+= USECS_PER_SEC
;
3171 while (tm
->tm_sec
< 0)
3173 tm
->tm_sec
+= SECS_PER_MINUTE
;
3177 while (tm
->tm_min
< 0)
3179 tm
->tm_min
+= MINS_PER_HOUR
;
3183 while (tm
->tm_hour
< 0)
3185 tm
->tm_hour
+= HOURS_PER_DAY
;
3189 while (tm
->tm_mday
< 0)
3193 tm
->tm_mday
+= day_tab
[isleap(tm1
->tm_year
)][tm1
->tm_mon
- 1];
3198 tm
->tm_mday
+= day_tab
[isleap(tm2
->tm_year
)][tm2
->tm_mon
- 1];
3203 while (tm
->tm_mon
< 0)
3205 tm
->tm_mon
+= MONTHS_PER_YEAR
;
3210 * Note: we deliberately ignore any difference between tz1 and tz2.
3213 /* recover sign if necessary... */
3217 tm
->tm_sec
= -tm
->tm_sec
;
3218 tm
->tm_min
= -tm
->tm_min
;
3219 tm
->tm_hour
= -tm
->tm_hour
;
3220 tm
->tm_mday
= -tm
->tm_mday
;
3221 tm
->tm_mon
= -tm
->tm_mon
;
3222 tm
->tm_year
= -tm
->tm_year
;
3225 if (tm2interval(tm
, fsec
, result
) != 0)
3227 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3228 errmsg("interval out of range")));
3232 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3233 errmsg("timestamp out of range")));
3235 PG_RETURN_INTERVAL_P(result
);
3239 /*----------------------------------------------------------
3240 * Conversion operators.
3241 *---------------------------------------------------------*/
3244 /* timestamp_trunc()
3245 * Truncate timestamp to specified units.
3248 timestamp_trunc(PG_FUNCTION_ARGS
)
3250 text
*units
= PG_GETARG_TEXT_PP(0);
3251 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
3260 if (TIMESTAMP_NOT_FINITE(timestamp
))
3261 PG_RETURN_TIMESTAMP(timestamp
);
3263 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
3264 VARSIZE_ANY_EXHDR(units
),
3267 type
= DecodeUnits(0, lowunits
, &val
);
3271 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
3273 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3274 errmsg("timestamp out of range")));
3282 woy
= date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3285 * If it is week 52/53 and the month is January, then the
3286 * week must belong to the previous year. Also, some
3287 * December dates belong to the next year.
3289 if (woy
>= 52 && tm
->tm_mon
== 1)
3291 if (woy
<= 1 && tm
->tm_mon
== MONTHS_PER_YEAR
)
3293 isoweek2date(woy
, &(tm
->tm_year
), &(tm
->tm_mon
), &(tm
->tm_mday
));
3300 case DTK_MILLENNIUM
:
3301 /* see comments in timestamptz_trunc */
3302 if (tm
->tm_year
> 0)
3303 tm
->tm_year
= ((tm
->tm_year
+ 999) / 1000) * 1000 - 999;
3305 tm
->tm_year
= -((999 - (tm
->tm_year
- 1)) / 1000) * 1000 + 1;
3307 /* see comments in timestamptz_trunc */
3308 if (tm
->tm_year
> 0)
3309 tm
->tm_year
= ((tm
->tm_year
+ 99) / 100) * 100 - 99;
3311 tm
->tm_year
= -((99 - (tm
->tm_year
- 1)) / 100) * 100 + 1;
3313 /* see comments in timestamptz_trunc */
3314 if (val
!= DTK_MILLENNIUM
&& val
!= DTK_CENTURY
)
3316 if (tm
->tm_year
> 0)
3317 tm
->tm_year
= (tm
->tm_year
/ 10) * 10;
3319 tm
->tm_year
= -((8 - (tm
->tm_year
- 1)) / 10) * 10;
3324 tm
->tm_mon
= (3 * ((tm
->tm_mon
- 1) / 3)) + 1;
3338 #ifdef HAVE_INT64_TIMESTAMP
3339 fsec
= (fsec
/ 1000) * 1000;
3341 fsec
= floor(fsec
* 1000) / 1000;
3346 #ifndef HAVE_INT64_TIMESTAMP
3347 fsec
= floor(fsec
* 1000000) / 1000000;
3353 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3354 errmsg("timestamp units \"%s\" not supported",
3359 if (tm2timestamp(tm
, fsec
, NULL
, &result
) != 0)
3361 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3362 errmsg("timestamp out of range")));
3367 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3368 errmsg("timestamp units \"%s\" not recognized",
3373 PG_RETURN_TIMESTAMP(result
);
3376 /* timestamptz_trunc()
3377 * Truncate timestamp to specified units.
3380 timestamptz_trunc(PG_FUNCTION_ARGS
)
3382 text
*units
= PG_GETARG_TEXT_PP(0);
3383 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
3388 bool redotz
= false;
3395 if (TIMESTAMP_NOT_FINITE(timestamp
))
3396 PG_RETURN_TIMESTAMPTZ(timestamp
);
3398 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
3399 VARSIZE_ANY_EXHDR(units
),
3402 type
= DecodeUnits(0, lowunits
, &val
);
3406 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, &tzn
, NULL
) != 0)
3408 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3409 errmsg("timestamp out of range")));
3417 woy
= date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3420 * If it is week 52/53 and the month is January, then the
3421 * week must belong to the previous year. Also, some
3422 * December dates belong to the next year.
3424 if (woy
>= 52 && tm
->tm_mon
== 1)
3426 if (woy
<= 1 && tm
->tm_mon
== MONTHS_PER_YEAR
)
3428 isoweek2date(woy
, &(tm
->tm_year
), &(tm
->tm_mon
), &(tm
->tm_mday
));
3436 /* one may consider DTK_THOUSAND and DTK_HUNDRED... */
3437 case DTK_MILLENNIUM
:
3440 * truncating to the millennium? what is this supposed to
3441 * mean? let us put the first year of the millennium... i.e.
3442 * -1000, 1, 1001, 2001...
3444 if (tm
->tm_year
> 0)
3445 tm
->tm_year
= ((tm
->tm_year
+ 999) / 1000) * 1000 - 999;
3447 tm
->tm_year
= -((999 - (tm
->tm_year
- 1)) / 1000) * 1000 + 1;
3450 /* truncating to the century? as above: -100, 1, 101... */
3451 if (tm
->tm_year
> 0)
3452 tm
->tm_year
= ((tm
->tm_year
+ 99) / 100) * 100 - 99;
3454 tm
->tm_year
= -((99 - (tm
->tm_year
- 1)) / 100) * 100 + 1;
3459 * truncating to the decade? first year of the decade. must
3460 * not be applied if year was truncated before!
3462 if (val
!= DTK_MILLENNIUM
&& val
!= DTK_CENTURY
)
3464 if (tm
->tm_year
> 0)
3465 tm
->tm_year
= (tm
->tm_year
/ 10) * 10;
3467 tm
->tm_year
= -((8 - (tm
->tm_year
- 1)) / 10) * 10;
3474 tm
->tm_mon
= (3 * ((tm
->tm_mon
- 1) / 3)) + 1;
3481 redotz
= true; /* for all cases >= DAY */
3494 #ifdef HAVE_INT64_TIMESTAMP
3495 fsec
= (fsec
/ 1000) * 1000;
3497 fsec
= floor(fsec
* 1000) / 1000;
3501 #ifndef HAVE_INT64_TIMESTAMP
3502 fsec
= floor(fsec
* 1000000) / 1000000;
3508 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3509 errmsg("timestamp with time zone units \"%s\" not "
3510 "supported", lowunits
)));
3515 tz
= DetermineTimeZoneOffset(tm
, session_timezone
);
3517 if (tm2timestamp(tm
, fsec
, &tz
, &result
) != 0)
3519 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3520 errmsg("timestamp out of range")));
3525 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3526 errmsg("timestamp with time zone units \"%s\" not recognized",
3531 PG_RETURN_TIMESTAMPTZ(result
);
3535 * Extract specified field from interval.
3538 interval_trunc(PG_FUNCTION_ARGS
)
3540 text
*units
= PG_GETARG_TEXT_PP(0);
3541 Interval
*interval
= PG_GETARG_INTERVAL_P(1);
3550 result
= (Interval
*) palloc(sizeof(Interval
));
3552 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
3553 VARSIZE_ANY_EXHDR(units
),
3556 type
= DecodeUnits(0, lowunits
, &val
);
3560 if (interval2tm(*interval
, tm
, &fsec
) == 0)
3565 case DTK_MILLENNIUM
:
3566 /* caution: C division may have negative remainder */
3567 tm
->tm_year
= (tm
->tm_year
/ 1000) * 1000;
3569 /* caution: C division may have negative remainder */
3570 tm
->tm_year
= (tm
->tm_year
/ 100) * 100;
3572 /* caution: C division may have negative remainder */
3573 tm
->tm_year
= (tm
->tm_year
/ 10) * 10;
3577 tm
->tm_mon
= 3 * (tm
->tm_mon
/ 3);
3591 #ifdef HAVE_INT64_TIMESTAMP
3592 fsec
= (fsec
/ 1000) * 1000;
3594 fsec
= floor(fsec
* 1000) / 1000;
3598 #ifndef HAVE_INT64_TIMESTAMP
3599 fsec
= floor(fsec
* 1000000) / 1000000;
3605 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3606 errmsg("interval units \"%s\" not supported",
3610 if (tm2interval(tm
, fsec
, result
) != 0)
3612 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3613 errmsg("interval out of range")));
3616 elog(ERROR
, "could not convert interval to tm");
3621 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
3622 errmsg("interval units \"%s\" not recognized",
3626 PG_RETURN_INTERVAL_P(result
);
3631 * Return the Julian day which corresponds to the first day (Monday) of the given ISO 8601 year and week.
3632 * Julian days are used to convert between ISO week dates and Gregorian dates.
3635 isoweek2j(int year
, int week
)
3640 /* fourth day of current year */
3641 day4
= date2j(year
, 1, 4);
3643 /* day0 == offset to first day of week (Monday) */
3644 day0
= j2day(day4
- 1);
3646 return ((week
- 1) * 7) + (day4
- day0
);
3650 * Convert ISO week of year number to date.
3651 * The year field must be specified with the ISO year!
3655 isoweek2date(int woy
, int *year
, int *mon
, int *mday
)
3657 j2date(isoweek2j(*year
, woy
), year
, mon
, mday
);
3660 /* isoweekdate2date()
3662 * Convert an ISO 8601 week date (ISO year, ISO week and day of week) into a Gregorian date.
3663 * Populates year, mon, and mday with the correct Gregorian values.
3664 * year must be passed in as the ISO year.
3667 isoweekdate2date(int isoweek
, int isowday
, int *year
, int *mon
, int *mday
)
3671 jday
= isoweek2j(*year
, isoweek
);
3672 jday
+= isowday
- 1;
3674 j2date(jday
, year
, mon
, mday
);
3679 * Returns ISO week number of year.
3682 date2isoweek(int year
, int mon
, int mday
)
3690 dayn
= date2j(year
, mon
, mday
);
3692 /* fourth day of current year */
3693 day4
= date2j(year
, 1, 4);
3695 /* day0 == offset to first day of week (Monday) */
3696 day0
= j2day(day4
- 1);
3699 * We need the first week containing a Thursday, otherwise this day falls
3700 * into the previous year for purposes of counting weeks
3702 if (dayn
< day4
- day0
)
3704 day4
= date2j(year
- 1, 1, 4);
3706 /* day0 == offset to first day of week (Monday) */
3707 day0
= j2day(day4
- 1);
3710 result
= (dayn
- (day4
- day0
)) / 7 + 1;
3713 * Sometimes the last few days in a year will fall into the first week of
3714 * the next year, so check for this.
3718 day4
= date2j(year
+ 1, 1, 4);
3720 /* day0 == offset to first day of week (Monday) */
3721 day0
= j2day(day4
- 1);
3723 if (dayn
>= day4
- day0
)
3724 result
= (dayn
- (day4
- day0
)) / 7 + 1;
3727 return (int) result
;
3733 * Returns ISO 8601 year number.
3736 date2isoyear(int year
, int mon
, int mday
)
3744 dayn
= date2j(year
, mon
, mday
);
3746 /* fourth day of current year */
3747 day4
= date2j(year
, 1, 4);
3749 /* day0 == offset to first day of week (Monday) */
3750 day0
= j2day(day4
- 1);
3753 * We need the first week containing a Thursday, otherwise this day falls
3754 * into the previous year for purposes of counting weeks
3756 if (dayn
< day4
- day0
)
3758 day4
= date2j(year
- 1, 1, 4);
3760 /* day0 == offset to first day of week (Monday) */
3761 day0
= j2day(day4
- 1);
3766 result
= (dayn
- (day4
- day0
)) / 7 + 1;
3769 * Sometimes the last few days in a year will fall into the first week of
3770 * the next year, so check for this.
3774 day4
= date2j(year
+ 1, 1, 4);
3776 /* day0 == offset to first day of week (Monday) */
3777 day0
= j2day(day4
- 1);
3779 if (dayn
>= day4
- day0
)
3787 /* date2isoyearday()
3789 * Returns the ISO 8601 day-of-year, given a Gregorian year, month and day.
3790 * Possible return values are 1 through 371 (364 in non-leap years).
3793 date2isoyearday(int year
, int mon
, int mday
)
3795 return date2j(year
, mon
, mday
) - isoweek2j(date2isoyear(year
, mon
, mday
), 1) + 1;
3799 * Extract specified field from timestamp.
3802 timestamp_part(PG_FUNCTION_ARGS
)
3804 text
*units
= PG_GETARG_TEXT_PP(0);
3805 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
3814 if (TIMESTAMP_NOT_FINITE(timestamp
))
3817 PG_RETURN_FLOAT8(result
);
3820 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
3821 VARSIZE_ANY_EXHDR(units
),
3824 type
= DecodeUnits(0, lowunits
, &val
);
3825 if (type
== UNKNOWN_FIELD
)
3826 type
= DecodeSpecial(0, lowunits
, &val
);
3830 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
3832 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3833 errmsg("timestamp out of range")));
3838 #ifdef HAVE_INT64_TIMESTAMP
3839 result
= tm
->tm_sec
* 1000000.0 + fsec
;
3841 result
= (tm
->tm_sec
+ fsec
) * 1000000;
3846 #ifdef HAVE_INT64_TIMESTAMP
3847 result
= tm
->tm_sec
* 1000.0 + fsec
/ 1000.0;
3849 result
= (tm
->tm_sec
+ fsec
) * 1000;
3854 #ifdef HAVE_INT64_TIMESTAMP
3855 result
= tm
->tm_sec
+ fsec
/ 1000000.0;
3857 result
= tm
->tm_sec
+ fsec
;
3862 result
= tm
->tm_min
;
3866 result
= tm
->tm_hour
;
3870 result
= tm
->tm_mday
;
3874 result
= tm
->tm_mon
;
3878 result
= (tm
->tm_mon
- 1) / 3 + 1;
3882 result
= (float8
) date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3886 if (tm
->tm_year
> 0)
3887 result
= tm
->tm_year
;
3889 /* there is no year 0, just 1 BC and 1 AD */
3890 result
= tm
->tm_year
- 1;
3896 * what is a decade wrt dates? let us assume that decade 199
3897 * is 1990 thru 1999... decade 0 starts on year 1 BC, and -1
3898 * is 11 BC thru 2 BC...
3900 if (tm
->tm_year
>= 0)
3901 result
= tm
->tm_year
/ 10;
3903 result
= -((8 - (tm
->tm_year
- 1)) / 10);
3909 * centuries AD, c>0: year in [ (c-1)* 100 + 1 : c*100 ]
3910 * centuries BC, c<0: year in [ c*100 : (c+1) * 100 - 1]
3911 * there is no number 0 century.
3914 if (tm
->tm_year
> 0)
3915 result
= (tm
->tm_year
+ 99) / 100;
3917 /* caution: C division may have negative remainder */
3918 result
= -((99 - (tm
->tm_year
- 1)) / 100);
3921 case DTK_MILLENNIUM
:
3922 /* see comments above. */
3923 if (tm
->tm_year
> 0)
3924 result
= (tm
->tm_year
+ 999) / 1000;
3926 result
= -((999 - (tm
->tm_year
- 1)) / 1000);
3930 result
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3931 #ifdef HAVE_INT64_TIMESTAMP
3932 result
+= ((((tm
->tm_hour
* MINS_PER_HOUR
) + tm
->tm_min
) * SECS_PER_MINUTE
) +
3933 tm
->tm_sec
+ (fsec
/ 1000000.0)) / (double) SECS_PER_DAY
;
3935 result
+= ((((tm
->tm_hour
* MINS_PER_HOUR
) + tm
->tm_min
) * SECS_PER_MINUTE
) +
3936 tm
->tm_sec
+ fsec
) / (double) SECS_PER_DAY
;
3941 result
= date2isoyear(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
3949 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
3950 errmsg("timestamp units \"%s\" not supported",
3955 else if (type
== RESERV
)
3962 TimestampTz timestamptz
;
3965 * convert to timestamptz to produce consistent results
3967 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
3969 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3970 errmsg("timestamp out of range")));
3972 tz
= DetermineTimeZoneOffset(tm
, session_timezone
);
3974 if (tm2timestamp(tm
, fsec
, &tz
, ×tamptz
) != 0)
3976 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3977 errmsg("timestamp out of range")));
3979 #ifdef HAVE_INT64_TIMESTAMP
3980 result
= (timestamptz
- SetEpochTimestamp()) / 1000000.0;
3982 result
= timestamptz
- SetEpochTimestamp();
3988 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
3990 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
3991 errmsg("timestamp out of range")));
3992 result
= j2day(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
));
3993 if (val
== DTK_ISODOW
&& result
== 0)
3998 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
4000 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4001 errmsg("timestamp out of range")));
4002 result
= (date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
)
4003 - date2j(tm
->tm_year
, 1, 1) + 1);
4008 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4009 errmsg("timestamp units \"%s\" not supported",
4018 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4019 errmsg("timestamp units \"%s\" not recognized", lowunits
)));
4023 PG_RETURN_FLOAT8(result
);
4026 /* timestamptz_part()
4027 * Extract specified field from timestamp with time zone.
4030 timestamptz_part(PG_FUNCTION_ARGS
)
4032 text
*units
= PG_GETARG_TEXT_PP(0);
4033 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
4045 if (TIMESTAMP_NOT_FINITE(timestamp
))
4048 PG_RETURN_FLOAT8(result
);
4051 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
4052 VARSIZE_ANY_EXHDR(units
),
4055 type
= DecodeUnits(0, lowunits
, &val
);
4056 if (type
== UNKNOWN_FIELD
)
4057 type
= DecodeSpecial(0, lowunits
, &val
);
4061 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, &tzn
, NULL
) != 0)
4063 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4064 errmsg("timestamp out of range")));
4074 result
/= MINS_PER_HOUR
;
4075 FMODULO(result
, dummy
, (double) MINS_PER_HOUR
);
4080 FMODULO(dummy
, result
, (double) SECS_PER_HOUR
);
4084 #ifdef HAVE_INT64_TIMESTAMP
4085 result
= tm
->tm_sec
* 1000000.0 + fsec
;
4087 result
= (tm
->tm_sec
+ fsec
) * 1000000;
4092 #ifdef HAVE_INT64_TIMESTAMP
4093 result
= tm
->tm_sec
* 1000.0 + fsec
/ 1000.0;
4095 result
= (tm
->tm_sec
+ fsec
) * 1000;
4100 #ifdef HAVE_INT64_TIMESTAMP
4101 result
= tm
->tm_sec
+ fsec
/ 1000000.0;
4103 result
= tm
->tm_sec
+ fsec
;
4108 result
= tm
->tm_min
;
4112 result
= tm
->tm_hour
;
4116 result
= tm
->tm_mday
;
4120 result
= tm
->tm_mon
;
4124 result
= (tm
->tm_mon
- 1) / 3 + 1;
4128 result
= (float8
) date2isoweek(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
4132 if (tm
->tm_year
> 0)
4133 result
= tm
->tm_year
;
4135 /* there is no year 0, just 1 BC and 1 AD */
4136 result
= tm
->tm_year
- 1;
4140 /* see comments in timestamp_part */
4141 if (tm
->tm_year
> 0)
4142 result
= tm
->tm_year
/ 10;
4144 result
= -((8 - (tm
->tm_year
- 1)) / 10);
4148 /* see comments in timestamp_part */
4149 if (tm
->tm_year
> 0)
4150 result
= (tm
->tm_year
+ 99) / 100;
4152 result
= -((99 - (tm
->tm_year
- 1)) / 100);
4155 case DTK_MILLENNIUM
:
4156 /* see comments in timestamp_part */
4157 if (tm
->tm_year
> 0)
4158 result
= (tm
->tm_year
+ 999) / 1000;
4160 result
= -((999 - (tm
->tm_year
- 1)) / 1000);
4164 result
= date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
4165 #ifdef HAVE_INT64_TIMESTAMP
4166 result
+= ((((tm
->tm_hour
* MINS_PER_HOUR
) + tm
->tm_min
) * SECS_PER_MINUTE
) +
4167 tm
->tm_sec
+ (fsec
/ 1000000.0)) / (double) SECS_PER_DAY
;
4169 result
+= ((((tm
->tm_hour
* MINS_PER_HOUR
) + tm
->tm_min
) * SECS_PER_MINUTE
) +
4170 tm
->tm_sec
+ fsec
) / (double) SECS_PER_DAY
;
4175 result
= date2isoyear(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
4180 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4181 errmsg("timestamp with time zone units \"%s\" not supported",
4187 else if (type
== RESERV
)
4192 #ifdef HAVE_INT64_TIMESTAMP
4193 result
= (timestamp
- SetEpochTimestamp()) / 1000000.0;
4195 result
= timestamp
- SetEpochTimestamp();
4201 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, &tzn
, NULL
) != 0)
4203 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4204 errmsg("timestamp out of range")));
4205 result
= j2day(date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
));
4206 if (val
== DTK_ISODOW
&& result
== 0)
4211 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, &tzn
, NULL
) != 0)
4213 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4214 errmsg("timestamp out of range")));
4215 result
= (date2j(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
)
4216 - date2j(tm
->tm_year
, 1, 1) + 1);
4221 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4222 errmsg("timestamp with time zone units \"%s\" not supported",
4230 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4231 errmsg("timestamp with time zone units \"%s\" not recognized",
4237 PG_RETURN_FLOAT8(result
);
4242 * Extract specified field from interval.
4245 interval_part(PG_FUNCTION_ARGS
)
4247 text
*units
= PG_GETARG_TEXT_PP(0);
4248 Interval
*interval
= PG_GETARG_INTERVAL_P(1);
4257 lowunits
= downcase_truncate_identifier(VARDATA_ANY(units
),
4258 VARSIZE_ANY_EXHDR(units
),
4261 type
= DecodeUnits(0, lowunits
, &val
);
4262 if (type
== UNKNOWN_FIELD
)
4263 type
= DecodeSpecial(0, lowunits
, &val
);
4267 if (interval2tm(*interval
, tm
, &fsec
) == 0)
4272 #ifdef HAVE_INT64_TIMESTAMP
4273 result
= tm
->tm_sec
* 1000000.0 + fsec
;
4275 result
= (tm
->tm_sec
+ fsec
) * 1000000;
4280 #ifdef HAVE_INT64_TIMESTAMP
4281 result
= tm
->tm_sec
* 1000.0 + fsec
/ 1000.0;
4283 result
= (tm
->tm_sec
+ fsec
) * 1000;
4288 #ifdef HAVE_INT64_TIMESTAMP
4289 result
= tm
->tm_sec
+ fsec
/ 1000000.0;
4291 result
= tm
->tm_sec
+ fsec
;
4296 result
= tm
->tm_min
;
4300 result
= tm
->tm_hour
;
4304 result
= tm
->tm_mday
;
4308 result
= tm
->tm_mon
;
4312 result
= (tm
->tm_mon
/ 3) + 1;
4316 result
= tm
->tm_year
;
4320 /* caution: C division may have negative remainder */
4321 result
= tm
->tm_year
/ 10;
4325 /* caution: C division may have negative remainder */
4326 result
= tm
->tm_year
/ 100;
4329 case DTK_MILLENNIUM
:
4330 /* caution: C division may have negative remainder */
4331 result
= tm
->tm_year
/ 1000;
4336 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
4337 errmsg("interval units \"%s\" not supported",
4345 elog(ERROR
, "could not convert interval to tm");
4349 else if (type
== RESERV
&& val
== DTK_EPOCH
)
4351 #ifdef HAVE_INT64_TIMESTAMP
4352 result
= interval
->time
/ 1000000.0;
4354 result
= interval
->time
;
4356 result
+= ((double) DAYS_PER_YEAR
* SECS_PER_DAY
) * (interval
->month
/ MONTHS_PER_YEAR
);
4357 result
+= ((double) DAYS_PER_MONTH
* SECS_PER_DAY
) * (interval
->month
% MONTHS_PER_YEAR
);
4358 result
+= ((double) SECS_PER_DAY
) * interval
->day
;
4363 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4364 errmsg("interval units \"%s\" not recognized",
4369 PG_RETURN_FLOAT8(result
);
4374 * Encode timestamp type with specified time zone.
4375 * This function is just timestamp2timestamptz() except instead of
4376 * shifting to the global timezone, we shift to the specified timezone.
4377 * This is different from the other AT TIME ZONE cases because instead
4378 * of shifting to a _to_ a new time zone, it sets the time to _be_ the
4379 * specified timezone.
4382 timestamp_zone(PG_FUNCTION_ARGS
)
4384 text
*zone
= PG_GETARG_TEXT_PP(0);
4385 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
4388 char tzname
[TZ_STRLEN_MAX
+ 1];
4394 if (TIMESTAMP_NOT_FINITE(timestamp
))
4395 PG_RETURN_TIMESTAMPTZ(timestamp
);
4398 * Look up the requested timezone. First we look in the date token table
4399 * (to handle cases like "EST"), and if that fails, we look in the
4400 * timezone database (to handle cases like "America/New_York"). (This
4401 * matches the order in which timestamp input checks the cases; it's
4402 * important because the timezone database unwisely uses a few zone names
4403 * that are identical to offset abbreviations.)
4405 text_to_cstring_buffer(zone
, tzname
, sizeof(tzname
));
4406 lowzone
= downcase_truncate_identifier(tzname
,
4410 type
= DecodeSpecial(0, lowzone
, &val
);
4412 if (type
== TZ
|| type
== DTZ
)
4415 result
= dt2local(timestamp
, tz
);
4419 tzp
= pg_tzset(tzname
);
4422 /* Apply the timezone change */
4426 if (timestamp2tm(timestamp
, NULL
, &tm
, &fsec
, NULL
, tzp
) != 0)
4428 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4429 errmsg("timestamp out of range")));
4430 tz
= DetermineTimeZoneOffset(&tm
, tzp
);
4431 if (tm2timestamp(&tm
, fsec
, &tz
, &result
) != 0)
4433 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4434 errmsg("could not convert to time zone \"%s\"",
4440 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4441 errmsg("time zone \"%s\" not recognized", tzname
)));
4442 result
= 0; /* keep compiler quiet */
4446 PG_RETURN_TIMESTAMPTZ(result
);
4449 /* timestamp_izone()
4450 * Encode timestamp type with specified time interval as time zone.
4453 timestamp_izone(PG_FUNCTION_ARGS
)
4455 Interval
*zone
= PG_GETARG_INTERVAL_P(0);
4456 Timestamp timestamp
= PG_GETARG_TIMESTAMP(1);
4460 if (TIMESTAMP_NOT_FINITE(timestamp
))
4461 PG_RETURN_TIMESTAMPTZ(timestamp
);
4463 if (zone
->month
!= 0)
4465 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4466 errmsg("interval time zone \"%s\" must not specify month",
4467 DatumGetCString(DirectFunctionCall1(interval_out
,
4468 PointerGetDatum(zone
))))));
4470 #ifdef HAVE_INT64_TIMESTAMP
4471 tz
= zone
->time
/ USECS_PER_SEC
;
4476 result
= dt2local(timestamp
, tz
);
4478 PG_RETURN_TIMESTAMPTZ(result
);
4479 } /* timestamp_izone() */
4481 /* timestamp_timestamptz()
4482 * Convert local timestamp to timestamp at GMT
4485 timestamp_timestamptz(PG_FUNCTION_ARGS
)
4487 Timestamp timestamp
= PG_GETARG_TIMESTAMP(0);
4489 PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp
));
4493 timestamp2timestamptz(Timestamp timestamp
)
4501 if (TIMESTAMP_NOT_FINITE(timestamp
))
4505 if (timestamp2tm(timestamp
, NULL
, tm
, &fsec
, NULL
, NULL
) != 0)
4507 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4508 errmsg("timestamp out of range")));
4510 tz
= DetermineTimeZoneOffset(tm
, session_timezone
);
4512 if (tm2timestamp(tm
, fsec
, &tz
, &result
) != 0)
4514 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4515 errmsg("timestamp out of range")));
4521 /* timestamptz_timestamp()
4522 * Convert timestamp at GMT to local timestamp
4525 timestamptz_timestamp(PG_FUNCTION_ARGS
)
4527 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(0);
4535 if (TIMESTAMP_NOT_FINITE(timestamp
))
4539 if (timestamp2tm(timestamp
, &tz
, tm
, &fsec
, &tzn
, NULL
) != 0)
4541 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4542 errmsg("timestamp out of range")));
4543 if (tm2timestamp(tm
, fsec
, NULL
, &result
) != 0)
4545 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4546 errmsg("timestamp out of range")));
4548 PG_RETURN_TIMESTAMP(result
);
4551 /* timestamptz_zone()
4552 * Evaluate timestamp with time zone type at the specified time zone.
4553 * Returns a timestamp without time zone.
4556 timestamptz_zone(PG_FUNCTION_ARGS
)
4558 text
*zone
= PG_GETARG_TEXT_PP(0);
4559 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
4562 char tzname
[TZ_STRLEN_MAX
+ 1];
4568 if (TIMESTAMP_NOT_FINITE(timestamp
))
4569 PG_RETURN_TIMESTAMP(timestamp
);
4572 * Look up the requested timezone. First we look in the date token table
4573 * (to handle cases like "EST"), and if that fails, we look in the
4574 * timezone database (to handle cases like "America/New_York"). (This
4575 * matches the order in which timestamp input checks the cases; it's
4576 * important because the timezone database unwisely uses a few zone names
4577 * that are identical to offset abbreviations.)
4579 text_to_cstring_buffer(zone
, tzname
, sizeof(tzname
));
4580 lowzone
= downcase_truncate_identifier(tzname
,
4584 type
= DecodeSpecial(0, lowzone
, &val
);
4586 if (type
== TZ
|| type
== DTZ
)
4589 result
= dt2local(timestamp
, tz
);
4593 tzp
= pg_tzset(tzname
);
4596 /* Apply the timezone change */
4600 if (timestamp2tm(timestamp
, &tz
, &tm
, &fsec
, NULL
, tzp
) != 0)
4602 (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE
),
4603 errmsg("timestamp out of range")));
4604 if (tm2timestamp(&tm
, fsec
, NULL
, &result
) != 0)
4606 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4607 errmsg("could not convert to time zone \"%s\"",
4613 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4614 errmsg("time zone \"%s\" not recognized", tzname
)));
4615 result
= 0; /* keep compiler quiet */
4619 PG_RETURN_TIMESTAMP(result
);
4622 /* timestamptz_izone()
4623 * Encode timestamp with time zone type with specified time interval as time zone.
4624 * Returns a timestamp without time zone.
4627 timestamptz_izone(PG_FUNCTION_ARGS
)
4629 Interval
*zone
= PG_GETARG_INTERVAL_P(0);
4630 TimestampTz timestamp
= PG_GETARG_TIMESTAMPTZ(1);
4634 if (TIMESTAMP_NOT_FINITE(timestamp
))
4635 PG_RETURN_TIMESTAMP(timestamp
);
4637 if (zone
->month
!= 0)
4639 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4640 errmsg("interval time zone \"%s\" must not specify month",
4641 DatumGetCString(DirectFunctionCall1(interval_out
,
4642 PointerGetDatum(zone
))))));
4644 #ifdef HAVE_INT64_TIMESTAMP
4645 tz
= -(zone
->time
/ USECS_PER_SEC
);
4650 result
= dt2local(timestamp
, tz
);
4652 PG_RETURN_TIMESTAMP(result
);
4655 /* generate_series_timestamp()
4656 * Generate the set of timestamps from start to finish by step
4659 generate_series_timestamp(PG_FUNCTION_ARGS
)
4661 FuncCallContext
*funcctx
;
4662 generate_series_timestamp_fctx
*fctx
;
4665 /* stuff done only on the first call of the function */
4666 if (SRF_IS_FIRSTCALL())
4668 Timestamp start
= PG_GETARG_TIMESTAMP(0);
4669 Timestamp finish
= PG_GETARG_TIMESTAMP(1);
4670 Interval
*step
= PG_GETARG_INTERVAL_P(2);
4671 MemoryContext oldcontext
;
4672 Interval interval_zero
;
4674 /* create a function context for cross-call persistence */
4675 funcctx
= SRF_FIRSTCALL_INIT();
4678 * switch to memory context appropriate for multiple function calls
4680 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
4682 /* allocate memory for user context */
4683 fctx
= (generate_series_timestamp_fctx
*)
4684 palloc(sizeof(generate_series_timestamp_fctx
));
4687 * Use fctx to keep state from call to call. Seed current with the
4688 * original start value
4690 fctx
->current
= start
;
4691 fctx
->finish
= finish
;
4694 /* Determine sign of the interval */
4695 MemSet(&interval_zero
, 0, sizeof(Interval
));
4696 fctx
->step_sign
= interval_cmp_internal(&fctx
->step
, &interval_zero
);
4698 if (fctx
->step_sign
== 0)
4700 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4701 errmsg("step size cannot equal zero")));
4703 funcctx
->user_fctx
= fctx
;
4704 MemoryContextSwitchTo(oldcontext
);
4707 /* stuff done on every call of the function */
4708 funcctx
= SRF_PERCALL_SETUP();
4711 * get the saved state and use current as the result for this iteration
4713 fctx
= funcctx
->user_fctx
;
4714 result
= fctx
->current
;
4716 if (fctx
->step_sign
> 0 ?
4717 timestamp_cmp_internal(result
, fctx
->finish
) <= 0 :
4718 timestamp_cmp_internal(result
, fctx
->finish
) >= 0)
4720 /* increment current in preparation for next iteration */
4721 fctx
->current
= DatumGetTimestamp(
4722 DirectFunctionCall2(timestamp_pl_interval
,
4723 TimestampGetDatum(fctx
->current
),
4724 PointerGetDatum(&fctx
->step
)));
4726 /* do when there is more left to send */
4727 SRF_RETURN_NEXT(funcctx
, TimestampGetDatum(result
));
4731 /* do when there is no more left */
4732 SRF_RETURN_DONE(funcctx
);
4736 /* generate_series_timestamptz()
4737 * Generate the set of timestamps from start to finish by step
4740 generate_series_timestamptz(PG_FUNCTION_ARGS
)
4742 FuncCallContext
*funcctx
;
4743 generate_series_timestamptz_fctx
*fctx
;
4746 /* stuff done only on the first call of the function */
4747 if (SRF_IS_FIRSTCALL())
4749 TimestampTz start
= PG_GETARG_TIMESTAMPTZ(0);
4750 TimestampTz finish
= PG_GETARG_TIMESTAMPTZ(1);
4751 Interval
*step
= PG_GETARG_INTERVAL_P(2);
4752 MemoryContext oldcontext
;
4753 Interval interval_zero
;
4755 /* create a function context for cross-call persistence */
4756 funcctx
= SRF_FIRSTCALL_INIT();
4759 * switch to memory context appropriate for multiple function calls
4761 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
4763 /* allocate memory for user context */
4764 fctx
= (generate_series_timestamptz_fctx
*)
4765 palloc(sizeof(generate_series_timestamptz_fctx
));
4768 * Use fctx to keep state from call to call. Seed current with the
4769 * original start value
4771 fctx
->current
= start
;
4772 fctx
->finish
= finish
;
4775 /* Determine sign of the interval */
4776 MemSet(&interval_zero
, 0, sizeof(Interval
));
4777 fctx
->step_sign
= interval_cmp_internal(&fctx
->step
, &interval_zero
);
4779 if (fctx
->step_sign
== 0)
4781 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
4782 errmsg("step size cannot equal zero")));
4784 funcctx
->user_fctx
= fctx
;
4785 MemoryContextSwitchTo(oldcontext
);
4788 /* stuff done on every call of the function */
4789 funcctx
= SRF_PERCALL_SETUP();
4792 * get the saved state and use current as the result for this iteration
4794 fctx
= funcctx
->user_fctx
;
4795 result
= fctx
->current
;
4797 if (fctx
->step_sign
> 0 ?
4798 timestamp_cmp_internal(result
, fctx
->finish
) <= 0 :
4799 timestamp_cmp_internal(result
, fctx
->finish
) >= 0)
4801 /* increment current in preparation for next iteration */
4802 fctx
->current
= DatumGetTimestampTz(
4803 DirectFunctionCall2(timestamptz_pl_interval
,
4804 TimestampTzGetDatum(fctx
->current
),
4805 PointerGetDatum(&fctx
->step
)));
4807 /* do when there is more left to send */
4808 SRF_RETURN_NEXT(funcctx
, TimestampTzGetDatum(result
));
4812 /* do when there is no more left */
4813 SRF_RETURN_DONE(funcctx
);