date.c 55.4 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * date.c
4
 *	  implements DATE and TIME data types specified in SQL-92 standard
5
 *
6
 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994-5, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.143 2008/10/14 17:12:33 tgl Exp $
12 13 14 15 16
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"
17

18
#include <ctype.h>
19
#include <limits.h>
20
#include <float.h>
21
#include <time.h>
22 23

#include "access/hash.h"
24
#include "libpq/pqformat.h"
Bruce Momjian's avatar
Bruce Momjian committed
25
#include "miscadmin.h"
26
#include "parser/scansup.h"
27
#include "utils/array.h"
28
#include "utils/builtins.h"
Bruce Momjian's avatar
Bruce Momjian committed
29 30
#include "utils/date.h"
#include "utils/nabstime.h"
31

32 33
/*
 * gcc's -ffast-math switch breaks routines that expect exact results from
34
 * expressions like timeval / SECS_PER_HOUR, where timeval is double.
35 36 37 38 39
 */
#ifdef __FAST_MATH__
#error -ffast-math is known to break this code
#endif

40

41
static void EncodeSpecialDate(DateADT dt, char *str);
42 43 44 45
static int	time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
static int	timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
static int	tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
static int	tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
46
static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
47

48 49 50 51 52

/* common code for timetypmodin and timetztypmodin */
static int32
anytime_typmodin(bool istz, ArrayType *ta)
{
Bruce Momjian's avatar
Bruce Momjian committed
53 54 55
	int32		typmod;
	int32	   *tl;
	int			n;
56

57
	tl = ArrayGetIntegerTypmods(ta, &n);
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

	/*
	 * we're not too tense about good error message here because grammar
	 * shouldn't allow wrong number of modifiers for TIME
	 */
	if (n != 1)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid type modifier")));

	if (*tl < 0)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("TIME(%d)%s precision must not be negative",
						*tl, (istz ? " WITH TIME ZONE" : ""))));
	if (*tl > MAX_TIME_PRECISION)
	{
		ereport(WARNING,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
Bruce Momjian's avatar
Bruce Momjian committed
78
						*tl, (istz ? " WITH TIME ZONE" : ""),
79 80
						MAX_TIME_PRECISION)));
		typmod = MAX_TIME_PRECISION;
Bruce Momjian's avatar
Bruce Momjian committed
81 82
	}
	else
83 84 85 86 87 88 89 90 91
		typmod = *tl;

	return typmod;
}

/* common code for timetypmodout and timetztypmodout */
static char *
anytime_typmodout(bool istz, int32 typmod)
{
Bruce Momjian's avatar
Bruce Momjian committed
92
	char	   *res = (char *) palloc(64);
93 94 95 96 97 98 99 100 101 102
	const char *tz = istz ? " with time zone" : " without time zone";

	if (typmod >= 0)
		snprintf(res, 64, "(%d)%s", (int) typmod, tz);
	else
		snprintf(res, 64, "%s", tz);
	return res;
}


103
/*****************************************************************************
104
 *	 Date ADT
105 106
 *****************************************************************************/

107 108 109

/* date_in()
 * Given date text string, convert to internal date format.
110
 */
111 112
Datum
date_in(PG_FUNCTION_ARGS)
113
{
114
	char	   *str = PG_GETARG_CSTRING(0);
115
	DateADT		date;
116
	fsec_t		fsec;
Bruce Momjian's avatar
Bruce Momjian committed
117
	struct pg_tm tt,
118
			   *tm = &tt;
119
	int			tzp;
120
	int			dtype;
121
	int			nf;
122
	int			dterr;
123
	char	   *field[MAXDATEFIELDS];
124
	int			ftype[MAXDATEFIELDS];
125
	char		workbuf[MAXDATELEN + 1];
126

127 128
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
						  field, ftype, MAXDATEFIELDS, &nf);
129 130 131 132
	if (dterr == 0)
		dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
	if (dterr != 0)
		DateTimeParseError(dterr, str, "date");
133

134 135
	switch (dtype)
	{
136 137 138 139
		case DTK_DATE:
			break;

		case DTK_CURRENT:
140 141
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
142
			  errmsg("date/time value \"current\" is no longer supported")));
143

Jan Wieck's avatar
Jan Wieck committed
144
			GetCurrentDateTime(tm);
145 146 147
			break;

		case DTK_EPOCH:
148
			GetEpochTime(tm);
149
			break;
150

151 152 153 154 155 156 157 158
		case DTK_LATE:
			DATE_NOEND(date);
			PG_RETURN_DATEADT(date);

		case DTK_EARLY:
			DATE_NOBEGIN(date);
			PG_RETURN_DATEADT(date);

159
		default:
160 161
			DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
			break;
162
	}
163

164 165 166 167 168
	if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("date out of range: \"%s\"", str)));

169
	date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
170

171 172
	PG_RETURN_DATEADT(date);
}
173

174 175
/* date_out()
 * Given internal format date, convert to text string.
176
 */
177 178
Datum
date_out(PG_FUNCTION_ARGS)
179
{
180
	DateADT		date = PG_GETARG_DATEADT(0);
181
	char	   *result;
Bruce Momjian's avatar
Bruce Momjian committed
182
	struct pg_tm tt,
183 184
			   *tm = &tt;
	char		buf[MAXDATELEN + 1];
185

186 187 188 189 190 191 192 193
	if (DATE_NOT_FINITE(date))
		EncodeSpecialDate(date, buf);
	else
	{
		j2date(date + POSTGRES_EPOCH_JDATE,
			   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
		EncodeDateOnly(tm, DateStyle, buf);
	}
194

195 196 197
	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}
198

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
/*
 *		date_recv			- converts external binary format to date
 */
Datum
date_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);

	PG_RETURN_DATEADT((DateADT) pq_getmsgint(buf, sizeof(DateADT)));
}

/*
 *		date_send			- converts date to binary format
 */
Datum
date_send(PG_FUNCTION_ARGS)
{
216
	DateADT		date = PG_GETARG_DATEADT(0);
217 218 219 220 221 222 223
	StringInfoData buf;

	pq_begintypsend(&buf);
	pq_sendint(&buf, date, sizeof(date));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

224 225 226 227 228 229 230 231 232 233 234 235 236 237
/*
 * Convert reserved date values to string.
 */
static void
EncodeSpecialDate(DateADT dt, char *str)
{
	if (DATE_IS_NOBEGIN(dt))
		strcpy(str, EARLY);
	else if (DATE_IS_NOEND(dt))
		strcpy(str, LATE);
	else						/* shouldn't happen */
		elog(ERROR, "invalid argument for EncodeSpecialDate");
}

238

239 240 241 242
/*
 * Comparison functions for dates
 */

243 244 245 246 247
Datum
date_eq(PG_FUNCTION_ARGS)
{
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);
248

249 250
	PG_RETURN_BOOL(dateVal1 == dateVal2);
}
251

252 253
Datum
date_ne(PG_FUNCTION_ARGS)
254
{
255 256 257 258
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(dateVal1 != dateVal2);
259
}
260

261 262
Datum
date_lt(PG_FUNCTION_ARGS)
263
{
264 265 266 267
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(dateVal1 < dateVal2);
268 269
}

270 271
Datum
date_le(PG_FUNCTION_ARGS)
272
{
273 274
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);
275

276 277
	PG_RETURN_BOOL(dateVal1 <= dateVal2);
}
278

279 280
Datum
date_gt(PG_FUNCTION_ARGS)
281
{
282 283 284 285 286
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(dateVal1 > dateVal2);
}
287

288 289
Datum
date_ge(PG_FUNCTION_ARGS)
290
{
291 292 293 294 295
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_BOOL(dateVal1 >= dateVal2);
}
296

297 298
Datum
date_cmp(PG_FUNCTION_ARGS)
299
{
300 301 302
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);

303
	if (dateVal1 < dateVal2)
304
		PG_RETURN_INT32(-1);
305
	else if (dateVal1 > dateVal2)
306 307 308
		PG_RETURN_INT32(1);
	PG_RETURN_INT32(0);
}
309

310 311 312 313 314 315 316 317
Datum
date_finite(PG_FUNCTION_ARGS)
{
	DateADT		date = PG_GETARG_DATEADT(0);

	PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
}

318 319
Datum
date_larger(PG_FUNCTION_ARGS)
320
{
321 322
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);
323

324 325 326 327 328
	PG_RETURN_DATEADT((dateVal1 > dateVal2) ? dateVal1 : dateVal2);
}

Datum
date_smaller(PG_FUNCTION_ARGS)
329
{
330 331 332 333 334
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);

	PG_RETURN_DATEADT((dateVal1 < dateVal2) ? dateVal1 : dateVal2);
}
335

336
/* Compute difference between two dates in days.
337
 */
338 339
Datum
date_mi(PG_FUNCTION_ARGS)
340
{
341 342 343
	DateADT		dateVal1 = PG_GETARG_DATEADT(0);
	DateADT		dateVal2 = PG_GETARG_DATEADT(1);

344 345 346 347 348
	if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("cannot subtract infinite dates")));

349 350
	PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
}
351

352 353
/* Add a number of days to a date, giving a new date.
 * Must handle both positive and negative numbers of days.
354
 */
