1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * date.c |
4 | * implements DATE and TIME data types specified in SQL standard |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994-5, Regents of the University of California |
8 | * |
9 | * |
10 | * IDENTIFICATION |
11 | * src/backend/utils/adt/date.c |
12 | * |
13 | *------------------------------------------------------------------------- |
14 | */ |
15 | |
16 | #include "postgres.h" |
17 | |
18 | #include <ctype.h> |
19 | #include <limits.h> |
20 | #include <float.h> |
21 | #include <time.h> |
22 | |
23 | #include "access/xact.h" |
24 | #include "libpq/pqformat.h" |
25 | #include "miscadmin.h" |
26 | #include "nodes/supportnodes.h" |
27 | #include "parser/scansup.h" |
28 | #include "utils/array.h" |
29 | #include "utils/builtins.h" |
30 | #include "utils/date.h" |
31 | #include "utils/datetime.h" |
32 | #include "utils/hashutils.h" |
33 | #include "utils/sortsupport.h" |
34 | |
35 | /* |
36 | * gcc's -ffast-math switch breaks routines that expect exact results from |
37 | * expressions like timeval / SECS_PER_HOUR, where timeval is double. |
38 | */ |
39 | #ifdef __FAST_MATH__ |
40 | #error -ffast-math is known to break this code |
41 | #endif |
42 | |
43 | |
44 | static int tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result); |
45 | static int tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result); |
46 | static void AdjustTimeForTypmod(TimeADT *time, int32 typmod); |
47 | |
48 | |
49 | /* common code for timetypmodin and timetztypmodin */ |
50 | static int32 |
51 | anytime_typmodin(bool istz, ArrayType *ta) |
52 | { |
53 | int32 *tl; |
54 | int n; |
55 | |
56 | tl = ArrayGetIntegerTypmods(ta, &n); |
57 | |
58 | /* |
59 | * we're not too tense about good error message here because grammar |
60 | * shouldn't allow wrong number of modifiers for TIME |
61 | */ |
62 | if (n != 1) |
63 | ereport(ERROR, |
64 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
65 | errmsg("invalid type modifier" ))); |
66 | |
67 | return anytime_typmod_check(istz, tl[0]); |
68 | } |
69 | |
70 | /* exported so parse_expr.c can use it */ |
71 | int32 |
72 | anytime_typmod_check(bool istz, int32 typmod) |
73 | { |
74 | if (typmod < 0) |
75 | ereport(ERROR, |
76 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
77 | errmsg("TIME(%d)%s precision must not be negative" , |
78 | typmod, (istz ? " WITH TIME ZONE" : "" )))); |
79 | if (typmod > MAX_TIME_PRECISION) |
80 | { |
81 | ereport(WARNING, |
82 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
83 | errmsg("TIME(%d)%s precision reduced to maximum allowed, %d" , |
84 | typmod, (istz ? " WITH TIME ZONE" : "" ), |
85 | MAX_TIME_PRECISION))); |
86 | typmod = MAX_TIME_PRECISION; |
87 | } |
88 | |
89 | return typmod; |
90 | } |
91 | |
92 | /* common code for timetypmodout and timetztypmodout */ |
93 | static char * |
94 | anytime_typmodout(bool istz, int32 typmod) |
95 | { |
96 | const char *tz = istz ? " with time zone" : " without time zone" ; |
97 | |
98 | if (typmod >= 0) |
99 | return psprintf("(%d)%s" , (int) typmod, tz); |
100 | else |
101 | return psprintf("%s" , tz); |
102 | } |
103 | |
104 | |
105 | /***************************************************************************** |
106 | * Date ADT |
107 | *****************************************************************************/ |
108 | |
109 | |
110 | /* date_in() |
111 | * Given date text string, convert to internal date format. |
112 | */ |
113 | Datum |
114 | date_in(PG_FUNCTION_ARGS) |
115 | { |
116 | char *str = PG_GETARG_CSTRING(0); |
117 | DateADT date; |
118 | fsec_t fsec; |
119 | struct pg_tm tt, |
120 | *tm = &tt; |
121 | int tzp; |
122 | int dtype; |
123 | int nf; |
124 | int dterr; |
125 | char *field[MAXDATEFIELDS]; |
126 | int ftype[MAXDATEFIELDS]; |
127 | char workbuf[MAXDATELEN + 1]; |
128 | |
129 | dterr = ParseDateTime(str, workbuf, sizeof(workbuf), |
130 | field, ftype, MAXDATEFIELDS, &nf); |
131 | if (dterr == 0) |
132 | dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp); |
133 | if (dterr != 0) |
134 | DateTimeParseError(dterr, str, "date" ); |
135 | |
136 | switch (dtype) |
137 | { |
138 | case DTK_DATE: |
139 | break; |
140 | |
141 | case DTK_EPOCH: |
142 | GetEpochTime(tm); |
143 | break; |
144 | |
145 | case DTK_LATE: |
146 | DATE_NOEND(date); |
147 | PG_RETURN_DATEADT(date); |
148 | |
149 | case DTK_EARLY: |
150 | DATE_NOBEGIN(date); |
151 | PG_RETURN_DATEADT(date); |
152 | |
153 | default: |
154 | DateTimeParseError(DTERR_BAD_FORMAT, str, "date" ); |
155 | break; |
156 | } |
157 | |
158 | /* Prevent overflow in Julian-day routines */ |
159 | if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) |
160 | ereport(ERROR, |
161 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
162 | errmsg("date out of range: \"%s\"" , str))); |
163 | |
164 | date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; |
165 | |
166 | /* Now check for just-out-of-range dates */ |
167 | if (!IS_VALID_DATE(date)) |
168 | ereport(ERROR, |
169 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
170 | errmsg("date out of range: \"%s\"" , str))); |
171 | |
172 | PG_RETURN_DATEADT(date); |
173 | } |
174 | |
175 | /* date_out() |
176 | * Given internal format date, convert to text string. |
177 | */ |
178 | Datum |
179 | date_out(PG_FUNCTION_ARGS) |
180 | { |
181 | DateADT date = PG_GETARG_DATEADT(0); |
182 | char *result; |
183 | struct pg_tm tt, |
184 | *tm = &tt; |
185 | char buf[MAXDATELEN + 1]; |
186 | |
187 | if (DATE_NOT_FINITE(date)) |
188 | EncodeSpecialDate(date, buf); |
189 | else |
190 | { |
191 | j2date(date + POSTGRES_EPOCH_JDATE, |
192 | &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); |
193 | EncodeDateOnly(tm, DateStyle, buf); |
194 | } |
195 | |
196 | result = pstrdup(buf); |
197 | PG_RETURN_CSTRING(result); |
198 | } |
199 | |
200 | /* |
201 | * date_recv - converts external binary format to date |
202 | */ |
203 | Datum |
204 | date_recv(PG_FUNCTION_ARGS) |
205 | { |
206 | StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); |
207 | DateADT result; |
208 | |
209 | result = (DateADT) pq_getmsgint(buf, sizeof(DateADT)); |
210 | |
211 | /* Limit to the same range that date_in() accepts. */ |
212 | if (DATE_NOT_FINITE(result)) |
213 | /* ok */ ; |
214 | else if (!IS_VALID_DATE(result)) |
215 | ereport(ERROR, |
216 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
217 | errmsg("date out of range" ))); |
218 | |
219 | PG_RETURN_DATEADT(result); |
220 | } |
221 | |
222 | /* |
223 | * date_send - converts date to binary format |
224 | */ |
225 | Datum |
226 | date_send(PG_FUNCTION_ARGS) |
227 | { |
228 | DateADT date = PG_GETARG_DATEADT(0); |
229 | StringInfoData buf; |
230 | |
231 | pq_begintypsend(&buf); |
232 | pq_sendint32(&buf, date); |
233 | PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
234 | } |
235 | |
236 | /* |
237 | * make_date - date constructor |
238 | */ |
239 | Datum |
240 | make_date(PG_FUNCTION_ARGS) |
241 | { |
242 | struct pg_tm tm; |
243 | DateADT date; |
244 | int dterr; |
245 | bool bc = false; |
246 | |
247 | tm.tm_year = PG_GETARG_INT32(0); |
248 | tm.tm_mon = PG_GETARG_INT32(1); |
249 | tm.tm_mday = PG_GETARG_INT32(2); |
250 | |
251 | /* Handle negative years as BC */ |
252 | if (tm.tm_year < 0) |
253 | { |
254 | bc = true; |
255 | tm.tm_year = -tm.tm_year; |
256 | } |
257 | |
258 | dterr = ValidateDate(DTK_DATE_M, false, false, bc, &tm); |
259 | |
260 | if (dterr != 0) |
261 | ereport(ERROR, |
262 | (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), |
263 | errmsg("date field value out of range: %d-%02d-%02d" , |
264 | tm.tm_year, tm.tm_mon, tm.tm_mday))); |
265 | |
266 | /* Prevent overflow in Julian-day routines */ |
267 | if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday)) |
268 | ereport(ERROR, |
269 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
270 | errmsg("date out of range: %d-%02d-%02d" , |
271 | tm.tm_year, tm.tm_mon, tm.tm_mday))); |
272 | |
273 | date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE; |
274 | |
275 | /* Now check for just-out-of-range dates */ |
276 | if (!IS_VALID_DATE(date)) |
277 | ereport(ERROR, |
278 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
279 | errmsg("date out of range: %d-%02d-%02d" , |
280 | tm.tm_year, tm.tm_mon, tm.tm_mday))); |
281 | |
282 | PG_RETURN_DATEADT(date); |
283 | } |
284 | |
285 | /* |
286 | * Convert reserved date values to string. |
287 | */ |
288 | void |
289 | EncodeSpecialDate(DateADT dt, char *str) |
290 | { |
291 | if (DATE_IS_NOBEGIN(dt)) |
292 | strcpy(str, EARLY); |
293 | else if (DATE_IS_NOEND(dt)) |
294 | strcpy(str, LATE); |
295 | else /* shouldn't happen */ |
296 | elog(ERROR, "invalid argument for EncodeSpecialDate" ); |
297 | } |
298 | |
299 | |
300 | /* |
301 | * GetSQLCurrentDate -- implements CURRENT_DATE |
302 | */ |
303 | DateADT |
304 | GetSQLCurrentDate(void) |
305 | { |
306 | TimestampTz ts; |
307 | struct pg_tm tt, |
308 | *tm = &tt; |
309 | fsec_t fsec; |
310 | int tz; |
311 | |
312 | ts = GetCurrentTransactionStartTimestamp(); |
313 | |
314 | if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0) |
315 | ereport(ERROR, |
316 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
317 | errmsg("timestamp out of range" ))); |
318 | |
319 | return date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; |
320 | } |
321 | |
322 | /* |
323 | * GetSQLCurrentTime -- implements CURRENT_TIME, CURRENT_TIME(n) |
324 | */ |
325 | TimeTzADT * |
326 | GetSQLCurrentTime(int32 typmod) |
327 | { |
328 | TimeTzADT *result; |
329 | TimestampTz ts; |
330 | struct pg_tm tt, |
331 | *tm = &tt; |
332 | fsec_t fsec; |
333 | int tz; |
334 | |
335 | ts = GetCurrentTransactionStartTimestamp(); |
336 | |
337 | if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0) |
338 | ereport(ERROR, |
339 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
340 | errmsg("timestamp out of range" ))); |
341 | |
342 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
343 | tm2timetz(tm, fsec, tz, result); |
344 | AdjustTimeForTypmod(&(result->time), typmod); |
345 | return result; |
346 | } |
347 | |
348 | /* |
349 | * GetSQLLocalTime -- implements LOCALTIME, LOCALTIME(n) |
350 | */ |
351 | TimeADT |
352 | GetSQLLocalTime(int32 typmod) |
353 | { |
354 | TimeADT result; |
355 | TimestampTz ts; |
356 | struct pg_tm tt, |
357 | *tm = &tt; |
358 | fsec_t fsec; |
359 | int tz; |
360 | |
361 | ts = GetCurrentTransactionStartTimestamp(); |
362 | |
363 | if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0) |
364 | ereport(ERROR, |
365 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
366 | errmsg("timestamp out of range" ))); |
367 | |
368 | tm2time(tm, fsec, &result); |
369 | AdjustTimeForTypmod(&result, typmod); |
370 | return result; |
371 | } |
372 | |
373 | |
374 | /* |
375 | * Comparison functions for dates |
376 | */ |
377 | |
378 | Datum |
379 | date_eq(PG_FUNCTION_ARGS) |
380 | { |
381 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
382 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
383 | |
384 | PG_RETURN_BOOL(dateVal1 == dateVal2); |
385 | } |
386 | |
387 | Datum |
388 | date_ne(PG_FUNCTION_ARGS) |
389 | { |
390 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
391 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
392 | |
393 | PG_RETURN_BOOL(dateVal1 != dateVal2); |
394 | } |
395 | |
396 | Datum |
397 | date_lt(PG_FUNCTION_ARGS) |
398 | { |
399 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
400 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
401 | |
402 | PG_RETURN_BOOL(dateVal1 < dateVal2); |
403 | } |
404 | |
405 | Datum |
406 | date_le(PG_FUNCTION_ARGS) |
407 | { |
408 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
409 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
410 | |
411 | PG_RETURN_BOOL(dateVal1 <= dateVal2); |
412 | } |
413 | |
414 | Datum |
415 | date_gt(PG_FUNCTION_ARGS) |
416 | { |
417 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
418 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
419 | |
420 | PG_RETURN_BOOL(dateVal1 > dateVal2); |
421 | } |
422 | |
423 | Datum |
424 | date_ge(PG_FUNCTION_ARGS) |
425 | { |
426 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
427 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
428 | |
429 | PG_RETURN_BOOL(dateVal1 >= dateVal2); |
430 | } |
431 | |
432 | Datum |
433 | date_cmp(PG_FUNCTION_ARGS) |
434 | { |
435 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
436 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
437 | |
438 | if (dateVal1 < dateVal2) |
439 | PG_RETURN_INT32(-1); |
440 | else if (dateVal1 > dateVal2) |
441 | PG_RETURN_INT32(1); |
442 | PG_RETURN_INT32(0); |
443 | } |
444 | |
445 | static int |
446 | date_fastcmp(Datum x, Datum y, SortSupport ssup) |
447 | { |
448 | DateADT a = DatumGetDateADT(x); |
449 | DateADT b = DatumGetDateADT(y); |
450 | |
451 | if (a < b) |
452 | return -1; |
453 | else if (a > b) |
454 | return 1; |
455 | return 0; |
456 | } |
457 | |
458 | Datum |
459 | date_sortsupport(PG_FUNCTION_ARGS) |
460 | { |
461 | SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); |
462 | |
463 | ssup->comparator = date_fastcmp; |
464 | PG_RETURN_VOID(); |
465 | } |
466 | |
467 | Datum |
468 | date_finite(PG_FUNCTION_ARGS) |
469 | { |
470 | DateADT date = PG_GETARG_DATEADT(0); |
471 | |
472 | PG_RETURN_BOOL(!DATE_NOT_FINITE(date)); |
473 | } |
474 | |
475 | Datum |
476 | date_larger(PG_FUNCTION_ARGS) |
477 | { |
478 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
479 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
480 | |
481 | PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2); |
482 | } |
483 | |
484 | Datum |
485 | date_smaller(PG_FUNCTION_ARGS) |
486 | { |
487 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
488 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
489 | |
490 | PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2); |
491 | } |
492 | |
493 | /* Compute difference between two dates in days. |
494 | */ |
495 | Datum |
496 | date_mi(PG_FUNCTION_ARGS) |
497 | { |
498 | DateADT dateVal1 = PG_GETARG_DATEADT(0); |
499 | DateADT dateVal2 = PG_GETARG_DATEADT(1); |
500 | |
501 | if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2)) |
502 | ereport(ERROR, |
503 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
504 | errmsg("cannot subtract infinite dates" ))); |
505 | |
506 | PG_RETURN_INT32((int32) (dateVal1 - dateVal2)); |
507 | } |
508 | |
509 | /* Add a number of days to a date, giving a new date. |
510 | * Must handle both positive and negative numbers of days. |
511 | */ |
512 | Datum |
513 | date_pli(PG_FUNCTION_ARGS) |
514 | { |
515 | DateADT dateVal = PG_GETARG_DATEADT(0); |
516 | int32 days = PG_GETARG_INT32(1); |
517 | DateADT result; |
518 | |
519 | if (DATE_NOT_FINITE(dateVal)) |
520 | PG_RETURN_DATEADT(dateVal); /* can't change infinity */ |
521 | |
522 | result = dateVal + days; |
523 | |
524 | /* Check for integer overflow and out-of-allowed-range */ |
525 | if ((days >= 0 ? (result < dateVal) : (result > dateVal)) || |
526 | !IS_VALID_DATE(result)) |
527 | ereport(ERROR, |
528 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
529 | errmsg("date out of range" ))); |
530 | |
531 | PG_RETURN_DATEADT(result); |
532 | } |
533 | |
534 | /* Subtract a number of days from a date, giving a new date. |
535 | */ |
536 | Datum |
537 | date_mii(PG_FUNCTION_ARGS) |
538 | { |
539 | DateADT dateVal = PG_GETARG_DATEADT(0); |
540 | int32 days = PG_GETARG_INT32(1); |
541 | DateADT result; |
542 | |
543 | if (DATE_NOT_FINITE(dateVal)) |
544 | PG_RETURN_DATEADT(dateVal); /* can't change infinity */ |
545 | |
546 | result = dateVal - days; |
547 | |
548 | /* Check for integer overflow and out-of-allowed-range */ |
549 | if ((days >= 0 ? (result > dateVal) : (result < dateVal)) || |
550 | !IS_VALID_DATE(result)) |
551 | ereport(ERROR, |
552 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
553 | errmsg("date out of range" ))); |
554 | |
555 | PG_RETURN_DATEADT(result); |
556 | } |
557 | |
558 | /* |
559 | * Internal routines for promoting date to timestamp and timestamp with |
560 | * time zone |
561 | */ |
562 | |
563 | static Timestamp |
564 | date2timestamp(DateADT dateVal) |
565 | { |
566 | Timestamp result; |
567 | |
568 | if (DATE_IS_NOBEGIN(dateVal)) |
569 | TIMESTAMP_NOBEGIN(result); |
570 | else if (DATE_IS_NOEND(dateVal)) |
571 | TIMESTAMP_NOEND(result); |
572 | else |
573 | { |
574 | /* |
575 | * Date's range is wider than timestamp's, so check for boundaries. |
576 | * Since dates have the same minimum values as timestamps, only upper |
577 | * boundary need be checked for overflow. |
578 | */ |
579 | if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) |
580 | ereport(ERROR, |
581 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
582 | errmsg("date out of range for timestamp" ))); |
583 | |
584 | /* date is days since 2000, timestamp is microseconds since same... */ |
585 | result = dateVal * USECS_PER_DAY; |
586 | } |
587 | |
588 | return result; |
589 | } |
590 | |
591 | static TimestampTz |
592 | date2timestamptz(DateADT dateVal) |
593 | { |
594 | TimestampTz result; |
595 | struct pg_tm tt, |
596 | *tm = &tt; |
597 | int tz; |
598 | |
599 | if (DATE_IS_NOBEGIN(dateVal)) |
600 | TIMESTAMP_NOBEGIN(result); |
601 | else if (DATE_IS_NOEND(dateVal)) |
602 | TIMESTAMP_NOEND(result); |
603 | else |
604 | { |
605 | /* |
606 | * Date's range is wider than timestamp's, so check for boundaries. |
607 | * Since dates have the same minimum values as timestamps, only upper |
608 | * boundary need be checked for overflow. |
609 | */ |
610 | if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) |
611 | ereport(ERROR, |
612 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
613 | errmsg("date out of range for timestamp" ))); |
614 | |
615 | j2date(dateVal + POSTGRES_EPOCH_JDATE, |
616 | &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); |
617 | tm->tm_hour = 0; |
618 | tm->tm_min = 0; |
619 | tm->tm_sec = 0; |
620 | tz = DetermineTimeZoneOffset(tm, session_timezone); |
621 | |
622 | result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC; |
623 | |
624 | /* |
625 | * Since it is possible to go beyond allowed timestamptz range because |
626 | * of time zone, check for allowed timestamp range after adding tz. |
627 | */ |
628 | if (!IS_VALID_TIMESTAMP(result)) |
629 | ereport(ERROR, |
630 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
631 | errmsg("date out of range for timestamp" ))); |
632 | } |
633 | |
634 | return result; |
635 | } |
636 | |
637 | /* |
638 | * date2timestamp_no_overflow |
639 | * |
640 | * This is chartered to produce a double value that is numerically |
641 | * equivalent to the corresponding Timestamp value, if the date is in the |
642 | * valid range of Timestamps, but in any case not throw an overflow error. |
643 | * We can do this since the numerical range of double is greater than |
644 | * that of non-erroneous timestamps. The results are currently only |
645 | * used for statistical estimation purposes. |
646 | */ |
647 | double |
648 | date2timestamp_no_overflow(DateADT dateVal) |
649 | { |
650 | double result; |
651 | |
652 | if (DATE_IS_NOBEGIN(dateVal)) |
653 | result = -DBL_MAX; |
654 | else if (DATE_IS_NOEND(dateVal)) |
655 | result = DBL_MAX; |
656 | else |
657 | { |
658 | /* date is days since 2000, timestamp is microseconds since same... */ |
659 | result = dateVal * (double) USECS_PER_DAY; |
660 | } |
661 | |
662 | return result; |
663 | } |
664 | |
665 | |
666 | /* |
667 | * Crosstype comparison functions for dates |
668 | */ |
669 | |
670 | Datum |
671 | date_eq_timestamp(PG_FUNCTION_ARGS) |
672 | { |
673 | DateADT dateVal = PG_GETARG_DATEADT(0); |
674 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
675 | Timestamp dt1; |
676 | |
677 | dt1 = date2timestamp(dateVal); |
678 | |
679 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0); |
680 | } |
681 | |
682 | Datum |
683 | date_ne_timestamp(PG_FUNCTION_ARGS) |
684 | { |
685 | DateADT dateVal = PG_GETARG_DATEADT(0); |
686 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
687 | Timestamp dt1; |
688 | |
689 | dt1 = date2timestamp(dateVal); |
690 | |
691 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0); |
692 | } |
693 | |
694 | Datum |
695 | date_lt_timestamp(PG_FUNCTION_ARGS) |
696 | { |
697 | DateADT dateVal = PG_GETARG_DATEADT(0); |
698 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
699 | Timestamp dt1; |
700 | |
701 | dt1 = date2timestamp(dateVal); |
702 | |
703 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0); |
704 | } |
705 | |
706 | Datum |
707 | date_gt_timestamp(PG_FUNCTION_ARGS) |
708 | { |
709 | DateADT dateVal = PG_GETARG_DATEADT(0); |
710 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
711 | Timestamp dt1; |
712 | |
713 | dt1 = date2timestamp(dateVal); |
714 | |
715 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0); |
716 | } |
717 | |
718 | Datum |
719 | date_le_timestamp(PG_FUNCTION_ARGS) |
720 | { |
721 | DateADT dateVal = PG_GETARG_DATEADT(0); |
722 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
723 | Timestamp dt1; |
724 | |
725 | dt1 = date2timestamp(dateVal); |
726 | |
727 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0); |
728 | } |
729 | |
730 | Datum |
731 | date_ge_timestamp(PG_FUNCTION_ARGS) |
732 | { |
733 | DateADT dateVal = PG_GETARG_DATEADT(0); |
734 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
735 | Timestamp dt1; |
736 | |
737 | dt1 = date2timestamp(dateVal); |
738 | |
739 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0); |
740 | } |
741 | |
742 | Datum |
743 | date_cmp_timestamp(PG_FUNCTION_ARGS) |
744 | { |
745 | DateADT dateVal = PG_GETARG_DATEADT(0); |
746 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
747 | Timestamp dt1; |
748 | |
749 | dt1 = date2timestamp(dateVal); |
750 | |
751 | PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2)); |
752 | } |
753 | |
754 | Datum |
755 | date_eq_timestamptz(PG_FUNCTION_ARGS) |
756 | { |
757 | DateADT dateVal = PG_GETARG_DATEADT(0); |
758 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
759 | TimestampTz dt1; |
760 | |
761 | dt1 = date2timestamptz(dateVal); |
762 | |
763 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0); |
764 | } |
765 | |
766 | Datum |
767 | date_ne_timestamptz(PG_FUNCTION_ARGS) |
768 | { |
769 | DateADT dateVal = PG_GETARG_DATEADT(0); |
770 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
771 | TimestampTz dt1; |
772 | |
773 | dt1 = date2timestamptz(dateVal); |
774 | |
775 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0); |
776 | } |
777 | |
778 | Datum |
779 | date_lt_timestamptz(PG_FUNCTION_ARGS) |
780 | { |
781 | DateADT dateVal = PG_GETARG_DATEADT(0); |
782 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
783 | TimestampTz dt1; |
784 | |
785 | dt1 = date2timestamptz(dateVal); |
786 | |
787 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0); |
788 | } |
789 | |
790 | Datum |
791 | date_gt_timestamptz(PG_FUNCTION_ARGS) |
792 | { |
793 | DateADT dateVal = PG_GETARG_DATEADT(0); |
794 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
795 | TimestampTz dt1; |
796 | |
797 | dt1 = date2timestamptz(dateVal); |
798 | |
799 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0); |
800 | } |
801 | |
802 | Datum |
803 | date_le_timestamptz(PG_FUNCTION_ARGS) |
804 | { |
805 | DateADT dateVal = PG_GETARG_DATEADT(0); |
806 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
807 | TimestampTz dt1; |
808 | |
809 | dt1 = date2timestamptz(dateVal); |
810 | |
811 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0); |
812 | } |
813 | |
814 | Datum |
815 | date_ge_timestamptz(PG_FUNCTION_ARGS) |
816 | { |
817 | DateADT dateVal = PG_GETARG_DATEADT(0); |
818 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
819 | TimestampTz dt1; |
820 | |
821 | dt1 = date2timestamptz(dateVal); |
822 | |
823 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0); |
824 | } |
825 | |
826 | Datum |
827 | date_cmp_timestamptz(PG_FUNCTION_ARGS) |
828 | { |
829 | DateADT dateVal = PG_GETARG_DATEADT(0); |
830 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
831 | TimestampTz dt1; |
832 | |
833 | dt1 = date2timestamptz(dateVal); |
834 | |
835 | PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2)); |
836 | } |
837 | |
838 | Datum |
839 | timestamp_eq_date(PG_FUNCTION_ARGS) |
840 | { |
841 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
842 | DateADT dateVal = PG_GETARG_DATEADT(1); |
843 | Timestamp dt2; |
844 | |
845 | dt2 = date2timestamp(dateVal); |
846 | |
847 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0); |
848 | } |
849 | |
850 | Datum |
851 | timestamp_ne_date(PG_FUNCTION_ARGS) |
852 | { |
853 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
854 | DateADT dateVal = PG_GETARG_DATEADT(1); |
855 | Timestamp dt2; |
856 | |
857 | dt2 = date2timestamp(dateVal); |
858 | |
859 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0); |
860 | } |
861 | |
862 | Datum |
863 | timestamp_lt_date(PG_FUNCTION_ARGS) |
864 | { |
865 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
866 | DateADT dateVal = PG_GETARG_DATEADT(1); |
867 | Timestamp dt2; |
868 | |
869 | dt2 = date2timestamp(dateVal); |
870 | |
871 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0); |
872 | } |
873 | |
874 | Datum |
875 | timestamp_gt_date(PG_FUNCTION_ARGS) |
876 | { |
877 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
878 | DateADT dateVal = PG_GETARG_DATEADT(1); |
879 | Timestamp dt2; |
880 | |
881 | dt2 = date2timestamp(dateVal); |
882 | |
883 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0); |
884 | } |
885 | |
886 | Datum |
887 | timestamp_le_date(PG_FUNCTION_ARGS) |
888 | { |
889 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
890 | DateADT dateVal = PG_GETARG_DATEADT(1); |
891 | Timestamp dt2; |
892 | |
893 | dt2 = date2timestamp(dateVal); |
894 | |
895 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0); |
896 | } |
897 | |
898 | Datum |
899 | timestamp_ge_date(PG_FUNCTION_ARGS) |
900 | { |
901 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
902 | DateADT dateVal = PG_GETARG_DATEADT(1); |
903 | Timestamp dt2; |
904 | |
905 | dt2 = date2timestamp(dateVal); |
906 | |
907 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0); |
908 | } |
909 | |
910 | Datum |
911 | timestamp_cmp_date(PG_FUNCTION_ARGS) |
912 | { |
913 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
914 | DateADT dateVal = PG_GETARG_DATEADT(1); |
915 | Timestamp dt2; |
916 | |
917 | dt2 = date2timestamp(dateVal); |
918 | |
919 | PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2)); |
920 | } |
921 | |
922 | Datum |
923 | timestamptz_eq_date(PG_FUNCTION_ARGS) |
924 | { |
925 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
926 | DateADT dateVal = PG_GETARG_DATEADT(1); |
927 | TimestampTz dt2; |
928 | |
929 | dt2 = date2timestamptz(dateVal); |
930 | |
931 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0); |
932 | } |
933 | |
934 | Datum |
935 | timestamptz_ne_date(PG_FUNCTION_ARGS) |
936 | { |
937 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
938 | DateADT dateVal = PG_GETARG_DATEADT(1); |
939 | TimestampTz dt2; |
940 | |
941 | dt2 = date2timestamptz(dateVal); |
942 | |
943 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0); |
944 | } |
945 | |
946 | Datum |
947 | timestamptz_lt_date(PG_FUNCTION_ARGS) |
948 | { |
949 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
950 | DateADT dateVal = PG_GETARG_DATEADT(1); |
951 | TimestampTz dt2; |
952 | |
953 | dt2 = date2timestamptz(dateVal); |
954 | |
955 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0); |
956 | } |
957 | |
958 | Datum |
959 | timestamptz_gt_date(PG_FUNCTION_ARGS) |
960 | { |
961 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
962 | DateADT dateVal = PG_GETARG_DATEADT(1); |
963 | TimestampTz dt2; |
964 | |
965 | dt2 = date2timestamptz(dateVal); |
966 | |
967 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0); |
968 | } |
969 | |
970 | Datum |
971 | timestamptz_le_date(PG_FUNCTION_ARGS) |
972 | { |
973 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
974 | DateADT dateVal = PG_GETARG_DATEADT(1); |
975 | TimestampTz dt2; |
976 | |
977 | dt2 = date2timestamptz(dateVal); |
978 | |
979 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0); |
980 | } |
981 | |
982 | Datum |
983 | timestamptz_ge_date(PG_FUNCTION_ARGS) |
984 | { |
985 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
986 | DateADT dateVal = PG_GETARG_DATEADT(1); |
987 | TimestampTz dt2; |
988 | |
989 | dt2 = date2timestamptz(dateVal); |
990 | |
991 | PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0); |
992 | } |
993 | |
994 | Datum |
995 | timestamptz_cmp_date(PG_FUNCTION_ARGS) |
996 | { |
997 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
998 | DateADT dateVal = PG_GETARG_DATEADT(1); |
999 | TimestampTz dt2; |
1000 | |
1001 | dt2 = date2timestamptz(dateVal); |
1002 | |
1003 | PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2)); |
1004 | } |
1005 | |
1006 | /* |
1007 | * in_range support function for date. |
1008 | * |
1009 | * We implement this by promoting the dates to timestamp (without time zone) |
1010 | * and then using the timestamp-and-interval in_range function. |
1011 | */ |
1012 | Datum |
1013 | in_range_date_interval(PG_FUNCTION_ARGS) |
1014 | { |
1015 | DateADT val = PG_GETARG_DATEADT(0); |
1016 | DateADT base = PG_GETARG_DATEADT(1); |
1017 | Interval *offset = PG_GETARG_INTERVAL_P(2); |
1018 | bool sub = PG_GETARG_BOOL(3); |
1019 | bool less = PG_GETARG_BOOL(4); |
1020 | Timestamp valStamp; |
1021 | Timestamp baseStamp; |
1022 | |
1023 | valStamp = date2timestamp(val); |
1024 | baseStamp = date2timestamp(base); |
1025 | |
1026 | return DirectFunctionCall5(in_range_timestamp_interval, |
1027 | TimestampGetDatum(valStamp), |
1028 | TimestampGetDatum(baseStamp), |
1029 | IntervalPGetDatum(offset), |
1030 | BoolGetDatum(sub), |
1031 | BoolGetDatum(less)); |
1032 | } |
1033 | |
1034 | |
1035 | /* Add an interval to a date, giving a new date. |
1036 | * Must handle both positive and negative intervals. |
1037 | * |
1038 | * We implement this by promoting the date to timestamp (without time zone) |
1039 | * and then using the timestamp plus interval function. |
1040 | */ |
1041 | Datum |
1042 | date_pl_interval(PG_FUNCTION_ARGS) |
1043 | { |
1044 | DateADT dateVal = PG_GETARG_DATEADT(0); |
1045 | Interval *span = PG_GETARG_INTERVAL_P(1); |
1046 | Timestamp dateStamp; |
1047 | |
1048 | dateStamp = date2timestamp(dateVal); |
1049 | |
1050 | return DirectFunctionCall2(timestamp_pl_interval, |
1051 | TimestampGetDatum(dateStamp), |
1052 | PointerGetDatum(span)); |
1053 | } |
1054 | |
1055 | /* Subtract an interval from a date, giving a new date. |
1056 | * Must handle both positive and negative intervals. |
1057 | * |
1058 | * We implement this by promoting the date to timestamp (without time zone) |
1059 | * and then using the timestamp minus interval function. |
1060 | */ |
1061 | Datum |
1062 | date_mi_interval(PG_FUNCTION_ARGS) |
1063 | { |
1064 | DateADT dateVal = PG_GETARG_DATEADT(0); |
1065 | Interval *span = PG_GETARG_INTERVAL_P(1); |
1066 | Timestamp dateStamp; |
1067 | |
1068 | dateStamp = date2timestamp(dateVal); |
1069 | |
1070 | return DirectFunctionCall2(timestamp_mi_interval, |
1071 | TimestampGetDatum(dateStamp), |
1072 | PointerGetDatum(span)); |
1073 | } |
1074 | |
1075 | /* date_timestamp() |
1076 | * Convert date to timestamp data type. |
1077 | */ |
1078 | Datum |
1079 | date_timestamp(PG_FUNCTION_ARGS) |
1080 | { |
1081 | DateADT dateVal = PG_GETARG_DATEADT(0); |
1082 | Timestamp result; |
1083 | |
1084 | result = date2timestamp(dateVal); |
1085 | |
1086 | PG_RETURN_TIMESTAMP(result); |
1087 | } |
1088 | |
1089 | /* timestamp_date() |
1090 | * Convert timestamp to date data type. |
1091 | */ |
1092 | Datum |
1093 | timestamp_date(PG_FUNCTION_ARGS) |
1094 | { |
1095 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
1096 | DateADT result; |
1097 | struct pg_tm tt, |
1098 | *tm = &tt; |
1099 | fsec_t fsec; |
1100 | |
1101 | if (TIMESTAMP_IS_NOBEGIN(timestamp)) |
1102 | DATE_NOBEGIN(result); |
1103 | else if (TIMESTAMP_IS_NOEND(timestamp)) |
1104 | DATE_NOEND(result); |
1105 | else |
1106 | { |
1107 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
1108 | ereport(ERROR, |
1109 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
1110 | errmsg("timestamp out of range" ))); |
1111 | |
1112 | result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; |
1113 | } |
1114 | |
1115 | PG_RETURN_DATEADT(result); |
1116 | } |
1117 | |
1118 | |
1119 | /* date_timestamptz() |
1120 | * Convert date to timestamp with time zone data type. |
1121 | */ |
1122 | Datum |
1123 | date_timestamptz(PG_FUNCTION_ARGS) |
1124 | { |
1125 | DateADT dateVal = PG_GETARG_DATEADT(0); |
1126 | TimestampTz result; |
1127 | |
1128 | result = date2timestamptz(dateVal); |
1129 | |
1130 | PG_RETURN_TIMESTAMP(result); |
1131 | } |
1132 | |
1133 | |
1134 | /* timestamptz_date() |
1135 | * Convert timestamp with time zone to date data type. |
1136 | */ |
1137 | Datum |
1138 | timestamptz_date(PG_FUNCTION_ARGS) |
1139 | { |
1140 | TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); |
1141 | DateADT result; |
1142 | struct pg_tm tt, |
1143 | *tm = &tt; |
1144 | fsec_t fsec; |
1145 | int tz; |
1146 | |
1147 | if (TIMESTAMP_IS_NOBEGIN(timestamp)) |
1148 | DATE_NOBEGIN(result); |
1149 | else if (TIMESTAMP_IS_NOEND(timestamp)) |
1150 | DATE_NOEND(result); |
1151 | else |
1152 | { |
1153 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
1154 | ereport(ERROR, |
1155 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
1156 | errmsg("timestamp out of range" ))); |
1157 | |
1158 | result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; |
1159 | } |
1160 | |
1161 | PG_RETURN_DATEADT(result); |
1162 | } |
1163 | |
1164 | |
1165 | /***************************************************************************** |
1166 | * Time ADT |
1167 | *****************************************************************************/ |
1168 | |
1169 | Datum |
1170 | time_in(PG_FUNCTION_ARGS) |
1171 | { |
1172 | char *str = PG_GETARG_CSTRING(0); |
1173 | |
1174 | #ifdef NOT_USED |
1175 | Oid typelem = PG_GETARG_OID(1); |
1176 | #endif |
1177 | int32 typmod = PG_GETARG_INT32(2); |
1178 | TimeADT result; |
1179 | fsec_t fsec; |
1180 | struct pg_tm tt, |
1181 | *tm = &tt; |
1182 | int tz; |
1183 | int nf; |
1184 | int dterr; |
1185 | char workbuf[MAXDATELEN + 1]; |
1186 | char *field[MAXDATEFIELDS]; |
1187 | int dtype; |
1188 | int ftype[MAXDATEFIELDS]; |
1189 | |
1190 | dterr = ParseDateTime(str, workbuf, sizeof(workbuf), |
1191 | field, ftype, MAXDATEFIELDS, &nf); |
1192 | if (dterr == 0) |
1193 | dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz); |
1194 | if (dterr != 0) |
1195 | DateTimeParseError(dterr, str, "time" ); |
1196 | |
1197 | tm2time(tm, fsec, &result); |
1198 | AdjustTimeForTypmod(&result, typmod); |
1199 | |
1200 | PG_RETURN_TIMEADT(result); |
1201 | } |
1202 | |
1203 | /* tm2time() |
1204 | * Convert a tm structure to a time data type. |
1205 | */ |
1206 | static int |
1207 | tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result) |
1208 | { |
1209 | *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) |
1210 | * USECS_PER_SEC) + fsec; |
1211 | return 0; |
1212 | } |
1213 | |
1214 | /* time2tm() |
1215 | * Convert time data type to POSIX time structure. |
1216 | * |
1217 | * For dates within the range of pg_time_t, convert to the local time zone. |
1218 | * If out of this range, leave as UTC (in practice that could only happen |
1219 | * if pg_time_t is just 32 bits) - thomas 97/05/27 |
1220 | */ |
1221 | int |
1222 | time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec) |
1223 | { |
1224 | tm->tm_hour = time / USECS_PER_HOUR; |
1225 | time -= tm->tm_hour * USECS_PER_HOUR; |
1226 | tm->tm_min = time / USECS_PER_MINUTE; |
1227 | time -= tm->tm_min * USECS_PER_MINUTE; |
1228 | tm->tm_sec = time / USECS_PER_SEC; |
1229 | time -= tm->tm_sec * USECS_PER_SEC; |
1230 | *fsec = time; |
1231 | return 0; |
1232 | } |
1233 | |
1234 | Datum |
1235 | time_out(PG_FUNCTION_ARGS) |
1236 | { |
1237 | TimeADT time = PG_GETARG_TIMEADT(0); |
1238 | char *result; |
1239 | struct pg_tm tt, |
1240 | *tm = &tt; |
1241 | fsec_t fsec; |
1242 | char buf[MAXDATELEN + 1]; |
1243 | |
1244 | time2tm(time, tm, &fsec); |
1245 | EncodeTimeOnly(tm, fsec, false, 0, DateStyle, buf); |
1246 | |
1247 | result = pstrdup(buf); |
1248 | PG_RETURN_CSTRING(result); |
1249 | } |
1250 | |
1251 | /* |
1252 | * time_recv - converts external binary format to time |
1253 | */ |
1254 | Datum |
1255 | time_recv(PG_FUNCTION_ARGS) |
1256 | { |
1257 | StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); |
1258 | |
1259 | #ifdef NOT_USED |
1260 | Oid typelem = PG_GETARG_OID(1); |
1261 | #endif |
1262 | int32 typmod = PG_GETARG_INT32(2); |
1263 | TimeADT result; |
1264 | |
1265 | result = pq_getmsgint64(buf); |
1266 | |
1267 | if (result < INT64CONST(0) || result > USECS_PER_DAY) |
1268 | ereport(ERROR, |
1269 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
1270 | errmsg("time out of range" ))); |
1271 | |
1272 | AdjustTimeForTypmod(&result, typmod); |
1273 | |
1274 | PG_RETURN_TIMEADT(result); |
1275 | } |
1276 | |
1277 | /* |
1278 | * time_send - converts time to binary format |
1279 | */ |
1280 | Datum |
1281 | time_send(PG_FUNCTION_ARGS) |
1282 | { |
1283 | TimeADT time = PG_GETARG_TIMEADT(0); |
1284 | StringInfoData buf; |
1285 | |
1286 | pq_begintypsend(&buf); |
1287 | pq_sendint64(&buf, time); |
1288 | PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
1289 | } |
1290 | |
1291 | Datum |
1292 | timetypmodin(PG_FUNCTION_ARGS) |
1293 | { |
1294 | ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); |
1295 | |
1296 | PG_RETURN_INT32(anytime_typmodin(false, ta)); |
1297 | } |
1298 | |
1299 | Datum |
1300 | timetypmodout(PG_FUNCTION_ARGS) |
1301 | { |
1302 | int32 typmod = PG_GETARG_INT32(0); |
1303 | |
1304 | PG_RETURN_CSTRING(anytime_typmodout(false, typmod)); |
1305 | } |
1306 | |
1307 | /* |
1308 | * make_time - time constructor |
1309 | */ |
1310 | Datum |
1311 | make_time(PG_FUNCTION_ARGS) |
1312 | { |
1313 | int tm_hour = PG_GETARG_INT32(0); |
1314 | int tm_min = PG_GETARG_INT32(1); |
1315 | double sec = PG_GETARG_FLOAT8(2); |
1316 | TimeADT time; |
1317 | |
1318 | /* This should match the checks in DecodeTimeOnly */ |
1319 | if (tm_hour < 0 || tm_min < 0 || tm_min > MINS_PER_HOUR - 1 || |
1320 | sec < 0 || sec > SECS_PER_MINUTE || |
1321 | tm_hour > HOURS_PER_DAY || |
1322 | /* test for > 24:00:00 */ |
1323 | (tm_hour == HOURS_PER_DAY && (tm_min > 0 || sec > 0))) |
1324 | ereport(ERROR, |
1325 | (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), |
1326 | errmsg("time field value out of range: %d:%02d:%02g" , |
1327 | tm_hour, tm_min, sec))); |
1328 | |
1329 | /* This should match tm2time */ |
1330 | time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE) |
1331 | * USECS_PER_SEC) + rint(sec * USECS_PER_SEC); |
1332 | |
1333 | PG_RETURN_TIMEADT(time); |
1334 | } |
1335 | |
1336 | |
1337 | /* time_support() |
1338 | * |
1339 | * Planner support function for the time_scale() and timetz_scale() |
1340 | * length coercion functions (we need not distinguish them here). |
1341 | */ |
1342 | Datum |
1343 | time_support(PG_FUNCTION_ARGS) |
1344 | { |
1345 | Node *rawreq = (Node *) PG_GETARG_POINTER(0); |
1346 | Node *ret = NULL; |
1347 | |
1348 | if (IsA(rawreq, SupportRequestSimplify)) |
1349 | { |
1350 | SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; |
1351 | |
1352 | ret = TemporalSimplify(MAX_TIME_PRECISION, (Node *) req->fcall); |
1353 | } |
1354 | |
1355 | PG_RETURN_POINTER(ret); |
1356 | } |
1357 | |
1358 | /* time_scale() |
1359 | * Adjust time type for specified scale factor. |
1360 | * Used by PostgreSQL type system to stuff columns. |
1361 | */ |
1362 | Datum |
1363 | time_scale(PG_FUNCTION_ARGS) |
1364 | { |
1365 | TimeADT time = PG_GETARG_TIMEADT(0); |
1366 | int32 typmod = PG_GETARG_INT32(1); |
1367 | TimeADT result; |
1368 | |
1369 | result = time; |
1370 | AdjustTimeForTypmod(&result, typmod); |
1371 | |
1372 | PG_RETURN_TIMEADT(result); |
1373 | } |
1374 | |
1375 | /* AdjustTimeForTypmod() |
1376 | * Force the precision of the time value to a specified value. |
1377 | * Uses *exactly* the same code as in AdjustTimestampForTypmod() |
1378 | * but we make a separate copy because those types do not |
1379 | * have a fundamental tie together but rather a coincidence of |
1380 | * implementation. - thomas |
1381 | */ |
1382 | static void |
1383 | AdjustTimeForTypmod(TimeADT *time, int32 typmod) |
1384 | { |
1385 | static const int64 TimeScales[MAX_TIME_PRECISION + 1] = { |
1386 | INT64CONST(1000000), |
1387 | INT64CONST(100000), |
1388 | INT64CONST(10000), |
1389 | INT64CONST(1000), |
1390 | INT64CONST(100), |
1391 | INT64CONST(10), |
1392 | INT64CONST(1) |
1393 | }; |
1394 | |
1395 | static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = { |
1396 | INT64CONST(500000), |
1397 | INT64CONST(50000), |
1398 | INT64CONST(5000), |
1399 | INT64CONST(500), |
1400 | INT64CONST(50), |
1401 | INT64CONST(5), |
1402 | INT64CONST(0) |
1403 | }; |
1404 | |
1405 | if (typmod >= 0 && typmod <= MAX_TIME_PRECISION) |
1406 | { |
1407 | if (*time >= INT64CONST(0)) |
1408 | *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) * |
1409 | TimeScales[typmod]; |
1410 | else |
1411 | *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) * |
1412 | TimeScales[typmod]); |
1413 | } |
1414 | } |
1415 | |
1416 | |
1417 | Datum |
1418 | time_eq(PG_FUNCTION_ARGS) |
1419 | { |
1420 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1421 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1422 | |
1423 | PG_RETURN_BOOL(time1 == time2); |
1424 | } |
1425 | |
1426 | Datum |
1427 | time_ne(PG_FUNCTION_ARGS) |
1428 | { |
1429 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1430 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1431 | |
1432 | PG_RETURN_BOOL(time1 != time2); |
1433 | } |
1434 | |
1435 | Datum |
1436 | time_lt(PG_FUNCTION_ARGS) |
1437 | { |
1438 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1439 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1440 | |
1441 | PG_RETURN_BOOL(time1 < time2); |
1442 | } |
1443 | |
1444 | Datum |
1445 | time_le(PG_FUNCTION_ARGS) |
1446 | { |
1447 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1448 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1449 | |
1450 | PG_RETURN_BOOL(time1 <= time2); |
1451 | } |
1452 | |
1453 | Datum |
1454 | time_gt(PG_FUNCTION_ARGS) |
1455 | { |
1456 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1457 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1458 | |
1459 | PG_RETURN_BOOL(time1 > time2); |
1460 | } |
1461 | |
1462 | Datum |
1463 | time_ge(PG_FUNCTION_ARGS) |
1464 | { |
1465 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1466 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1467 | |
1468 | PG_RETURN_BOOL(time1 >= time2); |
1469 | } |
1470 | |
1471 | Datum |
1472 | time_cmp(PG_FUNCTION_ARGS) |
1473 | { |
1474 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1475 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1476 | |
1477 | if (time1 < time2) |
1478 | PG_RETURN_INT32(-1); |
1479 | if (time1 > time2) |
1480 | PG_RETURN_INT32(1); |
1481 | PG_RETURN_INT32(0); |
1482 | } |
1483 | |
1484 | Datum |
1485 | time_hash(PG_FUNCTION_ARGS) |
1486 | { |
1487 | return hashint8(fcinfo); |
1488 | } |
1489 | |
1490 | Datum |
1491 | time_hash_extended(PG_FUNCTION_ARGS) |
1492 | { |
1493 | return hashint8extended(fcinfo); |
1494 | } |
1495 | |
1496 | Datum |
1497 | time_larger(PG_FUNCTION_ARGS) |
1498 | { |
1499 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1500 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1501 | |
1502 | PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2); |
1503 | } |
1504 | |
1505 | Datum |
1506 | time_smaller(PG_FUNCTION_ARGS) |
1507 | { |
1508 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1509 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1510 | |
1511 | PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2); |
1512 | } |
1513 | |
1514 | /* overlaps_time() --- implements the SQL OVERLAPS operator. |
1515 | * |
1516 | * Algorithm is per SQL spec. This is much harder than you'd think |
1517 | * because the spec requires us to deliver a non-null answer in some cases |
1518 | * where some of the inputs are null. |
1519 | */ |
1520 | Datum |
1521 | overlaps_time(PG_FUNCTION_ARGS) |
1522 | { |
1523 | /* |
1524 | * The arguments are TimeADT, but we leave them as generic Datums to avoid |
1525 | * dereferencing nulls (TimeADT is pass-by-reference!) |
1526 | */ |
1527 | Datum ts1 = PG_GETARG_DATUM(0); |
1528 | Datum te1 = PG_GETARG_DATUM(1); |
1529 | Datum ts2 = PG_GETARG_DATUM(2); |
1530 | Datum te2 = PG_GETARG_DATUM(3); |
1531 | bool ts1IsNull = PG_ARGISNULL(0); |
1532 | bool te1IsNull = PG_ARGISNULL(1); |
1533 | bool ts2IsNull = PG_ARGISNULL(2); |
1534 | bool te2IsNull = PG_ARGISNULL(3); |
1535 | |
1536 | #define TIMEADT_GT(t1,t2) \ |
1537 | (DatumGetTimeADT(t1) > DatumGetTimeADT(t2)) |
1538 | #define TIMEADT_LT(t1,t2) \ |
1539 | (DatumGetTimeADT(t1) < DatumGetTimeADT(t2)) |
1540 | |
1541 | /* |
1542 | * If both endpoints of interval 1 are null, the result is null (unknown). |
1543 | * If just one endpoint is null, take ts1 as the non-null one. Otherwise, |
1544 | * take ts1 as the lesser endpoint. |
1545 | */ |
1546 | if (ts1IsNull) |
1547 | { |
1548 | if (te1IsNull) |
1549 | PG_RETURN_NULL(); |
1550 | /* swap null for non-null */ |
1551 | ts1 = te1; |
1552 | te1IsNull = true; |
1553 | } |
1554 | else if (!te1IsNull) |
1555 | { |
1556 | if (TIMEADT_GT(ts1, te1)) |
1557 | { |
1558 | Datum tt = ts1; |
1559 | |
1560 | ts1 = te1; |
1561 | te1 = tt; |
1562 | } |
1563 | } |
1564 | |
1565 | /* Likewise for interval 2. */ |
1566 | if (ts2IsNull) |
1567 | { |
1568 | if (te2IsNull) |
1569 | PG_RETURN_NULL(); |
1570 | /* swap null for non-null */ |
1571 | ts2 = te2; |
1572 | te2IsNull = true; |
1573 | } |
1574 | else if (!te2IsNull) |
1575 | { |
1576 | if (TIMEADT_GT(ts2, te2)) |
1577 | { |
1578 | Datum tt = ts2; |
1579 | |
1580 | ts2 = te2; |
1581 | te2 = tt; |
1582 | } |
1583 | } |
1584 | |
1585 | /* |
1586 | * At this point neither ts1 nor ts2 is null, so we can consider three |
1587 | * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2 |
1588 | */ |
1589 | if (TIMEADT_GT(ts1, ts2)) |
1590 | { |
1591 | /* |
1592 | * This case is ts1 < te2 OR te1 < te2, which may look redundant but |
1593 | * in the presence of nulls it's not quite completely so. |
1594 | */ |
1595 | if (te2IsNull) |
1596 | PG_RETURN_NULL(); |
1597 | if (TIMEADT_LT(ts1, te2)) |
1598 | PG_RETURN_BOOL(true); |
1599 | if (te1IsNull) |
1600 | PG_RETURN_NULL(); |
1601 | |
1602 | /* |
1603 | * If te1 is not null then we had ts1 <= te1 above, and we just found |
1604 | * ts1 >= te2, hence te1 >= te2. |
1605 | */ |
1606 | PG_RETURN_BOOL(false); |
1607 | } |
1608 | else if (TIMEADT_LT(ts1, ts2)) |
1609 | { |
1610 | /* This case is ts2 < te1 OR te2 < te1 */ |
1611 | if (te1IsNull) |
1612 | PG_RETURN_NULL(); |
1613 | if (TIMEADT_LT(ts2, te1)) |
1614 | PG_RETURN_BOOL(true); |
1615 | if (te2IsNull) |
1616 | PG_RETURN_NULL(); |
1617 | |
1618 | /* |
1619 | * If te2 is not null then we had ts2 <= te2 above, and we just found |
1620 | * ts2 >= te1, hence te2 >= te1. |
1621 | */ |
1622 | PG_RETURN_BOOL(false); |
1623 | } |
1624 | else |
1625 | { |
1626 | /* |
1627 | * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a |
1628 | * rather silly way of saying "true if both are nonnull, else null". |
1629 | */ |
1630 | if (te1IsNull || te2IsNull) |
1631 | PG_RETURN_NULL(); |
1632 | PG_RETURN_BOOL(true); |
1633 | } |
1634 | |
1635 | #undef TIMEADT_GT |
1636 | #undef TIMEADT_LT |
1637 | } |
1638 | |
1639 | /* timestamp_time() |
1640 | * Convert timestamp to time data type. |
1641 | */ |
1642 | Datum |
1643 | timestamp_time(PG_FUNCTION_ARGS) |
1644 | { |
1645 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
1646 | TimeADT result; |
1647 | struct pg_tm tt, |
1648 | *tm = &tt; |
1649 | fsec_t fsec; |
1650 | |
1651 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
1652 | PG_RETURN_NULL(); |
1653 | |
1654 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
1655 | ereport(ERROR, |
1656 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
1657 | errmsg("timestamp out of range" ))); |
1658 | |
1659 | /* |
1660 | * Could also do this with time = (timestamp / USECS_PER_DAY * |
1661 | * USECS_PER_DAY) - timestamp; |
1662 | */ |
1663 | result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * |
1664 | USECS_PER_SEC) + fsec; |
1665 | |
1666 | PG_RETURN_TIMEADT(result); |
1667 | } |
1668 | |
1669 | /* timestamptz_time() |
1670 | * Convert timestamptz to time data type. |
1671 | */ |
1672 | Datum |
1673 | timestamptz_time(PG_FUNCTION_ARGS) |
1674 | { |
1675 | TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); |
1676 | TimeADT result; |
1677 | struct pg_tm tt, |
1678 | *tm = &tt; |
1679 | int tz; |
1680 | fsec_t fsec; |
1681 | |
1682 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
1683 | PG_RETURN_NULL(); |
1684 | |
1685 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
1686 | ereport(ERROR, |
1687 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
1688 | errmsg("timestamp out of range" ))); |
1689 | |
1690 | /* |
1691 | * Could also do this with time = (timestamp / USECS_PER_DAY * |
1692 | * USECS_PER_DAY) - timestamp; |
1693 | */ |
1694 | result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * |
1695 | USECS_PER_SEC) + fsec; |
1696 | |
1697 | PG_RETURN_TIMEADT(result); |
1698 | } |
1699 | |
1700 | /* datetime_timestamp() |
1701 | * Convert date and time to timestamp data type. |
1702 | */ |
1703 | Datum |
1704 | datetime_timestamp(PG_FUNCTION_ARGS) |
1705 | { |
1706 | DateADT date = PG_GETARG_DATEADT(0); |
1707 | TimeADT time = PG_GETARG_TIMEADT(1); |
1708 | Timestamp result; |
1709 | |
1710 | result = date2timestamp(date); |
1711 | if (!TIMESTAMP_NOT_FINITE(result)) |
1712 | { |
1713 | result += time; |
1714 | if (!IS_VALID_TIMESTAMP(result)) |
1715 | ereport(ERROR, |
1716 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
1717 | errmsg("timestamp out of range" ))); |
1718 | } |
1719 | |
1720 | PG_RETURN_TIMESTAMP(result); |
1721 | } |
1722 | |
1723 | /* time_interval() |
1724 | * Convert time to interval data type. |
1725 | */ |
1726 | Datum |
1727 | time_interval(PG_FUNCTION_ARGS) |
1728 | { |
1729 | TimeADT time = PG_GETARG_TIMEADT(0); |
1730 | Interval *result; |
1731 | |
1732 | result = (Interval *) palloc(sizeof(Interval)); |
1733 | |
1734 | result->time = time; |
1735 | result->day = 0; |
1736 | result->month = 0; |
1737 | |
1738 | PG_RETURN_INTERVAL_P(result); |
1739 | } |
1740 | |
1741 | /* interval_time() |
1742 | * Convert interval to time data type. |
1743 | * |
1744 | * This is defined as producing the fractional-day portion of the interval. |
1745 | * Therefore, we can just ignore the months field. It is not real clear |
1746 | * what to do with negative intervals, but we choose to subtract the floor, |
1747 | * so that, say, '-2 hours' becomes '22:00:00'. |
1748 | */ |
1749 | Datum |
1750 | interval_time(PG_FUNCTION_ARGS) |
1751 | { |
1752 | Interval *span = PG_GETARG_INTERVAL_P(0); |
1753 | TimeADT result; |
1754 | int64 days; |
1755 | |
1756 | result = span->time; |
1757 | if (result >= USECS_PER_DAY) |
1758 | { |
1759 | days = result / USECS_PER_DAY; |
1760 | result -= days * USECS_PER_DAY; |
1761 | } |
1762 | else if (result < 0) |
1763 | { |
1764 | days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY; |
1765 | result += days * USECS_PER_DAY; |
1766 | } |
1767 | |
1768 | PG_RETURN_TIMEADT(result); |
1769 | } |
1770 | |
1771 | /* time_mi_time() |
1772 | * Subtract two times to produce an interval. |
1773 | */ |
1774 | Datum |
1775 | time_mi_time(PG_FUNCTION_ARGS) |
1776 | { |
1777 | TimeADT time1 = PG_GETARG_TIMEADT(0); |
1778 | TimeADT time2 = PG_GETARG_TIMEADT(1); |
1779 | Interval *result; |
1780 | |
1781 | result = (Interval *) palloc(sizeof(Interval)); |
1782 | |
1783 | result->month = 0; |
1784 | result->day = 0; |
1785 | result->time = time1 - time2; |
1786 | |
1787 | PG_RETURN_INTERVAL_P(result); |
1788 | } |
1789 | |
1790 | /* time_pl_interval() |
1791 | * Add interval to time. |
1792 | */ |
1793 | Datum |
1794 | time_pl_interval(PG_FUNCTION_ARGS) |
1795 | { |
1796 | TimeADT time = PG_GETARG_TIMEADT(0); |
1797 | Interval *span = PG_GETARG_INTERVAL_P(1); |
1798 | TimeADT result; |
1799 | |
1800 | result = time + span->time; |
1801 | result -= result / USECS_PER_DAY * USECS_PER_DAY; |
1802 | if (result < INT64CONST(0)) |
1803 | result += USECS_PER_DAY; |
1804 | |
1805 | PG_RETURN_TIMEADT(result); |
1806 | } |
1807 | |
1808 | /* time_mi_interval() |
1809 | * Subtract interval from time. |
1810 | */ |
1811 | Datum |
1812 | time_mi_interval(PG_FUNCTION_ARGS) |
1813 | { |
1814 | TimeADT time = PG_GETARG_TIMEADT(0); |
1815 | Interval *span = PG_GETARG_INTERVAL_P(1); |
1816 | TimeADT result; |
1817 | |
1818 | result = time - span->time; |
1819 | result -= result / USECS_PER_DAY * USECS_PER_DAY; |
1820 | if (result < INT64CONST(0)) |
1821 | result += USECS_PER_DAY; |
1822 | |
1823 | PG_RETURN_TIMEADT(result); |
1824 | } |
1825 | |
1826 | /* |
1827 | * in_range support function for time. |
1828 | */ |
1829 | Datum |
1830 | in_range_time_interval(PG_FUNCTION_ARGS) |
1831 | { |
1832 | TimeADT val = PG_GETARG_TIMEADT(0); |
1833 | TimeADT base = PG_GETARG_TIMEADT(1); |
1834 | Interval *offset = PG_GETARG_INTERVAL_P(2); |
1835 | bool sub = PG_GETARG_BOOL(3); |
1836 | bool less = PG_GETARG_BOOL(4); |
1837 | TimeADT sum; |
1838 | |
1839 | /* |
1840 | * Like time_pl_interval/time_mi_interval, we disregard the month and day |
1841 | * fields of the offset. So our test for negative should too. |
1842 | */ |
1843 | if (offset->time < 0) |
1844 | ereport(ERROR, |
1845 | (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), |
1846 | errmsg("invalid preceding or following size in window function" ))); |
1847 | |
1848 | /* |
1849 | * We can't use time_pl_interval/time_mi_interval here, because their |
1850 | * wraparound behavior would give wrong (or at least undesirable) answers. |
1851 | * Fortunately the equivalent non-wrapping behavior is trivial, especially |
1852 | * since we don't worry about integer overflow. |
1853 | */ |
1854 | if (sub) |
1855 | sum = base - offset->time; |
1856 | else |
1857 | sum = base + offset->time; |
1858 | |
1859 | if (less) |
1860 | PG_RETURN_BOOL(val <= sum); |
1861 | else |
1862 | PG_RETURN_BOOL(val >= sum); |
1863 | } |
1864 | |
1865 | |
1866 | /* time_part() |
1867 | * Extract specified field from time type. |
1868 | */ |
1869 | Datum |
1870 | time_part(PG_FUNCTION_ARGS) |
1871 | { |
1872 | text *units = PG_GETARG_TEXT_PP(0); |
1873 | TimeADT time = PG_GETARG_TIMEADT(1); |
1874 | float8 result; |
1875 | int type, |
1876 | val; |
1877 | char *lowunits; |
1878 | |
1879 | lowunits = downcase_truncate_identifier(VARDATA_ANY(units), |
1880 | VARSIZE_ANY_EXHDR(units), |
1881 | false); |
1882 | |
1883 | type = DecodeUnits(0, lowunits, &val); |
1884 | if (type == UNKNOWN_FIELD) |
1885 | type = DecodeSpecial(0, lowunits, &val); |
1886 | |
1887 | if (type == UNITS) |
1888 | { |
1889 | fsec_t fsec; |
1890 | struct pg_tm tt, |
1891 | *tm = &tt; |
1892 | |
1893 | time2tm(time, tm, &fsec); |
1894 | |
1895 | switch (val) |
1896 | { |
1897 | case DTK_MICROSEC: |
1898 | result = tm->tm_sec * 1000000.0 + fsec; |
1899 | break; |
1900 | |
1901 | case DTK_MILLISEC: |
1902 | result = tm->tm_sec * 1000.0 + fsec / 1000.0; |
1903 | break; |
1904 | |
1905 | case DTK_SECOND: |
1906 | result = tm->tm_sec + fsec / 1000000.0; |
1907 | break; |
1908 | |
1909 | case DTK_MINUTE: |
1910 | result = tm->tm_min; |
1911 | break; |
1912 | |
1913 | case DTK_HOUR: |
1914 | result = tm->tm_hour; |
1915 | break; |
1916 | |
1917 | case DTK_TZ: |
1918 | case DTK_TZ_MINUTE: |
1919 | case DTK_TZ_HOUR: |
1920 | case DTK_DAY: |
1921 | case DTK_MONTH: |
1922 | case DTK_QUARTER: |
1923 | case DTK_YEAR: |
1924 | case DTK_DECADE: |
1925 | case DTK_CENTURY: |
1926 | case DTK_MILLENNIUM: |
1927 | case DTK_ISOYEAR: |
1928 | default: |
1929 | ereport(ERROR, |
1930 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1931 | errmsg("\"time\" units \"%s\" not recognized" , |
1932 | lowunits))); |
1933 | result = 0; |
1934 | } |
1935 | } |
1936 | else if (type == RESERV && val == DTK_EPOCH) |
1937 | { |
1938 | result = time / 1000000.0; |
1939 | } |
1940 | else |
1941 | { |
1942 | ereport(ERROR, |
1943 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
1944 | errmsg("\"time\" units \"%s\" not recognized" , |
1945 | lowunits))); |
1946 | result = 0; |
1947 | } |
1948 | |
1949 | PG_RETURN_FLOAT8(result); |
1950 | } |
1951 | |
1952 | |
1953 | /***************************************************************************** |
1954 | * Time With Time Zone ADT |
1955 | *****************************************************************************/ |
1956 | |
1957 | /* tm2timetz() |
1958 | * Convert a tm structure to a time data type. |
1959 | */ |
1960 | static int |
1961 | tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result) |
1962 | { |
1963 | result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * |
1964 | USECS_PER_SEC) + fsec; |
1965 | result->zone = tz; |
1966 | |
1967 | return 0; |
1968 | } |
1969 | |
1970 | Datum |
1971 | timetz_in(PG_FUNCTION_ARGS) |
1972 | { |
1973 | char *str = PG_GETARG_CSTRING(0); |
1974 | |
1975 | #ifdef NOT_USED |
1976 | Oid typelem = PG_GETARG_OID(1); |
1977 | #endif |
1978 | int32 typmod = PG_GETARG_INT32(2); |
1979 | TimeTzADT *result; |
1980 | fsec_t fsec; |
1981 | struct pg_tm tt, |
1982 | *tm = &tt; |
1983 | int tz; |
1984 | int nf; |
1985 | int dterr; |
1986 | char workbuf[MAXDATELEN + 1]; |
1987 | char *field[MAXDATEFIELDS]; |
1988 | int dtype; |
1989 | int ftype[MAXDATEFIELDS]; |
1990 | |
1991 | dterr = ParseDateTime(str, workbuf, sizeof(workbuf), |
1992 | field, ftype, MAXDATEFIELDS, &nf); |
1993 | if (dterr == 0) |
1994 | dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz); |
1995 | if (dterr != 0) |
1996 | DateTimeParseError(dterr, str, "time with time zone" ); |
1997 | |
1998 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
1999 | tm2timetz(tm, fsec, tz, result); |
2000 | AdjustTimeForTypmod(&(result->time), typmod); |
2001 | |
2002 | PG_RETURN_TIMETZADT_P(result); |
2003 | } |
2004 | |
2005 | Datum |
2006 | timetz_out(PG_FUNCTION_ARGS) |
2007 | { |
2008 | TimeTzADT *time = PG_GETARG_TIMETZADT_P(0); |
2009 | char *result; |
2010 | struct pg_tm tt, |
2011 | *tm = &tt; |
2012 | fsec_t fsec; |
2013 | int tz; |
2014 | char buf[MAXDATELEN + 1]; |
2015 | |
2016 | timetz2tm(time, tm, &fsec, &tz); |
2017 | EncodeTimeOnly(tm, fsec, true, tz, DateStyle, buf); |
2018 | |
2019 | result = pstrdup(buf); |
2020 | PG_RETURN_CSTRING(result); |
2021 | } |
2022 | |
2023 | /* |
2024 | * timetz_recv - converts external binary format to timetz |
2025 | */ |
2026 | Datum |
2027 | timetz_recv(PG_FUNCTION_ARGS) |
2028 | { |
2029 | StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); |
2030 | |
2031 | #ifdef NOT_USED |
2032 | Oid typelem = PG_GETARG_OID(1); |
2033 | #endif |
2034 | int32 typmod = PG_GETARG_INT32(2); |
2035 | TimeTzADT *result; |
2036 | |
2037 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
2038 | |
2039 | result->time = pq_getmsgint64(buf); |
2040 | |
2041 | if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY) |
2042 | ereport(ERROR, |
2043 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
2044 | errmsg("time out of range" ))); |
2045 | |
2046 | result->zone = pq_getmsgint(buf, sizeof(result->zone)); |
2047 | |
2048 | /* Check for sane GMT displacement; see notes in datatype/timestamp.h */ |
2049 | if (result->zone <= -TZDISP_LIMIT || result->zone >= TZDISP_LIMIT) |
2050 | ereport(ERROR, |
2051 | (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE), |
2052 | errmsg("time zone displacement out of range" ))); |
2053 | |
2054 | AdjustTimeForTypmod(&(result->time), typmod); |
2055 | |
2056 | PG_RETURN_TIMETZADT_P(result); |
2057 | } |
2058 | |
2059 | /* |
2060 | * timetz_send - converts timetz to binary format |
2061 | */ |
2062 | Datum |
2063 | timetz_send(PG_FUNCTION_ARGS) |
2064 | { |
2065 | TimeTzADT *time = PG_GETARG_TIMETZADT_P(0); |
2066 | StringInfoData buf; |
2067 | |
2068 | pq_begintypsend(&buf); |
2069 | pq_sendint64(&buf, time->time); |
2070 | pq_sendint32(&buf, time->zone); |
2071 | PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
2072 | } |
2073 | |
2074 | Datum |
2075 | timetztypmodin(PG_FUNCTION_ARGS) |
2076 | { |
2077 | ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); |
2078 | |
2079 | PG_RETURN_INT32(anytime_typmodin(true, ta)); |
2080 | } |
2081 | |
2082 | Datum |
2083 | timetztypmodout(PG_FUNCTION_ARGS) |
2084 | { |
2085 | int32 typmod = PG_GETARG_INT32(0); |
2086 | |
2087 | PG_RETURN_CSTRING(anytime_typmodout(true, typmod)); |
2088 | } |
2089 | |
2090 | |
2091 | /* timetz2tm() |
2092 | * Convert TIME WITH TIME ZONE data type to POSIX time structure. |
2093 | */ |
2094 | int |
2095 | timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp) |
2096 | { |
2097 | TimeOffset trem = time->time; |
2098 | |
2099 | tm->tm_hour = trem / USECS_PER_HOUR; |
2100 | trem -= tm->tm_hour * USECS_PER_HOUR; |
2101 | tm->tm_min = trem / USECS_PER_MINUTE; |
2102 | trem -= tm->tm_min * USECS_PER_MINUTE; |
2103 | tm->tm_sec = trem / USECS_PER_SEC; |
2104 | *fsec = trem - tm->tm_sec * USECS_PER_SEC; |
2105 | |
2106 | if (tzp != NULL) |
2107 | *tzp = time->zone; |
2108 | |
2109 | return 0; |
2110 | } |
2111 | |
2112 | /* timetz_scale() |
2113 | * Adjust time type for specified scale factor. |
2114 | * Used by PostgreSQL type system to stuff columns. |
2115 | */ |
2116 | Datum |
2117 | timetz_scale(PG_FUNCTION_ARGS) |
2118 | { |
2119 | TimeTzADT *time = PG_GETARG_TIMETZADT_P(0); |
2120 | int32 typmod = PG_GETARG_INT32(1); |
2121 | TimeTzADT *result; |
2122 | |
2123 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
2124 | |
2125 | result->time = time->time; |
2126 | result->zone = time->zone; |
2127 | |
2128 | AdjustTimeForTypmod(&(result->time), typmod); |
2129 | |
2130 | PG_RETURN_TIMETZADT_P(result); |
2131 | } |
2132 | |
2133 | |
2134 | static int |
2135 | timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2) |
2136 | { |
2137 | TimeOffset t1, |
2138 | t2; |
2139 | |
2140 | /* Primary sort is by true (GMT-equivalent) time */ |
2141 | t1 = time1->time + (time1->zone * USECS_PER_SEC); |
2142 | t2 = time2->time + (time2->zone * USECS_PER_SEC); |
2143 | |
2144 | if (t1 > t2) |
2145 | return 1; |
2146 | if (t1 < t2) |
2147 | return -1; |
2148 | |
2149 | /* |
2150 | * If same GMT time, sort by timezone; we only want to say that two |
2151 | * timetz's are equal if both the time and zone parts are equal. |
2152 | */ |
2153 | if (time1->zone > time2->zone) |
2154 | return 1; |
2155 | if (time1->zone < time2->zone) |
2156 | return -1; |
2157 | |
2158 | return 0; |
2159 | } |
2160 | |
2161 | Datum |
2162 | timetz_eq(PG_FUNCTION_ARGS) |
2163 | { |
2164 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2165 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2166 | |
2167 | PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0); |
2168 | } |
2169 | |
2170 | Datum |
2171 | timetz_ne(PG_FUNCTION_ARGS) |
2172 | { |
2173 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2174 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2175 | |
2176 | PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0); |
2177 | } |
2178 | |
2179 | Datum |
2180 | timetz_lt(PG_FUNCTION_ARGS) |
2181 | { |
2182 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2183 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2184 | |
2185 | PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0); |
2186 | } |
2187 | |
2188 | Datum |
2189 | timetz_le(PG_FUNCTION_ARGS) |
2190 | { |
2191 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2192 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2193 | |
2194 | PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0); |
2195 | } |
2196 | |
2197 | Datum |
2198 | timetz_gt(PG_FUNCTION_ARGS) |
2199 | { |
2200 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2201 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2202 | |
2203 | PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0); |
2204 | } |
2205 | |
2206 | Datum |
2207 | timetz_ge(PG_FUNCTION_ARGS) |
2208 | { |
2209 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2210 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2211 | |
2212 | PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0); |
2213 | } |
2214 | |
2215 | Datum |
2216 | timetz_cmp(PG_FUNCTION_ARGS) |
2217 | { |
2218 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2219 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2220 | |
2221 | PG_RETURN_INT32(timetz_cmp_internal(time1, time2)); |
2222 | } |
2223 | |
2224 | Datum |
2225 | timetz_hash(PG_FUNCTION_ARGS) |
2226 | { |
2227 | TimeTzADT *key = PG_GETARG_TIMETZADT_P(0); |
2228 | uint32 thash; |
2229 | |
2230 | /* |
2231 | * To avoid any problems with padding bytes in the struct, we figure the |
2232 | * field hashes separately and XOR them. |
2233 | */ |
2234 | thash = DatumGetUInt32(DirectFunctionCall1(hashint8, |
2235 | Int64GetDatumFast(key->time))); |
2236 | thash ^= DatumGetUInt32(hash_uint32(key->zone)); |
2237 | PG_RETURN_UINT32(thash); |
2238 | } |
2239 | |
2240 | Datum |
2241 | timetz_hash_extended(PG_FUNCTION_ARGS) |
2242 | { |
2243 | TimeTzADT *key = PG_GETARG_TIMETZADT_P(0); |
2244 | Datum seed = PG_GETARG_DATUM(1); |
2245 | uint64 thash; |
2246 | |
2247 | /* Same approach as timetz_hash */ |
2248 | thash = DatumGetUInt64(DirectFunctionCall2(hashint8extended, |
2249 | Int64GetDatumFast(key->time), |
2250 | seed)); |
2251 | thash ^= DatumGetUInt64(hash_uint32_extended(key->zone, |
2252 | DatumGetInt64(seed))); |
2253 | PG_RETURN_UINT64(thash); |
2254 | } |
2255 | |
2256 | Datum |
2257 | timetz_larger(PG_FUNCTION_ARGS) |
2258 | { |
2259 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2260 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2261 | TimeTzADT *result; |
2262 | |
2263 | if (timetz_cmp_internal(time1, time2) > 0) |
2264 | result = time1; |
2265 | else |
2266 | result = time2; |
2267 | PG_RETURN_TIMETZADT_P(result); |
2268 | } |
2269 | |
2270 | Datum |
2271 | timetz_smaller(PG_FUNCTION_ARGS) |
2272 | { |
2273 | TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0); |
2274 | TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1); |
2275 | TimeTzADT *result; |
2276 | |
2277 | if (timetz_cmp_internal(time1, time2) < 0) |
2278 | result = time1; |
2279 | else |
2280 | result = time2; |
2281 | PG_RETURN_TIMETZADT_P(result); |
2282 | } |
2283 | |
2284 | /* timetz_pl_interval() |
2285 | * Add interval to timetz. |
2286 | */ |
2287 | Datum |
2288 | timetz_pl_interval(PG_FUNCTION_ARGS) |
2289 | { |
2290 | TimeTzADT *time = PG_GETARG_TIMETZADT_P(0); |
2291 | Interval *span = PG_GETARG_INTERVAL_P(1); |
2292 | TimeTzADT *result; |
2293 | |
2294 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
2295 | |
2296 | result->time = time->time + span->time; |
2297 | result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY; |
2298 | if (result->time < INT64CONST(0)) |
2299 | result->time += USECS_PER_DAY; |
2300 | |
2301 | result->zone = time->zone; |
2302 | |
2303 | PG_RETURN_TIMETZADT_P(result); |
2304 | } |
2305 | |
2306 | /* timetz_mi_interval() |
2307 | * Subtract interval from timetz. |
2308 | */ |
2309 | Datum |
2310 | timetz_mi_interval(PG_FUNCTION_ARGS) |
2311 | { |
2312 | TimeTzADT *time = PG_GETARG_TIMETZADT_P(0); |
2313 | Interval *span = PG_GETARG_INTERVAL_P(1); |
2314 | TimeTzADT *result; |
2315 | |
2316 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
2317 | |
2318 | result->time = time->time - span->time; |
2319 | result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY; |
2320 | if (result->time < INT64CONST(0)) |
2321 | result->time += USECS_PER_DAY; |
2322 | |
2323 | result->zone = time->zone; |
2324 | |
2325 | PG_RETURN_TIMETZADT_P(result); |
2326 | } |
2327 | |
2328 | /* |
2329 | * in_range support function for timetz. |
2330 | */ |
2331 | Datum |
2332 | in_range_timetz_interval(PG_FUNCTION_ARGS) |
2333 | { |
2334 | TimeTzADT *val = PG_GETARG_TIMETZADT_P(0); |
2335 | TimeTzADT *base = PG_GETARG_TIMETZADT_P(1); |
2336 | Interval *offset = PG_GETARG_INTERVAL_P(2); |
2337 | bool sub = PG_GETARG_BOOL(3); |
2338 | bool less = PG_GETARG_BOOL(4); |
2339 | TimeTzADT sum; |
2340 | |
2341 | /* |
2342 | * Like timetz_pl_interval/timetz_mi_interval, we disregard the month and |
2343 | * day fields of the offset. So our test for negative should too. |
2344 | */ |
2345 | if (offset->time < 0) |
2346 | ereport(ERROR, |
2347 | (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), |
2348 | errmsg("invalid preceding or following size in window function" ))); |
2349 | |
2350 | /* |
2351 | * We can't use timetz_pl_interval/timetz_mi_interval here, because their |
2352 | * wraparound behavior would give wrong (or at least undesirable) answers. |
2353 | * Fortunately the equivalent non-wrapping behavior is trivial, especially |
2354 | * since we don't worry about integer overflow. |
2355 | */ |
2356 | if (sub) |
2357 | sum.time = base->time - offset->time; |
2358 | else |
2359 | sum.time = base->time + offset->time; |
2360 | sum.zone = base->zone; |
2361 | |
2362 | if (less) |
2363 | PG_RETURN_BOOL(timetz_cmp_internal(val, &sum) <= 0); |
2364 | else |
2365 | PG_RETURN_BOOL(timetz_cmp_internal(val, &sum) >= 0); |
2366 | } |
2367 | |
2368 | /* overlaps_timetz() --- implements the SQL OVERLAPS operator. |
2369 | * |
2370 | * Algorithm is per SQL spec. This is much harder than you'd think |
2371 | * because the spec requires us to deliver a non-null answer in some cases |
2372 | * where some of the inputs are null. |
2373 | */ |
2374 | Datum |
2375 | overlaps_timetz(PG_FUNCTION_ARGS) |
2376 | { |
2377 | /* |
2378 | * The arguments are TimeTzADT *, but we leave them as generic Datums for |
2379 | * convenience of notation --- and to avoid dereferencing nulls. |
2380 | */ |
2381 | Datum ts1 = PG_GETARG_DATUM(0); |
2382 | Datum te1 = PG_GETARG_DATUM(1); |
2383 | Datum ts2 = PG_GETARG_DATUM(2); |
2384 | Datum te2 = PG_GETARG_DATUM(3); |
2385 | bool ts1IsNull = PG_ARGISNULL(0); |
2386 | bool te1IsNull = PG_ARGISNULL(1); |
2387 | bool ts2IsNull = PG_ARGISNULL(2); |
2388 | bool te2IsNull = PG_ARGISNULL(3); |
2389 | |
2390 | #define TIMETZ_GT(t1,t2) \ |
2391 | DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2)) |
2392 | #define TIMETZ_LT(t1,t2) \ |
2393 | DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2)) |
2394 | |
2395 | /* |
2396 | * If both endpoints of interval 1 are null, the result is null (unknown). |
2397 | * If just one endpoint is null, take ts1 as the non-null one. Otherwise, |
2398 | * take ts1 as the lesser endpoint. |
2399 | */ |
2400 | if (ts1IsNull) |
2401 | { |
2402 | if (te1IsNull) |
2403 | PG_RETURN_NULL(); |
2404 | /* swap null for non-null */ |
2405 | ts1 = te1; |
2406 | te1IsNull = true; |
2407 | } |
2408 | else if (!te1IsNull) |
2409 | { |
2410 | if (TIMETZ_GT(ts1, te1)) |
2411 | { |
2412 | Datum tt = ts1; |
2413 | |
2414 | ts1 = te1; |
2415 | te1 = tt; |
2416 | } |
2417 | } |
2418 | |
2419 | /* Likewise for interval 2. */ |
2420 | if (ts2IsNull) |
2421 | { |
2422 | if (te2IsNull) |
2423 | PG_RETURN_NULL(); |
2424 | /* swap null for non-null */ |
2425 | ts2 = te2; |
2426 | te2IsNull = true; |
2427 | } |
2428 | else if (!te2IsNull) |
2429 | { |
2430 | if (TIMETZ_GT(ts2, te2)) |
2431 | { |
2432 | Datum tt = ts2; |
2433 | |
2434 | ts2 = te2; |
2435 | te2 = tt; |
2436 | } |
2437 | } |
2438 | |
2439 | /* |
2440 | * At this point neither ts1 nor ts2 is null, so we can consider three |
2441 | * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2 |
2442 | */ |
2443 | if (TIMETZ_GT(ts1, ts2)) |
2444 | { |
2445 | /* |
2446 | * This case is ts1 < te2 OR te1 < te2, which may look redundant but |
2447 | * in the presence of nulls it's not quite completely so. |
2448 | */ |
2449 | if (te2IsNull) |
2450 | PG_RETURN_NULL(); |
2451 | if (TIMETZ_LT(ts1, te2)) |
2452 | PG_RETURN_BOOL(true); |
2453 | if (te1IsNull) |
2454 | PG_RETURN_NULL(); |
2455 | |
2456 | /* |
2457 | * If te1 is not null then we had ts1 <= te1 above, and we just found |
2458 | * ts1 >= te2, hence te1 >= te2. |
2459 | */ |
2460 | PG_RETURN_BOOL(false); |
2461 | } |
2462 | else if (TIMETZ_LT(ts1, ts2)) |
2463 | { |
2464 | /* This case is ts2 < te1 OR te2 < te1 */ |
2465 | if (te1IsNull) |
2466 | PG_RETURN_NULL(); |
2467 | if (TIMETZ_LT(ts2, te1)) |
2468 | PG_RETURN_BOOL(true); |
2469 | if (te2IsNull) |
2470 | PG_RETURN_NULL(); |
2471 | |
2472 | /* |
2473 | * If te2 is not null then we had ts2 <= te2 above, and we just found |
2474 | * ts2 >= te1, hence te2 >= te1. |
2475 | */ |
2476 | PG_RETURN_BOOL(false); |
2477 | } |
2478 | else |
2479 | { |
2480 | /* |
2481 | * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a |
2482 | * rather silly way of saying "true if both are nonnull, else null". |
2483 | */ |
2484 | if (te1IsNull || te2IsNull) |
2485 | PG_RETURN_NULL(); |
2486 | PG_RETURN_BOOL(true); |
2487 | } |
2488 | |
2489 | #undef TIMETZ_GT |
2490 | #undef TIMETZ_LT |
2491 | } |
2492 | |
2493 | |
2494 | Datum |
2495 | timetz_time(PG_FUNCTION_ARGS) |
2496 | { |
2497 | TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0); |
2498 | TimeADT result; |
2499 | |
2500 | /* swallow the time zone and just return the time */ |
2501 | result = timetz->time; |
2502 | |
2503 | PG_RETURN_TIMEADT(result); |
2504 | } |
2505 | |
2506 | |
2507 | Datum |
2508 | time_timetz(PG_FUNCTION_ARGS) |
2509 | { |
2510 | TimeADT time = PG_GETARG_TIMEADT(0); |
2511 | TimeTzADT *result; |
2512 | struct pg_tm tt, |
2513 | *tm = &tt; |
2514 | fsec_t fsec; |
2515 | int tz; |
2516 | |
2517 | GetCurrentDateTime(tm); |
2518 | time2tm(time, tm, &fsec); |
2519 | tz = DetermineTimeZoneOffset(tm, session_timezone); |
2520 | |
2521 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
2522 | |
2523 | result->time = time; |
2524 | result->zone = tz; |
2525 | |
2526 | PG_RETURN_TIMETZADT_P(result); |
2527 | } |
2528 | |
2529 | |
2530 | /* timestamptz_timetz() |
2531 | * Convert timestamp to timetz data type. |
2532 | */ |
2533 | Datum |
2534 | timestamptz_timetz(PG_FUNCTION_ARGS) |
2535 | { |
2536 | TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); |
2537 | TimeTzADT *result; |
2538 | struct pg_tm tt, |
2539 | *tm = &tt; |
2540 | int tz; |
2541 | fsec_t fsec; |
2542 | |
2543 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
2544 | PG_RETURN_NULL(); |
2545 | |
2546 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
2547 | ereport(ERROR, |
2548 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
2549 | errmsg("timestamp out of range" ))); |
2550 | |
2551 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
2552 | |
2553 | tm2timetz(tm, fsec, tz, result); |
2554 | |
2555 | PG_RETURN_TIMETZADT_P(result); |
2556 | } |
2557 | |
2558 | |
2559 | /* datetimetz_timestamptz() |
2560 | * Convert date and timetz to timestamp with time zone data type. |
2561 | * Timestamp is stored in GMT, so add the time zone |
2562 | * stored with the timetz to the result. |
2563 | * - thomas 2000-03-10 |
2564 | */ |
2565 | Datum |
2566 | datetimetz_timestamptz(PG_FUNCTION_ARGS) |
2567 | { |
2568 | DateADT date = PG_GETARG_DATEADT(0); |
2569 | TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); |
2570 | TimestampTz result; |
2571 | |
2572 | if (DATE_IS_NOBEGIN(date)) |
2573 | TIMESTAMP_NOBEGIN(result); |
2574 | else if (DATE_IS_NOEND(date)) |
2575 | TIMESTAMP_NOEND(result); |
2576 | else |
2577 | { |
2578 | /* |
2579 | * Date's range is wider than timestamp's, so check for boundaries. |
2580 | * Since dates have the same minimum values as timestamps, only upper |
2581 | * boundary need be checked for overflow. |
2582 | */ |
2583 | if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE)) |
2584 | ereport(ERROR, |
2585 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
2586 | errmsg("date out of range for timestamp" ))); |
2587 | result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC; |
2588 | |
2589 | /* |
2590 | * Since it is possible to go beyond allowed timestamptz range because |
2591 | * of time zone, check for allowed timestamp range after adding tz. |
2592 | */ |
2593 | if (!IS_VALID_TIMESTAMP(result)) |
2594 | ereport(ERROR, |
2595 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
2596 | errmsg("date out of range for timestamp" ))); |
2597 | } |
2598 | |
2599 | PG_RETURN_TIMESTAMP(result); |
2600 | } |
2601 | |
2602 | |
2603 | /* timetz_part() |
2604 | * Extract specified field from time type. |
2605 | */ |
2606 | Datum |
2607 | timetz_part(PG_FUNCTION_ARGS) |
2608 | { |
2609 | text *units = PG_GETARG_TEXT_PP(0); |
2610 | TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); |
2611 | float8 result; |
2612 | int type, |
2613 | val; |
2614 | char *lowunits; |
2615 | |
2616 | lowunits = downcase_truncate_identifier(VARDATA_ANY(units), |
2617 | VARSIZE_ANY_EXHDR(units), |
2618 | false); |
2619 | |
2620 | type = DecodeUnits(0, lowunits, &val); |
2621 | if (type == UNKNOWN_FIELD) |
2622 | type = DecodeSpecial(0, lowunits, &val); |
2623 | |
2624 | if (type == UNITS) |
2625 | { |
2626 | double dummy; |
2627 | int tz; |
2628 | fsec_t fsec; |
2629 | struct pg_tm tt, |
2630 | *tm = &tt; |
2631 | |
2632 | timetz2tm(time, tm, &fsec, &tz); |
2633 | |
2634 | switch (val) |
2635 | { |
2636 | case DTK_TZ: |
2637 | result = -tz; |
2638 | break; |
2639 | |
2640 | case DTK_TZ_MINUTE: |
2641 | result = -tz; |
2642 | result /= SECS_PER_MINUTE; |
2643 | FMODULO(result, dummy, (double) SECS_PER_MINUTE); |
2644 | break; |
2645 | |
2646 | case DTK_TZ_HOUR: |
2647 | dummy = -tz; |
2648 | FMODULO(dummy, result, (double) SECS_PER_HOUR); |
2649 | break; |
2650 | |
2651 | case DTK_MICROSEC: |
2652 | result = tm->tm_sec * 1000000.0 + fsec; |
2653 | break; |
2654 | |
2655 | case DTK_MILLISEC: |
2656 | result = tm->tm_sec * 1000.0 + fsec / 1000.0; |
2657 | break; |
2658 | |
2659 | case DTK_SECOND: |
2660 | result = tm->tm_sec + fsec / 1000000.0; |
2661 | break; |
2662 | |
2663 | case DTK_MINUTE: |
2664 | result = tm->tm_min; |
2665 | break; |
2666 | |
2667 | case DTK_HOUR: |
2668 | result = tm->tm_hour; |
2669 | break; |
2670 | |
2671 | case DTK_DAY: |
2672 | case DTK_MONTH: |
2673 | case DTK_QUARTER: |
2674 | case DTK_YEAR: |
2675 | case DTK_DECADE: |
2676 | case DTK_CENTURY: |
2677 | case DTK_MILLENNIUM: |
2678 | default: |
2679 | ereport(ERROR, |
2680 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2681 | errmsg("\"time with time zone\" units \"%s\" not recognized" , |
2682 | lowunits))); |
2683 | result = 0; |
2684 | } |
2685 | } |
2686 | else if (type == RESERV && val == DTK_EPOCH) |
2687 | { |
2688 | result = time->time / 1000000.0 + time->zone; |
2689 | } |
2690 | else |
2691 | { |
2692 | ereport(ERROR, |
2693 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2694 | errmsg("\"time with time zone\" units \"%s\" not recognized" , |
2695 | lowunits))); |
2696 | result = 0; |
2697 | } |
2698 | |
2699 | PG_RETURN_FLOAT8(result); |
2700 | } |
2701 | |
2702 | /* timetz_zone() |
2703 | * Encode time with time zone type with specified time zone. |
2704 | * Applies DST rules as of the current date. |
2705 | */ |
2706 | Datum |
2707 | timetz_zone(PG_FUNCTION_ARGS) |
2708 | { |
2709 | text *zone = PG_GETARG_TEXT_PP(0); |
2710 | TimeTzADT *t = PG_GETARG_TIMETZADT_P(1); |
2711 | TimeTzADT *result; |
2712 | int tz; |
2713 | char tzname[TZ_STRLEN_MAX + 1]; |
2714 | char *lowzone; |
2715 | int type, |
2716 | val; |
2717 | pg_tz *tzp; |
2718 | |
2719 | /* |
2720 | * Look up the requested timezone. First we look in the timezone |
2721 | * abbreviation table (to handle cases like "EST"), and if that fails, we |
2722 | * look in the timezone database (to handle cases like |
2723 | * "America/New_York"). (This matches the order in which timestamp input |
2724 | * checks the cases; it's important because the timezone database unwisely |
2725 | * uses a few zone names that are identical to offset abbreviations.) |
2726 | */ |
2727 | text_to_cstring_buffer(zone, tzname, sizeof(tzname)); |
2728 | |
2729 | /* DecodeTimezoneAbbrev requires lowercase input */ |
2730 | lowzone = downcase_truncate_identifier(tzname, |
2731 | strlen(tzname), |
2732 | false); |
2733 | |
2734 | type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); |
2735 | |
2736 | if (type == TZ || type == DTZ) |
2737 | { |
2738 | /* fixed-offset abbreviation */ |
2739 | tz = -val; |
2740 | } |
2741 | else if (type == DYNTZ) |
2742 | { |
2743 | /* dynamic-offset abbreviation, resolve using current time */ |
2744 | pg_time_t now = (pg_time_t) time(NULL); |
2745 | struct pg_tm *tm; |
2746 | |
2747 | tm = pg_localtime(&now, tzp); |
2748 | tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp); |
2749 | } |
2750 | else |
2751 | { |
2752 | /* try it as a full zone name */ |
2753 | tzp = pg_tzset(tzname); |
2754 | if (tzp) |
2755 | { |
2756 | /* Get the offset-from-GMT that is valid today for the zone */ |
2757 | pg_time_t now = (pg_time_t) time(NULL); |
2758 | struct pg_tm *tm; |
2759 | |
2760 | tm = pg_localtime(&now, tzp); |
2761 | tz = -tm->tm_gmtoff; |
2762 | } |
2763 | else |
2764 | { |
2765 | ereport(ERROR, |
2766 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2767 | errmsg("time zone \"%s\" not recognized" , tzname))); |
2768 | tz = 0; /* keep compiler quiet */ |
2769 | } |
2770 | } |
2771 | |
2772 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
2773 | |
2774 | result->time = t->time + (t->zone - tz) * USECS_PER_SEC; |
2775 | while (result->time < INT64CONST(0)) |
2776 | result->time += USECS_PER_DAY; |
2777 | while (result->time >= USECS_PER_DAY) |
2778 | result->time -= USECS_PER_DAY; |
2779 | |
2780 | result->zone = tz; |
2781 | |
2782 | PG_RETURN_TIMETZADT_P(result); |
2783 | } |
2784 | |
2785 | /* timetz_izone() |
2786 | * Encode time with time zone type with specified time interval as time zone. |
2787 | */ |
2788 | Datum |
2789 | timetz_izone(PG_FUNCTION_ARGS) |
2790 | { |
2791 | Interval *zone = PG_GETARG_INTERVAL_P(0); |
2792 | TimeTzADT *time = PG_GETARG_TIMETZADT_P(1); |
2793 | TimeTzADT *result; |
2794 | int tz; |
2795 | |
2796 | if (zone->month != 0 || zone->day != 0) |
2797 | ereport(ERROR, |
2798 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
2799 | errmsg("interval time zone \"%s\" must not include months or days" , |
2800 | DatumGetCString(DirectFunctionCall1(interval_out, |
2801 | PointerGetDatum(zone)))))); |
2802 | |
2803 | tz = -(zone->time / USECS_PER_SEC); |
2804 | |
2805 | result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); |
2806 | |
2807 | result->time = time->time + (time->zone - tz) * USECS_PER_SEC; |
2808 | while (result->time < INT64CONST(0)) |
2809 | result->time += USECS_PER_DAY; |
2810 | while (result->time >= USECS_PER_DAY) |
2811 | result->time -= USECS_PER_DAY; |
2812 | |
2813 | result->zone = tz; |
2814 | |
2815 | PG_RETURN_TIMETZADT_P(result); |
2816 | } |
2817 | |