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