355 356
Datum
date_pli(PG_FUNCTION_ARGS)
357
{
358 359 360
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	int32		days = PG_GETARG_INT32(1);

361 362 363
	if (DATE_NOT_FINITE(dateVal))
		days = 0;				/* can't change infinity */

364 365
	PG_RETURN_DATEADT(dateVal + days);
}
366

367
/* Subtract a number of days from a date, giving a new date.
368
 */
369 370
Datum
date_mii(PG_FUNCTION_ARGS)
371
{
372 373
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	int32		days = PG_GETARG_INT32(1);
374

375 376 377
	if (DATE_NOT_FINITE(dateVal))
		days = 0;				/* can't change infinity */

378 379
	PG_RETURN_DATEADT(dateVal - days);
}
380

381 382 383
/*
 * Internal routines for promoting date to timestamp and timestamp with
 * time zone
384 385
 */

386 387 388
static Timestamp
date2timestamp(DateADT dateVal)
{
Bruce Momjian's avatar
Bruce Momjian committed
389
	Timestamp	result;
390

391 392 393 394 395 396
	if (DATE_IS_NOBEGIN(dateVal))
		TIMESTAMP_NOBEGIN(result);
	else if (DATE_IS_NOEND(dateVal))
		TIMESTAMP_NOEND(result);
	else
	{
397
#ifdef HAVE_INT64_TIMESTAMP
398 399 400 401 402 403 404
		/* date is days since 2000, timestamp is microseconds since same... */
		result = dateVal * USECS_PER_DAY;
		/* Date's range is wider than timestamp's, so check for overflow */
		if (result / USECS_PER_DAY != dateVal)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("date out of range for timestamp")));
405
#else
406 407
		/* date is days since 2000, timestamp is seconds since same... */
		result = dateVal * (double) SECS_PER_DAY;
408
#endif
409
	}
410

411 412 413
	return result;
}

414 415 416 417
static TimestampTz
date2timestamptz(DateADT dateVal)
{
	TimestampTz result;
Bruce Momjian's avatar
Bruce Momjian committed
418
	struct pg_tm tt,
419
			   *tm = &tt;
420
	int			tz;
421

422 423 424 425 426 427 428 429 430 431 432 433
	if (DATE_IS_NOBEGIN(dateVal))
		TIMESTAMP_NOBEGIN(result);
	else if (DATE_IS_NOEND(dateVal))
		TIMESTAMP_NOEND(result);
	else
	{
		j2date(dateVal + POSTGRES_EPOCH_JDATE,
			   &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
		tm->tm_hour = 0;
		tm->tm_min = 0;
		tm->tm_sec = 0;
		tz = DetermineTimeZoneOffset(tm, session_timezone);
434 435

#ifdef HAVE_INT64_TIMESTAMP
436 437 438 439 440 441
		result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
		/* Date's range is wider than timestamp's, so check for overflow */
		if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("date out of range for timestamp")));
442
#else
443
		result = dateVal * (double) SECS_PER_DAY + tz;
444
#endif
445
	}
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542

	return result;
}


/*
 * Crosstype comparison functions for dates
 */

Datum
date_eq_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	dt1;

	dt1 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
}

Datum
date_ne_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	dt1;

	dt1 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
}

Datum
date_lt_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	dt1;

	dt1 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
}

Datum
date_gt_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	dt1;

	dt1 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
}

Datum
date_le_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	dt1;

	dt1 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
}

Datum
date_ge_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	dt1;

	dt1 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
}

Datum
date_cmp_timestamp(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	dt1;

	dt1 = date2timestamp(dateVal);

	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}

Datum
date_eq_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
Bruce Momjian's avatar
Bruce Momjian committed
543 544
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
545 546 547 548 549 550 551 552 553 554

	dt1 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
}

Datum
date_ne_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
Bruce Momjian's avatar
Bruce Momjian committed
555 556
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
557 558 559 560 561 562 563 564 565 566

	dt1 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
}

Datum
date_lt_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
Bruce Momjian's avatar
Bruce Momjian committed
567 568
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
569 570 571 572 573 574 575 576 577 578

	dt1 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
}

Datum
date_gt_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
Bruce Momjian's avatar
Bruce Momjian committed
579 580
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
581 582 583 584 585 586 587 588 589 590

	dt1 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
}

Datum
date_le_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
Bruce Momjian's avatar
Bruce Momjian committed
591 592
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
593 594 595 596 597 598 599 600 601 602

	dt1 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
}

Datum
date_ge_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
Bruce Momjian's avatar
Bruce Momjian committed
603 604
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
605 606 607 608 609 610 611 612 613 614

	dt1 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
}

Datum
date_cmp_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
Bruce Momjian's avatar
Bruce Momjian committed
615 616
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709

	dt1 = date2timestamptz(dateVal);

	PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
}

Datum
timestamp_eq_date(PG_FUNCTION_ARGS)
{
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		dateVal = PG_GETARG_DATEADT(1);
	Timestamp	dt2;

	dt2 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
}

Datum
timestamp_ne_date(PG_FUNCTION_ARGS)
{
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		dateVal = PG_GETARG_DATEADT(1);
	Timestamp	dt2;

	dt2 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
}

Datum
timestamp_lt_date(PG_FUNCTION_ARGS)
{
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		dateVal = PG_GETARG_DATEADT(1);
	Timestamp	dt2;

	dt2 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
}

Datum
timestamp_gt_date(PG_FUNCTION_ARGS)
{
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		dateVal = PG_GETARG_DATEADT(1);
	Timestamp	dt2;

	dt2 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
}

Datum
timestamp_le_date(PG_FUNCTION_ARGS)
{
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		dateVal = PG_GETARG_DATEADT(1);
	Timestamp	dt2;

	dt2 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
}

Datum
timestamp_ge_date(PG_FUNCTION_ARGS)
{
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		dateVal = PG_GETARG_DATEADT(1);
	Timestamp	dt2;

	dt2 = date2timestamp(dateVal);

	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
}

Datum
timestamp_cmp_date(PG_FUNCTION_ARGS)
{
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	DateADT		dateVal = PG_GETARG_DATEADT(1);
	Timestamp	dt2;

	dt2 = date2timestamp(dateVal);

	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}

Datum
timestamptz_eq_date(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
710
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
711
	DateADT		dateVal = PG_GETARG_DATEADT(1);
Bruce Momjian's avatar
Bruce Momjian committed
712
	TimestampTz dt2;
713 714 715 716 717 718 719 720 721

	dt2 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) == 0);
}

Datum
timestamptz_ne_date(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
722
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
723
	DateADT		dateVal = PG_GETARG_DATEADT(1);
Bruce Momjian's avatar
Bruce Momjian committed
724
	TimestampTz dt2;
725 726 727 728 729 730 731 732 733

	dt2 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) != 0);
}

Datum
timestamptz_lt_date(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
734
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
735
	DateADT		dateVal = PG_GETARG_DATEADT(1);
Bruce Momjian's avatar
Bruce Momjian committed
736
	TimestampTz dt2;
737 738 739 740 741 742 743 744 745

	dt2 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) < 0);
}

Datum
timestamptz_gt_date(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
746
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
747
	DateADT		dateVal = PG_GETARG_DATEADT(1);
Bruce Momjian's avatar
Bruce Momjian committed
748
	TimestampTz dt2;
749 750 751 752 753 754 755 756 757

	dt2 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) > 0);
}

Datum
timestamptz_le_date(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
758
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
759
	DateADT		dateVal = PG_GETARG_DATEADT(1);
Bruce Momjian's avatar
Bruce Momjian committed
760
	TimestampTz dt2;
761 762 763 764 765 766 767 768 769

	dt2 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) <= 0);
}

Datum
timestamptz_ge_date(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
770
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
771
	DateADT		dateVal = PG_GETARG_DATEADT(1);
Bruce Momjian's avatar
Bruce Momjian committed
772
	TimestampTz dt2;
773 774 775 776 777 778 779 780 781

	dt2 = date2timestamptz(dateVal);

	PG_RETURN_BOOL(timestamptz_cmp_internal(dt1, dt2) >= 0);
}

Datum
timestamptz_cmp_date(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
782
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
783
	DateADT		dateVal = PG_GETARG_DATEADT(1);
Bruce Momjian's avatar
Bruce Momjian committed
784
	TimestampTz dt2;
785 786 787 788 789 790 791

	dt2 = date2timestamptz(dateVal);

	PG_RETURN_INT32(timestamptz_cmp_internal(dt1, dt2));
}


792 793
/* Add an interval to a date, giving a new date.
 * Must handle both positive and negative intervals.
794 795 796
 *
 * We implement this by promoting the date to timestamp (without time zone)
 * and then using the timestamp plus interval function.
797 798 799 800 801 802
 */
Datum
date_pl_interval(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Interval   *span = PG_GETARG_INTERVAL_P(1);
803
	Timestamp	dateStamp;
804

805
	dateStamp = date2timestamp(dateVal);
806

807 808 809
	return DirectFunctionCall2(timestamp_pl_interval,
							   TimestampGetDatum(dateStamp),
							   PointerGetDatum(span));
810 811 812 813
}

