Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * date.c
4 : : * implements DATE and TIME data types specified in SQL standard
5 : : *
6 : : * Portions Copyright (c) 1996-2025, 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 <math.h>
22 : : #include <time.h>
23 : :
24 : : #include "access/xact.h"
25 : : #include "catalog/pg_type.h"
26 : : #include "common/hashfn.h"
27 : : #include "common/int.h"
28 : : #include "libpq/pqformat.h"
29 : : #include "miscadmin.h"
30 : : #include "nodes/supportnodes.h"
31 : : #include "parser/scansup.h"
32 : : #include "utils/array.h"
33 : : #include "utils/builtins.h"
34 : : #include "utils/date.h"
35 : : #include "utils/datetime.h"
36 : : #include "utils/numeric.h"
37 : : #include "utils/skipsupport.h"
38 : : #include "utils/sortsupport.h"
39 : :
40 : : /*
41 : : * gcc's -ffast-math switch breaks routines that expect exact results from
42 : : * expressions like timeval / SECS_PER_HOUR, where timeval is double.
43 : : */
44 : : #ifdef __FAST_MATH__
45 : : #error -ffast-math is known to break this code
46 : : #endif
47 : :
48 : :
49 : : /* common code for timetypmodin and timetztypmodin */
50 : : static int32
843 michael@paquier.xyz 51 :CBC 22 : anytime_typmodin(bool istz, ArrayType *ta)
52 : : {
53 : : int32 *tl;
54 : : int n;
55 : :
56 : 22 : 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 [ - + ]: 22 : if (n != 1)
843 michael@paquier.xyz 63 [ # # ]:UBC 0 : ereport(ERROR,
64 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
65 : : errmsg("invalid type modifier")));
66 : :
843 michael@paquier.xyz 67 :CBC 22 : return anytime_typmod_check(istz, tl[0]);
68 : : }
69 : :
70 : : /* exported so parse_expr.c can use it */
71 : : int32
3308 tgl@sss.pgh.pa.us 72 : 226 : anytime_typmod_check(bool istz, int32 typmod)
73 : : {
74 [ - + ]: 226 : if (typmod < 0)
6825 tgl@sss.pgh.pa.us 75 [ # # # # ]:UBC 0 : ereport(ERROR,
76 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
77 : : errmsg("TIME(%d)%s precision must not be negative",
78 : : typmod, (istz ? " WITH TIME ZONE" : ""))));
3308 tgl@sss.pgh.pa.us 79 [ + + ]:CBC 226 : if (typmod > MAX_TIME_PRECISION)
80 : : {
6825 81 [ + - + + ]: 18 : 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 : 18 : typmod = MAX_TIME_PRECISION;
87 : : }
88 : :
89 : 226 : return typmod;
90 : : }
91 : :
92 : : /* common code for timetypmodout and timetztypmodout */
93 : : static char *
94 : 10 : anytime_typmodout(bool istz, int32 typmod)
95 : : {
96 [ + + ]: 10 : const char *tz = istz ? " with time zone" : " without time zone";
97 : :
98 [ + - ]: 10 : if (typmod >= 0)
4261 peter_e@gmx.net 99 : 10 : return psprintf("(%d)%s", (int) typmod, tz);
100 : : else
1096 drowley@postgresql.o 101 :UBC 0 : return pstrdup(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
9220 tgl@sss.pgh.pa.us 114 :CBC 5854 : date_in(PG_FUNCTION_ARGS)
115 : : {
116 : 5854 : char *str = PG_GETARG_CSTRING(0);
1002 117 : 5854 : Node *escontext = fcinfo->context;
118 : : DateADT date;
119 : : fsec_t fsec;
120 : : struct pg_tm tt,
10225 bruce@momjian.us 121 : 5854 : *tm = &tt;
122 : : int tzp;
123 : : int dtype;
124 : : int nf;
125 : : int dterr;
126 : : char *field[MAXDATEFIELDS];
127 : : int ftype[MAXDATEFIELDS];
128 : : char workbuf[MAXDATELEN + 1];
129 : : DateTimeErrorExtra extra;
130 : :
7408 neilc@samurai.com 131 : 5854 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
132 : : field, ftype, MAXDATEFIELDS, &nf);
8046 tgl@sss.pgh.pa.us 133 [ + - ]: 5854 : if (dterr == 0)
1002 134 : 5854 : dterr = DecodeDateTime(field, ftype, nf,
135 : : &dtype, tm, &fsec, &tzp, &extra);
8046 136 [ + + ]: 5854 : if (dterr != 0)
137 : : {
1002 138 : 147 : DateTimeParseError(dterr, &extra, str, "date", escontext);
139 : 6 : PG_RETURN_NULL();
140 : : }
141 : :
10226 bruce@momjian.us 142 [ + + + + : 5707 : switch (dtype)
- ]
143 : : {
9334 lockhart@fourpalms.o 144 : 5533 : case DTK_DATE:
145 : 5533 : break;
146 : :
147 : 3 : case DTK_EPOCH:
8744 148 : 3 : GetEpochTime(tm);
9334 149 : 3 : break;
150 : :
6171 tgl@sss.pgh.pa.us 151 : 111 : case DTK_LATE:
152 : 111 : DATE_NOEND(date);
153 : 111 : PG_RETURN_DATEADT(date);
154 : :
155 : 60 : case DTK_EARLY:
156 : 60 : DATE_NOBEGIN(date);
157 : 60 : PG_RETURN_DATEADT(date);
158 : :
10225 bruce@momjian.us 159 :UBC 0 : default:
1002 tgl@sss.pgh.pa.us 160 : 0 : DateTimeParseError(DTERR_BAD_FORMAT, &extra, str, "date", escontext);
161 : 0 : PG_RETURN_NULL();
162 : : }
163 : :
164 : : /* Prevent overflow in Julian-day routines */
7149 tgl@sss.pgh.pa.us 165 [ + + + - :CBC 5536 : if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ - + + +
+ - + ]
1002 166 [ + + ]: 6 : ereturn(escontext, (Datum) 0,
167 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
168 : : errmsg("date out of range: \"%s\"", str)));
169 : :
8191 170 : 5530 : date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
171 : :
172 : : /* Now check for just-out-of-range dates */
3461 173 [ + + + + ]: 5530 : if (!IS_VALID_DATE(date))
1002 174 [ + - ]: 6 : ereturn(escontext, (Datum) 0,
175 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
176 : : errmsg("date out of range: \"%s\"", str)));
177 : :
9220 178 : 5524 : PG_RETURN_DATEADT(date);
179 : : }
180 : :
181 : : /* date_out()
182 : : * Given internal format date, convert to text string.
183 : : */
184 : : Datum
185 : 7185 : date_out(PG_FUNCTION_ARGS)
186 : : {
7266 bruce@momjian.us 187 : 7185 : DateADT date = PG_GETARG_DATEADT(0);
188 : : char *result;
189 : : struct pg_tm tt,
10225 190 : 7185 : *tm = &tt;
191 : : char buf[MAXDATELEN + 1];
192 : :
6171 tgl@sss.pgh.pa.us 193 [ + + + + ]: 7185 : if (DATE_NOT_FINITE(date))
194 : 100 : EncodeSpecialDate(date, buf);
195 : : else
196 : : {
197 : 7085 : j2date(date + POSTGRES_EPOCH_JDATE,
198 : : &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
199 : 7085 : EncodeDateOnly(tm, DateStyle, buf);
200 : : }
201 : :
9220 202 : 7185 : result = pstrdup(buf);
203 : 7185 : PG_RETURN_CSTRING(result);
204 : : }
205 : :
206 : : /*
207 : : * date_recv - converts external binary format to date
208 : : */
209 : : Datum
8153 tgl@sss.pgh.pa.us 210 :UBC 0 : date_recv(PG_FUNCTION_ARGS)
211 : : {
212 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
213 : : DateADT result;
214 : :
5846 heikki.linnakangas@i 215 : 0 : result = (DateADT) pq_getmsgint(buf, sizeof(DateADT));
216 : :
217 : : /* Limit to the same range that date_in() accepts. */
5679 itagaki.takahiro@gma 218 [ # # # # ]: 0 : if (DATE_NOT_FINITE(result))
219 : : /* ok */ ;
3461 tgl@sss.pgh.pa.us 220 [ # # # # ]: 0 : else if (!IS_VALID_DATE(result))
5846 heikki.linnakangas@i 221 [ # # ]: 0 : ereport(ERROR,
222 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
223 : : errmsg("date out of range")));
224 : :
225 : 0 : PG_RETURN_DATEADT(result);
226 : : }
227 : :
228 : : /*
229 : : * date_send - converts date to binary format
230 : : */
231 : : Datum
8153 tgl@sss.pgh.pa.us 232 : 0 : date_send(PG_FUNCTION_ARGS)
233 : : {
7266 bruce@momjian.us 234 : 0 : DateADT date = PG_GETARG_DATEADT(0);
235 : : StringInfoData buf;
236 : :
8153 tgl@sss.pgh.pa.us 237 : 0 : pq_begintypsend(&buf);
2887 andres@anarazel.de 238 : 0 : pq_sendint32(&buf, date);
8153 tgl@sss.pgh.pa.us 239 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
240 : : }
241 : :
242 : : /*
243 : : * make_date - date constructor
244 : : */
245 : : Datum
4311 tgl@sss.pgh.pa.us 246 :CBC 21 : make_date(PG_FUNCTION_ARGS)
247 : : {
248 : : struct pg_tm tm;
249 : : DateADT date;
250 : : int dterr;
3152 alvherre@alvh.no-ip. 251 : 21 : bool bc = false;
252 : :
4311 tgl@sss.pgh.pa.us 253 : 21 : tm.tm_year = PG_GETARG_INT32(0);
254 : 21 : tm.tm_mon = PG_GETARG_INT32(1);
255 : 21 : tm.tm_mday = PG_GETARG_INT32(2);
256 : :
257 : : /* Handle negative years as BC */
3152 alvherre@alvh.no-ip. 258 [ + + ]: 21 : if (tm.tm_year < 0)
259 : : {
271 nathan@postgresql.or 260 : 6 : int year = tm.tm_year;
261 : :
3152 alvherre@alvh.no-ip. 262 : 6 : bc = true;
271 nathan@postgresql.or 263 [ + + ]: 6 : if (pg_neg_s32_overflow(year, &year))
264 [ + - ]: 3 : ereport(ERROR,
265 : : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
266 : : errmsg("date field value out of range: %d-%02d-%02d",
267 : : tm.tm_year, tm.tm_mon, tm.tm_mday)));
268 : 3 : tm.tm_year = year;
269 : : }
270 : :
3152 alvherre@alvh.no-ip. 271 : 18 : dterr = ValidateDate(DTK_DATE_M, false, false, bc, &tm);
272 : :
4311 tgl@sss.pgh.pa.us 273 [ + + ]: 18 : if (dterr != 0)
274 [ + - ]: 12 : ereport(ERROR,
275 : : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
276 : : errmsg("date field value out of range: %d-%02d-%02d",
277 : : tm.tm_year, tm.tm_mon, tm.tm_mday)));
278 : :
279 : : /* Prevent overflow in Julian-day routines */
280 [ - + - - : 6 : if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
- - - + -
- - - ]
4311 tgl@sss.pgh.pa.us 281 [ # # ]:UBC 0 : ereport(ERROR,
282 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
283 : : errmsg("date out of range: %d-%02d-%02d",
284 : : tm.tm_year, tm.tm_mon, tm.tm_mday)));
285 : :
4311 tgl@sss.pgh.pa.us 286 :CBC 6 : date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
287 : :
288 : : /* Now check for just-out-of-range dates */
3461 289 [ + - - + ]: 6 : if (!IS_VALID_DATE(date))
3461 tgl@sss.pgh.pa.us 290 [ # # ]:UBC 0 : ereport(ERROR,
291 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
292 : : errmsg("date out of range: %d-%02d-%02d",
293 : : tm.tm_year, tm.tm_mon, tm.tm_mday)));
294 : :
4311 tgl@sss.pgh.pa.us 295 :CBC 6 : PG_RETURN_DATEADT(date);
296 : : }
297 : :
298 : : /*
299 : : * Convert reserved date values to string.
300 : : */
301 : : void
6171 302 : 112 : EncodeSpecialDate(DateADT dt, char *str)
303 : : {
304 [ + + ]: 112 : if (DATE_IS_NOBEGIN(dt))
305 : 56 : strcpy(str, EARLY);
306 [ + - ]: 56 : else if (DATE_IS_NOEND(dt))
307 : 56 : strcpy(str, LATE);
308 : : else /* shouldn't happen */
6171 tgl@sss.pgh.pa.us 309 [ # # ]:UBC 0 : elog(ERROR, "invalid argument for EncodeSpecialDate");
6171 tgl@sss.pgh.pa.us 310 :CBC 112 : }
311 : :
312 : :
313 : : /*
314 : : * GetSQLCurrentDate -- implements CURRENT_DATE
315 : : */
316 : : DateADT
843 michael@paquier.xyz 317 : 25 : GetSQLCurrentDate(void)
318 : : {
319 : : struct pg_tm tm;
320 : :
321 : : static int cache_year = 0;
322 : : static int cache_mon = 0;
323 : : static int cache_mday = 0;
324 : : static DateADT cache_date;
325 : :
1804 tgl@sss.pgh.pa.us 326 : 25 : GetCurrentDateTime(&tm);
327 : :
328 : : /*
329 : : * date2j involves several integer divisions; moreover, unless our session
330 : : * lives across local midnight, we don't really have to do it more than
331 : : * once. So it seems worth having a separate cache here.
332 : : */
333 [ + + ]: 25 : if (tm.tm_year != cache_year ||
334 [ + - ]: 9 : tm.tm_mon != cache_mon ||
335 [ - + ]: 9 : tm.tm_mday != cache_mday)
336 : : {
337 : 16 : cache_date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
338 : 16 : cache_year = tm.tm_year;
339 : 16 : cache_mon = tm.tm_mon;
340 : 16 : cache_mday = tm.tm_mday;
341 : : }
342 : :
843 michael@paquier.xyz 343 : 25 : return cache_date;
344 : : }
345 : :
346 : : /*
347 : : * GetSQLCurrentTime -- implements CURRENT_TIME, CURRENT_TIME(n)
348 : : */
349 : : TimeTzADT *
350 : 12 : GetSQLCurrentTime(int32 typmod)
351 : : {
352 : : TimeTzADT *result;
353 : : struct pg_tm tt,
3308 tgl@sss.pgh.pa.us 354 : 12 : *tm = &tt;
355 : : fsec_t fsec;
356 : : int tz;
357 : :
1804 358 : 12 : GetCurrentTimeUsec(tm, &fsec, &tz);
359 : :
3308 360 : 12 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
361 : 12 : tm2timetz(tm, fsec, tz, result);
362 : 12 : AdjustTimeForTypmod(&(result->time), typmod);
843 michael@paquier.xyz 363 : 12 : return result;
364 : : }
365 : :
366 : : /*
367 : : * GetSQLLocalTime -- implements LOCALTIME, LOCALTIME(n)
368 : : */
369 : : TimeADT
370 : 12 : GetSQLLocalTime(int32 typmod)
371 : : {
372 : : TimeADT result;
373 : : struct pg_tm tt,
3308 tgl@sss.pgh.pa.us 374 : 12 : *tm = &tt;
375 : : fsec_t fsec;
376 : : int tz;
377 : :
1804 378 : 12 : GetCurrentTimeUsec(tm, &fsec, &tz);
379 : :
3308 380 : 12 : tm2time(tm, fsec, &result);
381 : 12 : AdjustTimeForTypmod(&result, typmod);
843 michael@paquier.xyz 382 : 12 : return result;
383 : : }
384 : :
385 : :
386 : : /*
387 : : * Comparison functions for dates
388 : : */
389 : :
390 : : Datum
9220 tgl@sss.pgh.pa.us 391 : 32888 : date_eq(PG_FUNCTION_ARGS)
392 : : {
393 : 32888 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
394 : 32888 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
395 : :
396 : 32888 : PG_RETURN_BOOL(dateVal1 == dateVal2);
397 : : }
398 : :
399 : : Datum
9220 tgl@sss.pgh.pa.us 400 :UBC 0 : date_ne(PG_FUNCTION_ARGS)
401 : : {
402 : 0 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
403 : 0 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
404 : :
405 : 0 : PG_RETURN_BOOL(dateVal1 != dateVal2);
406 : : }
407 : :
408 : : Datum
9220 tgl@sss.pgh.pa.us 409 :CBC 79021 : date_lt(PG_FUNCTION_ARGS)
410 : : {
411 : 79021 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
412 : 79021 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
413 : :
414 : 79021 : PG_RETURN_BOOL(dateVal1 < dateVal2);
415 : : }
416 : :
417 : : Datum
418 : 3438 : date_le(PG_FUNCTION_ARGS)
419 : : {
420 : 3438 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
421 : 3438 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
422 : :
423 : 3438 : PG_RETURN_BOOL(dateVal1 <= dateVal2);
424 : : }
425 : :
426 : : Datum
427 : 4434 : date_gt(PG_FUNCTION_ARGS)
428 : : {
429 : 4434 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
430 : 4434 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
431 : :
432 : 4434 : PG_RETURN_BOOL(dateVal1 > dateVal2);
433 : : }
434 : :
435 : : Datum
436 : 3230 : date_ge(PG_FUNCTION_ARGS)
437 : : {
438 : 3230 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
439 : 3230 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
440 : :
441 : 3230 : PG_RETURN_BOOL(dateVal1 >= dateVal2);
442 : : }
443 : :
444 : : Datum
445 : 20299 : date_cmp(PG_FUNCTION_ARGS)
446 : : {
447 : 20299 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
448 : 20299 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
449 : :
9334 lockhart@fourpalms.o 450 [ + + ]: 20299 : if (dateVal1 < dateVal2)
9220 tgl@sss.pgh.pa.us 451 : 11449 : PG_RETURN_INT32(-1);
9334 lockhart@fourpalms.o 452 [ + + ]: 8850 : else if (dateVal1 > dateVal2)
9220 tgl@sss.pgh.pa.us 453 : 6747 : PG_RETURN_INT32(1);
454 : 2103 : PG_RETURN_INT32(0);
455 : : }
456 : :
457 : : Datum
5022 458 : 370 : date_sortsupport(PG_FUNCTION_ARGS)
459 : : {
4836 bruce@momjian.us 460 : 370 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
461 : :
1253 john.naylor@postgres 462 : 370 : ssup->comparator = ssup_datum_int32_cmp;
5022 tgl@sss.pgh.pa.us 463 : 370 : PG_RETURN_VOID();
464 : : }
465 : :
466 : : static Datum
155 pg@bowt.ie 467 :UBC 0 : date_decrement(Relation rel, Datum existing, bool *underflow)
468 : : {
469 : 0 : DateADT dexisting = DatumGetDateADT(existing);
470 : :
471 [ # # ]: 0 : if (dexisting == DATEVAL_NOBEGIN)
472 : : {
473 : : /* return value is undefined */
474 : 0 : *underflow = true;
475 : 0 : return (Datum) 0;
476 : : }
477 : :
478 : 0 : *underflow = false;
479 : 0 : return DateADTGetDatum(dexisting - 1);
480 : : }
481 : :
482 : : static Datum
483 : 0 : date_increment(Relation rel, Datum existing, bool *overflow)
484 : : {
485 : 0 : DateADT dexisting = DatumGetDateADT(existing);
486 : :
487 [ # # ]: 0 : if (dexisting == DATEVAL_NOEND)
488 : : {
489 : : /* return value is undefined */
490 : 0 : *overflow = true;
491 : 0 : return (Datum) 0;
492 : : }
493 : :
494 : 0 : *overflow = false;
495 : 0 : return DateADTGetDatum(dexisting + 1);
496 : : }
497 : :
498 : : Datum
499 : 0 : date_skipsupport(PG_FUNCTION_ARGS)
500 : : {
501 : 0 : SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
502 : :
503 : 0 : sksup->decrement = date_decrement;
504 : 0 : sksup->increment = date_increment;
505 : 0 : sksup->low_elem = DateADTGetDatum(DATEVAL_NOBEGIN);
506 : 0 : sksup->high_elem = DateADTGetDatum(DATEVAL_NOEND);
507 : :
508 : 0 : PG_RETURN_VOID();
509 : : }
510 : :
511 : : Datum
359 peter@eisentraut.org 512 :CBC 134 : hashdate(PG_FUNCTION_ARGS)
513 : : {
514 : 134 : return hash_uint32(PG_GETARG_DATEADT(0));
515 : : }
516 : :
517 : : Datum
359 peter@eisentraut.org 518 :UBC 0 : hashdateextended(PG_FUNCTION_ARGS)
519 : : {
520 : 0 : return hash_uint32_extended(PG_GETARG_DATEADT(0), PG_GETARG_INT64(1));
521 : : }
522 : :
523 : : Datum
6171 tgl@sss.pgh.pa.us 524 :CBC 9 : date_finite(PG_FUNCTION_ARGS)
525 : : {
526 : 9 : DateADT date = PG_GETARG_DATEADT(0);
527 : :
528 [ + + + + ]: 9 : PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
529 : : }
530 : :
531 : : Datum
9220 532 : 8 : date_larger(PG_FUNCTION_ARGS)
533 : : {
534 : 8 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
535 : 8 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
536 : :
537 : 8 : PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
538 : : }
539 : :
540 : : Datum
9220 tgl@sss.pgh.pa.us 541 :UBC 0 : date_smaller(PG_FUNCTION_ARGS)
542 : : {
543 : 0 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
544 : 0 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
545 : :
546 : 0 : PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
547 : : }
548 : :
549 : : /* Compute difference between two dates in days.
550 : : */
551 : : Datum
9220 tgl@sss.pgh.pa.us 552 :CBC 934 : date_mi(PG_FUNCTION_ARGS)
553 : : {
554 : 934 : DateADT dateVal1 = PG_GETARG_DATEADT(0);
555 : 934 : DateADT dateVal2 = PG_GETARG_DATEADT(1);
556 : :
6171 557 [ + - + - : 934 : if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
+ - - + ]
6171 tgl@sss.pgh.pa.us 558 [ # # ]:UBC 0 : ereport(ERROR,
559 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
560 : : errmsg("cannot subtract infinite dates")));
561 : :
9220 tgl@sss.pgh.pa.us 562 :CBC 934 : PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
563 : : }
564 : :
565 : : /* Add a number of days to a date, giving a new date.
566 : : * Must handle both positive and negative numbers of days.
567 : : */
568 : : Datum
569 : 1329 : date_pli(PG_FUNCTION_ARGS)
570 : : {
571 : 1329 : DateADT dateVal = PG_GETARG_DATEADT(0);
572 : 1329 : int32 days = PG_GETARG_INT32(1);
573 : : DateADT result;
574 : :
6171 575 [ + - - + ]: 1329 : if (DATE_NOT_FINITE(dateVal))
2999 tgl@sss.pgh.pa.us 576 :UBC 0 : PG_RETURN_DATEADT(dateVal); /* can't change infinity */
577 : :
3461 tgl@sss.pgh.pa.us 578 :CBC 1329 : result = dateVal + days;
579 : :
580 : : /* Check for integer overflow and out-of-allowed-range */
581 [ + - + - : 1329 : if ((days >= 0 ? (result < dateVal) : (result > dateVal)) ||
- - + - ]
582 [ - + ]: 1329 : !IS_VALID_DATE(result))
3461 tgl@sss.pgh.pa.us 583 [ # # ]:UBC 0 : ereport(ERROR,
584 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
585 : : errmsg("date out of range")));
586 : :
3461 tgl@sss.pgh.pa.us 587 :CBC 1329 : PG_RETURN_DATEADT(result);
588 : : }
589 : :
590 : : /* Subtract a number of days from a date, giving a new date.
591 : : */
592 : : Datum
9220 593 : 18 : date_mii(PG_FUNCTION_ARGS)
594 : : {
595 : 18 : DateADT dateVal = PG_GETARG_DATEADT(0);
596 : 18 : int32 days = PG_GETARG_INT32(1);
597 : : DateADT result;
598 : :
6171 599 [ + - - + ]: 18 : if (DATE_NOT_FINITE(dateVal))
2999 tgl@sss.pgh.pa.us 600 :UBC 0 : PG_RETURN_DATEADT(dateVal); /* can't change infinity */
601 : :
3461 tgl@sss.pgh.pa.us 602 :CBC 18 : result = dateVal - days;
603 : :
604 : : /* Check for integer overflow and out-of-allowed-range */
605 [ + - + - : 18 : if ((days >= 0 ? (result > dateVal) : (result < dateVal)) ||
- - + - ]
606 [ - + ]: 18 : !IS_VALID_DATE(result))
3461 tgl@sss.pgh.pa.us 607 [ # # ]:UBC 0 : ereport(ERROR,
608 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
609 : : errmsg("date out of range")));
610 : :
3461 tgl@sss.pgh.pa.us 611 :CBC 18 : PG_RETURN_DATEADT(result);
612 : : }
613 : :
614 : :
615 : : /*
616 : : * Promote date to timestamp.
617 : : *
618 : : * On successful conversion, *overflow is set to zero if it's not NULL.
619 : : *
620 : : * If the date is finite but out of the valid range for timestamp, then:
621 : : * if overflow is NULL, we throw an out-of-range error.
622 : : * if overflow is not NULL, we store +1 or -1 there to indicate the sign
623 : : * of the overflow, and return the appropriate timestamp infinity.
624 : : *
625 : : * Note: *overflow = -1 is actually not possible currently, since both
626 : : * datatypes have the same lower bound, Julian day zero.
627 : : */
628 : : Timestamp
2147 akorotkov@postgresql 629 : 2377 : date2timestamp_opt_overflow(DateADT dateVal, int *overflow)
630 : : {
631 : : Timestamp result;
632 : :
1795 tgl@sss.pgh.pa.us 633 [ + + ]: 2377 : if (overflow)
634 : 250 : *overflow = 0;
635 : :
6171 636 [ + + ]: 2377 : if (DATE_IS_NOBEGIN(dateVal))
637 : 48 : TIMESTAMP_NOBEGIN(result);
638 [ + + ]: 2329 : else if (DATE_IS_NOEND(dateVal))
639 : 46 : TIMESTAMP_NOEND(result);
640 : : else
641 : : {
642 : : /*
643 : : * Since dates have the same minimum values as timestamps, only upper
644 : : * boundary need be checked for overflow.
645 : : */
3461 646 [ + + ]: 2283 : if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
647 : : {
2147 akorotkov@postgresql 648 [ + + ]: 12 : if (overflow)
649 : : {
650 : 9 : *overflow = 1;
1795 tgl@sss.pgh.pa.us 651 : 9 : TIMESTAMP_NOEND(result);
652 : 9 : return result;
653 : : }
654 : : else
655 : : {
2173 akorotkov@postgresql 656 [ + - ]: 3 : ereport(ERROR,
657 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
658 : : errmsg("date out of range for timestamp")));
659 : : }
660 : : }
661 : :
662 : : /* date is days since 2000, timestamp is microseconds since same... */
3461 tgl@sss.pgh.pa.us 663 : 2271 : result = dateVal * USECS_PER_DAY;
664 : : }
665 : :
6555 666 : 2365 : return result;
667 : : }
668 : :
669 : : /*
670 : : * Promote date to timestamp, throwing error for overflow.
671 : : */
672 : : static TimestampTz
2173 akorotkov@postgresql 673 : 2127 : date2timestamp(DateADT dateVal)
674 : : {
2147 675 : 2127 : return date2timestamp_opt_overflow(dateVal, NULL);
676 : : }
677 : :
678 : : /*
679 : : * Promote date to timestamp with time zone.
680 : : *
681 : : * On successful conversion, *overflow is set to zero if it's not NULL.
682 : : *
683 : : * If the date is finite but out of the valid range for timestamptz, then:
684 : : * if overflow is NULL, we throw an out-of-range error.
685 : : * if overflow is not NULL, we store +1 or -1 there to indicate the sign
686 : : * of the overflow, and return the appropriate timestamptz infinity.
687 : : */
688 : : TimestampTz
689 : 292 : date2timestamptz_opt_overflow(DateADT dateVal, int *overflow)
690 : : {
691 : : TimestampTz result;
692 : : struct pg_tm tt,
7875 tgl@sss.pgh.pa.us 693 : 292 : *tm = &tt;
694 : : int tz;
695 : :
1795 696 [ + + ]: 292 : if (overflow)
697 : 183 : *overflow = 0;
698 : :
6171 699 [ + + ]: 292 : if (DATE_IS_NOBEGIN(dateVal))
6171 tgl@sss.pgh.pa.us 700 :GBC 9 : TIMESTAMP_NOBEGIN(result);
6171 tgl@sss.pgh.pa.us 701 [ + + ]:CBC 283 : else if (DATE_IS_NOEND(dateVal))
6171 tgl@sss.pgh.pa.us 702 :GBC 9 : TIMESTAMP_NOEND(result);
703 : : else
704 : : {
705 : : /*
706 : : * Since dates have the same minimum values as timestamps, only upper
707 : : * boundary need be checked for overflow.
708 : : */
3461 tgl@sss.pgh.pa.us 709 [ + + ]:CBC 274 : if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
710 : : {
2147 akorotkov@postgresql 711 [ + + ]: 9 : if (overflow)
712 : : {
713 : 6 : *overflow = 1;
1795 tgl@sss.pgh.pa.us 714 : 6 : TIMESTAMP_NOEND(result);
715 : 6 : return result;
716 : : }
717 : : else
718 : : {
2173 akorotkov@postgresql 719 [ + - ]: 3 : ereport(ERROR,
720 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
721 : : errmsg("date out of range for timestamp")));
722 : : }
723 : : }
724 : :
6171 tgl@sss.pgh.pa.us 725 : 265 : j2date(dateVal + POSTGRES_EPOCH_JDATE,
726 : : &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
727 : 265 : tm->tm_hour = 0;
728 : 265 : tm->tm_min = 0;
729 : 265 : tm->tm_sec = 0;
730 : 265 : tz = DetermineTimeZoneOffset(tm, session_timezone);
731 : :
732 : 265 : result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
733 : :
734 : : /*
735 : : * Since it is possible to go beyond allowed timestamptz range because
736 : : * of time zone, check for allowed timestamp range after adding tz.
737 : : */
3461 738 [ + + - + ]: 265 : if (!IS_VALID_TIMESTAMP(result))
739 : : {
2147 akorotkov@postgresql 740 [ + + ]: 9 : if (overflow)
741 : : {
742 [ + - ]: 6 : if (result < MIN_TIMESTAMP)
743 : : {
744 : 6 : *overflow = -1;
1795 tgl@sss.pgh.pa.us 745 : 6 : TIMESTAMP_NOBEGIN(result);
746 : : }
747 : : else
748 : : {
2147 akorotkov@postgresql 749 :UBC 0 : *overflow = 1;
1795 tgl@sss.pgh.pa.us 750 : 0 : TIMESTAMP_NOEND(result);
751 : : }
752 : : }
753 : : else
754 : : {
2173 akorotkov@postgresql 755 [ + - ]:CBC 3 : ereport(ERROR,
756 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
757 : : errmsg("date out of range for timestamp")));
758 : : }
759 : : }
760 : : }
761 : :
7875 tgl@sss.pgh.pa.us 762 : 280 : return result;
763 : : }
764 : :
765 : : /*
766 : : * Promote date to timestamptz, throwing error for overflow.
767 : : */
768 : : static TimestampTz
2173 akorotkov@postgresql 769 : 109 : date2timestamptz(DateADT dateVal)
770 : : {
2147 771 : 109 : return date2timestamptz_opt_overflow(dateVal, NULL);
772 : : }
773 : :
774 : : /*
775 : : * date2timestamp_no_overflow
776 : : *
777 : : * This is chartered to produce a double value that is numerically
778 : : * equivalent to the corresponding Timestamp value, if the date is in the
779 : : * valid range of Timestamps, but in any case not throw an overflow error.
780 : : * We can do this since the numerical range of double is greater than
781 : : * that of non-erroneous timestamps. The results are currently only
782 : : * used for statistical estimation purposes.
783 : : */
784 : : double
5366 tgl@sss.pgh.pa.us 785 :UBC 0 : date2timestamp_no_overflow(DateADT dateVal)
786 : : {
787 : : double result;
788 : :
789 [ # # ]: 0 : if (DATE_IS_NOBEGIN(dateVal))
790 : 0 : result = -DBL_MAX;
791 [ # # ]: 0 : else if (DATE_IS_NOEND(dateVal))
792 : 0 : result = DBL_MAX;
793 : : else
794 : : {
795 : : /* date is days since 2000, timestamp is microseconds since same... */
796 : 0 : result = dateVal * (double) USECS_PER_DAY;
797 : : }
798 : :
799 : 0 : return result;
800 : : }
801 : :
802 : :
803 : : /*
804 : : * Crosstype comparison functions for dates
805 : : */
806 : :
807 : : int32
1795 tgl@sss.pgh.pa.us 808 :CBC 232 : date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2)
809 : : {
810 : : Timestamp dt1;
811 : : int overflow;
812 : :
813 : 232 : dt1 = date2timestamp_opt_overflow(dateVal, &overflow);
814 [ + + ]: 232 : if (overflow > 0)
815 : : {
816 : : /* dt1 is larger than any finite timestamp, but less than infinity */
817 [ - + ]: 9 : return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
818 : : }
819 [ - + ]: 223 : Assert(overflow == 0); /* -1 case cannot occur */
820 : :
821 : 223 : return timestamp_cmp_internal(dt1, dt2);
822 : : }
823 : :
824 : : Datum
7875 tgl@sss.pgh.pa.us 825 :UBC 0 : date_eq_timestamp(PG_FUNCTION_ARGS)
826 : : {
827 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
828 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
829 : :
1795 830 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) == 0);
831 : : }
832 : :
833 : : Datum
7875 834 : 0 : date_ne_timestamp(PG_FUNCTION_ARGS)
835 : : {
836 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
837 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
838 : :
1795 839 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) != 0);
840 : : }
841 : :
842 : : Datum
7875 843 : 0 : date_lt_timestamp(PG_FUNCTION_ARGS)
844 : : {
845 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
846 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
847 : :
1795 848 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) < 0);
849 : : }
850 : :
851 : : Datum
7875 tgl@sss.pgh.pa.us 852 :CBC 3 : date_gt_timestamp(PG_FUNCTION_ARGS)
853 : : {
854 : 3 : DateADT dateVal = PG_GETARG_DATEADT(0);
855 : 3 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
856 : :
1795 857 : 3 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) > 0);
858 : : }
859 : :
860 : : Datum
7875 tgl@sss.pgh.pa.us 861 :UBC 0 : date_le_timestamp(PG_FUNCTION_ARGS)
862 : : {
863 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
864 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
865 : :
1795 866 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) <= 0);
867 : : }
868 : :
869 : : Datum
7875 870 : 0 : date_ge_timestamp(PG_FUNCTION_ARGS)
871 : : {
872 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
873 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
874 : :
1795 875 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt2) >= 0);
876 : : }
877 : :
878 : : Datum
7875 tgl@sss.pgh.pa.us 879 :GBC 55 : date_cmp_timestamp(PG_FUNCTION_ARGS)
880 : : {
881 : 55 : DateADT dateVal = PG_GETARG_DATEADT(0);
882 : 55 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
883 : :
1795 884 : 55 : PG_RETURN_INT32(date_cmp_timestamp_internal(dateVal, dt2));
885 : : }
886 : :
887 : : int32
1795 tgl@sss.pgh.pa.us 888 :CBC 177 : date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2)
889 : : {
890 : : TimestampTz dt1;
891 : : int overflow;
892 : :
893 : 177 : dt1 = date2timestamptz_opt_overflow(dateVal, &overflow);
894 [ + + ]: 177 : if (overflow > 0)
895 : : {
896 : : /* dt1 is larger than any finite timestamp, but less than infinity */
897 [ - + ]: 6 : return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
898 : : }
899 [ + + ]: 171 : if (overflow < 0)
900 : : {
901 : : /* dt1 is less than any finite timestamp, but more than -infinity */
902 [ - + ]: 6 : return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1;
903 : : }
904 : :
905 : 165 : return timestamptz_cmp_internal(dt1, dt2);
906 : : }
907 : :
908 : : Datum
7875 tgl@sss.pgh.pa.us 909 :UBC 0 : date_eq_timestamptz(PG_FUNCTION_ARGS)
910 : : {
911 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
7678 bruce@momjian.us 912 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
913 : :
1795 tgl@sss.pgh.pa.us 914 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) == 0);
915 : : }
916 : :
917 : : Datum
7875 918 : 0 : date_ne_timestamptz(PG_FUNCTION_ARGS)
919 : : {
920 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
7678 bruce@momjian.us 921 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
922 : :
1795 tgl@sss.pgh.pa.us 923 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) != 0);
924 : : }
925 : :
926 : : Datum
7875 tgl@sss.pgh.pa.us 927 :CBC 3 : date_lt_timestamptz(PG_FUNCTION_ARGS)
928 : : {
929 : 3 : DateADT dateVal = PG_GETARG_DATEADT(0);
7678 bruce@momjian.us 930 : 3 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
931 : :
1795 tgl@sss.pgh.pa.us 932 : 3 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) < 0);
933 : : }
934 : :
935 : : Datum
7875 936 : 3 : date_gt_timestamptz(PG_FUNCTION_ARGS)
937 : : {
938 : 3 : DateADT dateVal = PG_GETARG_DATEADT(0);
7678 bruce@momjian.us 939 : 3 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
940 : :
1795 tgl@sss.pgh.pa.us 941 : 3 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) > 0);
942 : : }
943 : :
944 : : Datum
7875 tgl@sss.pgh.pa.us 945 :UBC 0 : date_le_timestamptz(PG_FUNCTION_ARGS)
946 : : {
947 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
7678 bruce@momjian.us 948 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
949 : :
1795 tgl@sss.pgh.pa.us 950 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) <= 0);
951 : : }
952 : :
953 : : Datum
7875 954 : 0 : date_ge_timestamptz(PG_FUNCTION_ARGS)
955 : : {
956 : 0 : DateADT dateVal = PG_GETARG_DATEADT(0);
7678 bruce@momjian.us 957 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
958 : :
1795 tgl@sss.pgh.pa.us 959 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt2) >= 0);
960 : : }
961 : :
962 : : Datum
7875 tgl@sss.pgh.pa.us 963 :GBC 15 : date_cmp_timestamptz(PG_FUNCTION_ARGS)
964 : : {
965 : 15 : DateADT dateVal = PG_GETARG_DATEADT(0);
7678 bruce@momjian.us 966 : 15 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
967 : :
1795 tgl@sss.pgh.pa.us 968 : 15 : PG_RETURN_INT32(date_cmp_timestamptz_internal(dateVal, dt2));
969 : : }
970 : :
971 : : Datum
7875 tgl@sss.pgh.pa.us 972 :UBC 0 : timestamp_eq_date(PG_FUNCTION_ARGS)
973 : : {
974 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
975 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
976 : :
1795 977 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) == 0);
978 : : }
979 : :
980 : : Datum
7875 981 : 0 : timestamp_ne_date(PG_FUNCTION_ARGS)
982 : : {
983 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
984 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
985 : :
1795 986 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) != 0);
987 : : }
988 : :
989 : : Datum
7875 990 : 0 : timestamp_lt_date(PG_FUNCTION_ARGS)
991 : : {
992 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
993 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
994 : :
1795 995 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) > 0);
996 : : }
997 : :
998 : : Datum
7875 tgl@sss.pgh.pa.us 999 :CBC 3 : timestamp_gt_date(PG_FUNCTION_ARGS)
1000 : : {
1001 : 3 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1002 : 3 : DateADT dateVal = PG_GETARG_DATEADT(1);
1003 : :
1795 1004 : 3 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) < 0);
1005 : : }
1006 : :
1007 : : Datum
7875 tgl@sss.pgh.pa.us 1008 :UBC 0 : timestamp_le_date(PG_FUNCTION_ARGS)
1009 : : {
1010 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1011 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1012 : :
1795 1013 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) >= 0);
1014 : : }
1015 : :
1016 : : Datum
7875 1017 : 0 : timestamp_ge_date(PG_FUNCTION_ARGS)
1018 : : {
1019 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1020 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1021 : :
1795 1022 : 0 : PG_RETURN_BOOL(date_cmp_timestamp_internal(dateVal, dt1) <= 0);
1023 : : }
1024 : :
1025 : : Datum
7875 tgl@sss.pgh.pa.us 1026 :GBC 78 : timestamp_cmp_date(PG_FUNCTION_ARGS)
1027 : : {
1028 : 78 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
1029 : 78 : DateADT dateVal = PG_GETARG_DATEADT(1);
1030 : :
1795 1031 : 78 : PG_RETURN_INT32(-date_cmp_timestamp_internal(dateVal, dt1));
1032 : : }
1033 : :
1034 : : Datum
7875 tgl@sss.pgh.pa.us 1035 :UBC 0 : timestamptz_eq_date(PG_FUNCTION_ARGS)
1036 : : {
7678 bruce@momjian.us 1037 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7875 tgl@sss.pgh.pa.us 1038 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1039 : :
1795 1040 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) == 0);
1041 : : }
1042 : :
1043 : : Datum
7875 1044 : 0 : timestamptz_ne_date(PG_FUNCTION_ARGS)
1045 : : {
7678 bruce@momjian.us 1046 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7875 tgl@sss.pgh.pa.us 1047 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1048 : :
1795 1049 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) != 0);
1050 : : }
1051 : :
1052 : : Datum
7875 1053 : 0 : timestamptz_lt_date(PG_FUNCTION_ARGS)
1054 : : {
7678 bruce@momjian.us 1055 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7875 tgl@sss.pgh.pa.us 1056 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1057 : :
1795 1058 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) > 0);
1059 : : }
1060 : :
1061 : : Datum
7875 tgl@sss.pgh.pa.us 1062 :CBC 3 : timestamptz_gt_date(PG_FUNCTION_ARGS)
1063 : : {
7678 bruce@momjian.us 1064 : 3 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7875 tgl@sss.pgh.pa.us 1065 : 3 : DateADT dateVal = PG_GETARG_DATEADT(1);
1066 : :
1795 1067 : 3 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) < 0);
1068 : : }
1069 : :
1070 : : Datum
7875 tgl@sss.pgh.pa.us 1071 :UBC 0 : timestamptz_le_date(PG_FUNCTION_ARGS)
1072 : : {
7678 bruce@momjian.us 1073 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7875 tgl@sss.pgh.pa.us 1074 : 0 : DateADT dateVal = PG_GETARG_DATEADT(1);
1075 : :
1795 1076 : 0 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) >= 0);
1077 : : }
1078 : :
1079 : : Datum
7875 tgl@sss.pgh.pa.us 1080 :CBC 3 : timestamptz_ge_date(PG_FUNCTION_ARGS)
1081 : : {
7678 bruce@momjian.us 1082 : 3 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7875 tgl@sss.pgh.pa.us 1083 : 3 : DateADT dateVal = PG_GETARG_DATEADT(1);
1084 : :
1795 1085 : 3 : PG_RETURN_BOOL(date_cmp_timestamptz_internal(dateVal, dt1) <= 0);
1086 : : }
1087 : :
1088 : : Datum
7875 tgl@sss.pgh.pa.us 1089 :GBC 78 : timestamptz_cmp_date(PG_FUNCTION_ARGS)
1090 : : {
7678 bruce@momjian.us 1091 : 78 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7875 tgl@sss.pgh.pa.us 1092 : 78 : DateADT dateVal = PG_GETARG_DATEADT(1);
1093 : :
1795 1094 : 78 : PG_RETURN_INT32(-date_cmp_timestamptz_internal(dateVal, dt1));
1095 : : }
1096 : :
1097 : : /*
1098 : : * in_range support function for date.
1099 : : *
1100 : : * We implement this by promoting the dates to timestamp (without time zone)
1101 : : * and then using the timestamp-and-interval in_range function.
1102 : : */
1103 : : Datum
2768 tgl@sss.pgh.pa.us 1104 :CBC 669 : in_range_date_interval(PG_FUNCTION_ARGS)
1105 : : {
1106 : 669 : DateADT val = PG_GETARG_DATEADT(0);
1107 : 669 : DateADT base = PG_GETARG_DATEADT(1);
1108 : 669 : Interval *offset = PG_GETARG_INTERVAL_P(2);
1109 : 669 : bool sub = PG_GETARG_BOOL(3);
1110 : 669 : bool less = PG_GETARG_BOOL(4);
1111 : : Timestamp valStamp;
1112 : : Timestamp baseStamp;
1113 : :
1114 : : /* XXX we could support out-of-range cases here, perhaps */
1115 : 669 : valStamp = date2timestamp(val);
1116 : 669 : baseStamp = date2timestamp(base);
1117 : :
1118 : 669 : return DirectFunctionCall5(in_range_timestamp_interval,
1119 : : TimestampGetDatum(valStamp),
1120 : : TimestampGetDatum(baseStamp),
1121 : : IntervalPGetDatum(offset),
1122 : : BoolGetDatum(sub),
1123 : : BoolGetDatum(less));
1124 : : }
1125 : :
1126 : :
1127 : : /* extract_date()
1128 : : * Extract specified field from date type.
1129 : : */
1130 : : Datum
1614 peter@eisentraut.org 1131 : 339 : extract_date(PG_FUNCTION_ARGS)
1132 : : {
1133 : 339 : text *units = PG_GETARG_TEXT_PP(0);
1134 : 339 : DateADT date = PG_GETARG_DATEADT(1);
1135 : : int64 intresult;
1136 : : int type,
1137 : : val;
1138 : : char *lowunits;
1139 : : int year,
1140 : : mon,
1141 : : mday;
1142 : :
1143 [ - + ]: 339 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
1144 [ - + - - : 339 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
1145 : : false);
1146 : :
1147 : 339 : type = DecodeUnits(0, lowunits, &val);
1148 [ + + ]: 339 : if (type == UNKNOWN_FIELD)
1149 : 57 : type = DecodeSpecial(0, lowunits, &val);
1150 : :
1151 [ + + + + : 339 : if (DATE_NOT_FINITE(date) && (type == UNITS || type == RESERV))
+ + + + ]
1152 : : {
1153 [ + + - ]: 54 : switch (val)
1154 : : {
1155 : : /* Oscillating units */
1156 : 27 : case DTK_DAY:
1157 : : case DTK_MONTH:
1158 : : case DTK_QUARTER:
1159 : : case DTK_WEEK:
1160 : : case DTK_DOW:
1161 : : case DTK_ISODOW:
1162 : : case DTK_DOY:
1163 : 27 : PG_RETURN_NULL();
1164 : : break;
1165 : :
1166 : : /* Monotonically-increasing units */
1167 : 27 : case DTK_YEAR:
1168 : : case DTK_DECADE:
1169 : : case DTK_CENTURY:
1170 : : case DTK_MILLENNIUM:
1171 : : case DTK_JULIAN:
1172 : : case DTK_ISOYEAR:
1173 : : case DTK_EPOCH:
1174 [ + + ]: 27 : if (DATE_IS_NOBEGIN(date))
1175 : 3 : PG_RETURN_NUMERIC(DatumGetNumeric(DirectFunctionCall3(numeric_in,
1176 : : CStringGetDatum("-Infinity"),
1177 : : ObjectIdGetDatum(InvalidOid),
1178 : : Int32GetDatum(-1))));
1179 : : else
1180 : 24 : PG_RETURN_NUMERIC(DatumGetNumeric(DirectFunctionCall3(numeric_in,
1181 : : CStringGetDatum("Infinity"),
1182 : : ObjectIdGetDatum(InvalidOid),
1183 : : Int32GetDatum(-1))));
1614 peter@eisentraut.org 1184 :UBC 0 : default:
1185 [ # # ]: 0 : ereport(ERROR,
1186 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1187 : : errmsg("unit \"%s\" not supported for type %s",
1188 : : lowunits, format_type_be(DATEOID))));
1189 : : }
1190 : : }
1614 peter@eisentraut.org 1191 [ + + ]:CBC 285 : else if (type == UNITS)
1192 : : {
1193 : 276 : j2date(date + POSTGRES_EPOCH_JDATE, &year, &mon, &mday);
1194 : :
1195 [ + + + + : 276 : switch (val)
+ + + + +
+ + + + ]
1196 : : {
1197 : 3 : case DTK_DAY:
1198 : 3 : intresult = mday;
1199 : 3 : break;
1200 : :
1201 : 45 : case DTK_MONTH:
1202 : 45 : intresult = mon;
1203 : 45 : break;
1204 : :
1205 : 3 : case DTK_QUARTER:
1206 : 3 : intresult = (mon - 1) / 3 + 1;
1207 : 3 : break;
1208 : :
1209 : 3 : case DTK_WEEK:
1210 : 3 : intresult = date2isoweek(year, mon, mday);
1211 : 3 : break;
1212 : :
1213 : 93 : case DTK_YEAR:
1214 [ + + ]: 93 : if (year > 0)
1215 : 90 : intresult = year;
1216 : : else
1217 : : /* there is no year 0, just 1 BC and 1 AD */
1218 : 3 : intresult = year - 1;
1219 : 93 : break;
1220 : :
1221 : 24 : case DTK_DECADE:
1222 : : /* see comments in timestamp_part */
1223 [ + + ]: 24 : if (year >= 0)
1224 : 15 : intresult = year / 10;
1225 : : else
1226 : 9 : intresult = -((8 - (year - 1)) / 10);
1227 : 24 : break;
1228 : :
1229 : 33 : case DTK_CENTURY:
1230 : : /* see comments in timestamp_part */
1231 [ + + ]: 33 : if (year > 0)
1232 : 24 : intresult = (year + 99) / 100;
1233 : : else
1234 : 9 : intresult = -((99 - (year - 1)) / 100);
1235 : 33 : break;
1236 : :
1237 : 24 : case DTK_MILLENNIUM:
1238 : : /* see comments in timestamp_part */
1239 [ + + ]: 24 : if (year > 0)
1240 : 21 : intresult = (year + 999) / 1000;
1241 : : else
1242 : 3 : intresult = -((999 - (year - 1)) / 1000);
1243 : 24 : break;
1244 : :
1245 : 3 : case DTK_JULIAN:
1246 : 3 : intresult = date + POSTGRES_EPOCH_JDATE;
1247 : 3 : break;
1248 : :
1249 : 6 : case DTK_ISOYEAR:
1250 : 6 : intresult = date2isoyear(year, mon, mday);
1251 : : /* Adjust BC years */
1252 [ + + ]: 6 : if (intresult <= 0)
1253 : 3 : intresult -= 1;
1254 : 6 : break;
1255 : :
1256 : 12 : case DTK_DOW:
1257 : : case DTK_ISODOW:
1258 : 12 : intresult = j2day(date + POSTGRES_EPOCH_JDATE);
1259 [ + + + + ]: 12 : if (val == DTK_ISODOW && intresult == 0)
1260 : 3 : intresult = 7;
1261 : 12 : break;
1262 : :
1263 : 3 : case DTK_DOY:
1264 : 3 : intresult = date2j(year, mon, mday) - date2j(year, 1, 1) + 1;
1265 : 3 : break;
1266 : :
1267 : 24 : default:
1268 [ + - ]: 24 : ereport(ERROR,
1269 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1270 : : errmsg("unit \"%s\" not supported for type %s",
1271 : : lowunits, format_type_be(DATEOID))));
1272 : : intresult = 0;
1273 : : }
1274 : : }
1275 [ + + ]: 9 : else if (type == RESERV)
1276 : : {
1277 [ + - ]: 6 : switch (val)
1278 : : {
1279 : 6 : case DTK_EPOCH:
1280 : 6 : intresult = ((int64) date + POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
1281 : 6 : break;
1282 : :
1614 peter@eisentraut.org 1283 :UBC 0 : default:
1284 [ # # ]: 0 : ereport(ERROR,
1285 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1286 : : errmsg("unit \"%s\" not supported for type %s",
1287 : : lowunits, format_type_be(DATEOID))));
1288 : : intresult = 0;
1289 : : }
1290 : : }
1291 : : else
1292 : : {
1614 peter@eisentraut.org 1293 [ + - ]:CBC 3 : ereport(ERROR,
1294 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1295 : : errmsg("unit \"%s\" not recognized for type %s",
1296 : : lowunits, format_type_be(DATEOID))));
1297 : : intresult = 0;
1298 : : }
1299 : :
1300 : 258 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
1301 : : }
1302 : :
1303 : :
1304 : : /* Add an interval to a date, giving a new date.
1305 : : * Must handle both positive and negative intervals.
1306 : : *
1307 : : * We implement this by promoting the date to timestamp (without time zone)
1308 : : * and then using the timestamp plus interval function.
1309 : : */
1310 : : Datum
8539 lockhart@fourpalms.o 1311 : 21 : date_pl_interval(PG_FUNCTION_ARGS)
1312 : : {
1313 : 21 : DateADT dateVal = PG_GETARG_DATEADT(0);
1314 : 21 : Interval *span = PG_GETARG_INTERVAL_P(1);
1315 : : Timestamp dateStamp;
1316 : :
7875 tgl@sss.pgh.pa.us 1317 : 21 : dateStamp = date2timestamp(dateVal);
1318 : :
1319 : 21 : return DirectFunctionCall2(timestamp_pl_interval,
1320 : : TimestampGetDatum(dateStamp),
1321 : : PointerGetDatum(span));
1322 : : }
1323 : :
1324 : : /* Subtract an interval from a date, giving a new date.
1325 : : * Must handle both positive and negative intervals.
1326 : : *
1327 : : * We implement this by promoting the date to timestamp (without time zone)
1328 : : * and then using the timestamp minus interval function.
1329 : : */
1330 : : Datum
8539 lockhart@fourpalms.o 1331 : 24 : date_mi_interval(PG_FUNCTION_ARGS)
1332 : : {
1333 : 24 : DateADT dateVal = PG_GETARG_DATEADT(0);
1334 : 24 : Interval *span = PG_GETARG_INTERVAL_P(1);
1335 : : Timestamp dateStamp;
1336 : :
7875 tgl@sss.pgh.pa.us 1337 : 24 : dateStamp = date2timestamp(dateVal);
1338 : :
1339 : 24 : return DirectFunctionCall2(timestamp_mi_interval,
1340 : : TimestampGetDatum(dateStamp),
1341 : : PointerGetDatum(span));
1342 : : }
1343 : :
1344 : : /* date_timestamp()
1345 : : * Convert date to timestamp data type.
1346 : : */
1347 : : Datum
9220 1348 : 729 : date_timestamp(PG_FUNCTION_ARGS)
1349 : : {
1350 : 729 : DateADT dateVal = PG_GETARG_DATEADT(0);
1351 : : Timestamp result;
1352 : :
7875 1353 : 729 : result = date2timestamp(dateVal);
1354 : :
8744 lockhart@fourpalms.o 1355 : 726 : PG_RETURN_TIMESTAMP(result);
1356 : : }
1357 : :
1358 : : /* timestamp_date()
1359 : : * Convert timestamp to date data type.
1360 : : */
1361 : : Datum
1362 : 2009 : timestamp_date(PG_FUNCTION_ARGS)
1363 : : {
7266 bruce@momjian.us 1364 : 2009 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1365 : : DateADT result;
1366 : :
65 tgl@sss.pgh.pa.us 1367 :GNC 2009 : result = timestamp2date_opt_overflow(timestamp, NULL);
1368 : 2009 : PG_RETURN_DATEADT(result);
1369 : : }
1370 : :
1371 : : /*
1372 : : * Convert timestamp to date.
1373 : : *
1374 : : * On successful conversion, *overflow is set to zero if it's not NULL.
1375 : : *
1376 : : * If the timestamp is finite but out of the valid range for date, then:
1377 : : * if overflow is NULL, we throw an out-of-range error.
1378 : : * if overflow is not NULL, we store +1 or -1 there to indicate the sign
1379 : : * of the overflow, and return the appropriate date infinity.
1380 : : *
1381 : : * Note: given the ranges of the types, overflow is only possible at
1382 : : * the minimum end of the range, but we don't assume that in this code.
1383 : : */
1384 : : DateADT
1385 : 2033 : timestamp2date_opt_overflow(Timestamp timestamp, int *overflow)
1386 : : {
1387 : : DateADT result;
1388 : : struct pg_tm tt,
8080 tgl@sss.pgh.pa.us 1389 :CBC 2033 : *tm = &tt;
1390 : : fsec_t fsec;
1391 : :
65 tgl@sss.pgh.pa.us 1392 [ + + ]:GNC 2033 : if (overflow)
1393 : 24 : *overflow = 0;
1394 : :
6171 tgl@sss.pgh.pa.us 1395 [ + + ]:CBC 2033 : if (TIMESTAMP_IS_NOBEGIN(timestamp))
6171 tgl@sss.pgh.pa.us 1396 :GBC 6 : DATE_NOBEGIN(result);
6171 tgl@sss.pgh.pa.us 1397 [ + + ]:CBC 2027 : else if (TIMESTAMP_IS_NOEND(timestamp))
6171 tgl@sss.pgh.pa.us 1398 :GBC 6 : DATE_NOEND(result);
1399 : : else
1400 : : {
6171 tgl@sss.pgh.pa.us 1401 [ - + ]:CBC 2021 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1402 : : {
65 tgl@sss.pgh.pa.us 1403 [ # # ]:UNC 0 : if (overflow)
1404 : : {
1405 [ # # ]: 0 : if (timestamp < 0)
1406 : : {
1407 : 0 : *overflow = -1;
1408 : 0 : DATE_NOBEGIN(result);
1409 : : }
1410 : : else
1411 : : {
1412 : 0 : *overflow = 1; /* not actually reachable */
1413 : 0 : DATE_NOEND(result);
1414 : : }
1415 : 0 : return result;
1416 : : }
6171 tgl@sss.pgh.pa.us 1417 [ # # ]:UBC 0 : ereport(ERROR,
1418 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1419 : : errmsg("timestamp out of range")));
1420 : : }
1421 : :
6171 tgl@sss.pgh.pa.us 1422 :CBC 2021 : result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1423 : : }
1424 : :
65 tgl@sss.pgh.pa.us 1425 :GNC 2033 : return result;
1426 : : }
1427 : :
1428 : :
1429 : : /* date_timestamptz()
1430 : : * Convert date to timestamp with time zone data type.
1431 : : */
1432 : : Datum
8744 lockhart@fourpalms.o 1433 :CBC 109 : date_timestamptz(PG_FUNCTION_ARGS)
1434 : : {
1435 : 109 : DateADT dateVal = PG_GETARG_DATEADT(0);
1436 : : TimestampTz result;
1437 : :
7875 tgl@sss.pgh.pa.us 1438 : 109 : result = date2timestamptz(dateVal);
1439 : :
9220 1440 : 103 : PG_RETURN_TIMESTAMP(result);
1441 : : }
1442 : :
1443 : :
1444 : : /* timestamptz_date()
1445 : : * Convert timestamp with time zone to date data type.
1446 : : */
1447 : : Datum
8744 lockhart@fourpalms.o 1448 : 1962 : timestamptz_date(PG_FUNCTION_ARGS)
1449 : : {
8717 bruce@momjian.us 1450 : 1962 : TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
1451 : : DateADT result;
1452 : :
65 tgl@sss.pgh.pa.us 1453 :GNC 1962 : result = timestamptz2date_opt_overflow(timestamp, NULL);
1454 : 1962 : PG_RETURN_DATEADT(result);
1455 : : }
1456 : :
1457 : : /*
1458 : : * Convert timestamptz to date.
1459 : : *
1460 : : * On successful conversion, *overflow is set to zero if it's not NULL.
1461 : : *
1462 : : * If the timestamptz is finite but out of the valid range for date, then:
1463 : : * if overflow is NULL, we throw an out-of-range error.
1464 : : * if overflow is not NULL, we store +1 or -1 there to indicate the sign
1465 : : * of the overflow, and return the appropriate date infinity.
1466 : : *
1467 : : * Note: given the ranges of the types, overflow is only possible at
1468 : : * the minimum end of the range, but we don't assume that in this code.
1469 : : */
1470 : : DateADT
1471 : 1986 : timestamptz2date_opt_overflow(TimestampTz timestamp, int *overflow)
1472 : : {
1473 : : DateADT result;
1474 : : struct pg_tm tt,
9334 lockhart@fourpalms.o 1475 :CBC 1986 : *tm = &tt;
1476 : : fsec_t fsec;
1477 : : int tz;
1478 : :
65 tgl@sss.pgh.pa.us 1479 [ + + ]:GNC 1986 : if (overflow)
1480 : 24 : *overflow = 0;
1481 : :
6171 tgl@sss.pgh.pa.us 1482 [ + + ]:CBC 1986 : if (TIMESTAMP_IS_NOBEGIN(timestamp))
6171 tgl@sss.pgh.pa.us 1483 :GBC 6 : DATE_NOBEGIN(result);
6171 tgl@sss.pgh.pa.us 1484 [ + + ]:CBC 1980 : else if (TIMESTAMP_IS_NOEND(timestamp))
6171 tgl@sss.pgh.pa.us 1485 :GBC 6 : DATE_NOEND(result);
1486 : : else
1487 : : {
4923 peter_e@gmx.net 1488 [ - + ]:CBC 1974 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
1489 : : {
65 tgl@sss.pgh.pa.us 1490 [ # # ]:UNC 0 : if (overflow)
1491 : : {
1492 [ # # ]: 0 : if (timestamp < 0)
1493 : : {
1494 : 0 : *overflow = -1;
1495 : 0 : DATE_NOBEGIN(result);
1496 : : }
1497 : : else
1498 : : {
1499 : 0 : *overflow = 1; /* not actually reachable */
1500 : 0 : DATE_NOEND(result);
1501 : : }
1502 : 0 : return result;
1503 : : }
6171 tgl@sss.pgh.pa.us 1504 [ # # ]:UBC 0 : ereport(ERROR,
1505 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1506 : : errmsg("timestamp out of range")));
1507 : : }
1508 : :
6171 tgl@sss.pgh.pa.us 1509 :CBC 1974 : result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1510 : : }
1511 : :
65 tgl@sss.pgh.pa.us 1512 :GNC 1986 : return result;
1513 : : }
1514 : :
1515 : :
1516 : : /*****************************************************************************
1517 : : * Time ADT
1518 : : *****************************************************************************/
1519 : :
1520 : : Datum
9220 tgl@sss.pgh.pa.us 1521 :CBC 1261 : time_in(PG_FUNCTION_ARGS)
1522 : : {
1523 : 1261 : char *str = PG_GETARG_CSTRING(0);
1524 : : #ifdef NOT_USED
1525 : : Oid typelem = PG_GETARG_OID(1);
1526 : : #endif
8739 lockhart@fourpalms.o 1527 : 1261 : int32 typmod = PG_GETARG_INT32(2);
1002 tgl@sss.pgh.pa.us 1528 : 1261 : Node *escontext = fcinfo->context;
1529 : : TimeADT result;
1530 : : fsec_t fsec;
1531 : : struct pg_tm tt,
9334 lockhart@fourpalms.o 1532 : 1261 : *tm = &tt;
1533 : : int tz;
1534 : : int nf;
1535 : : int dterr;
1536 : : char workbuf[MAXDATELEN + 1];
1537 : : char *field[MAXDATEFIELDS];
1538 : : int dtype;
1539 : : int ftype[MAXDATEFIELDS];
1540 : : DateTimeErrorExtra extra;
1541 : :
7408 neilc@samurai.com 1542 : 1261 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
1543 : : field, ftype, MAXDATEFIELDS, &nf);
8046 tgl@sss.pgh.pa.us 1544 [ + - ]: 1261 : if (dterr == 0)
1002 1545 : 1261 : dterr = DecodeTimeOnly(field, ftype, nf,
1546 : : &dtype, tm, &fsec, &tz, &extra);
8046 1547 [ + + ]: 1261 : if (dterr != 0)
1548 : : {
1002 1549 : 30 : DateTimeParseError(dterr, &extra, str, "time", escontext);
1550 : 12 : PG_RETURN_NULL();
1551 : : }
1552 : :
8539 lockhart@fourpalms.o 1553 : 1231 : tm2time(tm, fsec, &result);
8739 1554 : 1231 : AdjustTimeForTypmod(&result, typmod);
1555 : :
1556 : 1231 : PG_RETURN_TIMEADT(result);
1557 : : }
1558 : :
1559 : : /* tm2time()
1560 : : * Convert a tm structure to a time data type.
1561 : : */
1562 : : int
2999 tgl@sss.pgh.pa.us 1563 : 1924 : tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result)
1564 : : {
7352 bruce@momjian.us 1565 : 1924 : *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
7266 1566 : 1924 : * USECS_PER_SEC) + fsec;
8539 lockhart@fourpalms.o 1567 : 1924 : return 0;
1568 : : }
1569 : :
1570 : : /* time_overflows()
1571 : : * Check to see if a broken-down time-of-day is out of range.
1572 : : */
1573 : : bool
1920 tgl@sss.pgh.pa.us 1574 : 29989 : time_overflows(int hour, int min, int sec, fsec_t fsec)
1575 : : {
1576 : : /* Range-check the fields individually. */
1577 [ + - + + : 29989 : if (hour < 0 || hour > HOURS_PER_DAY ||
+ - ]
1578 [ + - + - ]: 29971 : min < 0 || min >= MINS_PER_HOUR ||
1579 [ + - + - ]: 29971 : sec < 0 || sec > SECS_PER_MINUTE ||
1580 [ - + ]: 29971 : fsec < 0 || fsec > USECS_PER_SEC)
1581 : 18 : return true;
1582 : :
1583 : : /*
1584 : : * Because we allow, eg, hour = 24 or sec = 60, we must check separately
1585 : : * that the total time value doesn't exceed 24:00:00.
1586 : : */
1587 : 29971 : if ((((((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
1588 [ + + ]: 29971 : + sec) * USECS_PER_SEC) + fsec) > USECS_PER_DAY)
1589 : 18 : return true;
1590 : :
1591 : 29953 : return false;
1592 : : }
1593 : :
1594 : : /* float_time_overflows()
1595 : : * Same, when we have seconds + fractional seconds as one "double" value.
1596 : : */
1597 : : bool
1598 : 123 : float_time_overflows(int hour, int min, double sec)
1599 : : {
1600 : : /* Range-check the fields individually. */
1601 [ + - + - : 123 : if (hour < 0 || hour > HOURS_PER_DAY ||
+ - ]
1602 [ - + ]: 123 : min < 0 || min >= MINS_PER_HOUR)
1920 tgl@sss.pgh.pa.us 1603 :UBC 0 : return true;
1604 : :
1605 : : /*
1606 : : * "sec", being double, requires extra care. Cope with NaN, and round off
1607 : : * before applying the range check to avoid unexpected errors due to
1608 : : * imprecise input. (We assume rint() behaves sanely with infinities.)
1609 : : */
1920 tgl@sss.pgh.pa.us 1610 [ - + ]:CBC 123 : if (isnan(sec))
1920 tgl@sss.pgh.pa.us 1611 :UBC 0 : return true;
1920 tgl@sss.pgh.pa.us 1612 :CBC 123 : sec = rint(sec * USECS_PER_SEC);
1613 [ + - + + ]: 123 : if (sec < 0 || sec > SECS_PER_MINUTE * USECS_PER_SEC)
1614 : 3 : return true;
1615 : :
1616 : : /*
1617 : : * Because we allow, eg, hour = 24 or sec = 60, we must check separately
1618 : : * that the total time value doesn't exceed 24:00:00. This must match the
1619 : : * way that callers will convert the fields to a time.
1620 : : */
1621 : 120 : if (((((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
1622 [ + + ]: 120 : * USECS_PER_SEC) + (int64) sec) > USECS_PER_DAY)
1623 : 3 : return true;
1624 : :
1625 : 117 : return false;
1626 : : }
1627 : :
1628 : :
1629 : : /* time2tm()
1630 : : * Convert time data type to POSIX time structure.
1631 : : *
1632 : : * Note that only the hour/min/sec/fractional-sec fields are filled in.
1633 : : */
1634 : : int
2999 1635 : 3707 : time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
1636 : : {
7410 bruce@momjian.us 1637 : 3707 : tm->tm_hour = time / USECS_PER_HOUR;
1638 : 3707 : time -= tm->tm_hour * USECS_PER_HOUR;
1639 : 3707 : tm->tm_min = time / USECS_PER_MINUTE;
1640 : 3707 : time -= tm->tm_min * USECS_PER_MINUTE;
1641 : 3707 : tm->tm_sec = time / USECS_PER_SEC;
1642 : 3707 : time -= tm->tm_sec * USECS_PER_SEC;
8539 lockhart@fourpalms.o 1643 : 3707 : *fsec = time;
1644 : 3707 : return 0;
1645 : : }
1646 : :
1647 : : Datum
9220 tgl@sss.pgh.pa.us 1648 : 3386 : time_out(PG_FUNCTION_ARGS)
1649 : : {
1650 : 3386 : TimeADT time = PG_GETARG_TIMEADT(0);
1651 : : char *result;
1652 : : struct pg_tm tt,
9334 lockhart@fourpalms.o 1653 : 3386 : *tm = &tt;
1654 : : fsec_t fsec;
1655 : : char buf[MAXDATELEN + 1];
1656 : :
8539 1657 : 3386 : time2tm(time, tm, &fsec);
4924 peter_e@gmx.net 1658 : 3386 : EncodeTimeOnly(tm, fsec, false, 0, DateStyle, buf);
1659 : :
9220 tgl@sss.pgh.pa.us 1660 : 3386 : result = pstrdup(buf);
1661 : 3386 : PG_RETURN_CSTRING(result);
1662 : : }
1663 : :
1664 : : /*
1665 : : * time_recv - converts external binary format to time
1666 : : */
1667 : : Datum
8153 tgl@sss.pgh.pa.us 1668 :UBC 0 : time_recv(PG_FUNCTION_ARGS)
1669 : : {
1670 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1671 : :
1672 : : #ifdef NOT_USED
1673 : : Oid typelem = PG_GETARG_OID(1);
1674 : : #endif
7363 1675 : 0 : int32 typmod = PG_GETARG_INT32(2);
1676 : : TimeADT result;
1677 : :
1678 : 0 : result = pq_getmsgint64(buf);
1679 : :
5947 1680 [ # # # # ]: 0 : if (result < INT64CONST(0) || result > USECS_PER_DAY)
1681 [ # # ]: 0 : ereport(ERROR,
1682 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1683 : : errmsg("time out of range")));
1684 : :
7363 1685 : 0 : AdjustTimeForTypmod(&result, typmod);
1686 : :
1687 : 0 : PG_RETURN_TIMEADT(result);
1688 : : }
1689 : :
1690 : : /*
1691 : : * time_send - converts time to binary format
1692 : : */
1693 : : Datum
8153 1694 : 0 : time_send(PG_FUNCTION_ARGS)
1695 : : {
1696 : 0 : TimeADT time = PG_GETARG_TIMEADT(0);
1697 : : StringInfoData buf;
1698 : :
1699 : 0 : pq_begintypsend(&buf);
1700 : 0 : pq_sendint64(&buf, time);
1701 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1702 : : }
1703 : :
1704 : : Datum
6825 tgl@sss.pgh.pa.us 1705 :CBC 11 : timetypmodin(PG_FUNCTION_ARGS)
1706 : : {
6505 bruce@momjian.us 1707 : 11 : ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1708 : :
6825 tgl@sss.pgh.pa.us 1709 : 11 : PG_RETURN_INT32(anytime_typmodin(false, ta));
1710 : : }
1711 : :
1712 : : Datum
1713 : 5 : timetypmodout(PG_FUNCTION_ARGS)
1714 : : {
6505 bruce@momjian.us 1715 : 5 : int32 typmod = PG_GETARG_INT32(0);
1716 : :
6825 tgl@sss.pgh.pa.us 1717 : 5 : PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
1718 : : }
1719 : :
1720 : : /*
1721 : : * make_time - time constructor
1722 : : */
1723 : : Datum
4311 1724 : 9 : make_time(PG_FUNCTION_ARGS)
1725 : : {
1726 : 9 : int tm_hour = PG_GETARG_INT32(0);
1727 : 9 : int tm_min = PG_GETARG_INT32(1);
1728 : 9 : double sec = PG_GETARG_FLOAT8(2);
1729 : : TimeADT time;
1730 : :
1731 : : /* Check for time overflow */
1920 1732 [ + + ]: 9 : if (float_time_overflows(tm_hour, tm_min, sec))
4311 1733 [ + - ]: 6 : ereport(ERROR,
1734 : : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
1735 : : errmsg("time field value out of range: %d:%02d:%02g",
1736 : : tm_hour, tm_min, sec)));
1737 : :
1738 : : /* This should match tm2time */
1739 : 3 : time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
1920 1740 : 3 : * USECS_PER_SEC) + (int64) rint(sec * USECS_PER_SEC);
1741 : :
4311 1742 : 3 : PG_RETURN_TIMEADT(time);
1743 : : }
1744 : :
1745 : :
1746 : : /* time_support()
1747 : : *
1748 : : * Planner support function for the time_scale() and timetz_scale()
1749 : : * length coercion functions (we need not distinguish them here).
1750 : : */
1751 : : Datum
2401 1752 : 12 : time_support(PG_FUNCTION_ARGS)
1753 : : {
1754 : 12 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
1755 : 12 : Node *ret = NULL;
1756 : :
1757 [ + + ]: 12 : if (IsA(rawreq, SupportRequestSimplify))
1758 : : {
1759 : 6 : SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
1760 : :
1761 : 6 : ret = TemporalSimplify(MAX_TIME_PRECISION, (Node *) req->fcall);
1762 : : }
1763 : :
1764 : 12 : PG_RETURN_POINTER(ret);
1765 : : }
1766 : :
1767 : : /* time_scale()
1768 : : * Adjust time type for specified scale factor.
1769 : : * Used by PostgreSQL type system to stuff columns.
1770 : : */
1771 : : Datum
8739 lockhart@fourpalms.o 1772 : 33 : time_scale(PG_FUNCTION_ARGS)
1773 : : {
1774 : 33 : TimeADT time = PG_GETARG_TIMEADT(0);
1775 : 33 : int32 typmod = PG_GETARG_INT32(1);
1776 : : TimeADT result;
1777 : :
1778 : 33 : result = time;
1779 : 33 : AdjustTimeForTypmod(&result, typmod);
1780 : :
1781 : 33 : PG_RETURN_TIMEADT(result);
1782 : : }
1783 : :
1784 : : /* AdjustTimeForTypmod()
1785 : : * Force the precision of the time value to a specified value.
1786 : : * Uses *exactly* the same code as in AdjustTimestampForTypmod()
1787 : : * but we make a separate copy because those types do not
1788 : : * have a fundamental tie together but rather a coincidence of
1789 : : * implementation. - thomas
1790 : : */
1791 : : void
1792 : 4234 : AdjustTimeForTypmod(TimeADT *time, int32 typmod)
1793 : : {
1794 : : static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1795 : : INT64CONST(1000000),
1796 : : INT64CONST(100000),
1797 : : INT64CONST(10000),
1798 : : INT64CONST(1000),
1799 : : INT64CONST(100),
1800 : : INT64CONST(10),
1801 : : INT64CONST(1)
1802 : : };
1803 : :
1804 : : static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1805 : : INT64CONST(500000),
1806 : : INT64CONST(50000),
1807 : : INT64CONST(5000),
1808 : : INT64CONST(500),
1809 : : INT64CONST(50),
1810 : : INT64CONST(5),
1811 : : INT64CONST(0)
1812 : : };
1813 : :
7410 bruce@momjian.us 1814 [ + + + - ]: 4234 : if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1815 : : {
8434 lockhart@fourpalms.o 1816 [ + - ]: 264 : if (*time >= INT64CONST(0))
7410 bruce@momjian.us 1817 : 264 : *time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
7266 1818 : 264 : TimeScales[typmod];
1819 : : else
7410 bruce@momjian.us 1820 :UBC 0 : *time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
7266 1821 : 0 : TimeScales[typmod]);
1822 : : }
8739 lockhart@fourpalms.o 1823 :CBC 4234 : }
1824 : :
1825 : :
1826 : : Datum
9220 tgl@sss.pgh.pa.us 1827 : 19570 : time_eq(PG_FUNCTION_ARGS)
1828 : : {
1829 : 19570 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1830 : 19570 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1831 : :
1832 : 19570 : PG_RETURN_BOOL(time1 == time2);
1833 : : }
1834 : :
1835 : : Datum
9220 tgl@sss.pgh.pa.us 1836 :UBC 0 : time_ne(PG_FUNCTION_ARGS)
1837 : : {
1838 : 0 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1839 : 0 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1840 : :
1841 : 0 : PG_RETURN_BOOL(time1 != time2);
1842 : : }
1843 : :
1844 : : Datum
9220 tgl@sss.pgh.pa.us 1845 :CBC 48061 : time_lt(PG_FUNCTION_ARGS)
1846 : : {
1847 : 48061 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1848 : 48061 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1849 : :
1850 : 48061 : PG_RETURN_BOOL(time1 < time2);
1851 : : }
1852 : :
1853 : : Datum
1854 : 4882 : time_le(PG_FUNCTION_ARGS)
1855 : : {
1856 : 4882 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1857 : 4882 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1858 : :
1859 : 4882 : PG_RETURN_BOOL(time1 <= time2);
1860 : : }
1861 : :
1862 : : Datum
1863 : 6014 : time_gt(PG_FUNCTION_ARGS)
1864 : : {
1865 : 6014 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1866 : 6014 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1867 : :
1868 : 6014 : PG_RETURN_BOOL(time1 > time2);
1869 : : }
1870 : :
1871 : : Datum
1872 : 3583 : time_ge(PG_FUNCTION_ARGS)
1873 : : {
1874 : 3583 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1875 : 3583 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1876 : :
1877 : 3583 : PG_RETURN_BOOL(time1 >= time2);
1878 : : }
1879 : :
1880 : : Datum
1881 : 18963 : time_cmp(PG_FUNCTION_ARGS)
1882 : : {
1883 : 18963 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1884 : 18963 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1885 : :
1886 [ + + ]: 18963 : if (time1 < time2)
1887 : 9263 : PG_RETURN_INT32(-1);
1888 [ + + ]: 9700 : if (time1 > time2)
1889 : 8484 : PG_RETURN_INT32(1);
1890 : 1216 : PG_RETURN_INT32(0);
1891 : : }
1892 : :
1893 : : Datum
6637 1894 : 1137 : time_hash(PG_FUNCTION_ARGS)
1895 : : {
1896 : 1137 : return hashint8(fcinfo);
1897 : : }
1898 : :
1899 : : Datum
2928 rhaas@postgresql.org 1900 : 30 : time_hash_extended(PG_FUNCTION_ARGS)
1901 : : {
1902 : 30 : return hashint8extended(fcinfo);
1903 : : }
1904 : :
1905 : : Datum
9220 tgl@sss.pgh.pa.us 1906 :UBC 0 : time_larger(PG_FUNCTION_ARGS)
1907 : : {
1908 : 0 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1909 : 0 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1910 : :
1911 : 0 : PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
1912 : : }
1913 : :
1914 : : Datum
1915 : 0 : time_smaller(PG_FUNCTION_ARGS)
1916 : : {
1917 : 0 : TimeADT time1 = PG_GETARG_TIMEADT(0);
1918 : 0 : TimeADT time2 = PG_GETARG_TIMEADT(1);
1919 : :
1920 : 0 : PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
1921 : : }
1922 : :
1923 : : /* overlaps_time() --- implements the SQL OVERLAPS operator.
1924 : : *
1925 : : * Algorithm is per SQL spec. This is much harder than you'd think
1926 : : * because the spec requires us to deliver a non-null answer in some cases
1927 : : * where some of the inputs are null.
1928 : : */
1929 : : Datum
9220 tgl@sss.pgh.pa.us 1930 :CBC 12 : overlaps_time(PG_FUNCTION_ARGS)
1931 : : {
1932 : : /*
1933 : : * The arguments are TimeADT, but we leave them as generic Datums to avoid
1934 : : * dereferencing nulls (TimeADT is pass-by-reference!)
1935 : : */
9039 1936 : 12 : Datum ts1 = PG_GETARG_DATUM(0);
1937 : 12 : Datum te1 = PG_GETARG_DATUM(1);
1938 : 12 : Datum ts2 = PG_GETARG_DATUM(2);
1939 : 12 : Datum te2 = PG_GETARG_DATUM(3);
1940 : 12 : bool ts1IsNull = PG_ARGISNULL(0);
1941 : 12 : bool te1IsNull = PG_ARGISNULL(1);
1942 : 12 : bool ts2IsNull = PG_ARGISNULL(2);
1943 : 12 : bool te2IsNull = PG_ARGISNULL(3);
1944 : :
1945 : : #define TIMEADT_GT(t1,t2) \
1946 : : (DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
1947 : : #define TIMEADT_LT(t1,t2) \
1948 : : (DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1949 : :
1950 : : /*
1951 : : * If both endpoints of interval 1 are null, the result is null (unknown).
1952 : : * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
1953 : : * take ts1 as the lesser endpoint.
1954 : : */
1955 [ - + ]: 12 : if (ts1IsNull)
1956 : : {
9039 tgl@sss.pgh.pa.us 1957 [ # # ]:UBC 0 : if (te1IsNull)
1958 : 0 : PG_RETURN_NULL();
1959 : : /* swap null for non-null */
9307 lockhart@fourpalms.o 1960 : 0 : ts1 = te1;
9039 tgl@sss.pgh.pa.us 1961 : 0 : te1IsNull = true;
1962 : : }
9039 tgl@sss.pgh.pa.us 1963 [ + - ]:CBC 12 : else if (!te1IsNull)
1964 : : {
1965 [ - + ]: 12 : if (TIMEADT_GT(ts1, te1))
1966 : : {
8934 bruce@momjian.us 1967 :UBC 0 : Datum tt = ts1;
1968 : :
9039 tgl@sss.pgh.pa.us 1969 : 0 : ts1 = te1;
1970 : 0 : te1 = tt;
1971 : : }
1972 : : }
1973 : :
1974 : : /* Likewise for interval 2. */
9039 tgl@sss.pgh.pa.us 1975 [ - + ]:CBC 12 : if (ts2IsNull)
1976 : : {
9039 tgl@sss.pgh.pa.us 1977 [ # # ]:UBC 0 : if (te2IsNull)
1978 : 0 : PG_RETURN_NULL();
1979 : : /* swap null for non-null */
9307 lockhart@fourpalms.o 1980 : 0 : ts2 = te2;
9039 tgl@sss.pgh.pa.us 1981 : 0 : te2IsNull = true;
1982 : : }
9039 tgl@sss.pgh.pa.us 1983 [ + - ]:CBC 12 : else if (!te2IsNull)
1984 : : {
1985 [ - + ]: 12 : if (TIMEADT_GT(ts2, te2))
1986 : : {
8934 bruce@momjian.us 1987 :UBC 0 : Datum tt = ts2;
1988 : :
9039 tgl@sss.pgh.pa.us 1989 : 0 : ts2 = te2;
1990 : 0 : te2 = tt;
1991 : : }
1992 : : }
1993 : :
1994 : : /*
1995 : : * At this point neither ts1 nor ts2 is null, so we can consider three
1996 : : * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
1997 : : */
9039 tgl@sss.pgh.pa.us 1998 [ - + ]:CBC 12 : if (TIMEADT_GT(ts1, ts2))
1999 : : {
2000 : : /*
2001 : : * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2002 : : * in the presence of nulls it's not quite completely so.
2003 : : */
9039 tgl@sss.pgh.pa.us 2004 [ # # ]:UBC 0 : if (te2IsNull)
2005 : 0 : PG_RETURN_NULL();
2006 [ # # ]: 0 : if (TIMEADT_LT(ts1, te2))
2007 : 0 : PG_RETURN_BOOL(true);
2008 [ # # ]: 0 : if (te1IsNull)
2009 : 0 : PG_RETURN_NULL();
2010 : :
2011 : : /*
2012 : : * If te1 is not null then we had ts1 <= te1 above, and we just found
2013 : : * ts1 >= te2, hence te1 >= te2.
2014 : : */
2015 : 0 : PG_RETURN_BOOL(false);
2016 : : }
9039 tgl@sss.pgh.pa.us 2017 [ + - ]:CBC 12 : else if (TIMEADT_LT(ts1, ts2))
2018 : : {
2019 : : /* This case is ts2 < te1 OR te2 < te1 */
2020 [ - + ]: 12 : if (te1IsNull)
9039 tgl@sss.pgh.pa.us 2021 :UBC 0 : PG_RETURN_NULL();
9039 tgl@sss.pgh.pa.us 2022 [ + + ]:CBC 12 : if (TIMEADT_LT(ts2, te1))
2023 : 6 : PG_RETURN_BOOL(true);
2024 [ - + ]: 6 : if (te2IsNull)
9039 tgl@sss.pgh.pa.us 2025 :UBC 0 : PG_RETURN_NULL();
2026 : :
2027 : : /*
2028 : : * If te2 is not null then we had ts2 <= te2 above, and we just found
2029 : : * ts2 >= te1, hence te2 >= te1.
2030 : : */
9039 tgl@sss.pgh.pa.us 2031 :CBC 6 : PG_RETURN_BOOL(false);
2032 : : }
2033 : : else
2034 : : {
2035 : : /*
2036 : : * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2037 : : * rather silly way of saying "true if both are nonnull, else null".
2038 : : */
9039 tgl@sss.pgh.pa.us 2039 [ # # # # ]:UBC 0 : if (te1IsNull || te2IsNull)
2040 : 0 : PG_RETURN_NULL();
2041 : 0 : PG_RETURN_BOOL(true);
2042 : : }
2043 : :
2044 : : #undef TIMEADT_GT
2045 : : #undef TIMEADT_LT
2046 : : }
2047 : :
2048 : : /* timestamp_time()
2049 : : * Convert timestamp to time data type.
2050 : : */
2051 : : Datum
9220 tgl@sss.pgh.pa.us 2052 :CBC 18 : timestamp_time(PG_FUNCTION_ARGS)
2053 : : {
7266 bruce@momjian.us 2054 : 18 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
2055 : : TimeADT result;
2056 : : struct pg_tm tt,
10225 2057 : 18 : *tm = &tt;
2058 : : fsec_t fsec;
2059 : :
9220 tgl@sss.pgh.pa.us 2060 [ + - - + ]: 18 : if (TIMESTAMP_NOT_FINITE(timestamp))
8744 lockhart@fourpalms.o 2061 :UBC 0 : PG_RETURN_NULL();
2062 : :
7352 bruce@momjian.us 2063 [ - + ]:CBC 18 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
8077 tgl@sss.pgh.pa.us 2064 [ # # ]:UBC 0 : ereport(ERROR,
2065 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2066 : : errmsg("timestamp out of range")));
2067 : :
2068 : : /*
2069 : : * Could also do this with time = (timestamp / USECS_PER_DAY *
2070 : : * USECS_PER_DAY) - timestamp;
2071 : : */
7352 bruce@momjian.us 2072 :CBC 18 : result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
7266 2073 : 18 : USECS_PER_SEC) + fsec;
2074 : :
8539 lockhart@fourpalms.o 2075 : 18 : PG_RETURN_TIMEADT(result);
2076 : : }
2077 : :
2078 : : /* timestamptz_time()
2079 : : * Convert timestamptz to time data type.
2080 : : */
2081 : : Datum
2082 : 27 : timestamptz_time(PG_FUNCTION_ARGS)
2083 : : {
2084 : 27 : TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2085 : : TimeADT result;
2086 : : struct pg_tm tt,
2087 : 27 : *tm = &tt;
2088 : : int tz;
2089 : : fsec_t fsec;
2090 : :
2091 [ + - - + ]: 27 : if (TIMESTAMP_NOT_FINITE(timestamp))
8539 lockhart@fourpalms.o 2092 :UBC 0 : PG_RETURN_NULL();
2093 : :
4923 peter_e@gmx.net 2094 [ - + ]:CBC 27 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
8077 tgl@sss.pgh.pa.us 2095 [ # # ]:UBC 0 : ereport(ERROR,
2096 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2097 : : errmsg("timestamp out of range")));
2098 : :
2099 : : /*
2100 : : * Could also do this with time = (timestamp / USECS_PER_DAY *
2101 : : * USECS_PER_DAY) - timestamp;
2102 : : */
7352 bruce@momjian.us 2103 :CBC 27 : result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
7266 2104 : 27 : USECS_PER_SEC) + fsec;
2105 : :
9220 tgl@sss.pgh.pa.us 2106 : 27 : PG_RETURN_TIMEADT(result);
2107 : : }
2108 : :
2109 : : /* datetime_timestamp()
2110 : : * Convert date and time to timestamp data type.
2111 : : */
2112 : : Datum
2113 : 15 : datetime_timestamp(PG_FUNCTION_ARGS)
2114 : : {
7266 bruce@momjian.us 2115 : 15 : DateADT date = PG_GETARG_DATEADT(0);
9220 tgl@sss.pgh.pa.us 2116 : 15 : TimeADT time = PG_GETARG_TIMEADT(1);
2117 : : Timestamp result;
2118 : :
6171 2119 : 15 : result = date2timestamp(date);
2120 [ + - + - ]: 15 : if (!TIMESTAMP_NOT_FINITE(result))
2121 : : {
2122 : 15 : result += time;
3461 2123 [ + - - + ]: 15 : if (!IS_VALID_TIMESTAMP(result))
3461 tgl@sss.pgh.pa.us 2124 [ # # ]:UBC 0 : ereport(ERROR,
2125 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2126 : : errmsg("timestamp out of range")));
2127 : : }
2128 : :
9220 tgl@sss.pgh.pa.us 2129 :CBC 15 : PG_RETURN_TIMESTAMP(result);
2130 : : }
2131 : :
2132 : : /* time_interval()
2133 : : * Convert time to interval data type.
2134 : : */
2135 : : Datum
2136 : 6 : time_interval(PG_FUNCTION_ARGS)
2137 : : {
2138 : 6 : TimeADT time = PG_GETARG_TIMEADT(0);
2139 : : Interval *result;
2140 : :
2141 : 6 : result = (Interval *) palloc(sizeof(Interval));
2142 : :
2143 : 6 : result->time = time;
7353 bruce@momjian.us 2144 : 6 : result->day = 0;
9307 lockhart@fourpalms.o 2145 : 6 : result->month = 0;
2146 : :
9220 tgl@sss.pgh.pa.us 2147 : 6 : PG_RETURN_INTERVAL_P(result);
2148 : : }
2149 : :
2150 : : /* interval_time()
2151 : : * Convert interval to time data type.
2152 : : *
2153 : : * This is defined as producing the fractional-day portion of the interval.
2154 : : * Therefore, we can just ignore the months field. It is not real clear
2155 : : * what to do with negative intervals, but we choose to subtract the floor,
2156 : : * so that, say, '-2 hours' becomes '22:00:00'.
2157 : : */
2158 : : Datum
9043 lockhart@fourpalms.o 2159 : 15 : interval_time(PG_FUNCTION_ARGS)
2160 : : {
2161 : 15 : Interval *span = PG_GETARG_INTERVAL_P(0);
2162 : : TimeADT result;
2163 : :
662 dean.a.rasheed@gmail 2164 [ + + + - : 15 : if (INTERVAL_NOT_FINITE(span))
- + + + +
- + - ]
2165 [ + - ]: 6 : ereport(ERROR,
2166 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2167 : : errmsg("cannot convert infinite interval to time")));
2168 : :
667 2169 : 9 : result = span->time % USECS_PER_DAY;
2170 [ + + ]: 9 : if (result < 0)
2171 : 3 : result += USECS_PER_DAY;
2172 : :
9043 lockhart@fourpalms.o 2173 : 9 : PG_RETURN_TIMEADT(result);
2174 : : }
2175 : :
2176 : : /* time_mi_time()
2177 : : * Subtract two times to produce an interval.
2178 : : */
2179 : : Datum
8744 2180 : 820 : time_mi_time(PG_FUNCTION_ARGS)
2181 : : {
2182 : 820 : TimeADT time1 = PG_GETARG_TIMEADT(0);
2183 : 820 : TimeADT time2 = PG_GETARG_TIMEADT(1);
2184 : : Interval *result;
2185 : :
2186 : 820 : result = (Interval *) palloc(sizeof(Interval));
2187 : :
2188 : 820 : result->month = 0;
7353 bruce@momjian.us 2189 : 820 : result->day = 0;
2190 : 820 : result->time = time1 - time2;
2191 : :
8744 lockhart@fourpalms.o 2192 : 820 : PG_RETURN_INTERVAL_P(result);
2193 : : }
2194 : :
2195 : : /* time_pl_interval()
2196 : : * Add interval to time.
2197 : : */
2198 : : Datum
9043 2199 : 1323 : time_pl_interval(PG_FUNCTION_ARGS)
2200 : : {
2201 : 1323 : TimeADT time = PG_GETARG_TIMEADT(0);
2202 : 1323 : Interval *span = PG_GETARG_INTERVAL_P(1);
2203 : : TimeADT result;
2204 : :
662 dean.a.rasheed@gmail 2205 [ + + + - : 1323 : if (INTERVAL_NOT_FINITE(span))
- + + + +
- + - ]
2206 [ + - ]: 6 : ereport(ERROR,
2207 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2208 : : errmsg("cannot add infinite interval to time")));
2209 : :
7352 bruce@momjian.us 2210 : 1317 : result = time + span->time;
2211 : 1317 : result -= result / USECS_PER_DAY * USECS_PER_DAY;
8539 lockhart@fourpalms.o 2212 [ + + ]: 1317 : if (result < INT64CONST(0))
7411 bruce@momjian.us 2213 : 3 : result += USECS_PER_DAY;
2214 : :
9043 lockhart@fourpalms.o 2215 : 1317 : PG_RETURN_TIMEADT(result);
2216 : : }
2217 : :
2218 : : /* time_mi_interval()
2219 : : * Subtract interval from time.
2220 : : */
2221 : : Datum
2222 : 309 : time_mi_interval(PG_FUNCTION_ARGS)
2223 : : {
2224 : 309 : TimeADT time = PG_GETARG_TIMEADT(0);
2225 : 309 : Interval *span = PG_GETARG_INTERVAL_P(1);
2226 : : TimeADT result;
2227 : :
662 dean.a.rasheed@gmail 2228 [ + + + - : 309 : if (INTERVAL_NOT_FINITE(span))
- + + + +
- + - ]
2229 [ + - ]: 6 : ereport(ERROR,
2230 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2231 : : errmsg("cannot subtract infinite interval from time")));
2232 : :
7352 bruce@momjian.us 2233 : 303 : result = time - span->time;
2234 : 303 : result -= result / USECS_PER_DAY * USECS_PER_DAY;
8539 lockhart@fourpalms.o 2235 [ + + ]: 303 : if (result < INT64CONST(0))
7411 bruce@momjian.us 2236 : 36 : result += USECS_PER_DAY;
2237 : :
9043 lockhart@fourpalms.o 2238 : 303 : PG_RETURN_TIMEADT(result);
2239 : : }
2240 : :
2241 : : /*
2242 : : * in_range support function for time.
2243 : : */
2244 : : Datum
2768 tgl@sss.pgh.pa.us 2245 : 480 : in_range_time_interval(PG_FUNCTION_ARGS)
2246 : : {
2247 : 480 : TimeADT val = PG_GETARG_TIMEADT(0);
2248 : 480 : TimeADT base = PG_GETARG_TIMEADT(1);
2249 : 480 : Interval *offset = PG_GETARG_INTERVAL_P(2);
2250 : 480 : bool sub = PG_GETARG_BOOL(3);
2251 : 480 : bool less = PG_GETARG_BOOL(4);
2252 : : TimeADT sum;
2253 : :
2254 : : /*
2255 : : * Like time_pl_interval/time_mi_interval, we disregard the month and day
2256 : : * fields of the offset. So our test for negative should too. This also
2257 : : * catches -infinity, so we only need worry about +infinity below.
2258 : : */
2259 [ + + ]: 480 : if (offset->time < 0)
2260 [ + - ]: 6 : ereport(ERROR,
2261 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2262 : : errmsg("invalid preceding or following size in window function")));
2263 : :
2264 : : /*
2265 : : * We can't use time_pl_interval/time_mi_interval here, because their
2266 : : * wraparound behavior would give wrong (or at least undesirable) answers.
2267 : : * Fortunately the equivalent non-wrapping behavior is trivial, except
2268 : : * that adding an infinite (or very large) interval might cause integer
2269 : : * overflow. Subtraction cannot overflow here.
2270 : : */
2271 [ + + ]: 474 : if (sub)
2272 : 237 : sum = base - offset->time;
662 dean.a.rasheed@gmail 2273 [ + + ]: 237 : else if (pg_add_s64_overflow(base, offset->time, &sum))
2274 : 108 : PG_RETURN_BOOL(less);
2275 : :
2768 tgl@sss.pgh.pa.us 2276 [ + + ]: 366 : if (less)
2277 : 165 : PG_RETURN_BOOL(val <= sum);
2278 : : else
2279 : 201 : PG_RETURN_BOOL(val >= sum);
2280 : : }
2281 : :
2282 : :
2283 : : /* time_part() and extract_time()
2284 : : * Extract specified field from time type.
2285 : : */
2286 : : static Datum
1614 peter@eisentraut.org 2287 : 39 : time_part_common(PG_FUNCTION_ARGS, bool retnumeric)
2288 : : {
6374 tgl@sss.pgh.pa.us 2289 : 39 : text *units = PG_GETARG_TEXT_PP(0);
8539 lockhart@fourpalms.o 2290 : 39 : TimeADT time = PG_GETARG_TIMEADT(1);
2291 : : int64 intresult;
2292 : : int type,
2293 : : val;
2294 : : char *lowunits;
2295 : :
6374 tgl@sss.pgh.pa.us 2296 [ - + ]: 39 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
2297 [ - + - - : 39 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
2298 : : false);
2299 : :
8539 lockhart@fourpalms.o 2300 : 39 : type = DecodeUnits(0, lowunits, &val);
2301 [ + + ]: 39 : if (type == UNKNOWN_FIELD)
2302 : 9 : type = DecodeSpecial(0, lowunits, &val);
2303 : :
2304 [ + + ]: 39 : if (type == UNITS)
2305 : : {
2306 : : fsec_t fsec;
2307 : : struct pg_tm tt,
2308 : 30 : *tm = &tt;
2309 : :
2310 : 30 : time2tm(time, tm, &fsec);
2311 : :
2312 [ + + + + : 30 : switch (val)
+ + ]
2313 : : {
2314 : 6 : case DTK_MICROSEC:
1609 tgl@sss.pgh.pa.us 2315 : 6 : intresult = tm->tm_sec * INT64CONST(1000000) + fsec;
8539 lockhart@fourpalms.o 2316 : 6 : break;
2317 : :
2318 : 6 : case DTK_MILLISEC:
1614 peter@eisentraut.org 2319 [ + + ]: 6 : if (retnumeric)
2320 : : /*---
2321 : : * tm->tm_sec * 1000 + fsec / 1000
2322 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1000
2323 : : */
1609 tgl@sss.pgh.pa.us 2324 : 12 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 3));
2325 : : else
1614 peter@eisentraut.org 2326 : 3 : PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0);
2327 : : break;
2328 : :
8539 lockhart@fourpalms.o 2329 : 6 : case DTK_SECOND:
1614 peter@eisentraut.org 2330 [ + + ]: 6 : if (retnumeric)
2331 : : /*---
2332 : : * tm->tm_sec + fsec / 1'000'000
2333 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
2334 : : */
1609 tgl@sss.pgh.pa.us 2335 : 3 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 6));
2336 : : else
1614 peter@eisentraut.org 2337 : 3 : PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0);
2338 : : break;
2339 : :
8539 lockhart@fourpalms.o 2340 : 3 : case DTK_MINUTE:
1614 peter@eisentraut.org 2341 : 3 : intresult = tm->tm_min;
8539 lockhart@fourpalms.o 2342 : 3 : break;
2343 : :
2344 : 3 : case DTK_HOUR:
1614 peter@eisentraut.org 2345 : 3 : intresult = tm->tm_hour;
8539 lockhart@fourpalms.o 2346 : 3 : break;
2347 : :
2348 : 6 : case DTK_TZ:
2349 : : case DTK_TZ_MINUTE:
2350 : : case DTK_TZ_HOUR:
2351 : : case DTK_DAY:
2352 : : case DTK_MONTH:
2353 : : case DTK_QUARTER:
2354 : : case DTK_YEAR:
2355 : : case DTK_DECADE:
2356 : : case DTK_CENTURY:
2357 : : case DTK_MILLENNIUM:
2358 : : case DTK_ISOYEAR:
2359 : : default:
8077 tgl@sss.pgh.pa.us 2360 [ + - ]: 6 : ereport(ERROR,
2361 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2362 : : errmsg("unit \"%s\" not supported for type %s",
2363 : : lowunits, format_type_be(TIMEOID))));
2364 : : intresult = 0;
2365 : : }
2366 : : }
7410 bruce@momjian.us 2367 [ + + + - ]: 9 : else if (type == RESERV && val == DTK_EPOCH)
2368 : : {
1614 peter@eisentraut.org 2369 [ + + ]: 6 : if (retnumeric)
2370 : 3 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(time, 6));
2371 : : else
2372 : 3 : PG_RETURN_FLOAT8(time / 1000000.0);
2373 : : }
2374 : : else
2375 : : {
8077 tgl@sss.pgh.pa.us 2376 [ + - ]: 3 : ereport(ERROR,
2377 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2378 : : errmsg("unit \"%s\" not recognized for type %s",
2379 : : lowunits, format_type_be(TIMEOID))));
2380 : : intresult = 0;
2381 : : }
2382 : :
1614 peter@eisentraut.org 2383 [ + + ]: 12 : if (retnumeric)
2384 : 9 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
2385 : : else
2386 : 3 : PG_RETURN_FLOAT8(intresult);
2387 : : }
2388 : :
2389 : : Datum
2390 : 12 : time_part(PG_FUNCTION_ARGS)
2391 : : {
2392 : 12 : return time_part_common(fcinfo, false);
2393 : : }
2394 : :
2395 : : Datum
2396 : 27 : extract_time(PG_FUNCTION_ARGS)
2397 : : {
2398 : 27 : return time_part_common(fcinfo, true);
2399 : : }
2400 : :
2401 : :
2402 : : /*****************************************************************************
2403 : : * Time With Time Zone ADT
2404 : : *****************************************************************************/
2405 : :
2406 : : /* tm2timetz()
2407 : : * Convert a tm structure to a time data type.
2408 : : */
2409 : : int
2999 tgl@sss.pgh.pa.us 2410 : 2088 : tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result)
2411 : : {
7352 bruce@momjian.us 2412 : 2088 : result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
7410 2413 : 2088 : USECS_PER_SEC) + fsec;
8539 lockhart@fourpalms.o 2414 : 2088 : result->zone = tz;
2415 : :
2416 : 2088 : return 0;
2417 : : }
2418 : :
2419 : : Datum
9220 tgl@sss.pgh.pa.us 2420 : 1200 : timetz_in(PG_FUNCTION_ARGS)
2421 : : {
2422 : 1200 : char *str = PG_GETARG_CSTRING(0);
2423 : : #ifdef NOT_USED
2424 : : Oid typelem = PG_GETARG_OID(1);
2425 : : #endif
8739 lockhart@fourpalms.o 2426 : 1200 : int32 typmod = PG_GETARG_INT32(2);
1002 tgl@sss.pgh.pa.us 2427 : 1200 : Node *escontext = fcinfo->context;
2428 : : TimeTzADT *result;
2429 : : fsec_t fsec;
2430 : : struct pg_tm tt,
9307 lockhart@fourpalms.o 2431 : 1200 : *tm = &tt;
2432 : : int tz;
2433 : : int nf;
2434 : : int dterr;
2435 : : char workbuf[MAXDATELEN + 1];
2436 : : char *field[MAXDATEFIELDS];
2437 : : int dtype;
2438 : : int ftype[MAXDATEFIELDS];
2439 : : DateTimeErrorExtra extra;
2440 : :
7408 neilc@samurai.com 2441 : 1200 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
2442 : : field, ftype, MAXDATEFIELDS, &nf);
8046 tgl@sss.pgh.pa.us 2443 [ + - ]: 1200 : if (dterr == 0)
1002 2444 : 1200 : dterr = DecodeTimeOnly(field, ftype, nf,
2445 : : &dtype, tm, &fsec, &tz, &extra);
8046 2446 [ + + ]: 1200 : if (dterr != 0)
2447 : : {
1002 2448 : 39 : DateTimeParseError(dterr, &extra, str, "time with time zone",
2449 : : escontext);
2450 : 12 : PG_RETURN_NULL();
2451 : : }
2452 : :
8739 lockhart@fourpalms.o 2453 : 1161 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
8539 2454 : 1161 : tm2timetz(tm, fsec, tz, result);
8739 2455 : 1161 : AdjustTimeForTypmod(&(result->time), typmod);
2456 : :
2457 : 1161 : PG_RETURN_TIMETZADT_P(result);
2458 : : }
2459 : :
2460 : : Datum
9220 tgl@sss.pgh.pa.us 2461 : 3586 : timetz_out(PG_FUNCTION_ARGS)
2462 : : {
2463 : 3586 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2464 : : char *result;
2465 : : struct pg_tm tt,
9307 lockhart@fourpalms.o 2466 : 3586 : *tm = &tt;
2467 : : fsec_t fsec;
2468 : : int tz;
2469 : : char buf[MAXDATELEN + 1];
2470 : :
8539 2471 : 3586 : timetz2tm(time, tm, &fsec, &tz);
4924 peter_e@gmx.net 2472 : 3586 : EncodeTimeOnly(tm, fsec, true, tz, DateStyle, buf);
2473 : :
8539 lockhart@fourpalms.o 2474 : 3586 : result = pstrdup(buf);
2475 : 3586 : PG_RETURN_CSTRING(result);
2476 : : }
2477 : :
2478 : : /*
2479 : : * timetz_recv - converts external binary format to timetz
2480 : : */
2481 : : Datum
8153 tgl@sss.pgh.pa.us 2482 :UBC 0 : timetz_recv(PG_FUNCTION_ARGS)
2483 : : {
2484 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
2485 : :
2486 : : #ifdef NOT_USED
2487 : : Oid typelem = PG_GETARG_OID(1);
2488 : : #endif
7363 2489 : 0 : int32 typmod = PG_GETARG_INT32(2);
2490 : : TimeTzADT *result;
2491 : :
2492 : 0 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2493 : :
2494 : 0 : result->time = pq_getmsgint64(buf);
2495 : :
5947 2496 [ # # # # ]: 0 : if (result->time < INT64CONST(0) || result->time > USECS_PER_DAY)
2497 [ # # ]: 0 : ereport(ERROR,
2498 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2499 : : errmsg("time out of range")));
2500 : :
7363 2501 : 0 : result->zone = pq_getmsgint(buf, sizeof(result->zone));
2502 : :
2503 : : /* Check for sane GMT displacement; see notes in datatype/timestamp.h */
4847 2504 [ # # # # ]: 0 : if (result->zone <= -TZDISP_LIMIT || result->zone >= TZDISP_LIMIT)
5947 2505 [ # # ]: 0 : ereport(ERROR,
2506 : : (errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
2507 : : errmsg("time zone displacement out of range")));
2508 : :
7363 2509 : 0 : AdjustTimeForTypmod(&(result->time), typmod);
2510 : :
2511 : 0 : PG_RETURN_TIMETZADT_P(result);
2512 : : }
2513 : :
2514 : : /*
2515 : : * timetz_send - converts timetz to binary format
2516 : : */
2517 : : Datum
8153 2518 : 0 : timetz_send(PG_FUNCTION_ARGS)
2519 : : {
2520 : 0 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2521 : : StringInfoData buf;
2522 : :
2523 : 0 : pq_begintypsend(&buf);
2524 : 0 : pq_sendint64(&buf, time->time);
2887 andres@anarazel.de 2525 : 0 : pq_sendint32(&buf, time->zone);
8153 tgl@sss.pgh.pa.us 2526 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
2527 : : }
2528 : :
2529 : : Datum
6825 tgl@sss.pgh.pa.us 2530 :CBC 11 : timetztypmodin(PG_FUNCTION_ARGS)
2531 : : {
6505 bruce@momjian.us 2532 : 11 : ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
2533 : :
6825 tgl@sss.pgh.pa.us 2534 : 11 : PG_RETURN_INT32(anytime_typmodin(true, ta));
2535 : : }
2536 : :
2537 : : Datum
2538 : 5 : timetztypmodout(PG_FUNCTION_ARGS)
2539 : : {
6505 bruce@momjian.us 2540 : 5 : int32 typmod = PG_GETARG_INT32(0);
2541 : :
6825 tgl@sss.pgh.pa.us 2542 : 5 : PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
2543 : : }
2544 : :
2545 : :
2546 : : /* timetz2tm()
2547 : : * Convert TIME WITH TIME ZONE data type to POSIX time structure.
2548 : : */
2549 : : int
2999 2550 : 3793 : timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
2551 : : {
6378 2552 : 3793 : TimeOffset trem = time->time;
2553 : :
7410 bruce@momjian.us 2554 : 3793 : tm->tm_hour = trem / USECS_PER_HOUR;
2555 : 3793 : trem -= tm->tm_hour * USECS_PER_HOUR;
2556 : 3793 : tm->tm_min = trem / USECS_PER_MINUTE;
2557 : 3793 : trem -= tm->tm_min * USECS_PER_MINUTE;
2558 : 3793 : tm->tm_sec = trem / USECS_PER_SEC;
2559 : 3793 : *fsec = trem - tm->tm_sec * USECS_PER_SEC;
2560 : :
8539 lockhart@fourpalms.o 2561 [ + - ]: 3793 : if (tzp != NULL)
2562 : 3793 : *tzp = time->zone;
2563 : :
2564 : 3793 : return 0;
2565 : : }
2566 : :
2567 : : /* timetz_scale()
2568 : : * Adjust time type for specified scale factor.
2569 : : * Used by PostgreSQL type system to stuff columns.
2570 : : */
2571 : : Datum
8739 2572 : 39 : timetz_scale(PG_FUNCTION_ARGS)
2573 : : {
2574 : 39 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2575 : 39 : int32 typmod = PG_GETARG_INT32(1);
2576 : : TimeTzADT *result;
2577 : :
2578 : 39 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2579 : :
2580 : 39 : result->time = time->time;
2581 : 39 : result->zone = time->zone;
2582 : :
2583 : 39 : AdjustTimeForTypmod(&(result->time), typmod);
2584 : :
2585 : 39 : PG_RETURN_TIMETZADT_P(result);
2586 : : }
2587 : :
2588 : :
2589 : : static int
8892 tgl@sss.pgh.pa.us 2590 : 106574 : timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
2591 : : {
2592 : : TimeOffset t1,
2593 : : t2;
2594 : :
2595 : : /* Primary sort is by true (GMT-equivalent) time */
7411 bruce@momjian.us 2596 : 106574 : t1 = time1->time + (time1->zone * USECS_PER_SEC);
2597 : 106574 : t2 = time2->time + (time2->zone * USECS_PER_SEC);
2598 : :
8892 tgl@sss.pgh.pa.us 2599 [ + + ]: 106574 : if (t1 > t2)
2600 : 51137 : return 1;
2601 [ + + ]: 55437 : if (t1 < t2)
2602 : 50255 : return -1;
2603 : :
2604 : : /*
2605 : : * If same GMT time, sort by timezone; we only want to say that two
2606 : : * timetz's are equal if both the time and zone parts are equal.
2607 : : */
8744 lockhart@fourpalms.o 2608 [ + + ]: 5182 : if (time1->zone > time2->zone)
2609 : 39 : return 1;
2610 [ + + ]: 5143 : if (time1->zone < time2->zone)
2611 : 18 : return -1;
2612 : :
2613 : 5125 : return 0;
2614 : : }
2615 : :
2616 : : Datum
9220 tgl@sss.pgh.pa.us 2617 : 14834 : timetz_eq(PG_FUNCTION_ARGS)
2618 : : {
8934 bruce@momjian.us 2619 : 14834 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2620 : 14834 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2621 : :
8892 tgl@sss.pgh.pa.us 2622 : 14834 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
2623 : : }
2624 : :
2625 : : Datum
9220 tgl@sss.pgh.pa.us 2626 :UBC 0 : timetz_ne(PG_FUNCTION_ARGS)
2627 : : {
8934 bruce@momjian.us 2628 : 0 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2629 : 0 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2630 : :
8892 tgl@sss.pgh.pa.us 2631 : 0 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
2632 : : }
2633 : :
2634 : : Datum
9220 tgl@sss.pgh.pa.us 2635 :CBC 70432 : timetz_lt(PG_FUNCTION_ARGS)
2636 : : {
8934 bruce@momjian.us 2637 : 70432 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2638 : 70432 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2639 : :
8892 tgl@sss.pgh.pa.us 2640 : 70432 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
2641 : : }
2642 : :
2643 : : Datum
9220 2644 : 4226 : timetz_le(PG_FUNCTION_ARGS)
2645 : : {
8934 bruce@momjian.us 2646 : 4226 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2647 : 4226 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2648 : :
8892 tgl@sss.pgh.pa.us 2649 : 4226 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
2650 : : }
2651 : :
2652 : : Datum
9220 2653 : 4717 : timetz_gt(PG_FUNCTION_ARGS)
2654 : : {
8934 bruce@momjian.us 2655 : 4717 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2656 : 4717 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2657 : :
8892 tgl@sss.pgh.pa.us 2658 : 4717 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
2659 : : }
2660 : :
2661 : : Datum
9220 2662 : 4178 : timetz_ge(PG_FUNCTION_ARGS)
2663 : : {
8934 bruce@momjian.us 2664 : 4178 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2665 : 4178 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2666 : :
8892 tgl@sss.pgh.pa.us 2667 : 4178 : PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
2668 : : }
2669 : :
2670 : : Datum
9220 2671 : 7818 : timetz_cmp(PG_FUNCTION_ARGS)
2672 : : {
8934 bruce@momjian.us 2673 : 7818 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2674 : 7818 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2675 : :
8892 tgl@sss.pgh.pa.us 2676 : 7818 : PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
2677 : : }
2678 : :
2679 : : Datum
9210 2680 : 1137 : timetz_hash(PG_FUNCTION_ARGS)
2681 : : {
8934 bruce@momjian.us 2682 : 1137 : TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2683 : : uint32 thash;
2684 : :
2685 : : /*
2686 : : * To avoid any problems with padding bytes in the struct, we figure the
2687 : : * field hashes separately and XOR them.
2688 : : */
6637 tgl@sss.pgh.pa.us 2689 : 1137 : thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
2690 : : Int64GetDatumFast(key->time)));
2691 : 1137 : thash ^= DatumGetUInt32(hash_uint32(key->zone));
2692 : 1137 : PG_RETURN_UINT32(thash);
2693 : : }
2694 : :
2695 : : Datum
2928 rhaas@postgresql.org 2696 : 30 : timetz_hash_extended(PG_FUNCTION_ARGS)
2697 : : {
2698 : 30 : TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
2927 2699 : 30 : Datum seed = PG_GETARG_DATUM(1);
2700 : : uint64 thash;
2701 : :
2702 : : /* Same approach as timetz_hash */
2928 2703 : 30 : thash = DatumGetUInt64(DirectFunctionCall2(hashint8extended,
2704 : : Int64GetDatumFast(key->time),
2705 : : seed));
2927 2706 : 30 : thash ^= DatumGetUInt64(hash_uint32_extended(key->zone,
2838 2707 : 30 : DatumGetInt64(seed)));
2928 2708 : 30 : PG_RETURN_UINT64(thash);
2709 : : }
2710 : :
2711 : : Datum
9220 tgl@sss.pgh.pa.us 2712 :UBC 0 : timetz_larger(PG_FUNCTION_ARGS)
2713 : : {
8934 bruce@momjian.us 2714 : 0 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2715 : 0 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2716 : : TimeTzADT *result;
2717 : :
8065 tgl@sss.pgh.pa.us 2718 [ # # ]: 0 : if (timetz_cmp_internal(time1, time2) > 0)
2719 : 0 : result = time1;
2720 : : else
2721 : 0 : result = time2;
2722 : 0 : PG_RETURN_TIMETZADT_P(result);
2723 : : }
2724 : :
2725 : : Datum
9220 2726 : 0 : timetz_smaller(PG_FUNCTION_ARGS)
2727 : : {
8934 bruce@momjian.us 2728 : 0 : TimeTzADT *time1 = PG_GETARG_TIMETZADT_P(0);
2729 : 0 : TimeTzADT *time2 = PG_GETARG_TIMETZADT_P(1);
2730 : : TimeTzADT *result;
2731 : :
8065 tgl@sss.pgh.pa.us 2732 [ # # ]: 0 : if (timetz_cmp_internal(time1, time2) < 0)
2733 : 0 : result = time1;
2734 : : else
2735 : 0 : result = time2;
2736 : 0 : PG_RETURN_TIMETZADT_P(result);
2737 : : }
2738 : :
2739 : : /* timetz_pl_interval()
2740 : : * Add interval to timetz.
2741 : : */
2742 : : Datum
9043 lockhart@fourpalms.o 2743 :CBC 1359 : timetz_pl_interval(PG_FUNCTION_ARGS)
2744 : : {
2745 : 1359 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2746 : 1359 : Interval *span = PG_GETARG_INTERVAL_P(1);
2747 : : TimeTzADT *result;
2748 : :
662 dean.a.rasheed@gmail 2749 [ + + + - : 1359 : if (INTERVAL_NOT_FINITE(span))
- + + + +
- + - ]
2750 [ + - ]: 6 : ereport(ERROR,
2751 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2752 : : errmsg("cannot add infinite interval to time")));
2753 : :
9043 lockhart@fourpalms.o 2754 : 1353 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2755 : :
7352 bruce@momjian.us 2756 : 1353 : result->time = time->time + span->time;
2757 : 1353 : result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
8539 lockhart@fourpalms.o 2758 [ - + ]: 1353 : if (result->time < INT64CONST(0))
7411 bruce@momjian.us 2759 :UBC 0 : result->time += USECS_PER_DAY;
2760 : :
9043 lockhart@fourpalms.o 2761 :CBC 1353 : result->zone = time->zone;
2762 : :
2763 : 1353 : PG_RETURN_TIMETZADT_P(result);
2764 : : }
2765 : :
2766 : : /* timetz_mi_interval()
2767 : : * Subtract interval from timetz.
2768 : : */
2769 : : Datum
2770 : 369 : timetz_mi_interval(PG_FUNCTION_ARGS)
2771 : : {
2772 : 369 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(0);
2773 : 369 : Interval *span = PG_GETARG_INTERVAL_P(1);
2774 : : TimeTzADT *result;
2775 : :
662 dean.a.rasheed@gmail 2776 [ + + + - : 369 : if (INTERVAL_NOT_FINITE(span))
- + + + +
- + - ]
2777 [ + - ]: 6 : ereport(ERROR,
2778 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2779 : : errmsg("cannot subtract infinite interval from time")));
2780 : :
9043 lockhart@fourpalms.o 2781 : 363 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2782 : :
7352 bruce@momjian.us 2783 : 363 : result->time = time->time - span->time;
2784 : 363 : result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
8539 lockhart@fourpalms.o 2785 [ + + ]: 363 : if (result->time < INT64CONST(0))
7411 bruce@momjian.us 2786 : 39 : result->time += USECS_PER_DAY;
2787 : :
9043 lockhart@fourpalms.o 2788 : 363 : result->zone = time->zone;
2789 : :
2790 : 363 : PG_RETURN_TIMETZADT_P(result);
2791 : : }
2792 : :
2793 : : /*
2794 : : * in_range support function for timetz.
2795 : : */
2796 : : Datum
2768 tgl@sss.pgh.pa.us 2797 : 519 : in_range_timetz_interval(PG_FUNCTION_ARGS)
2798 : : {
2799 : 519 : TimeTzADT *val = PG_GETARG_TIMETZADT_P(0);
2800 : 519 : TimeTzADT *base = PG_GETARG_TIMETZADT_P(1);
2801 : 519 : Interval *offset = PG_GETARG_INTERVAL_P(2);
2802 : 519 : bool sub = PG_GETARG_BOOL(3);
2803 : 519 : bool less = PG_GETARG_BOOL(4);
2804 : : TimeTzADT sum;
2805 : :
2806 : : /*
2807 : : * Like timetz_pl_interval/timetz_mi_interval, we disregard the month and
2808 : : * day fields of the offset. So our test for negative should too. This
2809 : : * also catches -infinity, so we only need worry about +infinity below.
2810 : : */
2811 [ + + ]: 519 : if (offset->time < 0)
2812 [ + - ]: 6 : ereport(ERROR,
2813 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2814 : : errmsg("invalid preceding or following size in window function")));
2815 : :
2816 : : /*
2817 : : * We can't use timetz_pl_interval/timetz_mi_interval here, because their
2818 : : * wraparound behavior would give wrong (or at least undesirable) answers.
2819 : : * Fortunately the equivalent non-wrapping behavior is trivial, except
2820 : : * that adding an infinite (or very large) interval might cause integer
2821 : : * overflow. Subtraction cannot overflow here.
2822 : : */
2823 [ + + ]: 513 : if (sub)
2824 : 237 : sum.time = base->time - offset->time;
662 dean.a.rasheed@gmail 2825 [ + + ]: 276 : else if (pg_add_s64_overflow(base->time, offset->time, &sum.time))
2826 : 144 : PG_RETURN_BOOL(less);
2768 tgl@sss.pgh.pa.us 2827 : 369 : sum.zone = base->zone;
2828 : :
2829 [ + + ]: 369 : if (less)
2830 : 168 : PG_RETURN_BOOL(timetz_cmp_internal(val, &sum) <= 0);
2831 : : else
2832 : 201 : PG_RETURN_BOOL(timetz_cmp_internal(val, &sum) >= 0);
2833 : : }
2834 : :
2835 : : /* overlaps_timetz() --- implements the SQL OVERLAPS operator.
2836 : : *
2837 : : * Algorithm is per SQL spec. This is much harder than you'd think
2838 : : * because the spec requires us to deliver a non-null answer in some cases
2839 : : * where some of the inputs are null.
2840 : : */
2841 : : Datum
9220 tgl@sss.pgh.pa.us 2842 :UBC 0 : overlaps_timetz(PG_FUNCTION_ARGS)
2843 : : {
2844 : : /*
2845 : : * The arguments are TimeTzADT *, but we leave them as generic Datums for
2846 : : * convenience of notation --- and to avoid dereferencing nulls.
2847 : : */
2848 : 0 : Datum ts1 = PG_GETARG_DATUM(0);
2849 : 0 : Datum te1 = PG_GETARG_DATUM(1);
2850 : 0 : Datum ts2 = PG_GETARG_DATUM(2);
2851 : 0 : Datum te2 = PG_GETARG_DATUM(3);
9039 2852 : 0 : bool ts1IsNull = PG_ARGISNULL(0);
2853 : 0 : bool te1IsNull = PG_ARGISNULL(1);
2854 : 0 : bool ts2IsNull = PG_ARGISNULL(2);
2855 : 0 : bool te2IsNull = PG_ARGISNULL(3);
2856 : :
2857 : : #define TIMETZ_GT(t1,t2) \
2858 : : DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
2859 : : #define TIMETZ_LT(t1,t2) \
2860 : : DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
2861 : :
2862 : : /*
2863 : : * If both endpoints of interval 1 are null, the result is null (unknown).
2864 : : * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2865 : : * take ts1 as the lesser endpoint.
2866 : : */
2867 [ # # ]: 0 : if (ts1IsNull)
2868 : : {
2869 [ # # ]: 0 : if (te1IsNull)
2870 : 0 : PG_RETURN_NULL();
2871 : : /* swap null for non-null */
9307 lockhart@fourpalms.o 2872 : 0 : ts1 = te1;
9039 tgl@sss.pgh.pa.us 2873 : 0 : te1IsNull = true;
2874 : : }
2875 [ # # ]: 0 : else if (!te1IsNull)
2876 : : {
2877 [ # # ]: 0 : if (TIMETZ_GT(ts1, te1))
2878 : : {
8934 bruce@momjian.us 2879 : 0 : Datum tt = ts1;
2880 : :
9039 tgl@sss.pgh.pa.us 2881 : 0 : ts1 = te1;
2882 : 0 : te1 = tt;
2883 : : }
2884 : : }
2885 : :
2886 : : /* Likewise for interval 2. */
2887 [ # # ]: 0 : if (ts2IsNull)
2888 : : {
2889 [ # # ]: 0 : if (te2IsNull)
2890 : 0 : PG_RETURN_NULL();
2891 : : /* swap null for non-null */
9307 lockhart@fourpalms.o 2892 : 0 : ts2 = te2;
9039 tgl@sss.pgh.pa.us 2893 : 0 : te2IsNull = true;
2894 : : }
2895 [ # # ]: 0 : else if (!te2IsNull)
2896 : : {
2897 [ # # ]: 0 : if (TIMETZ_GT(ts2, te2))
2898 : : {
8934 bruce@momjian.us 2899 : 0 : Datum tt = ts2;
2900 : :
9039 tgl@sss.pgh.pa.us 2901 : 0 : ts2 = te2;
2902 : 0 : te2 = tt;
2903 : : }
2904 : : }
2905 : :
2906 : : /*
2907 : : * At this point neither ts1 nor ts2 is null, so we can consider three
2908 : : * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2909 : : */
2910 [ # # ]: 0 : if (TIMETZ_GT(ts1, ts2))
2911 : : {
2912 : : /*
2913 : : * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2914 : : * in the presence of nulls it's not quite completely so.
2915 : : */
2916 [ # # ]: 0 : if (te2IsNull)
2917 : 0 : PG_RETURN_NULL();
2918 [ # # ]: 0 : if (TIMETZ_LT(ts1, te2))
2919 : 0 : PG_RETURN_BOOL(true);
2920 [ # # ]: 0 : if (te1IsNull)
2921 : 0 : PG_RETURN_NULL();
2922 : :
2923 : : /*
2924 : : * If te1 is not null then we had ts1 <= te1 above, and we just found
2925 : : * ts1 >= te2, hence te1 >= te2.
2926 : : */
2927 : 0 : PG_RETURN_BOOL(false);
2928 : : }
2929 [ # # ]: 0 : else if (TIMETZ_LT(ts1, ts2))
2930 : : {
2931 : : /* This case is ts2 < te1 OR te2 < te1 */
2932 [ # # ]: 0 : if (te1IsNull)
2933 : 0 : PG_RETURN_NULL();
2934 [ # # ]: 0 : if (TIMETZ_LT(ts2, te1))
2935 : 0 : PG_RETURN_BOOL(true);
2936 [ # # ]: 0 : if (te2IsNull)
2937 : 0 : PG_RETURN_NULL();
2938 : :
2939 : : /*
2940 : : * If te2 is not null then we had ts2 <= te2 above, and we just found
2941 : : * ts2 >= te1, hence te2 >= te1.
2942 : : */
2943 : 0 : PG_RETURN_BOOL(false);
2944 : : }
2945 : : else
2946 : : {
2947 : : /*
2948 : : * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2949 : : * rather silly way of saying "true if both are nonnull, else null".
2950 : : */
2951 [ # # # # ]: 0 : if (te1IsNull || te2IsNull)
2952 : 0 : PG_RETURN_NULL();
2953 : 0 : PG_RETURN_BOOL(true);
2954 : : }
2955 : :
2956 : : #undef TIMETZ_GT
2957 : : #undef TIMETZ_LT
2958 : : }
2959 : :
2960 : :
2961 : : Datum
8744 lockhart@fourpalms.o 2962 :CBC 42 : timetz_time(PG_FUNCTION_ARGS)
2963 : : {
2964 : 42 : TimeTzADT *timetz = PG_GETARG_TIMETZADT_P(0);
2965 : : TimeADT result;
2966 : :
2967 : : /* swallow the time zone and just return the time */
2968 : 42 : result = timetz->time;
2969 : :
2970 : 42 : PG_RETURN_TIMEADT(result);
2971 : : }
2972 : :
2973 : :
2974 : : Datum
2975 : 156 : time_timetz(PG_FUNCTION_ARGS)
2976 : : {
2977 : 156 : TimeADT time = PG_GETARG_TIMEADT(0);
2978 : : TimeTzADT *result;
2979 : : struct pg_tm tt,
2980 : 156 : *tm = &tt;
2981 : : fsec_t fsec;
2982 : : int tz;
2983 : :
8488 JanWieck@Yahoo.com 2984 : 156 : GetCurrentDateTime(tm);
8539 lockhart@fourpalms.o 2985 : 156 : time2tm(time, tm, &fsec);
6608 tgl@sss.pgh.pa.us 2986 : 156 : tz = DetermineTimeZoneOffset(tm, session_timezone);
2987 : :
8744 lockhart@fourpalms.o 2988 : 156 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2989 : :
2990 : 156 : result->time = time;
2991 : 156 : result->zone = tz;
2992 : :
2993 : 156 : PG_RETURN_TIMETZADT_P(result);
2994 : : }
2995 : :
2996 : :
2997 : : /* timestamptz_timetz()
2998 : : * Convert timestamp to timetz data type.
2999 : : */
3000 : : Datum
3001 : 30 : timestamptz_timetz(PG_FUNCTION_ARGS)
3002 : : {
8717 bruce@momjian.us 3003 : 30 : TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
3004 : : TimeTzADT *result;
3005 : : struct pg_tm tt,
9307 lockhart@fourpalms.o 3006 : 30 : *tm = &tt;
3007 : : int tz;
3008 : : fsec_t fsec;
3009 : :
9220 tgl@sss.pgh.pa.us 3010 [ + - - + ]: 30 : if (TIMESTAMP_NOT_FINITE(timestamp))
8744 lockhart@fourpalms.o 3011 :UBC 0 : PG_RETURN_NULL();
3012 : :
4923 peter_e@gmx.net 3013 [ - + ]:CBC 30 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
8077 tgl@sss.pgh.pa.us 3014 [ # # ]:UBC 0 : ereport(ERROR,
3015 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3016 : : errmsg("timestamp out of range")));
3017 : :
9220 tgl@sss.pgh.pa.us 3018 :CBC 30 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
3019 : :
8539 lockhart@fourpalms.o 3020 : 30 : tm2timetz(tm, fsec, tz, result);
3021 : :
9220 tgl@sss.pgh.pa.us 3022 : 30 : PG_RETURN_TIMETZADT_P(result);
3023 : : }
3024 : :
3025 : :
3026 : : /* datetimetz_timestamptz()
3027 : : * Convert date and timetz to timestamp with time zone data type.
3028 : : * Timestamp is stored in GMT, so add the time zone
3029 : : * stored with the timetz to the result.
3030 : : * - thomas 2000-03-10
3031 : : */
3032 : : Datum
8744 lockhart@fourpalms.o 3033 : 27 : datetimetz_timestamptz(PG_FUNCTION_ARGS)
3034 : : {
7266 bruce@momjian.us 3035 : 27 : DateADT date = PG_GETARG_DATEADT(0);
9220 tgl@sss.pgh.pa.us 3036 : 27 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
3037 : : TimestampTz result;
3038 : :
6171 3039 [ - + ]: 27 : if (DATE_IS_NOBEGIN(date))
6171 tgl@sss.pgh.pa.us 3040 :UBC 0 : TIMESTAMP_NOBEGIN(result);
6171 tgl@sss.pgh.pa.us 3041 [ - + ]:CBC 27 : else if (DATE_IS_NOEND(date))
6171 tgl@sss.pgh.pa.us 3042 :UBC 0 : TIMESTAMP_NOEND(result);
3043 : : else
3044 : : {
3045 : : /*
3046 : : * Date's range is wider than timestamp's, so check for boundaries.
3047 : : * Since dates have the same minimum values as timestamps, only upper
3048 : : * boundary need be checked for overflow.
3049 : : */
3461 tgl@sss.pgh.pa.us 3050 [ - + ]:CBC 27 : if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE))
3461 tgl@sss.pgh.pa.us 3051 [ # # ]:UBC 0 : ereport(ERROR,
3052 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3053 : : errmsg("date out of range for timestamp")));
6171 tgl@sss.pgh.pa.us 3054 :CBC 27 : result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
3055 : :
3056 : : /*
3057 : : * Since it is possible to go beyond allowed timestamptz range because
3058 : : * of time zone, check for allowed timestamp range after adding tz.
3059 : : */
3461 3060 [ + - - + ]: 27 : if (!IS_VALID_TIMESTAMP(result))
3461 tgl@sss.pgh.pa.us 3061 [ # # ]:UBC 0 : ereport(ERROR,
3062 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3063 : : errmsg("date out of range for timestamp")));
3064 : : }
3065 : :
9220 tgl@sss.pgh.pa.us 3066 :CBC 27 : PG_RETURN_TIMESTAMP(result);
3067 : : }
3068 : :
3069 : :
3070 : : /* timetz_part() and extract_timetz()
3071 : : * Extract specified field from time type.
3072 : : */
3073 : : static Datum
1614 peter@eisentraut.org 3074 : 45 : timetz_part_common(PG_FUNCTION_ARGS, bool retnumeric)
3075 : : {
6374 tgl@sss.pgh.pa.us 3076 : 45 : text *units = PG_GETARG_TEXT_PP(0);
8724 lockhart@fourpalms.o 3077 : 45 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
3078 : : int64 intresult;
3079 : : int type,
3080 : : val;
3081 : : char *lowunits;
3082 : :
6374 tgl@sss.pgh.pa.us 3083 [ - + ]: 45 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
3084 [ - + - - : 45 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
3085 : : false);
3086 : :
8724 lockhart@fourpalms.o 3087 : 45 : type = DecodeUnits(0, lowunits, &val);
3088 [ + + ]: 45 : if (type == UNKNOWN_FIELD)
3089 : 9 : type = DecodeSpecial(0, lowunits, &val);
3090 : :
3091 [ + + ]: 45 : if (type == UNITS)
3092 : : {
3093 : : int tz;
3094 : : fsec_t fsec;
3095 : : struct pg_tm tt,
3096 : 36 : *tm = &tt;
3097 : :
8539 3098 : 36 : timetz2tm(time, tm, &fsec, &tz);
3099 : :
8724 3100 [ + + + + : 36 : switch (val)
+ + + +
+ ]
3101 : : {
3102 : 3 : case DTK_TZ:
1614 peter@eisentraut.org 3103 : 3 : intresult = -tz;
8724 lockhart@fourpalms.o 3104 : 3 : break;
3105 : :
3106 : 3 : case DTK_TZ_MINUTE:
1614 peter@eisentraut.org 3107 : 3 : intresult = (-tz / SECS_PER_MINUTE) % MINS_PER_HOUR;
8724 lockhart@fourpalms.o 3108 : 3 : break;
3109 : :
3110 : 3 : case DTK_TZ_HOUR:
1614 peter@eisentraut.org 3111 : 3 : intresult = -tz / SECS_PER_HOUR;
8724 lockhart@fourpalms.o 3112 : 3 : break;
3113 : :
3114 : 6 : case DTK_MICROSEC:
1609 tgl@sss.pgh.pa.us 3115 : 6 : intresult = tm->tm_sec * INT64CONST(1000000) + fsec;
8724 lockhart@fourpalms.o 3116 : 6 : break;
3117 : :
3118 : 6 : case DTK_MILLISEC:
1614 peter@eisentraut.org 3119 [ + + ]: 6 : if (retnumeric)
3120 : : /*---
3121 : : * tm->tm_sec * 1000 + fsec / 1000
3122 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1000
3123 : : */
1609 tgl@sss.pgh.pa.us 3124 : 12 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 3));
3125 : : else
1614 peter@eisentraut.org 3126 : 3 : PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0);
3127 : : break;
3128 : :
8724 lockhart@fourpalms.o 3129 : 6 : case DTK_SECOND:
1614 peter@eisentraut.org 3130 [ + + ]: 6 : if (retnumeric)
3131 : : /*---
3132 : : * tm->tm_sec + fsec / 1'000'000
3133 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
3134 : : */
1609 tgl@sss.pgh.pa.us 3135 : 3 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 6));
3136 : : else
1614 peter@eisentraut.org 3137 : 3 : PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0);
3138 : : break;
3139 : :
8724 lockhart@fourpalms.o 3140 : 3 : case DTK_MINUTE:
1614 peter@eisentraut.org 3141 : 3 : intresult = tm->tm_min;
8724 lockhart@fourpalms.o 3142 : 3 : break;
3143 : :
3144 : 3 : case DTK_HOUR:
1614 peter@eisentraut.org 3145 : 3 : intresult = tm->tm_hour;
8724 lockhart@fourpalms.o 3146 : 3 : break;
3147 : :
3148 : 3 : case DTK_DAY:
3149 : : case DTK_MONTH:
3150 : : case DTK_QUARTER:
3151 : : case DTK_YEAR:
3152 : : case DTK_DECADE:
3153 : : case DTK_CENTURY:
3154 : : case DTK_MILLENNIUM:
3155 : : default:
8077 tgl@sss.pgh.pa.us 3156 [ + - ]: 3 : ereport(ERROR,
3157 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3158 : : errmsg("unit \"%s\" not supported for type %s",
3159 : : lowunits, format_type_be(TIMETZOID))));
3160 : : intresult = 0;
3161 : : }
3162 : : }
7410 bruce@momjian.us 3163 [ + + + - ]: 9 : else if (type == RESERV && val == DTK_EPOCH)
3164 : : {
1614 peter@eisentraut.org 3165 [ + + ]: 6 : if (retnumeric)
3166 : : /*---
3167 : : * time->time / 1'000'000 + time->zone
3168 : : * = (time->time + time->zone * 1'000'000) / 1'000'000
3169 : : */
1609 tgl@sss.pgh.pa.us 3170 : 3 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(time->time + time->zone * INT64CONST(1000000), 6));
3171 : : else
1614 peter@eisentraut.org 3172 : 3 : PG_RETURN_FLOAT8(time->time / 1000000.0 + time->zone);
3173 : : }
3174 : : else
3175 : : {
8077 tgl@sss.pgh.pa.us 3176 [ + - ]: 3 : ereport(ERROR,
3177 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3178 : : errmsg("unit \"%s\" not recognized for type %s",
3179 : : lowunits, format_type_be(TIMETZOID))));
3180 : : intresult = 0;
3181 : : }
3182 : :
1614 peter@eisentraut.org 3183 [ + + ]: 21 : if (retnumeric)
3184 : 18 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
3185 : : else
3186 : 3 : PG_RETURN_FLOAT8(intresult);
3187 : : }
3188 : :
3189 : :
3190 : : Datum
3191 : 12 : timetz_part(PG_FUNCTION_ARGS)
3192 : : {
3193 : 12 : return timetz_part_common(fcinfo, false);
3194 : : }
3195 : :
3196 : : Datum
3197 : 33 : extract_timetz(PG_FUNCTION_ARGS)
3198 : : {
3199 : 33 : return timetz_part_common(fcinfo, true);
3200 : : }
3201 : :
3202 : : /* timetz_zone()
3203 : : * Encode time with time zone type with specified time zone.
3204 : : * Applies DST rules as of the transaction start time.
3205 : : */
3206 : : Datum
8744 lockhart@fourpalms.o 3207 : 144 : timetz_zone(PG_FUNCTION_ARGS)
3208 : : {
6374 tgl@sss.pgh.pa.us 3209 : 144 : text *zone = PG_GETARG_TEXT_PP(0);
7388 bruce@momjian.us 3210 : 144 : TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
3211 : : TimeTzADT *result;
3212 : : int tz;
3213 : : char tzname[TZ_STRLEN_MAX + 1];
3214 : : int type,
3215 : : val;
3216 : : pg_tz *tzp;
3217 : :
3218 : : /*
3219 : : * Look up the requested timezone.
3220 : : */
6374 tgl@sss.pgh.pa.us 3221 : 144 : text_to_cstring_buffer(zone, tzname, sizeof(tzname));
3222 : :
904 3223 : 144 : type = DecodeTimezoneName(tzname, &val, &tzp);
3224 : :
3225 [ + + ]: 144 : if (type == TZNAME_FIXED_OFFSET)
3226 : : {
3227 : : /* fixed-offset abbreviation */
3978 3228 : 108 : tz = -val;
3229 : : }
904 3230 [ - + ]: 36 : else if (type == TZNAME_DYNTZ)
3231 : : {
3232 : : /* dynamic-offset abbreviation, resolve using transaction start time */
1461 tgl@sss.pgh.pa.us 3233 :UBC 0 : TimestampTz now = GetCurrentTransactionStartTimestamp();
3234 : : int isdst;
3235 : :
3236 : 0 : tz = DetermineTimeZoneAbbrevOffsetTS(now, tzname, tzp, &isdst);
3237 : : }
3238 : : else
3239 : : {
3240 : : /* Get the offset-from-GMT that is valid now for the zone name */
904 tgl@sss.pgh.pa.us 3241 :CBC 36 : TimestampTz now = GetCurrentTransactionStartTimestamp();
3242 : : struct pg_tm tm;
3243 : : fsec_t fsec;
3244 : :
3245 [ - + ]: 36 : if (timestamp2tm(now, &tz, &tm, &fsec, NULL, tzp) != 0)
7302 tgl@sss.pgh.pa.us 3246 [ # # ]:UBC 0 : ereport(ERROR,
3247 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3248 : : errmsg("timestamp out of range")));
3249 : : }
3250 : :
7302 tgl@sss.pgh.pa.us 3251 :CBC 144 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
3252 : :
7388 bruce@momjian.us 3253 : 144 : result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
3254 : : /* C99 modulo has the wrong sign convention for negative input */
3255 [ + + ]: 153 : while (result->time < INT64CONST(0))
3256 : 9 : result->time += USECS_PER_DAY;
690 tgl@sss.pgh.pa.us 3257 [ + + ]: 144 : if (result->time >= USECS_PER_DAY)
3258 : 18 : result->time %= USECS_PER_DAY;
3259 : :
7388 bruce@momjian.us 3260 : 144 : result->zone = tz;
3261 : :
8744 lockhart@fourpalms.o 3262 : 144 : PG_RETURN_TIMETZADT_P(result);
3263 : : }
3264 : :
3265 : : /* timetz_izone()
3266 : : * Encode time with time zone type with specified time interval as time zone.
3267 : : */
3268 : : Datum
3269 : 84 : timetz_izone(PG_FUNCTION_ARGS)
3270 : : {
3271 : 84 : Interval *zone = PG_GETARG_INTERVAL_P(0);
3272 : 84 : TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
3273 : : TimeTzADT *result;
3274 : : int tz;
3275 : :
662 dean.a.rasheed@gmail 3276 [ + + + - : 84 : if (INTERVAL_NOT_FINITE(zone))
- + + + +
- + - ]
3277 [ + - ]: 12 : ereport(ERROR,
3278 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3279 : : errmsg("interval time zone \"%s\" must be finite",
3280 : : DatumGetCString(DirectFunctionCall1(interval_out,
3281 : : PointerGetDatum(zone))))));
3282 : :
4601 tgl@sss.pgh.pa.us 3283 [ + - - + ]: 72 : if (zone->month != 0 || zone->day != 0)
8077 tgl@sss.pgh.pa.us 3284 [ # # ]:UBC 0 : ereport(ERROR,
3285 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3286 : : errmsg("interval time zone \"%s\" must not include months or days",
3287 : : DatumGetCString(DirectFunctionCall1(interval_out,
3288 : : PointerGetDatum(zone))))));
3289 : :
7411 bruce@momjian.us 3290 :CBC 72 : tz = -(zone->time / USECS_PER_SEC);
3291 : :
8744 lockhart@fourpalms.o 3292 : 72 : result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
3293 : :
7410 bruce@momjian.us 3294 : 72 : result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
3295 : : /* C99 modulo has the wrong sign convention for negative input */
8539 lockhart@fourpalms.o 3296 [ + + ]: 81 : while (result->time < INT64CONST(0))
7411 bruce@momjian.us 3297 : 9 : result->time += USECS_PER_DAY;
690 tgl@sss.pgh.pa.us 3298 [ + + ]: 72 : if (result->time >= USECS_PER_DAY)
3299 : 6 : result->time %= USECS_PER_DAY;
3300 : :
8744 lockhart@fourpalms.o 3301 : 72 : result->zone = tz;
3302 : :
3303 : 72 : PG_RETURN_TIMETZADT_P(result);
3304 : : }
3305 : :
3306 : : /* timetz_at_local()
3307 : : *
3308 : : * Unlike for timestamp[tz]_at_local, the type for timetz does not flip between
3309 : : * time with/without time zone, so we cannot just call the conversion function.
3310 : : */
3311 : : Datum
694 michael@paquier.xyz 3312 : 72 : timetz_at_local(PG_FUNCTION_ARGS)
3313 : : {
3314 : 72 : Datum time = PG_GETARG_DATUM(0);
3315 : 72 : const char *tzn = pg_get_timezone_name(session_timezone);
3316 : 72 : Datum zone = PointerGetDatum(cstring_to_text(tzn));
3317 : :
3318 : 72 : return DirectFunctionCall2(timetz_zone, zone, time);
3319 : : }
|