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