/* Subtract an interval from a date, giving a new date.
 * Must handle both positive and negative intervals.
814 815 816
 *
 * We implement this by promoting the date to timestamp (without time zone)
 * and then using the timestamp minus interval function.
817 818 819 820 821 822
 */
Datum
date_mi_interval(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Interval   *span = PG_GETARG_INTERVAL_P(1);
823
	Timestamp	dateStamp;
824

825
	dateStamp = date2timestamp(dateVal);
826

827 828 829
	return DirectFunctionCall2(timestamp_mi_interval,
							   TimestampGetDatum(dateStamp),
							   PointerGetDatum(span));
830 831
}

832 833
/* date_timestamp()
 * Convert date to timestamp data type.
834
 */
835 836
Datum
date_timestamp(PG_FUNCTION_ARGS)
837
{
838 839
	DateADT		dateVal = PG_GETARG_DATEADT(0);
	Timestamp	result;
840

841
	result = date2timestamp(dateVal);
842 843 844 845 846 847 848 849 850 851 852

	PG_RETURN_TIMESTAMP(result);
}


/* timestamp_date()
 * Convert timestamp to date data type.
 */
Datum
timestamp_date(PG_FUNCTION_ARGS)
{
853
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
854
	DateADT		result;
Bruce Momjian's avatar
Bruce Momjian committed
855
	struct pg_tm tt,
856
			   *tm = &tt;
857
	fsec_t		fsec;
858

859 860 861 862 863 864 865 866 867 868
	if (TIMESTAMP_IS_NOBEGIN(timestamp))
		DATE_NOBEGIN(result);
	else if (TIMESTAMP_IS_NOEND(timestamp))
		DATE_NOEND(result);
	else
	{
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
869

870 871
		result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
	}
872 873 874 875 876 877 878 879 880 881 882 883

	PG_RETURN_DATEADT(result);
}


/* date_timestamptz()
 * Convert date to timestamp with time zone data type.
 */
Datum
date_timestamptz(PG_FUNCTION_ARGS)
{
	DateADT		dateVal = PG_GETARG_DATEADT(0);
884
	TimestampTz result;
885

886
	result = date2timestamptz(dateVal);
887

888 889
	PG_RETURN_TIMESTAMP(result);
}
890 891


892 893
/* timestamptz_date()
 * Convert timestamp with time zone to date data type.
894
 */
895
Datum
896
timestamptz_date(PG_FUNCTION_ARGS)
897
{
898
	TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
899
	DateADT		result;
Bruce Momjian's avatar
Bruce Momjian committed
900
	struct pg_tm tt,
901
			   *tm = &tt;
902
	fsec_t		fsec;
903
	int			tz;
904
	char	   *tzn;
905

906 907 908 909 910 911 912 913 914 915
	if (TIMESTAMP_IS_NOBEGIN(timestamp))
		DATE_NOBEGIN(result);
	else if (TIMESTAMP_IS_NOEND(timestamp))
		DATE_NOEND(result);
	else
	{
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
916

917 918
		result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
	}
919

920 921
	PG_RETURN_DATEADT(result);
}
922 923


924 925
/* abstime_date()
 * Convert abstime to date data type.
926
 */
927 928
Datum
abstime_date(PG_FUNCTION_ARGS)
929
{
930
	AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0);
931
	DateADT		result;
Bruce Momjian's avatar
Bruce Momjian committed
932
	struct pg_tm tt,
933 934
			   *tm = &tt;
	int			tz;
935

936 937 938
	switch (abstime)
	{
		case INVALID_ABSTIME:
939 940
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
941
				   errmsg("cannot convert reserved abstime value to date")));
942 943
			result = 0;			/* keep compiler quiet */
			break;
944

945 946 947 948 949 950 951
		case NOSTART_ABSTIME:
			DATE_NOBEGIN(result);
			break;

		case NOEND_ABSTIME:
			DATE_NOEND(result);
			break;
952 953 954

		default:
			abstime2tm(abstime, &tz, tm, NULL);
955
			result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
956 957
			break;
	}
958

959 960
	PG_RETURN_DATEADT(result);
}
961 962


963 964 965
/*****************************************************************************
 *	 Time ADT
 *****************************************************************************/
966

967 968
Datum
time_in(PG_FUNCTION_ARGS)
969
{
970
	char	   *str = PG_GETARG_CSTRING(0);
Bruce Momjian's avatar
Bruce Momjian committed
971

972 973 974 975 976
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
	TimeADT		result;
977
	fsec_t		fsec;
Bruce Momjian's avatar
Bruce Momjian committed
978
	struct pg_tm tt,
979
			   *tm = &tt;
980
	int			tz;
981
	int			nf;
982
	int			dterr;
983
	char		workbuf[MAXDATELEN + 1];
984 985 986
	char	   *field[MAXDATEFIELDS];
	int			dtype;
	int			ftype[MAXDATEFIELDS];
987

988 989
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
						  field, ftype, MAXDATEFIELDS, &nf);
990 991 992 993
	if (dterr == 0)
		dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
	if (dterr != 0)
		DateTimeParseError(dterr, str, "time");
994

995
	tm2time(tm, fsec, &result);
996
	AdjustTimeForTypmod(&result, typmod);
997

998
	PG_RETURN_TIMEADT(result);
999
}
1000

1001 1002 1003
/* tm2time()
 * Convert a tm structure to a time data type.
 */
1004
static int
1005
tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
1006 1007
{
#ifdef HAVE_INT64_TIMESTAMP
1008
	*result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
1009
			   * USECS_PER_SEC) + fsec;
1010
#else
1011
	*result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1012 1013 1014 1015 1016 1017
#endif
	return 0;
}

/* time2tm()
 * Convert time data type to POSIX time structure.
1018 1019 1020 1021
 *
 * For dates within the range of pg_time_t, convert to the local time zone.
 * If out of this range, leave as UTC (in practice that could only happen
 * if pg_time_t is just 32 bits) - thomas 97/05/27
1022
 */
1023
static int
1024
time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec)
1025 1026
{
#ifdef HAVE_INT64_TIMESTAMP
1027 1028 1029 1030 1031 1032
	tm->tm_hour = time / USECS_PER_HOUR;
	time -= tm->tm_hour * USECS_PER_HOUR;
	tm->tm_min = time / USECS_PER_MINUTE;
	time -= tm->tm_min * USECS_PER_MINUTE;
	tm->tm_sec = time / USECS_PER_SEC;
	time -= tm->tm_sec * USECS_PER_SEC;
1033 1034 1035 1036
	*fsec = time;
#else
	double		trem;

1037
recalc:
1038
	trem = time;
1039 1040
	TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
	TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1041
	TMODULO(trem, tm->tm_sec, 1.0);
1042 1043 1044 1045 1046 1047 1048
	trem = TIMEROUND(trem);
	/* roundoff may need to propagate to higher-order fields */
	if (trem >= 1.0)
	{
		time = ceil(time);
		goto recalc;
	}
1049 1050 1051 1052 1053 1054
	*fsec = trem;
#endif

	return 0;
}

1055 1056
Datum
time_out(PG_FUNCTION_ARGS)
1057
{
1058
	TimeADT		time = PG_GETARG_TIMEADT(0);
1059
	char	   *result;
Bruce Momjian's avatar
Bruce Momjian committed
1060
	struct pg_tm tt,
1061
			   *tm = &tt;
1062
	fsec_t		fsec;
1063
	char		buf[MAXDATELEN + 1];
1064

1065
	time2tm(time, tm, &fsec);
1066
	EncodeTimeOnly(tm, fsec, NULL, DateStyle, buf);
1067

1068 1069 1070
	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}
1071

1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
/*
 *		time_recv			- converts external binary format to time
 *
 * We make no attempt to provide compatibility between int and float
 * time representations ...
 */
Datum
time_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
1082

1083 1084 1085 1086 1087
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
	TimeADT		result;
1088 1089

#ifdef HAVE_INT64_TIMESTAMP
1090
	result = pq_getmsgint64(buf);
1091
#else
1092
	result = pq_getmsgfloat8(buf);
1093
#endif
1094 1095 1096 1097

	AdjustTimeForTypmod(&result, typmod);

	PG_RETURN_TIMEADT(result);
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
}

/*
 *		time_send			- converts time to binary format
 */
