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