Datum
time_send(PG_FUNCTION_ARGS)
{
	TimeADT		time = PG_GETARG_TIMEADT(0);
	StringInfoData buf;

	pq_begintypsend(&buf);
#ifdef HAVE_INT64_TIMESTAMP
	pq_sendint64(&buf, time);
#else
	pq_sendfloat8(&buf, time);
#endif
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

1118 1119 1120
Datum
timetypmodin(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
1121
	ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
1122 1123 1124 1125 1126 1127 1128

	PG_RETURN_INT32(anytime_typmodin(false, ta));
}

Datum
timetypmodout(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
1129
	int32		typmod = PG_GETARG_INT32(0);
1130 1131 1132 1133

	PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
}

1134

1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
/* time_scale()
 * Adjust time type for specified scale factor.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
time_scale(PG_FUNCTION_ARGS)
{
	TimeADT		time = PG_GETARG_TIMEADT(0);
	int32		typmod = PG_GETARG_INT32(1);
	TimeADT		result;

	result = time;
	AdjustTimeForTypmod(&result, typmod);

	PG_RETURN_TIMEADT(result);
}

1152 1153 1154 1155 1156 1157 1158
/* AdjustTimeForTypmod()
 * Force the precision of the time value to a specified value.
 * Uses *exactly* the same code as in AdjustTimestampForTypemod()
 * but we make a separate copy because those types do not
 * have a fundamental tie together but rather a coincidence of
 * implementation. - thomas
 */
1159 1160 1161
static void
AdjustTimeForTypmod(TimeADT *time, int32 typmod)
{
1162
#ifdef HAVE_INT64_TIMESTAMP
1163
	static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
1164 1165 1166 1167 1168 1169 1170 1171 1172
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

1173
	static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
1174 1175 1176 1177 1178 1179
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
1180 1181
		INT64CONST(0)
	};
1182
#else
1183 1184
	/* note MAX_TIME_PRECISION differs in this case */
	static const double TimeScales[MAX_TIME_PRECISION + 1] = {
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195
		1.0,
		10.0,
		100.0,
		1000.0,
		10000.0,
		100000.0,
		1000000.0,
		10000000.0,
		100000000.0,
		1000000000.0,
		10000000000.0
1196
	};
1197
#endif
1198

1199
	if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
1200
	{
1201
		/*
1202 1203 1204 1205 1206
		 * Note: this round-to-nearest code is not completely consistent about
		 * rounding values that are exactly halfway between integral values.
		 * On most platforms, rint() will implement round-to-nearest-even, but
		 * the integer code always rounds up (away from zero).	Is it worth
		 * trying to be consistent?
1207
		 */
1208
#ifdef HAVE_INT64_TIMESTAMP
1209
		if (*time >= INT64CONST(0))
1210
			*time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
1211
				TimeScales[typmod];
1212
		else
1213
			*time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
1214
					  TimeScales[typmod]);
1215
#else
1216
		*time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
1217
#endif
1218 1219 1220
	}
}

1221

1222 1223 1224 1225 1226
Datum
time_eq(PG_FUNCTION_ARGS)
{
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
1227

1228 1229
	PG_RETURN_BOOL(time1 == time2);
}
1230

1231 1232
Datum
time_ne(PG_FUNCTION_ARGS)
1233
{
1234 1235
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
1236

1237 1238
	PG_RETURN_BOOL(time1 != time2);
}
1239

1240 1241
Datum
time_lt(PG_FUNCTION_ARGS)
1242
{
1243 1244
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
1245

1246 1247
	PG_RETURN_BOOL(time1 < time2);
}
1248

1249 1250
Datum
time_le(PG_FUNCTION_ARGS)
1251
{
1252 1253
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
1254

1255 1256
	PG_RETURN_BOOL(time1 <= time2);
}
1257

1258 1259
Datum
time_gt(PG_FUNCTION_ARGS)
1260
{
1261 1262
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
1263

1264 1265
	PG_RETURN_BOOL(time1 > time2);
}
1266

1267 1268
Datum
time_ge(PG_FUNCTION_ARGS)
1269
{
1270 1271
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
1272

1273 1274
	PG_RETURN_BOOL(time1 >= time2);
}
1275

1276 1277
Datum
time_cmp(PG_FUNCTION_ARGS)
1278
{
1279 1280
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
1281

1282 1283 1284 1285 1286 1287
	if (time1 < time2)
		PG_RETURN_INT32(-1);
	if (time1 > time2)
		PG_RETURN_INT32(1);
	PG_RETURN_INT32(0);
}
1288

1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299
Datum
time_hash(PG_FUNCTION_ARGS)
{
	/* We can use either hashint8 or hashfloat8 directly */
#ifdef HAVE_INT64_TIMESTAMP
	return hashint8(fcinfo);
#else
	return hashfloat8(fcinfo);
#endif
}

1300 1301
Datum
time_larger(PG_FUNCTION_ARGS)
1302
{
1303 1304
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
1305

1306 1307
	PG_RETURN_TIMEADT((time1 > time2) ? time1 : time2);
}
1308

1309 1310
Datum
time_smaller(PG_FUNCTION_ARGS)
1311
{
1312 1313 1314 1315 1316
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);

	PG_RETURN_TIMEADT((time1 < time2) ? time1 : time2);
}
1317

1318 1319 1320 1321 1322
/* overlaps_time() --- implements the SQL92 OVERLAPS operator.
 *
 * Algorithm is per SQL92 spec.  This is much harder than you'd think
 * because the spec requires us to deliver a non-null answer in some cases
 * where some of the inputs are null.
1323
 */
1324 1325
Datum
overlaps_time(PG_FUNCTION_ARGS)
1326
{
1327
	/*
1328 1329
	 * The arguments are TimeADT, but we leave them as generic Datums to avoid
	 * dereferencing nulls (TimeADT is pass-by-reference!)
1330 1331 1332 1333 1334 1335 1336 1337 1338
	 */
	Datum		ts1 = PG_GETARG_DATUM(0);
	Datum		te1 = PG_GETARG_DATUM(1);
	Datum		ts2 = PG_GETARG_DATUM(2);
	Datum		te2 = PG_GETARG_DATUM(3);
	bool		ts1IsNull = PG_ARGISNULL(0);
	bool		te1IsNull = PG_ARGISNULL(1);
	bool		ts2IsNull = PG_ARGISNULL(2);
	bool		te2IsNull = PG_ARGISNULL(3);
1339

1340 1341 1342 1343
#define TIMEADT_GT(t1,t2) \
	(DatumGetTimeADT(t1) > DatumGetTimeADT(t2))
#define TIMEADT_LT(t1,t2) \
	(DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
1344

1345
	/*
1346 1347 1348
	 * If both endpoints of interval 1 are null, the result is null (unknown).
	 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
	 * take ts1 as the lesser endpoint.
1349 1350 1351 1352 1353 1354
	 */
	if (ts1IsNull)
	{
		if (te1IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
1355
		ts1 = te1;
1356
		te1IsNull = true;
1357
	}
1358
	else if (!te1IsNull)
1359
	{
1360 1361
		if (TIMEADT_GT(ts1, te1))
		{
1362
			Datum		tt = ts1;
1363 1364 1365 1366 1367

			ts1 = te1;
			te1 = tt;
		}
	}
1368

1369 1370 1371 1372 1373 1374
	/* Likewise for interval 2. */
	if (ts2IsNull)
	{
		if (te2IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
1375
		ts2 = te2;
1376 1377 1378 1379 1380 1381
		te2IsNull = true;
	}
	else if (!te2IsNull)
	{
		if (TIMEADT_GT(ts2, te2))
		{
1382
			Datum		tt = ts2;
1383 1384 1385 1386

			ts2 = te2;
			te2 = tt;
		}
1387 1388
	}

1389 1390 1391 1392 1393 1394
	/*
	 * At this point neither ts1 nor ts2 is null, so we can consider three
	 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
	 */
	if (TIMEADT_GT(ts1, ts2))
	{
1395
		/*
1396 1397
		 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
		 * in the presence of nulls it's not quite completely so.
1398 1399 1400 1401 1402 1403 1404
		 */
		if (te2IsNull)
			PG_RETURN_NULL();
		if (TIMEADT_LT(ts1, te2))
			PG_RETURN_BOOL(true);
		if (te1IsNull)
			PG_RETURN_NULL();
1405 1406

		/*
1407 1408
		 * If te1 is not null then we had ts1 <= te1 above, and we just found
		 * ts1 >= te2, hence te1 >= te2.
1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420
		 */
		PG_RETURN_BOOL(false);
	}
	else if (TIMEADT_LT(ts1, ts2))
	{
		/* This case is ts2 < te1 OR te2 < te1 */
		if (te1IsNull)
			PG_RETURN_NULL();
		if (TIMEADT_LT(ts2, te1))
			PG_RETURN_BOOL(true);
		if (te2IsNull)
			PG_RETURN_NULL();
1421 1422

		/*
1423 1424
		 * If te2 is not null then we had ts2 <= te2 above, and we just found
		 * ts2 >= te1, hence te2 >= te1.
1425 1426 1427 1428 1429
		 */
		PG_RETURN_BOOL(false);
	}
	else
	{
1430 1431
		/*
		 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
1432
		 * rather silly way of saying "true if both are nonnull, else null".
1433 1434 1435 1436 1437 1438 1439 1440
		 */
		if (te1IsNull || te2IsNull)
			PG_RETURN_NULL();
		PG_RETURN_BOOL(true);
	}

#undef TIMEADT_GT
#undef TIMEADT_LT
1441
}
1442

1443 1444
/* timestamp_time()
 * Convert timestamp to time data type.
1445
 */
1446 1447
Datum
timestamp_time(PG_FUNCTION_ARGS)
1448
{
1449
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
1450
	TimeADT		result;
Bruce Momjian's avatar
Bruce Momjian committed
1451
	struct pg_tm tt,
1452
			   *tm = &tt;
1453
	fsec_t		fsec;
1454

1455
	if (TIMESTAMP_NOT_FINITE(timestamp))
1456
		PG_RETURN_NULL();
1457

1458
	if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1459 1460 1461
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
1462 1463

#ifdef HAVE_INT64_TIMESTAMP
Bruce Momjian's avatar
Bruce Momjian committed
1464 1465

	/*
1466 1467
	 * Could also do this with time = (timestamp / USECS_PER_DAY *
	 * USECS_PER_DAY) - timestamp;
1468
	 */
1469
	result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1470
			  USECS_PER_SEC) + fsec;
1471
#else
1472
	result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485
#endif

	PG_RETURN_TIMEADT(result);
}

/* timestamptz_time()
 * Convert timestamptz to time data type.
 */
Datum
timestamptz_time(PG_FUNCTION_ARGS)
{
	TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
	TimeADT		result;
Bruce Momjian's avatar
Bruce Momjian committed
1486
	struct pg_tm tt,
1487 1488 1489 1490
			   *tm = &tt;
	int			tz;
	fsec_t		fsec;
	char	   *tzn;
1491

1492 1493 1494
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_NULL();

1495
	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
1496 1497 1498
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
1499 1500

#ifdef HAVE_INT64_TIMESTAMP
Bruce Momjian's avatar
Bruce Momjian committed
1501 1502

	/*
1503 1504
	 * Could also do this with time = (timestamp / USECS_PER_DAY *
	 * USECS_PER_DAY) - timestamp;
1505
	 */
1506
	result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1507
			  USECS_PER_SEC) + fsec;
1508
#else
1509
	result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1510
#endif
1511

1512 1513
	PG_RETURN_TIMEADT(result);
}
1514

1515 1516
/* datetime_timestamp()
 * Convert date and time to timestamp data type.
1517
 */
1518 1519
Datum
datetime_timestamp(PG_FUNCTION_ARGS)
1520
{
1521
	DateADT		date = PG_GETARG_DATEADT(0);
1522 1523
	TimeADT		time = PG_GETARG_TIMEADT(1);
	Timestamp	result;
1524

1525 1526 1527
	result = date2timestamp(date);
	if (!TIMESTAMP_NOT_FINITE(result))
		result += time;
1528

1529 1530
	PG_RETURN_TIMESTAMP(result);
}
1531 1532 1533 1534

/* time_interval()
 * Convert time to interval data type.
 */
1535 1536
Datum
time_interval(PG_FUNCTION_ARGS)
1537
{
1538
	TimeADT		time = PG_GETARG_TIMEADT(0);
1539 1540
	Interval   *result;

1541
	result = (Interval *) palloc(sizeof(Interval));
1542

1543
	result->time = time;
1544
	result->day = 0;
1545 1546
	result->month = 0;

1547 1548
	PG_RETURN_INTERVAL_P(result);
}
1549

1550 1551
/* interval_time()
 * Convert interval to time data type.
1552 1553
 *
 * This is defined as producing the fractional-day portion of the interval.
Bruce Momjian's avatar
Bruce Momjian committed
1554
 * Therefore, we can just ignore the months field.	It is not real clear
1555 1556
 * what to do with negative intervals, but we choose to subtract the floor,
 * so that, say, '-2 hours' becomes '22:00:00'.
1557 1558 1559 1560 1561 1562
 */
Datum
interval_time(PG_FUNCTION_ARGS)
{
	Interval   *span = PG_GETARG_INTERVAL_P(0);
	TimeADT		result;
1563 1564

#ifdef HAVE_INT64_TIMESTAMP
1565 1566
	int64		days;

1567
	result = span->time;
1568
	if (result >= USECS_PER_DAY)
1569
	{
1570 1571
		days = result / USECS_PER_DAY;
		result -= days * USECS_PER_DAY;
1572 1573 1574
	}
	else if (result < 0)
	{
1575 1576
		days = (-result + USECS_PER_DAY - 1) / USECS_PER_DAY;
		result += days * USECS_PER_DAY;
1577
	}
1578
#else
1579
	result = span->time;
1580 1581
	if (result >= (double) SECS_PER_DAY || result < 0)
		result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
1582
#endif
1583 1584 1585 1586

	PG_RETURN_TIMEADT(result);
}

1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599
/* time_mi_time()
 * Subtract two times to produce an interval.
 */
Datum
time_mi_time(PG_FUNCTION_ARGS)
{
	TimeADT		time1 = PG_GETARG_TIMEADT(0);
	TimeADT		time2 = PG_GETARG_TIMEADT(1);
	Interval   *result;

	result = (Interval *) palloc(sizeof(Interval));

	result->month = 0;
1600 1601
	result->day = 0;
	result->time = time1 - time2;
1602 1603 1604 1605

	PG_RETURN_INTERVAL_P(result);
}

1606 1607 1608 1609 1610 1611 1612 1613 1614
/* time_pl_interval()
 * Add interval to time.
 */
Datum
time_pl_interval(PG_FUNCTION_ARGS)
{
	TimeADT		time = PG_GETARG_TIMEADT(0);
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	TimeADT		result;
1615 1616

#ifdef HAVE_INT64_TIMESTAMP
1617 1618
	result = time + span->time;
	result -= result / USECS_PER_DAY * USECS_PER_DAY;
1619
	if (result < INT64CONST(0))
1620
		result += USECS_PER_DAY;
1621
#else
1622 1623
	TimeADT		time1;

1624
	result = time + span->time;
1625
	TMODULO(result, time1, (double) SECS_PER_DAY);
1626
	if (result < 0)
1627
		result += SECS_PER_DAY;
1628
#endif
1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641

	PG_RETURN_TIMEADT(result);
}

/* time_mi_interval()
 * Subtract interval from time.
 */
Datum
time_mi_interval(PG_FUNCTION_ARGS)
{
	TimeADT		time = PG_GETARG_TIMEADT(0);
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	TimeADT		result;
1642 1643

#ifdef HAVE_INT64_TIMESTAMP
1644 1645
	result = time - span->time;
	result -= result / USECS_PER_DAY * USECS_PER_DAY;
1646
	if (result < INT64CONST(0))
1647
		result += USECS_PER_DAY;
1648
#else
1649 1650
	TimeADT		time1;

1651
	result = time - span->time;
1652
	TMODULO(result, time1, (double) SECS_PER_DAY);
1653
	if (result < 0)
1654
		result += SECS_PER_DAY;
1655
#endif
1656 1657 1658 1659

	PG_RETURN_TIMEADT(result);
}

1660

1661 1662 1663 1664 1665 1666
/* time_part()
 * Extract specified field from time type.
 */
Datum
time_part(PG_FUNCTION_ARGS)
{
1667
	text	   *units = PG_GETARG_TEXT_PP(0);
1668 1669 1670 1671
	TimeADT		time = PG_GETARG_TIMEADT(1);
	float8		result;
	int			type,
				val;
1672
	char	   *lowunits;
1673

1674 1675
	lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
											VARSIZE_ANY_EXHDR(units),
1676
											false);
1677 1678 1679 1680 1681 1682 1683 1684

	type = DecodeUnits(0, lowunits, &val);
	if (type == UNKNOWN_FIELD)
		type = DecodeSpecial(0, lowunits, &val);

	if (type == UNITS)
	{
		fsec_t		fsec;
Bruce Momjian's avatar
Bruce Momjian committed
1685
		struct pg_tm tt,
1686 1687 1688 1689 1690 1691 1692 1693
				   *tm = &tt;

		time2tm(time, tm, &fsec);

		switch (val)
		{
			case DTK_MICROSEC:
#ifdef HAVE_INT64_TIMESTAMP
1694
				result = tm->tm_sec * USECS_PER_SEC + fsec;
1695
#else
1696
				result = (tm->tm_sec + fsec) * 1000000;
1697 1698 1699 1700 1701
#endif
				break;

			case DTK_MILLISEC:
#ifdef HAVE_INT64_TIMESTAMP
1702
				result = tm->tm_sec * INT64CONST(1000) + fsec / INT64CONST(1000);
1703
#else
1704
				result = (tm->tm_sec + fsec) * 1000;
1705 1706 1707 1708 1709
#endif
				break;

			case DTK_SECOND:
#ifdef HAVE_INT64_TIMESTAMP
1710
				result = tm->tm_sec + fsec / USECS_PER_SEC;
1711
#else
1712
				result = tm->tm_sec + fsec;
1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733
#endif
				break;

			case DTK_MINUTE:
				result = tm->tm_min;
				break;

			case DTK_HOUR:
				result = tm->tm_hour;
				break;

			case DTK_TZ:
			case DTK_TZ_MINUTE:
			case DTK_TZ_HOUR:
			case DTK_DAY:
			case DTK_MONTH:
			case DTK_QUARTER:
			case DTK_YEAR:
			case DTK_DECADE:
			case DTK_CENTURY:
			case DTK_MILLENNIUM:
1734
			case DTK_ISOYEAR:
1735
			default:
1736 1737
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1738
						 errmsg("\"time\" units \"%s\" not recognized",
1739
								lowunits)));
1740 1741 1742
				result = 0;
		}
	}
1743
	else if (type == RESERV && val == DTK_EPOCH)
1744 1745
	{
#ifdef HAVE_INT64_TIMESTAMP
1746
		result = time / 1000000.0;
1747 1748 1749 1750 1751 1752
#else
		result = time;
#endif
	}
	else
	{
1753 1754
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1755
				 errmsg("\"time\" units \"%s\" not recognized",
1756
						lowunits)));
1757 1758 1759 1760 1761 1762
		result = 0;
	}

	PG_RETURN_FLOAT8(result);
}

1763

1764 1765 1766 1767
/*****************************************************************************
 *	 Time With Time Zone ADT
 *****************************************************************************/

1768 1769 1770
/* tm2timetz()
 * Convert a tm structure to a time data type.
 */
1771
static int
1772
tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
1773 1774
{
#ifdef HAVE_INT64_TIMESTAMP
1775
	result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
1776
					USECS_PER_SEC) + fsec;
1777
#else
1778
	result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
1779 1780 1781 1782 1783 1784
#endif
	result->zone = tz;

	return 0;
}

1785 1786
Datum
timetz_in(PG_FUNCTION_ARGS)
1787
{
1788
	char	   *str = PG_GETARG_CSTRING(0);
1789

1790 1791 1792 1793 1794
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
	TimeTzADT  *result;
1795
	fsec_t		fsec;
Bruce Momjian's avatar
Bruce Momjian committed
1796
	struct pg_tm tt,
1797 1798 1799
			   *tm = &tt;
	int			tz;
	int			nf;
1800
	int			dterr;
1801
	char		workbuf[MAXDATELEN + 1];
1802 1803 1804 1805
	char	   *field[MAXDATEFIELDS];
	int			dtype;
	int			ftype[MAXDATEFIELDS];

1806 1807
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
						  field, ftype, MAXDATEFIELDS, &nf);
1808 1809 1810 1811
	if (dterr == 0)
		dterr = DecodeTimeOnly(field, ftype, nf, &dtype, tm, &fsec, &tz);
	if (dterr != 0)
		DateTimeParseError(dterr, str, "time with time zone");
1812

1813
	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1814
	tm2timetz(tm, fsec, tz, result);
1815 1816 1817
	AdjustTimeForTypmod(&(result->time), typmod);

	PG_RETURN_TIMETZADT_P(result);
1818
}
1819

1820 1821
Datum
timetz_out(PG_FUNCTION_ARGS)
1822
{
1823
	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
1824
	char	   *result;
Bruce Momjian's avatar
Bruce Momjian committed
1825
	struct pg_tm tt,
1826
			   *tm = &tt;
1827
	fsec_t		fsec;
1828 1829 1830
	int			tz;
	char		buf[MAXDATELEN + 1];

1831 1832 1833 1834 1835 1836 1837
	timetz2tm(time, tm, &fsec, &tz);
	EncodeTimeOnly(tm, fsec, &tz, DateStyle, buf);

	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}

1838 1839 1840 1841 1842 1843 1844
/*
 *		timetz_recv			- converts external binary format to timetz
 */
Datum
timetz_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
1845

1846 1847 1848 1849 1850
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
	TimeTzADT  *result;
1851

1852
	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
1853 1854

#ifdef HAVE_INT64_TIMESTAMP
1855
	result->time = pq_getmsgint64(buf);
1856
#else
1857
	result->time = pq_getmsgfloat8(buf);
1858
#endif
1859 1860 1861
	result->zone = pq_getmsgint(buf, sizeof(result->zone));

	AdjustTimeForTypmod(&(result->time), typmod);
1862

1863
	PG_RETURN_TIMETZADT_P(result);
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884
}

/*
 *		timetz_send			- converts timetz to binary format
 */
Datum
timetz_send(PG_FUNCTION_ARGS)
{
	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
	StringInfoData buf;

	pq_begintypsend(&buf);
#ifdef HAVE_INT64_TIMESTAMP
	pq_sendint64(&buf, time->time);
#else
	pq_sendfloat8(&buf, time->time);
#endif
	pq_sendint(&buf, time->zone, sizeof(time->zone));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

1885 1886 1887
Datum
timetztypmodin(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
1888
	ArrayType  *ta = PG_GETARG_ARRAYTYPE_P(0);
1889 1890 1891 1892 1893 1894 1895

	PG_RETURN_INT32(anytime_typmodin(true, ta));
}

Datum
timetztypmodout(PG_FUNCTION_ARGS)
{
Bruce Momjian's avatar
Bruce Momjian committed
1896
	int32		typmod = PG_GETARG_INT32(0);
1897 1898 1899 1900

	PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
}

1901

1902 1903 1904
/* timetz2tm()
 * Convert TIME WITH TIME ZONE data type to POSIX time structure.
 */
1905
static int
1906
timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp)
1907
{
1908
	TimeOffset	trem = time->time;
1909

1910
#ifdef HAVE_INT64_TIMESTAMP
1911 1912 1913 1914 1915 1916
	tm->tm_hour = trem / USECS_PER_HOUR;
	trem -= tm->tm_hour * USECS_PER_HOUR;
	tm->tm_min = trem / USECS_PER_MINUTE;
	trem -= tm->tm_min * USECS_PER_MINUTE;
	tm->tm_sec = trem / USECS_PER_SEC;
	*fsec = trem - tm->tm_sec * USECS_PER_SEC;
1917
#else
1918
recalc:
1919 1920
	TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
	TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
1921
	TMODULO(trem, tm->tm_sec, 1.0);
1922 1923 1924 1925 1926 1927 1928
	trem = TIMEROUND(trem);
	/* roundoff may need to propagate to higher-order fields */
	if (trem >= 1.0)
	{
		trem = ceil(time->time);
		goto recalc;
	}
1929 1930
	*fsec = trem;
#endif
1931

1932 1933
	if (tzp != NULL)
		*tzp = time->zone;
1934

1935
	return 0;
1936
}
1937

1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958
/* timetz_scale()
 * Adjust time type for specified scale factor.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
timetz_scale(PG_FUNCTION_ARGS)
{
	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
	int32		typmod = PG_GETARG_INT32(1);
	TimeTzADT  *result;

	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	result->time = time->time;
	result->zone = time->zone;

	AdjustTimeForTypmod(&(result->time), typmod);

	PG_RETURN_TIMETZADT_P(result);
}

1959

1960 1961 1962
static int
timetz_cmp_internal(TimeTzADT *time1, TimeTzADT *time2)
{
1963
	TimeOffset	t1,
1964 1965
				t2;

1966 1967
	/* Primary sort is by true (GMT-equivalent) time */
#ifdef HAVE_INT64_TIMESTAMP
1968 1969
	t1 = time1->time + (time1->zone * USECS_PER_SEC);
	t2 = time2->time + (time2->zone * USECS_PER_SEC);
1970
#else
1971 1972
	t1 = time1->time + time1->zone;
	t2 = time2->time + time2->zone;
1973
#endif
1974 1975 1976 1977 1978 1979 1980 1981 1982 1983

	if (t1 > t2)
		return 1;
	if (t1 < t2)
		return -1;

	/*
	 * If same GMT time, sort by timezone; we only want to say that two
	 * timetz's are equal if both the time and zone parts are equal.
	 */
1984 1985 1986 1987 1988 1989
	if (time1->zone > time2->zone)
		return 1;
	if (time1->zone < time2->zone)
		return -1;

	return 0;
1990 1991
}

1992 1993
Datum
timetz_eq(PG_FUNCTION_ARGS)
1994
{
1995 1996
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
1997

1998
	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) == 0);
1999
}
2000

2001 2002
Datum
timetz_ne(PG_FUNCTION_ARGS)
2003
{
2004 2005
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2006

2007
	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) != 0);
2008
}
2009

2010 2011
Datum
timetz_lt(PG_FUNCTION_ARGS)
2012
{
2013 2014
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2015

2016
	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) < 0);
2017
}
2018

2019 2020
Datum
timetz_le(PG_FUNCTION_ARGS)
2021
{
2022 2023
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2024

2025
	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) <= 0);
2026
}
2027

2028 2029
Datum
timetz_gt(PG_FUNCTION_ARGS)
2030
{
2031 2032
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2033

2034
	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) > 0);
2035
}
2036

2037 2038
Datum
timetz_ge(PG_FUNCTION_ARGS)
2039
{
2040 2041
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2042

2043
	PG_RETURN_BOOL(timetz_cmp_internal(time1, time2) >= 0);
2044
}
2045

2046 2047 2048
Datum
timetz_cmp(PG_FUNCTION_ARGS)
{
2049 2050
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2051

2052
	PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
2053
}
2054

2055 2056 2057
Datum
timetz_hash(PG_FUNCTION_ARGS)
{
2058
	TimeTzADT  *key = PG_GETARG_TIMETZADT_P(0);
2059
	uint32		thash;
2060 2061

	/*
Bruce Momjian's avatar
Bruce Momjian committed
2062 2063 2064 2065
	 * To avoid any problems with padding bytes in the struct, we figure the
	 * field hashes separately and XOR them.  This also provides a convenient
	 * framework for dealing with the fact that the time field might be either
	 * double or int64.
2066
	 */
2067 2068 2069 2070 2071
#ifdef HAVE_INT64_TIMESTAMP
	thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
											   Int64GetDatumFast(key->time)));
#else
	thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8,
Bruce Momjian's avatar
Bruce Momjian committed
2072
											 Float8GetDatumFast(key->time)));
2073 2074 2075
#endif
	thash ^= DatumGetUInt32(hash_uint32(key->zone));
	PG_RETURN_UINT32(thash);
2076 2077
}

2078 2079
Datum
timetz_larger(PG_FUNCTION_ARGS)
2080
{
2081 2082
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2083
	TimeTzADT  *result;
2084

2085 2086 2087 2088 2089
	if (timetz_cmp_internal(time1, time2) > 0)
		result = time1;
	else
		result = time2;
	PG_RETURN_TIMETZADT_P(result);
2090
}
2091

2092 2093
Datum
timetz_smaller(PG_FUNCTION_ARGS)
2094
{
2095 2096
	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
	TimeTzADT  *time2 = PG_GETARG_TIMETZADT_P(1);
2097
	TimeTzADT  *result;
2098

2099 2100 2101 2102 2103
	if (timetz_cmp_internal(time1, time2) < 0)
		result = time1;
	else
		result = time2;
	PG_RETURN_TIMETZADT_P(result);
2104
}
2105

2106 2107 2108 2109 2110 2111 2112 2113 2114
/* timetz_pl_interval()
 * Add interval to timetz.
 */
Datum
timetz_pl_interval(PG_FUNCTION_ARGS)
{
	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	TimeTzADT  *result;
Bruce Momjian's avatar
Bruce Momjian committed
2115

2116
#ifndef HAVE_INT64_TIMESTAMP
2117
	TimeTzADT	time1;
2118
#endif
2119 2120 2121

	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

2122
#ifdef HAVE_INT64_TIMESTAMP
2123 2124
	result->time = time->time + span->time;
	result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2125
	if (result->time < INT64CONST(0))
2126
		result->time += USECS_PER_DAY;
2127
#else
2128
	result->time = time->time + span->time;
2129
	TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2130
	if (result->time < 0)
2131
		result->time += SECS_PER_DAY;
2132 2133
#endif

2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147
	result->zone = time->zone;

	PG_RETURN_TIMETZADT_P(result);
}

/* timetz_mi_interval()
 * Subtract interval from timetz.
 */
Datum
timetz_mi_interval(PG_FUNCTION_ARGS)
{
	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(0);
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	TimeTzADT  *result;
Bruce Momjian's avatar
Bruce Momjian committed
2148

2149
#ifndef HAVE_INT64_TIMESTAMP
2150
	TimeTzADT	time1;
2151
#endif
2152 2153 2154

	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

2155
#ifdef HAVE_INT64_TIMESTAMP
2156 2157
	result->time = time->time - span->time;
	result->time -= result->time / USECS_PER_DAY * USECS_PER_DAY;
2158
	if (result->time < INT64CONST(0))
2159
		result->time += USECS_PER_DAY;
2160
#else
2161
	result->time = time->time - span->time;
2162
	TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
2163
	if (result->time < 0)
2164
		result->time += SECS_PER_DAY;
2165 2166
#endif

2167 2168 2169 2170 2171
	result->zone = time->zone;

	PG_RETURN_TIMETZADT_P(result);
}

2172 2173 2174 2175 2176
/* overlaps_timetz() --- implements the SQL92 OVERLAPS operator.
 *
 * Algorithm is per SQL92 spec.  This is much harder than you'd think
 * because the spec requires us to deliver a non-null answer in some cases
 * where some of the inputs are null.
2177
 */
2178 2179 2180
Datum
overlaps_timetz(PG_FUNCTION_ARGS)
{
2181
	/*
2182 2183
	 * The arguments are TimeTzADT *, but we leave them as generic Datums for
	 * convenience of notation --- and to avoid dereferencing nulls.
2184 2185 2186 2187 2188
	 */
	Datum		ts1 = PG_GETARG_DATUM(0);
	Datum		te1 = PG_GETARG_DATUM(1);
	Datum		ts2 = PG_GETARG_DATUM(2);
	Datum		te2 = PG_GETARG_DATUM(3);
2189 2190 2191 2192
	bool		ts1IsNull = PG_ARGISNULL(0);
	bool		te1IsNull = PG_ARGISNULL(1);
	bool		ts2IsNull = PG_ARGISNULL(2);
	bool		te2IsNull = PG_ARGISNULL(3);
2193 2194 2195 2196 2197 2198

#define TIMETZ_GT(t1,t2) \
	DatumGetBool(DirectFunctionCall2(timetz_gt,t1,t2))
#define TIMETZ_LT(t1,t2) \
	DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))

2199
	/*
2200 2201 2202
	 * If both endpoints of interval 1 are null, the result is null (unknown).
	 * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
	 * take ts1 as the lesser endpoint.
2203 2204
	 */
	if (ts1IsNull)
2205
	{
2206 2207 2208
		if (te1IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
2209
		ts1 = te1;
2210
		te1IsNull = true;
2211
	}
2212
	else if (!te1IsNull)
2213
	{
2214 2215
		if (TIMETZ_GT(ts1, te1))
		{
2216
			Datum		tt = ts1;
2217 2218 2219 2220 2221

			ts1 = te1;
			te1 = tt;
		}
	}
2222

2223 2224 2225 2226 2227 2228
	/* Likewise for interval 2. */
	if (ts2IsNull)
	{
		if (te2IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
2229
		ts2 = te2;
2230 2231 2232 2233 2234 2235
		te2IsNull = true;
	}
	else if (!te2IsNull)
	{
		if (TIMETZ_GT(ts2, te2))
		{
2236
			Datum		tt = ts2;
2237 2238 2239 2240

			ts2 = te2;
			te2 = tt;
		}
2241 2242
	}

2243 2244 2245 2246 2247 2248
	/*
	 * At this point neither ts1 nor ts2 is null, so we can consider three
	 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
	 */
	if (TIMETZ_GT(ts1, ts2))
	{
2249
		/*
2250 2251
		 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
		 * in the presence of nulls it's not quite completely so.
2252 2253 2254 2255 2256 2257 2258
		 */
		if (te2IsNull)
			PG_RETURN_NULL();
		if (TIMETZ_LT(ts1, te2))
			PG_RETURN_BOOL(true);
		if (te1IsNull)
			PG_RETURN_NULL();
2259 2260

		/*
2261 2262
		 * If te1 is not null then we had ts1 <= te1 above, and we just found
		 * ts1 >= te2, hence te1 >= te2.
2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274
		 */
		PG_RETURN_BOOL(false);
	}
	else if (TIMETZ_LT(ts1, ts2))
	{
		/* This case is ts2 < te1 OR te2 < te1 */
		if (te1IsNull)
			PG_RETURN_NULL();
		if (TIMETZ_LT(ts2, te1))
			PG_RETURN_BOOL(true);
		if (te2IsNull)
			PG_RETURN_NULL();
2275 2276

		/*
2277 2278
		 * If te2 is not null then we had ts2 <= te2 above, and we just found
		 * ts2 >= te1, hence te2 >= te1.
2279 2280 2281 2282 2283
		 */
		PG_RETURN_BOOL(false);
	}
	else
	{
2284 2285
		/*
		 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2286
		 * rather silly way of saying "true if both are nonnull, else null".
2287 2288 2289 2290 2291
		 */
		if (te1IsNull || te2IsNull)
			PG_RETURN_NULL();
		PG_RETURN_BOOL(true);
	}
2292 2293 2294 2295

#undef TIMETZ_GT
#undef TIMETZ_LT
}
2296

2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315

Datum
timetz_time(PG_FUNCTION_ARGS)
{
	TimeTzADT  *timetz = PG_GETARG_TIMETZADT_P(0);
	TimeADT		result;

	/* swallow the time zone and just return the time */
	result = timetz->time;

	PG_RETURN_TIMEADT(result);
}


Datum
time_timetz(PG_FUNCTION_ARGS)
{
	TimeADT		time = PG_GETARG_TIMEADT(0);
	TimeTzADT  *result;
Bruce Momjian's avatar
Bruce Momjian committed
2316
	struct pg_tm tt,
2317
			   *tm = &tt;
2318
	fsec_t		fsec;
2319 2320
	int			tz;

Jan Wieck's avatar
Jan Wieck committed
2321
	GetCurrentDateTime(tm);
2322
	time2tm(time, tm, &fsec);
2323
	tz = DetermineTimeZoneOffset(tm, session_timezone);
2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334

	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

	result->time = time;
	result->zone = tz;

	PG_RETURN_TIMETZADT_P(result);
}


/* timestamptz_timetz()
2335 2336
 * Convert timestamp to timetz data type.
 */
2337
Datum
2338
timestamptz_timetz(PG_FUNCTION_ARGS)
2339
{
2340
	TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
2341
	TimeTzADT  *result;
Bruce Momjian's avatar
Bruce Momjian committed
2342
	struct pg_tm tt,
2343 2344
			   *tm = &tt;
	int			tz;
2345
	fsec_t		fsec;
2346 2347
	char	   *tzn;

2348
	if (TIMESTAMP_NOT_FINITE(timestamp))
2349
		PG_RETURN_NULL();
2350

2351
	if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2352 2353 2354
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2355

2356
	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2357

2358
	tm2timetz(tm, fsec, tz, result);
2359

2360 2361
	PG_RETURN_TIMETZADT_P(result);
}
2362 2363


2364 2365
/* datetimetz_timestamptz()
 * Convert date and timetz to timestamp with time zone data type.
2366 2367 2368 2369
 * Timestamp is stored in GMT, so add the time zone
 * stored with the timetz to the result.
 * - thomas 2000-03-10
 */
2370
Datum
2371
datetimetz_timestamptz(PG_FUNCTION_ARGS)
2372
{
2373
	DateADT		date = PG_GETARG_DATEADT(0);
2374
	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
2375
	TimestampTz result;
2376

2377 2378 2379 2380 2381 2382
	if (DATE_IS_NOBEGIN(date))
		TIMESTAMP_NOBEGIN(result);
	else if (DATE_IS_NOEND(date))
		TIMESTAMP_NOEND(result);
	else
	{
2383
#ifdef HAVE_INT64_TIMESTAMP
2384
		result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2385
#else
2386
		result = date * (double) SECS_PER_DAY + time->time + time->zone;
2387
#endif
2388
	}
2389

2390 2391
	PG_RETURN_TIMESTAMP(result);
}
2392 2393


2394 2395 2396 2397 2398 2399
/* timetz_part()
 * Extract specified field from time type.
 */
Datum
timetz_part(PG_FUNCTION_ARGS)
{
2400
	text	   *units = PG_GETARG_TEXT_PP(0);
2401 2402 2403 2404
	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
	float8		result;
	int			type,
				val;
2405
	char	   *lowunits;
2406

2407 2408
	lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
											VARSIZE_ANY_EXHDR(units),
2409
											false);
2410 2411 2412 2413 2414 2415 2416 2417 2418

	type = DecodeUnits(0, lowunits, &val);
	if (type == UNKNOWN_FIELD)
		type = DecodeSpecial(0, lowunits, &val);

	if (type == UNITS)
	{
		double		dummy;
		int			tz;
2419
		fsec_t		fsec;
Bruce Momjian's avatar
Bruce Momjian committed
2420
		struct pg_tm tt,
2421 2422
				   *tm = &tt;

2423
		timetz2tm(time, tm, &fsec, &tz);
2424 2425 2426 2427

		switch (val)
		{
			case DTK_TZ:
2428
				result = -tz;
2429 2430 2431
				break;

			case DTK_TZ_MINUTE:
2432
				result = -tz;
2433
				result /= SECS_PER_MINUTE;
2434
				FMODULO(result, dummy, (double) SECS_PER_MINUTE);
2435 2436 2437
				break;

			case DTK_TZ_HOUR:
2438
				dummy = -tz;
2439
				FMODULO(dummy, result, (double) SECS_PER_HOUR);
2440 2441 2442
				break;

			case DTK_MICROSEC:
2443
#ifdef HAVE_INT64_TIMESTAMP
2444
				result = tm->tm_sec * USECS_PER_SEC + fsec;
2445
#else
2446
				result = (tm->tm_sec + fsec) * 1000000;
2447
#endif
2448 2449 2450
				break;

			case DTK_MILLISEC:
2451
#ifdef HAVE_INT64_TIMESTAMP
2452
				result = tm->tm_sec * INT64CONST(1000) + fsec / INT64CONST(1000);
2453
#else
2454
				result = (tm->tm_sec + fsec) * 1000;
2455
#endif
2456 2457 2458
				break;

			case DTK_SECOND:
2459
#ifdef HAVE_INT64_TIMESTAMP
2460
				result = tm->tm_sec + fsec / USECS_PER_SEC;
2461
#else
2462
				result = tm->tm_sec + fsec;
2463
#endif
2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481
				break;

			case DTK_MINUTE:
				result = tm->tm_min;
				break;

			case DTK_HOUR:
				result = tm->tm_hour;
				break;

			case DTK_DAY:
			case DTK_MONTH:
			case DTK_QUARTER:
			case DTK_YEAR:
			case DTK_DECADE:
			case DTK_CENTURY:
			case DTK_MILLENNIUM:
			default:
2482 2483
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2484
				errmsg("\"time with time zone\" units \"%s\" not recognized",
2485
					   lowunits)));
2486 2487 2488
				result = 0;
		}
	}
2489
	else if (type == RESERV && val == DTK_EPOCH)
2490 2491
	{
#ifdef HAVE_INT64_TIMESTAMP
2492
		result = time->time / 1000000.0 + time->zone;
2493
#else
2494
		result = time->time + time->zone;
2495 2496
#endif
	}
2497 2498
	else
	{
2499 2500
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2501
				 errmsg("\"time with time zone\" units \"%s\" not recognized",
2502
						lowunits)));
2503 2504 2505 2506 2507 2508
		result = 0;
	}

	PG_RETURN_FLOAT8(result);
}

2509 2510
/* timetz_zone()
 * Encode time with time zone type with specified time zone.
2511
 * Applies DST rules as of the current date.
2512 2513 2514 2515
 */
Datum
timetz_zone(PG_FUNCTION_ARGS)
{
2516
	text	   *zone = PG_GETARG_TEXT_PP(0);
2517
	TimeTzADT  *t = PG_GETARG_TIMETZADT_P(1);
2518 2519
	TimeTzADT  *result;
	int			tz;
2520
	char		tzname[TZ_STRLEN_MAX + 1];
2521 2522 2523
	char	   *lowzone;
	int			type,
				val;
2524 2525
	pg_tz	   *tzp;

2526
	/*
2527 2528 2529 2530 2531 2532
	 * Look up the requested timezone.  First we look in the date token table
	 * (to handle cases like "EST"), and if that fails, we look in the
	 * timezone database (to handle cases like "America/New_York").  (This
	 * matches the order in which timestamp input checks the cases; it's
	 * important because the timezone database unwisely uses a few zone names
	 * that are identical to offset abbreviations.)
2533
	 */
2534
	text_to_cstring_buffer(zone, tzname, sizeof(tzname));
2535 2536 2537
	lowzone = downcase_truncate_identifier(tzname,
										   strlen(tzname),
										   false);
2538

2539 2540 2541 2542
	type = DecodeSpecial(0, lowzone, &val);

	if (type == TZ || type == DTZ)
		tz = val * 60;
2543 2544
	else
	{
2545 2546 2547 2548 2549 2550
		tzp = pg_tzset(tzname);
		if (tzp)
		{
			/* Get the offset-from-GMT that is valid today for the zone */
			pg_time_t	now = (pg_time_t) time(NULL);
			struct pg_tm *tm;
2551

2552 2553 2554
			tm = pg_localtime(&now, tzp);
			tz = -tm->tm_gmtoff;
		}
2555 2556 2557 2558 2559 2560 2561
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("time zone \"%s\" not recognized", tzname)));
			tz = 0;				/* keep compiler quiet */
		}
2562
	}
2563

2564
	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
2565

2566
#ifdef HAVE_INT64_TIMESTAMP
2567 2568 2569 2570 2571
	result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
	while (result->time < INT64CONST(0))
		result->time += USECS_PER_DAY;
	while (result->time >= USECS_PER_DAY)
		result->time -= USECS_PER_DAY;
2572
#else
2573 2574 2575 2576 2577
	result->time = t->time + (t->zone - tz);
	while (result->time < 0)
		result->time += SECS_PER_DAY;
	while (result->time >= SECS_PER_DAY)
		result->time -= SECS_PER_DAY;
2578 2579
#endif

2580
	result->zone = tz;
2581 2582

	PG_RETURN_TIMETZADT_P(result);
2583
}
2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596

/* timetz_izone()
 * Encode time with time zone type with specified time interval as time zone.
 */
Datum
timetz_izone(PG_FUNCTION_ARGS)
{
	Interval   *zone = PG_GETARG_INTERVAL_P(0);
	TimeTzADT  *time = PG_GETARG_TIMETZADT_P(1);
	TimeTzADT  *result;
	int			tz;

	if (zone->month != 0)
2597 2598
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2599
				 errmsg("\"interval\" time zone \"%s\" not valid",
Bruce Momjian's avatar
Bruce Momjian committed
2600
						DatumGetCString(DirectFunctionCall1(interval_out,
2601
												  PointerGetDatum(zone))))));
2602

2603
#ifdef HAVE_INT64_TIMESTAMP
2604
	tz = -(zone->time / USECS_PER_SEC);
2605
#else
2606
	tz = -(zone->time);
2607
#endif
2608 2609 2610

	result = (TimeTzADT *) palloc(sizeof(TimeTzADT));

2611
#ifdef HAVE_INT64_TIMESTAMP
2612
	result->time = time->time + (time->zone - tz) * USECS_PER_SEC;
2613
	while (result->time < INT64CONST(0))
2614 2615 2616
		result->time += USECS_PER_DAY;
	while (result->time >= USECS_PER_DAY)
		result->time -= USECS_PER_DAY;
2617
#else
2618
	result->time = time->time + (time->zone - tz);
2619
	while (result->time < 0)
2620 2621 2622
		result->time += SECS_PER_DAY;
	while (result->time >= SECS_PER_DAY)
		result->time -= SECS_PER_DAY;
2623 2624
#endif

2625 2626 2627
	result->zone = tz;

	PG_RETURN_TIMETZADT_P(result);
2628
}