timestamp.c 98.1 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * timestamp.c
4
 *	  Functions for the built-in SQL92 types "timestamp" and "interval".
5
 *
P
 
PostgreSQL Daemon 已提交
6
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7 8 9 10
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.157 2005/10/27 02:45:22 momjian Exp $
12 13 14
 *
 *-------------------------------------------------------------------------
 */
15 16 17

#include "postgres.h"

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

24
#include "access/hash.h"
25
#include "access/xact.h"
26
#include "catalog/pg_type.h"
27
#include "libpq/pqformat.h"
28
#include "miscadmin.h"
29
#include "parser/scansup.h"
30
#include "utils/array.h"
M
Marc G. Fournier 已提交
31
#include "utils/builtins.h"
32 33
#include "utils/datetime.h"

M
Marc G. Fournier 已提交
34

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

43

44 45 46 47
/* Set at postmaster start */
TimestampTz PgStartTime;


48 49 50 51 52
#ifdef HAVE_INT64_TIMESTAMP
static int64 time2t(const int hour, const int min, const int sec, const fsec_t fsec);
#else
static double time2t(const int hour, const int min, const int sec, const fsec_t fsec);
#endif
53 54
static int	EncodeSpecialTimestamp(Timestamp dt, char *str);
static Timestamp dt2local(Timestamp dt, int timezone);
55
static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
56
static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
57
static TimestampTz timestamp2timestamptz(Timestamp timestamp);
58

59 60 61 62 63 64 65 66

/*****************************************************************************
 *	 USER I/O ROUTINES														 *
 *****************************************************************************/

/* timestamp_in()
 * Convert a string to internal form.
 */
67 68
Datum
timestamp_in(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
69
{
70
	char	   *str = PG_GETARG_CSTRING(0);
71

72 73 74 75
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
76
	Timestamp	result;
77
	fsec_t		fsec;
B
Bruce Momjian 已提交
78
	struct pg_tm tt,
79 80 81 82
			   *tm = &tt;
	int			tz;
	int			dtype;
	int			nf;
83
	int			dterr;
84 85
	char	   *field[MAXDATEFIELDS];
	int			ftype[MAXDATEFIELDS];
86
	char		workbuf[MAXDATELEN + MAXDATEFIELDS];
87

88 89
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
						  field, ftype, MAXDATEFIELDS, &nf);
90 91 92 93
	if (dterr == 0)
		dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
	if (dterr != 0)
		DateTimeParseError(dterr, str, "timestamp");
94 95 96 97

	switch (dtype)
	{
		case DTK_DATE:
98
			if (tm2timestamp(tm, fsec, NULL, &result) != 0)
99 100 101
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range: \"%s\"", str)));
102 103 104
			break;

		case DTK_EPOCH:
105
			result = SetEpochTimestamp();
106 107 108
			break;

		case DTK_LATE:
109
			TIMESTAMP_NOEND(result);
110 111 112
			break;

		case DTK_EARLY:
113
			TIMESTAMP_NOBEGIN(result);
114 115 116
			break;

		case DTK_INVALID:
117 118
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
119
			  errmsg("date/time value \"%s\" is no longer supported", str)));
120

121
			TIMESTAMP_NOEND(result);
122
			break;
123

124
		default:
125 126
			elog(ERROR, "unexpected dtype %d while parsing timestamp \"%s\"",
				 dtype, str);
127
			TIMESTAMP_NOEND(result);
128
	}
M
Marc G. Fournier 已提交
129

130 131
	AdjustTimestampForTypmod(&result, typmod);

132 133
	PG_RETURN_TIMESTAMP(result);
}
M
Marc G. Fournier 已提交
134

135 136 137
/* timestamp_out()
 * Convert a timestamp to external form.
 */
138 139
Datum
timestamp_out(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
140
{
B
Bruce Momjian 已提交
141
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
142
	char	   *result;
B
Bruce Momjian 已提交
143
	struct pg_tm tt,
144
			   *tm = &tt;
145
	fsec_t		fsec;
146 147 148
	char	   *tzn = NULL;
	char		buf[MAXDATELEN + 1];

149 150
	if (TIMESTAMP_NOT_FINITE(timestamp))
		EncodeSpecialTimestamp(timestamp, buf);
151
	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
152 153
		EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf);
	else
154 155 156
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
157 158 159 160 161

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

162 163 164 165 166 167 168 169 170 171
/*
 *		timestamp_recv			- converts external binary format to timestamp
 *
 * We make no attempt to provide compatibility between int and float
 * timestamp representations ...
 */
Datum
timestamp_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
B
Bruce Momjian 已提交
172

173 174 175 176
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
B
Bruce Momjian 已提交
177
	Timestamp	timestamp;
B
Bruce Momjian 已提交
178
	struct pg_tm tt,
179 180
			   *tm = &tt;
	fsec_t		fsec;
181 182

#ifdef HAVE_INT64_TIMESTAMP
183
	timestamp = (Timestamp) pq_getmsgint64(buf);
184
#else
185
	timestamp = (Timestamp) pq_getmsgfloat8(buf);
186
#endif
187 188 189

	/* rangecheck: see if timestamp_out would like it */
	if (TIMESTAMP_NOT_FINITE(timestamp))
B
Bruce Momjian 已提交
190
		 /* ok */ ;
191
	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
192 193 194 195
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));

196 197
	AdjustTimestampForTypmod(&timestamp, typmod);

198
	PG_RETURN_TIMESTAMP(timestamp);
199 200 201 202 203 204 205 206
}

/*
 *		timestamp_send			- converts timestamp to binary format
 */
Datum
timestamp_send(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
207
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
208 209 210 211 212 213 214 215 216 217 218 219
	StringInfoData buf;

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


220 221 222 223 224 225 226
/* timestamp_scale()
 * Adjust time type for specified scale factor.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
timestamp_scale(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
227
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
228 229 230 231 232
	int32		typmod = PG_GETARG_INT32(1);
	Timestamp	result;

	result = timestamp;

233
	AdjustTimestampForTypmod(&result, typmod);
234 235 236 237 238 239 240

	PG_RETURN_TIMESTAMP(result);
}

static void
AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
{
241
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
242
	static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
243 244 245 246 247 248 249 250 251
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

B
Bruce Momjian 已提交
252
	static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
253 254 255 256 257 258
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
259 260 261
		INT64CONST(0)
	};
#else
B
Bruce Momjian 已提交
262
	static const double TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
263 264 265 266 267 268 269 270 271 272 273 274
		1,
		10,
		100,
		1000,
		10000,
		100000,
		1000000
	};
#endif

	if (!TIMESTAMP_NOT_FINITE(*time)
		&& (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
275
	{
276
		if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION)
277 278
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
279 280
				  errmsg("timestamp(%d) precision must be between %d and %d",
						 typmod, 0, MAX_TIMESTAMP_PRECISION)));
281

282
		/*
B
Bruce Momjian 已提交
283 284 285 286 287
		 * 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?
288
		 */
289 290 291
#ifdef HAVE_INT64_TIMESTAMP
		if (*time >= INT64CONST(0))
		{
292
			*time = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) *
B
Bruce Momjian 已提交
293
				TimestampScales[typmod];
294 295 296
		}
		else
		{
B
Bruce Momjian 已提交
297 298
			*time = -((((-*time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
					  * TimestampScales[typmod]);
299
		}
300
#else
B
Bruce Momjian 已提交
301
		*time = rint((double) *time * TimestampScales[typmod]) / TimestampScales[typmod];
302
#endif
303 304 305
	}
}

306 307 308 309 310 311 312 313

/* timestamptz_in()
 * Convert a string to internal form.
 */
Datum
timestamptz_in(PG_FUNCTION_ARGS)
{
	char	   *str = PG_GETARG_CSTRING(0);
314

315 316 317 318
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
319
	TimestampTz result;
320
	fsec_t		fsec;
B
Bruce Momjian 已提交
321
	struct pg_tm tt,
322 323 324 325
			   *tm = &tt;
	int			tz;
	int			dtype;
	int			nf;
326
	int			dterr;
327 328
	char	   *field[MAXDATEFIELDS];
	int			ftype[MAXDATEFIELDS];
329
	char		workbuf[MAXDATELEN + MAXDATEFIELDS];
330

331 332
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
						  field, ftype, MAXDATEFIELDS, &nf);
333 334 335 336
	if (dterr == 0)
		dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
	if (dterr != 0)
		DateTimeParseError(dterr, str, "timestamp with time zone");
337 338 339 340 341

	switch (dtype)
	{
		case DTK_DATE:
			if (tm2timestamp(tm, fsec, &tz, &result) != 0)
342 343 344
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range: \"%s\"", str)));
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
			break;

		case DTK_EPOCH:
			result = SetEpochTimestamp();
			break;

		case DTK_LATE:
			TIMESTAMP_NOEND(result);
			break;

		case DTK_EARLY:
			TIMESTAMP_NOBEGIN(result);
			break;

		case DTK_INVALID:
360 361
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
362
			  errmsg("date/time value \"%s\" is no longer supported", str)));
363

364 365 366 367
			TIMESTAMP_NOEND(result);
			break;

		default:
368 369
			elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"",
				 dtype, str);
370 371 372
			TIMESTAMP_NOEND(result);
	}

373 374
	AdjustTimestampForTypmod(&result, typmod);

375 376 377 378 379 380 381 382 383
	PG_RETURN_TIMESTAMPTZ(result);
}

/* timestamptz_out()
 * Convert a timestamp to external form.
 */
Datum
timestamptz_out(PG_FUNCTION_ARGS)
{
384
	TimestampTz dt = PG_GETARG_TIMESTAMPTZ(0);
385
	char	   *result;
386
	int			tz;
B
Bruce Momjian 已提交
387
	struct pg_tm tt,
388
			   *tm = &tt;
389
	fsec_t		fsec;
390
	char	   *tzn;
391
	char		buf[MAXDATELEN + 1];
M
Marc G. Fournier 已提交
392

393
	if (TIMESTAMP_NOT_FINITE(dt))
394
		EncodeSpecialTimestamp(dt, buf);
395
	else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0)
396 397
		EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf);
	else
398 399 400
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
401

402 403 404
	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}
405

406 407 408 409 410 411 412 413 414 415
/*
 *		timestamptz_recv			- converts external binary format to timestamptz
 *
 * We make no attempt to provide compatibility between int and float
 * timestamp representations ...
 */
Datum
timestamptz_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
B
Bruce Momjian 已提交
416

417 418 419 420
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
B
Bruce Momjian 已提交
421
	TimestampTz timestamp;
422
	int			tz;
B
Bruce Momjian 已提交
423
	struct pg_tm tt,
424 425 426
			   *tm = &tt;
	fsec_t		fsec;
	char	   *tzn;
427 428

#ifdef HAVE_INT64_TIMESTAMP
429
	timestamp = (TimestampTz) pq_getmsgint64(buf);
430
#else
431
	timestamp = (TimestampTz) pq_getmsgfloat8(buf);
432
#endif
433 434 435

	/* rangecheck: see if timestamptz_out would like it */
	if (TIMESTAMP_NOT_FINITE(timestamp))
B
Bruce Momjian 已提交
436
		 /* ok */ ;
437
	else if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
438 439 440 441
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));

442 443
	AdjustTimestampForTypmod(&timestamp, typmod);

444
	PG_RETURN_TIMESTAMPTZ(timestamp);
445 446 447 448 449 450 451 452
}

/*
 *		timestamptz_send			- converts timestamptz to binary format
 */
Datum
timestamptz_send(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
453
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
454 455 456 457 458 459 460 461 462 463 464 465
	StringInfoData buf;

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


466 467 468 469 470 471 472
/* timestamptz_scale()
 * Adjust time type for specified scale factor.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
timestamptz_scale(PG_FUNCTION_ARGS)
{
473
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
474
	int32		typmod = PG_GETARG_INT32(1);
475
	TimestampTz result;
476 477 478

	result = timestamp;

479
	AdjustTimestampForTypmod(&result, typmod);
480 481 482 483

	PG_RETURN_TIMESTAMPTZ(result);
}

484 485 486 487 488 489 490

/* interval_in()
 * Convert a string to internal form.
 *
 * External format(s):
 *	Uses the generic date/time parsing and decoding routines.
 */
491 492
Datum
interval_in(PG_FUNCTION_ARGS)
493
{
494
	char	   *str = PG_GETARG_CSTRING(0);
495

496 497 498 499
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
500
	Interval   *result;
501
	fsec_t		fsec;
B
Bruce Momjian 已提交
502
	struct pg_tm tt,
503 504 505
			   *tm = &tt;
	int			dtype;
	int			nf;
506
	int			dterr;
507 508
	char	   *field[MAXDATEFIELDS];
	int			ftype[MAXDATEFIELDS];
509
	char		workbuf[256];
510 511 512 513 514 515 516 517 518

	tm->tm_year = 0;
	tm->tm_mon = 0;
	tm->tm_mday = 0;
	tm->tm_hour = 0;
	tm->tm_min = 0;
	tm->tm_sec = 0;
	fsec = 0;

519 520
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field,
						  ftype, MAXDATEFIELDS, &nf);
521 522 523 524 525 526 527 528
	if (dterr == 0)
		dterr = DecodeInterval(field, ftype, nf, &dtype, tm, &fsec);
	if (dterr != 0)
	{
		if (dterr == DTERR_FIELD_OVERFLOW)
			dterr = DTERR_INTERVAL_OVERFLOW;
		DateTimeParseError(dterr, str, "interval");
	}
529

530
	result = (Interval *) palloc(sizeof(Interval));
531 532 533 534

	switch (dtype)
	{
		case DTK_DELTA:
535
			if (tm2interval(tm, fsec, result) != 0)
536 537 538
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("interval out of range")));
539 540 541
			break;

		case DTK_INVALID:
542 543
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
544
			  errmsg("date/time value \"%s\" is no longer supported", str)));
545
			break;
546

547
		default:
548 549
			elog(ERROR, "unexpected dtype %d while parsing interval \"%s\"",
				 dtype, str);
550
	}
551

552 553
	AdjustIntervalForTypmod(result, typmod);

554
	PG_RETURN_INTERVAL_P(result);
555
}
556 557 558 559

/* interval_out()
 * Convert a time span to external form.
 */
560 561
Datum
interval_out(PG_FUNCTION_ARGS)
562
{
563
	Interval   *span = PG_GETARG_INTERVAL_P(0);
564
	char	   *result;
B
Bruce Momjian 已提交
565
	struct pg_tm tt,
566
			   *tm = &tt;
567
	fsec_t		fsec;
568 569 570
	char		buf[MAXDATELEN + 1];

	if (interval2tm(*span, tm, &fsec) != 0)
571
		elog(ERROR, "could not convert interval to tm");
572

573
	if (EncodeInterval(tm, fsec, DateStyle, buf) != 0)
574
		elog(ERROR, "could not format interval");
575

576 577 578
	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}
579

580 581 582 583 584 585 586
/*
 *		interval_recv			- converts external binary format to interval
 */
Datum
interval_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
B
Bruce Momjian 已提交
587

588 589 590 591
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
592 593 594 595 596
	Interval   *interval;

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

#ifdef HAVE_INT64_TIMESTAMP
597
	interval->time = pq_getmsgint64(buf);
598
#else
599
	interval->time = pq_getmsgfloat8(buf);
600
#endif
601
	interval->day = pq_getmsgint(buf, sizeof(interval->day));
602
	interval->month = pq_getmsgint(buf, sizeof(interval->month));
603

604 605
	AdjustIntervalForTypmod(interval, typmod);

606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
	PG_RETURN_INTERVAL_P(interval);
}

/*
 *		interval_send			- converts interval to binary format
 */
Datum
interval_send(PG_FUNCTION_ARGS)
{
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
	StringInfoData buf;

	pq_begintypsend(&buf);
#ifdef HAVE_INT64_TIMESTAMP
	pq_sendint64(&buf, interval->time);
#else
	pq_sendfloat8(&buf, interval->time);
#endif
624
	pq_sendint(&buf, interval->day, sizeof(interval->day));
625 626 627 628 629
	pq_sendint(&buf, interval->month, sizeof(interval->month));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}


630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
/* interval_scale()
 * Adjust interval type for specified fields.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
interval_scale(PG_FUNCTION_ARGS)
{
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
	int32		typmod = PG_GETARG_INT32(1);
	Interval   *result;

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

	AdjustIntervalForTypmod(result, typmod);

	PG_RETURN_INTERVAL_P(result);
}
B
Bruce Momjian 已提交
648

649 650 651 652
/*
 *	Adjust interval for specified precision, in both YEAR to SECOND
 *	range and sub-second precision.
 */
653 654 655
static void
AdjustIntervalForTypmod(Interval *interval, int32 typmod)
{
656
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
657
	static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
658 659 660 661 662 663 664 665 666
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

B
Bruce Momjian 已提交
667
	static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
668 669 670 671 672 673
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
674 675 676
		INT64CONST(0)
	};
#else
B
Bruce Momjian 已提交
677
	static const double IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
678
		1,
679
		10,
680 681 682 683 684
		100,
		1000,
		10000,
		100000,
		1000000
685 686 687
	};
#endif

B
Bruce Momjian 已提交
688
	/*
B
Bruce Momjian 已提交
689 690
	 * Unspecified range and precision? Then not necessary to adjust. Setting
	 * typmod to -1 is the convention for all types.
691
	 */
692 693
	if (typmod != -1)
	{
694 695
		int			range = INTERVAL_RANGE(typmod);
		int			precision = INTERVAL_PRECISION(typmod);
696

697
		if (range == INTERVAL_FULL_RANGE)
698 699 700
		{
			/* Do nothing... */
		}
701
		else if (range == INTERVAL_MASK(YEAR))
702
		{
703
			interval->month = (interval->month / MONTHS_PER_YEAR) * MONTHS_PER_YEAR;
704
			interval->day = 0;
705
			interval->time = 0;
706
		}
707
		else if (range == INTERVAL_MASK(MONTH))
708
		{
709
			interval->month %= MONTHS_PER_YEAR;
710
			interval->day = 0;
711
			interval->time = 0;
712 713
		}
		/* YEAR TO MONTH */
714
		else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
715 716 717
		{
			/* month is already year to month */
			interval->day = 0;
718
			interval->time = 0;
719
		}
720
		else if (range == INTERVAL_MASK(DAY))
721
		{
722
			interval->month = 0;
723
			interval->time = 0;
724
		}
725
		else if (range == INTERVAL_MASK(HOUR))
726
		{
727
			interval->month = 0;
728
			interval->day = 0;
729

730
#ifdef HAVE_INT64_TIMESTAMP
731
			interval->time = (interval->time / USECS_PER_HOUR) *
B
Bruce Momjian 已提交
732
				USECS_PER_HOUR;
733
#else
B
Bruce Momjian 已提交
734
			interval->time = ((int) (interval->time / SECS_PER_HOUR)) * (double) SECS_PER_HOUR;
735
#endif
736
		}
737
		else if (range == INTERVAL_MASK(MINUTE))
738
		{
739 740 741
#ifdef HAVE_INT64_TIMESTAMP
			int64		hour;
#else
742
			double		hour;
743
#endif
744

745
			interval->month = 0;
746
			interval->day = 0;
747

748
#ifdef HAVE_INT64_TIMESTAMP
749 750 751
			hour = interval->time / USECS_PER_HOUR;
			interval->time -= hour * USECS_PER_HOUR;
			interval->time = (interval->time / USECS_PER_MINUTE) *
B
Bruce Momjian 已提交
752
				USECS_PER_MINUTE;
753
#else
B
Bruce Momjian 已提交
754 755
			TMODULO(interval->time, hour, (double) SECS_PER_HOUR);
			interval->time = ((int) (interval->time / SECS_PER_MINUTE)) * (double) SECS_PER_MINUTE;
756
#endif
757
		}
758
		else if (range == INTERVAL_MASK(SECOND))
759
		{
760 761 762 763 764
#ifdef HAVE_INT64_TIMESTAMP
			int64		minute;
#else
			double		minute;
#endif
765

766
			interval->month = 0;
767
			interval->day = 0;
768

769
#ifdef HAVE_INT64_TIMESTAMP
770 771
			minute = interval->time / USECS_PER_MINUTE;
			interval->time -= minute * USECS_PER_MINUTE;
772
#else
B
Bruce Momjian 已提交
773
			TMODULO(interval->time, minute, (double) SECS_PER_MINUTE);
774
			/* return subseconds too */
775
#endif
776 777
		}
		/* DAY TO HOUR */
778 779
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR)))
780
		{
781
			interval->month = 0;
782

783
#ifdef HAVE_INT64_TIMESTAMP
784
			interval->time = (interval->time / USECS_PER_HOUR) *
B
Bruce Momjian 已提交
785
				USECS_PER_HOUR;
786
#else
B
Bruce Momjian 已提交
787
			interval->time = ((int) (interval->time / SECS_PER_HOUR)) * (double) SECS_PER_HOUR;
788
#endif
789 790
		}
		/* DAY TO MINUTE */
791 792 793
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE)))
794
		{
795
			interval->month = 0;
796

797
#ifdef HAVE_INT64_TIMESTAMP
798
			interval->time = (interval->time / USECS_PER_MINUTE) *
B
Bruce Momjian 已提交
799
				USECS_PER_MINUTE;
800
#else
B
Bruce Momjian 已提交
801
			interval->time = ((int) (interval->time / SECS_PER_MINUTE)) * (double) SECS_PER_MINUTE;
802
#endif
803 804
		}
		/* DAY TO SECOND */
805 806 807 808
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
809
			interval->month = 0;
810

811
		/* HOUR TO MINUTE */
812 813
		else if (range == (INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE)))
814
		{
815
			interval->month = 0;
816
			interval->day = 0;
817

818
#ifdef HAVE_INT64_TIMESTAMP
819
			interval->time = (interval->time / USECS_PER_MINUTE) *
B
Bruce Momjian 已提交
820
				USECS_PER_MINUTE;
821
#else
B
Bruce Momjian 已提交
822
			interval->time = ((int) (interval->time / SECS_PER_MINUTE)) * (double) SECS_PER_MINUTE;
823
#endif
824 825
		}
		/* HOUR TO SECOND */
826 827 828
		else if (range == (INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
829
		{
830
			interval->month = 0;
831 832
			interval->day = 0;
			/* return subseconds too */
833 834
		}
		/* MINUTE TO SECOND */
835 836
		else if (range == (INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
837
		{
838 839 840
#ifdef HAVE_INT64_TIMESTAMP
			int64		hour;
#else
841
			double		hour;
842
#endif
843

844
			interval->month = 0;
845
			interval->day = 0;
846

847
#ifdef HAVE_INT64_TIMESTAMP
848 849
			hour = interval->time / USECS_PER_HOUR;
			interval->time -= hour * USECS_PER_HOUR;
850
#else
B
Bruce Momjian 已提交
851
			TMODULO(interval->time, hour, (double) SECS_PER_HOUR);
852
#endif
853 854
		}
		else
855
			elog(ERROR, "unrecognized interval typmod: %d", typmod);
856

857
		/* Need to adjust subsecond precision? */
858
		if (precision != INTERVAL_FULL_PRECISION)
859
		{
860
			if (precision < 0 || precision > MAX_INTERVAL_PRECISION)
861 862
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
863 864
				   errmsg("interval(%d) precision must be between %d and %d",
						  precision, 0, MAX_INTERVAL_PRECISION)));
865

866
			/*
B
Bruce Momjian 已提交
867 868 869 870 871
			 * 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?
872
			 */
873 874 875
#ifdef HAVE_INT64_TIMESTAMP
			if (interval->time >= INT64CONST(0))
			{
876
				interval->time = ((interval->time +
B
Bruce Momjian 已提交
877 878 879
								   IntervalOffsets[precision]) /
								  IntervalScales[precision]) *
					IntervalScales[precision];
880 881 882
			}
			else
			{
883
				interval->time = -(((-interval->time +
B
Bruce Momjian 已提交
884
									 IntervalOffsets[precision]) /
885
									IntervalScales[precision]) *
B
Bruce Momjian 已提交
886
								   IntervalScales[precision]);
887
			}
888
#else
889
			interval->time = rint(((double) interval->time) *
B
Bruce Momjian 已提交
890 891
								  IntervalScales[precision]) /
				IntervalScales[precision];
892
#endif
893 894 895 896 897 898
		}
	}

	return;
}

899 900 901 902

/* EncodeSpecialTimestamp()
 * Convert reserved timestamp data type to string.
 */
903
static int
904 905
EncodeSpecialTimestamp(Timestamp dt, char *str)
{
906 907 908 909 910 911
	if (TIMESTAMP_IS_NOBEGIN(dt))
		strcpy(str, EARLY);
	else if (TIMESTAMP_IS_NOEND(dt))
		strcpy(str, LATE);
	else
		return FALSE;
M
Marc G. Fournier 已提交
912

913
	return TRUE;
914 915
}	/* EncodeSpecialTimestamp() */

916 917
Datum
now(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
918
{
919
	PG_RETURN_TIMESTAMPTZ(GetCurrentTransactionStartTimestamp());
M
Marc G. Fournier 已提交
920 921
}

922 923 924
Datum
pgsql_postmaster_start_time(PG_FUNCTION_ARGS)
{
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
	PG_RETURN_TIMESTAMPTZ(PgStartTime);
}

/*
 * GetCurrentTimestamp -- get the current operating system time
 *
 * Result is in the form of a TimestampTz value, and is expressed to the
 * full precision of the gettimeofday() syscall
 */
TimestampTz
GetCurrentTimestamp(void)
{
	TimestampTz result;
	struct timeval tp;

	gettimeofday(&tp, NULL);

942
	result = (TimestampTz) tp.tv_sec -
943 944 945 946 947 948 949 950 951
		((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);

#ifdef HAVE_INT64_TIMESTAMP
	result = (result * USECS_PER_SEC) + tp.tv_usec;
#else
	result = result + (tp.tv_usec / 1000000.0);
#endif

	return result;
952 953
}

954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976

/*
 * Convert a time_t to TimestampTz.
 *
 * We do not use time_t internally in Postgres, but this is provided for use
 * by functions that need to interpret, say, a stat(2) result.
 */
TimestampTz
time_t_to_timestamptz(time_t tm)
{
	TimestampTz result;

	result = (TimestampTz) tm -
		((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);

#ifdef HAVE_INT64_TIMESTAMP
	result *= USECS_PER_SEC;
#endif

	return result;
}


977
void
978
dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
979
{
980 981 982
#ifdef HAVE_INT64_TIMESTAMP
	int64		time;
#else
983
	double		time;
984
#endif
985 986 987

	time = jd;

988
#ifdef HAVE_INT64_TIMESTAMP
989 990 991 992 993 994
	*hour = time / USECS_PER_HOUR;
	time -= (*hour) * USECS_PER_HOUR;
	*min = time / USECS_PER_MINUTE;
	time -= (*min) * USECS_PER_MINUTE;
	*sec = time / USECS_PER_SEC;
	*fsec = time - (*sec * USECS_PER_SEC);
995
#else
996 997 998 999
	*hour = time / SECS_PER_HOUR;
	time -= (*hour) * SECS_PER_HOUR;
	*min = time / SECS_PER_MINUTE;
	time -= (*min) * SECS_PER_MINUTE;
1000
	*sec = time;
1001
	*fsec = time - *sec;
1002
#endif
1003 1004 1005
}	/* dt2time() */


1006 1007 1008
/*
 * timestamp2tm() - Convert timestamp data type to POSIX time structure.
 *
1009 1010 1011 1012 1013
 * Note that year is _not_ 1900-based, but is an explicit full value.
 * Also, month is one-based, _not_ zero-based.
 * Returns:
 *	 0 on success
 *	-1 on out of range
1014
 *
1015
 * If attimezone is NULL, the global timezone (including possibly brute forced
1016
 * timezone) will be used.
1017 1018
 */
int
B
Bruce Momjian 已提交
1019
timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn, pg_tz *attimezone)
1020
{
B
Bruce Momjian 已提交
1021
	Timestamp	date;
1022
	Timestamp	time;
1023
	pg_time_t	utime;
1024

1025
	/*
B
Bruce Momjian 已提交
1026 1027 1028
	 * If HasCTZSet is true then we have a brute force time zone specified. Go
	 * ahead and rotate to the local time zone since we will later bypass any
	 * calls which adjust the tm fields.
1029
	 */
1030
	if (attimezone == NULL && HasCTZSet && tzp != NULL)
1031
	{
1032
#ifdef HAVE_INT64_TIMESTAMP
1033
		dt -= CTimeZone * USECS_PER_SEC;
1034
#else
1035
		dt -= CTimeZone;
1036
#endif
1037
	}
1038

1039
#ifdef HAVE_INT64_TIMESTAMP
1040
	time = dt;
1041
	TMODULO(time, date, USECS_PER_DAY);
1042 1043 1044

	if (time < INT64CONST(0))
	{
1045
		time += USECS_PER_DAY;
1046
		date -= 1;
1047
	}
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057

	/* add offset to go from J2000 back to standard Julian date */
	date += POSTGRES_EPOCH_JDATE;

	/* Julian day routine does not work for negative Julian days */
	if (date < 0 || date > (Timestamp) INT_MAX)
		return -1;

	j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
	dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1058
#else
1059
	time = dt;
B
Bruce Momjian 已提交
1060
	TMODULO(time, date, (double) SECS_PER_DAY);
1061 1062 1063

	if (time < 0)
	{
1064
		time += SECS_PER_DAY;
1065
		date -= 1;
1066 1067 1068
	}

	/* add offset to go from J2000 back to standard Julian date */
1069
	date += POSTGRES_EPOCH_JDATE;
1070

1071
recalc_d:
1072
	/* Julian day routine does not work for negative Julian days */
1073
	if (date < 0 || date > (Timestamp) INT_MAX)
1074 1075
		return -1;

1076
	j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1077
recalc_t:
1078
	dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1079

1080 1081 1082 1083 1084
	*fsec = TSROUND(*fsec);
	/* roundoff may need to propagate to higher-order fields */
	if (*fsec >= 1.0)
	{
		time = ceil(time);
B
Bruce Momjian 已提交
1085
		if (time >= (double) SECS_PER_DAY)
1086 1087 1088 1089 1090 1091 1092 1093 1094
		{
			time = 0;
			date += 1;
			goto recalc_d;
		}
		goto recalc_t;
	}
#endif

1095 1096
	/* Done if no TZ conversion wanted */
	if (tzp == NULL)
1097
	{
1098 1099 1100 1101 1102 1103 1104
		tm->tm_isdst = -1;
		tm->tm_gmtoff = 0;
		tm->tm_zone = NULL;
		if (tzn != NULL)
			*tzn = NULL;
		return 0;
	}
1105

1106
	/*
B
Bruce Momjian 已提交
1107 1108
	 * We have a brute force time zone per SQL99? Then use it without change
	 * since we have already rotated to the time zone.
1109
	 */
1110
	if (attimezone == NULL && HasCTZSet)
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
	{
		*tzp = CTimeZone;
		tm->tm_isdst = 0;
		tm->tm_gmtoff = CTimeZone;
		tm->tm_zone = NULL;
		if (tzn != NULL)
			*tzn = NULL;
		return 0;
	}

	/*
B
Bruce Momjian 已提交
1122 1123
	 * If the time falls within the range of pg_time_t, use pg_localtime() to
	 * rotate to the local time zone.
1124 1125 1126
	 *
	 * First, convert to an integral timestamp, avoiding possibly
	 * platform-specific roundoff-in-wrong-direction errors, and adjust to
B
Bruce Momjian 已提交
1127 1128 1129
	 * Unix epoch.	Then see if we can convert to pg_time_t without loss. This
	 * coding avoids hardwiring any assumptions about the width of pg_time_t,
	 * so it should behave sanely on machines without int64.
1130
	 */
1131
#ifdef HAVE_INT64_TIMESTAMP
1132
	dt = (dt - *fsec) / USECS_PER_SEC +
1133
		(POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
1134
#else
1135
	dt = rint(dt - *fsec +
1136
			  (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
1137
#endif
1138 1139 1140
	utime = (pg_time_t) dt;
	if ((Timestamp) utime == dt)
	{
1141
		struct pg_tm *tx = pg_localtime(&utime,
B
Bruce Momjian 已提交
1142
								  attimezone ? attimezone : global_timezone);
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152

		tm->tm_year = tx->tm_year + 1900;
		tm->tm_mon = tx->tm_mon + 1;
		tm->tm_mday = tx->tm_mday;
		tm->tm_hour = tx->tm_hour;
		tm->tm_min = tx->tm_min;
		tm->tm_sec = tx->tm_sec;
		tm->tm_isdst = tx->tm_isdst;
		tm->tm_gmtoff = tx->tm_gmtoff;
		tm->tm_zone = tx->tm_zone;
1153
		*tzp = -tm->tm_gmtoff;
1154 1155
		if (tzn != NULL)
			*tzn = (char *) tm->tm_zone;
1156 1157 1158
	}
	else
	{
1159 1160 1161 1162 1163
		/*
		 * When out of range of pg_time_t, treat as GMT
		 */
		*tzp = 0;
		/* Mark this as *no* time zone available */
1164
		tm->tm_isdst = -1;
1165 1166
		tm->tm_gmtoff = 0;
		tm->tm_zone = NULL;
1167 1168 1169 1170 1171
		if (tzn != NULL)
			*tzn = NULL;
	}

	return 0;
1172
}
1173 1174 1175 1176 1177 1178


/* tm2timestamp()
 * Convert a tm structure to a timestamp data type.
 * Note that year is _not_ 1900-based, but is an explicit full value.
 * Also, month is one-based, _not_ zero-based.
1179
 *
1180
 * Returns -1 on failure (value out of range).
1181 1182
 */
int
B
Bruce Momjian 已提交
1183
tm2timestamp(struct pg_tm * tm, fsec_t fsec, int *tzp, Timestamp *result)
1184
{
1185
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
1186
	int			date;
B
Bruce Momjian 已提交
1187
	int64		time;
1188
#else
B
Bruce Momjian 已提交
1189
	double		date,
1190
				time;
1191
#endif
1192 1193 1194 1195 1196

	/* Julian day routines are not correct for negative Julian days */
	if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
		return -1;

1197
	date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1198
	time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
1199

1200
#ifdef HAVE_INT64_TIMESTAMP
1201
	*result = date * USECS_PER_DAY + time;
1202
	/* check for major overflow */
1203
	if ((*result - time) / USECS_PER_DAY != date)
1204 1205
		return -1;
	/* check for just-barely overflow (okay except time-of-day wraps) */
1206 1207
	if ((*result < 0 && date >= 0) ||
		(*result >= 0 && date < 0))
1208
		return -1;
1209
#else
1210
	*result = date * SECS_PER_DAY + time;
1211
#endif
1212 1213 1214 1215
	if (tzp != NULL)
		*result = dt2local(*result, -(*tzp));

	return 0;
1216
}
1217 1218 1219 1220 1221


/* interval2tm()
 * Convert a interval data type to a tm structure.
 */
B
Bruce Momjian 已提交
1222
int
B
Bruce Momjian 已提交
1223
interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
1224
{
1225 1226
#ifdef HAVE_INT64_TIMESTAMP
	int64		time;
1227
	int64		tfrac;
1228
#else
1229
	double		time;
1230
	double		tfrac;
1231
#endif
1232

1233 1234
	tm->tm_year = span.month / MONTHS_PER_YEAR;
	tm->tm_mon = span.month % MONTHS_PER_YEAR;
1235
	tm->tm_mday = span.day;
1236 1237
	time = span.time;

1238
#ifdef HAVE_INT64_TIMESTAMP
1239 1240 1241 1242 1243 1244 1245 1246 1247
	tfrac = time / USECS_PER_HOUR;
	time -= tfrac * USECS_PER_HOUR;
	tm->tm_hour = tfrac;		/* could overflow ... */
	tfrac = time / USECS_PER_MINUTE;
	time -= tfrac * USECS_PER_MINUTE;
	tm->tm_min = tfrac;
	tfrac = time / USECS_PER_SEC;
	*fsec = time - (tfrac * USECS_PER_SEC);
	tm->tm_sec = tfrac;
1248
#else
1249
recalc:
1250 1251 1252 1253 1254 1255
	TMODULO(time, tfrac, (double) SECS_PER_HOUR);
	tm->tm_hour = tfrac;		/* could overflow ... */
	TMODULO(time, tfrac, (double) SECS_PER_MINUTE);
	tm->tm_min = tfrac;
	TMODULO(time, tfrac, 1.0);
	tm->tm_sec = tfrac;
1256 1257 1258 1259 1260 1261 1262
	time = TSROUND(time);
	/* roundoff may need to propagate to higher-order fields */
	if (time >= 1.0)
	{
		time = ceil(span.time);
		goto recalc;
	}
1263
	*fsec = time;
1264
#endif
1265 1266

	return 0;
1267
}
1268

B
Bruce Momjian 已提交
1269
int
B
Bruce Momjian 已提交
1270
tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span)
1271
{
1272
	span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
B
Bruce Momjian 已提交
1273
	span->day = tm->tm_mday;
1274
#ifdef HAVE_INT64_TIMESTAMP
1275
	span->time = (((((tm->tm_hour * INT64CONST(60)) +
B
Bruce Momjian 已提交
1276 1277
					 tm->tm_min) * INT64CONST(60)) +
				   tm->tm_sec) * USECS_PER_SEC) + fsec;
1278
#else
B
Bruce Momjian 已提交
1279 1280 1281
	span->time = (((tm->tm_hour * (double) MINS_PER_HOUR) +
				   tm->tm_min) * (double) SECS_PER_MINUTE) +
		tm->tm_sec + fsec;
1282
#endif
1283 1284

	return 0;
1285
}
1286

1287 1288 1289 1290
#ifdef HAVE_INT64_TIMESTAMP
static int64
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
{
B
Bruce Momjian 已提交
1291
	return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
1292 1293
}	/* time2t() */
#else
1294
static double
1295
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
1296
{
B
Bruce Momjian 已提交
1297
	return (((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec + fsec;
1298
}	/* time2t() */
1299
#endif
1300

1301
static Timestamp
1302 1303
dt2local(Timestamp dt, int tz)
{
1304
#ifdef HAVE_INT64_TIMESTAMP
1305
	dt -= (tz * USECS_PER_SEC);
1306
#else
1307
	dt -= tz;
1308
#endif
1309 1310 1311 1312 1313 1314 1315 1316 1317
	return dt;
}	/* dt2local() */


/*****************************************************************************
 *	 PUBLIC ROUTINES														 *
 *****************************************************************************/


1318 1319
Datum
timestamp_finite(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1320
{
B
Bruce Momjian 已提交
1321
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
1322

B
Bruce Momjian 已提交
1323
	PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp));
1324
}
M
Marc G. Fournier 已提交
1325

1326 1327
Datum
interval_finite(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1328
{
1329
	PG_RETURN_BOOL(true);
1330
}
1331 1332 1333 1334 1335 1336


/*----------------------------------------------------------
 *	Relational operators for timestamp.
 *---------------------------------------------------------*/

1337
void
B
Bruce Momjian 已提交
1338
GetEpochTime(struct pg_tm * tm)
1339
{
B
Bruce Momjian 已提交
1340 1341
	struct pg_tm *t0;
	pg_time_t	epoch = 0;
1342

1343
	t0 = pg_gmtime(&epoch);
1344 1345 1346 1347 1348 1349 1350 1351

	tm->tm_year = t0->tm_year;
	tm->tm_mon = t0->tm_mon;
	tm->tm_mday = t0->tm_mday;
	tm->tm_hour = t0->tm_hour;
	tm->tm_min = t0->tm_min;
	tm->tm_sec = t0->tm_sec;

1352
	tm->tm_year += 1900;
1353
	tm->tm_mon++;
1354
}
1355 1356

Timestamp
1357
SetEpochTimestamp(void)
1358
{
1359
	Timestamp	dt;
B
Bruce Momjian 已提交
1360
	struct pg_tm tt,
1361
			   *tm = &tt;
1362

1363
	GetEpochTime(tm);
1364
	/* we don't bother to test for failure ... */
1365
	tm2timestamp(tm, 0, NULL, &dt);
M
Marc G. Fournier 已提交
1366

1367
	return dt;
1368
}	/* SetEpochTimestamp() */
1369

1370
/*
1371 1372 1373
 * We are currently sharing some code between timestamp and timestamptz.
 * The comparison functions are among them. - thomas 2001-09-25
 *
1374
 *		timestamp_relop - is timestamp1 relop timestamp2
1375 1376
 *
 *		collate invalid timestamp at the end
1377
 */
1378
int
1379 1380
timestamp_cmp_internal(Timestamp dt1, Timestamp dt2)
{
1381
#ifdef HAVE_INT64_TIMESTAMP
1382
	return (dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0);
1383
#else
1384

1385 1386 1387
	/*
	 * When using float representation, we have to be wary of NaNs.
	 *
1388
	 * We consider all NANs to be equal and larger than any non-NAN. This is
B
Bruce Momjian 已提交
1389 1390
	 * somewhat arbitrary; the important thing is to have a consistent sort
	 * order.
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
	 */
	if (isnan(dt1))
	{
		if (isnan(dt2))
			return 0;			/* NAN = NAN */
		else
			return 1;			/* NAN > non-NAN */
	}
	else if (isnan(dt2))
	{
		return -1;				/* non-NAN < NAN */
	}
	else
	{
		if (dt1 > dt2)
			return 1;
		else if (dt1 < dt2)
			return -1;
		else
			return 0;
	}
#endif
1413 1414
}

1415 1416
Datum
timestamp_eq(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1417
{
1418 1419
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1420

1421
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
1422
}
M
Marc G. Fournier 已提交
1423

1424 1425
Datum
timestamp_ne(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1426
{
1427 1428
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1429

1430
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
1431
}
M
Marc G. Fournier 已提交
1432

1433 1434
Datum
timestamp_lt(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1435
{
1436 1437
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1438

1439
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
1440
}
M
Marc G. Fournier 已提交
1441

1442 1443
Datum
timestamp_gt(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1444
{
1445 1446
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1447

1448
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
1449
}
1450

1451 1452
Datum
timestamp_le(PG_FUNCTION_ARGS)
1453
{
1454 1455
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1456

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

1460 1461
Datum
timestamp_ge(PG_FUNCTION_ARGS)
1462
{
1463 1464
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1465

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

1469 1470
Datum
timestamp_cmp(PG_FUNCTION_ARGS)
1471
{
1472 1473
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1474

1475
	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
1476
}
1477 1478


1479 1480 1481 1482 1483 1484 1485 1486
/*
 * Crosstype comparison functions for timestamp vs timestamptz
 */

Datum
timestamp_eq_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1487 1488
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1489 1490 1491 1492 1493 1494 1495 1496 1497 1498

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_ne_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1499 1500
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1501 1502 1503 1504 1505 1506 1507 1508 1509 1510

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_lt_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1511 1512
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_gt_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1523 1524
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1525 1526 1527 1528 1529 1530 1531 1532 1533 1534

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_le_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1535 1536
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1537 1538 1539 1540 1541 1542 1543 1544 1545 1546

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_ge_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1547 1548
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1549 1550 1551 1552 1553 1554 1555 1556 1557 1558

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_cmp_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1559 1560
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1561 1562 1563 1564 1565 1566 1567 1568 1569

	dt1 = timestamp2timestamptz(timestampVal);

	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}

Datum
timestamptz_eq_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1570
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1571
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1572
	TimestampTz dt2;
1573 1574 1575 1576 1577 1578 1579 1580 1581

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_ne_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1582
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1583
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1584
	TimestampTz dt2;
1585 1586 1587 1588 1589 1590 1591 1592 1593

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_lt_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1594
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1595
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1596
	TimestampTz dt2;
1597 1598 1599 1600 1601 1602 1603 1604 1605

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_gt_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1606
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1607
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1608
	TimestampTz dt2;
1609 1610 1611 1612 1613 1614 1615 1616 1617

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_le_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1618
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1619
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1620
	TimestampTz dt2;
1621 1622 1623 1624 1625 1626 1627 1628 1629

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_ge_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1630
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1631
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1632
	TimestampTz dt2;
1633 1634 1635 1636 1637 1638 1639 1640 1641

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_cmp_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1642
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1643
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1644
	TimestampTz dt2;
1645 1646 1647 1648 1649 1650 1651

	dt2 = timestamp2timestamptz(timestampVal);

	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}


1652 1653
/*
 *		interval_relop	- is interval1 relop interval2
1654 1655
 *
 *		collate invalid interval at the end
1656
 */
1657 1658 1659
static int
interval_cmp_internal(Interval *interval1, Interval *interval2)
{
1660 1661 1662 1663
#ifdef HAVE_INT64_TIMESTAMP
	int64		span1,
				span2;
#else
1664 1665
	double		span1,
				span2;
1666
#endif
1667

1668
	span1 = interval1->time;
1669 1670 1671
	span2 = interval2->time;

#ifdef HAVE_INT64_TIMESTAMP
1672 1673 1674 1675
	span1 += interval1->month * INT64CONST(30) * USECS_PER_DAY;
	span1 += interval1->day * INT64CONST(24) * USECS_PER_HOUR;
	span2 += interval2->month * INT64CONST(30) * USECS_PER_DAY;
	span2 += interval2->day * INT64CONST(24) * USECS_PER_HOUR;
1676
#else
B
Bruce Momjian 已提交
1677 1678 1679 1680
	span1 += interval1->month * ((double) DAYS_PER_MONTH * SECS_PER_DAY);
	span1 += interval1->day * ((double) HOURS_PER_DAY * SECS_PER_HOUR);
	span2 += interval2->month * ((double) DAYS_PER_MONTH * SECS_PER_DAY);
	span2 += interval2->day * ((double) HOURS_PER_DAY * SECS_PER_HOUR);
1681
#endif
1682

1683
	return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
1684 1685
}

1686 1687
Datum
interval_eq(PG_FUNCTION_ARGS)
1688
{
1689 1690
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1691

1692
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0);
1693
}
1694

1695 1696
Datum
interval_ne(PG_FUNCTION_ARGS)
1697
{
1698 1699
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1700

1701
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0);
1702
}
1703

1704 1705
Datum
interval_lt(PG_FUNCTION_ARGS)
1706
{
1707 1708
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1709

1710
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0);
1711
}
1712

1713 1714
Datum
interval_gt(PG_FUNCTION_ARGS)
1715
{
1716 1717
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1718

1719
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0);
1720
}
1721

1722 1723
Datum
interval_le(PG_FUNCTION_ARGS)
1724
{
1725 1726
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1727

1728
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0);
1729
}
1730

1731 1732
Datum
interval_ge(PG_FUNCTION_ARGS)
1733
{
1734 1735
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1736

1737
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0);
1738
}
1739

1740 1741
Datum
interval_cmp(PG_FUNCTION_ARGS)
1742
{
1743 1744
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1745

1746
	PG_RETURN_INT32(interval_cmp_internal(interval1, interval2));
1747
}
1748

1749 1750 1751 1752 1753 1754 1755 1756 1757 1758
/*
 * interval, being an unusual size, needs a specialized hash function.
 */
Datum
interval_hash(PG_FUNCTION_ARGS)
{
	Interval   *key = PG_GETARG_INTERVAL_P(0);

	/*
	 * Specify hash length as sizeof(double) + sizeof(int4), not as
B
Bruce Momjian 已提交
1759 1760
	 * sizeof(Interval), so that any garbage pad bytes in the structure won't
	 * be included in the hash!
1761
	 */
B
Bruce Momjian 已提交
1762 1763
	return hash_any((unsigned char *) key,
				  sizeof(key->time) + sizeof(key->day) + sizeof(key->month));
1764 1765
}

1766 1767 1768 1769 1770
/* overlaps_timestamp() --- 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.
1771
 */
1772 1773
Datum
overlaps_timestamp(PG_FUNCTION_ARGS)
1774
{
B
Bruce Momjian 已提交
1775
	/*
B
Bruce Momjian 已提交
1776 1777 1778
	 * The arguments are Timestamps, but we leave them as generic Datums to
	 * avoid unnecessary conversions between value and reference forms --- not
	 * to mention possible dereferences of null pointers.
1779 1780 1781 1782 1783
	 */
	Datum		ts1 = PG_GETARG_DATUM(0);
	Datum		te1 = PG_GETARG_DATUM(1);
	Datum		ts2 = PG_GETARG_DATUM(2);
	Datum		te2 = PG_GETARG_DATUM(3);
1784 1785 1786 1787
	bool		ts1IsNull = PG_ARGISNULL(0);
	bool		te1IsNull = PG_ARGISNULL(1);
	bool		ts2IsNull = PG_ARGISNULL(2);
	bool		te2IsNull = PG_ARGISNULL(3);
1788 1789 1790 1791 1792 1793

#define TIMESTAMP_GT(t1,t2) \
	DatumGetBool(DirectFunctionCall2(timestamp_gt,t1,t2))
#define TIMESTAMP_LT(t1,t2) \
	DatumGetBool(DirectFunctionCall2(timestamp_lt,t1,t2))

1794
	/*
B
Bruce Momjian 已提交
1795 1796 1797
	 * 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.
1798 1799
	 */
	if (ts1IsNull)
1800
	{
1801 1802 1803
		if (te1IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
1804
		ts1 = te1;
1805
		te1IsNull = true;
1806
	}
1807
	else if (!te1IsNull)
1808
	{
1809 1810
		if (TIMESTAMP_GT(ts1, te1))
		{
B
Bruce Momjian 已提交
1811
			Datum		tt = ts1;
1812

1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823
			ts1 = te1;
			te1 = tt;
		}
	}

	/* Likewise for interval 2. */
	if (ts2IsNull)
	{
		if (te2IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
1824
		ts2 = te2;
1825
		te2IsNull = true;
1826
	}
1827 1828 1829 1830
	else if (!te2IsNull)
	{
		if (TIMESTAMP_GT(ts2, te2))
		{
B
Bruce Momjian 已提交
1831
			Datum		tt = ts2;
1832

1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843
			ts2 = te2;
			te2 = tt;
		}
	}

	/*
	 * At this point neither ts1 nor ts2 is null, so we can consider three
	 * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
	 */
	if (TIMESTAMP_GT(ts1, ts2))
	{
B
Bruce Momjian 已提交
1844
		/*
B
Bruce Momjian 已提交
1845 1846
		 * This case is ts1 < te2 OR te1 < te2, which may look redundant but
		 * in the presence of nulls it's not quite completely so.
1847 1848 1849 1850 1851 1852 1853
		 */
		if (te2IsNull)
			PG_RETURN_NULL();
		if (TIMESTAMP_LT(ts1, te2))
			PG_RETURN_BOOL(true);
		if (te1IsNull)
			PG_RETURN_NULL();
B
Bruce Momjian 已提交
1854 1855

		/*
B
Bruce Momjian 已提交
1856 1857
		 * If te1 is not null then we had ts1 <= te1 above, and we just found
		 * ts1 >= te2, hence te1 >= te2.
1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869
		 */
		PG_RETURN_BOOL(false);
	}
	else if (TIMESTAMP_LT(ts1, ts2))
	{
		/* This case is ts2 < te1 OR te2 < te1 */
		if (te1IsNull)
			PG_RETURN_NULL();
		if (TIMESTAMP_LT(ts2, te1))
			PG_RETURN_BOOL(true);
		if (te2IsNull)
			PG_RETURN_NULL();
B
Bruce Momjian 已提交
1870 1871

		/*
B
Bruce Momjian 已提交
1872 1873
		 * If te2 is not null then we had ts2 <= te2 above, and we just found
		 * ts2 >= te1, hence te2 >= te1.
1874 1875 1876 1877 1878
		 */
		PG_RETURN_BOOL(false);
	}
	else
	{
B
Bruce Momjian 已提交
1879 1880
		/*
		 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
B
Bruce Momjian 已提交
1881
		 * rather silly way of saying "true if both are nonnull, else null".
1882 1883 1884 1885 1886
		 */
		if (te1IsNull || te2IsNull)
			PG_RETURN_NULL();
		PG_RETURN_BOOL(true);
	}
1887 1888 1889 1890

#undef TIMESTAMP_GT
#undef TIMESTAMP_LT
}
1891

1892 1893 1894 1895 1896

/*----------------------------------------------------------
 *	"Arithmetic" operators on date/times.
 *---------------------------------------------------------*/

1897 1898
Datum
timestamp_smaller(PG_FUNCTION_ARGS)
1899
{
1900 1901 1902
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
1903

1904 1905 1906 1907 1908
	/* use timestamp_cmp_internal to be sure this agrees with comparisons */
	if (timestamp_cmp_internal(dt1, dt2) < 0)
		result = dt1;
	else
		result = dt2;
1909 1910
	PG_RETURN_TIMESTAMP(result);
}
1911

1912 1913
Datum
timestamp_larger(PG_FUNCTION_ARGS)
1914
{
1915 1916 1917
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
1918

1919 1920 1921 1922
	if (timestamp_cmp_internal(dt1, dt2) > 0)
		result = dt1;
	else
		result = dt2;
1923 1924
	PG_RETURN_TIMESTAMP(result);
}
1925 1926


1927 1928
Datum
timestamp_mi(PG_FUNCTION_ARGS)
1929
{
1930 1931
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1932
	Interval   *result;
1933

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

1936
	if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
1937 1938
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1939
				 errmsg("cannot subtract infinite timestamps")));
1940

1941
	result->time = dt1 - dt2;
1942

1943
	result->month = 0;
1944 1945
	result->day = 0;

1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970
	/*
	 *	This is wrong, but removing it breaks a lot of regression tests.
	 *	For example:
	 *
	 *	test=> SET timezone = 'EST5EDT';
	 *	test=> SELECT
	 *	test-> ('2005-10-30 13:22:00-05'::timestamptz -
	 *	test(>  '2005-10-29 13:22:00-04'::timestamptz);
	 *	?column?
	 *	----------------
	 *	 1 day 01:00:00
	 *	 (1 row)
	 *
	 *	so adding that to the first timestamp gets:
	 *
	 *	 test=> SELECT
	 *	 test-> ('2005-10-29 13:22:00-04'::timestamptz +
	 *	 test(> ('2005-10-30 13:22:00-05'::timestamptz -
	 *	 test(>  '2005-10-29 13:22:00-04'::timestamptz)) at time zone 'EST';
	 *	    timezone
	 *	--------------------
	 *	2005-10-30 14:22:00
	 *	(1 row)
	 */
 	result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
B
Bruce Momjian 已提交
1971
												 IntervalPGetDatum(result)));
1972

1973
	PG_RETURN_INTERVAL_P(result);
1974 1975
}

1976 1977 1978 1979 1980
/*
 *	interval_justify_hours()
 *
 *	Adjust interval so 'time' contains less than a whole day, adding
 *	the excess to 'day'.  This is useful for
1981
 *	situations (such as non-TZ) where '1 day' = '24 hours' is valid,
1982
 *	e.g. interval subtraction and division.
1983 1984 1985 1986
 */
Datum
interval_justify_hours(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1987 1988
	Interval   *span = PG_GETARG_INTERVAL_P(0);
	Interval   *result;
1989 1990 1991 1992 1993
#ifdef HAVE_INT64_TIMESTAMP
	int64		wholeday;
#else
	double		wholeday;
#endif
1994 1995 1996

	result = (Interval *) palloc(sizeof(Interval));
	result->month = span->month;
1997
	result->day = span->day;
1998 1999 2000
	result->time = span->time;

#ifdef HAVE_INT64_TIMESTAMP
2001
	TMODULO(result->time, wholeday, USECS_PER_DAY);
2002
#else
2003
	TMODULO(result->time, wholeday, (double) SECS_PER_DAY);
2004
#endif
2005
	result->day += wholeday;	/* could overflow... */
2006

2007 2008
	PG_RETURN_INTERVAL_P(result);
}
2009

2010 2011 2012 2013 2014
/*
 *	interval_justify_days()
 *
 *	Adjust interval so 'day' contains less than 30 days, adding
 *	the excess to 'month'.
2015 2016 2017 2018
 */
Datum
interval_justify_days(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
2019 2020
	Interval   *span = PG_GETARG_INTERVAL_P(0);
	Interval   *result;
2021
	int32		wholemonth;
2022 2023

	result = (Interval *) palloc(sizeof(Interval));
2024
	result->month = span->month;
2025 2026 2027
	result->day = span->day;
	result->time = span->time;

2028 2029 2030
	wholemonth = result->day / DAYS_PER_MONTH;
	result->day -= wholemonth * DAYS_PER_MONTH;
	result->month += wholemonth;
2031 2032 2033

	PG_RETURN_INTERVAL_P(result);
}
2034

2035
/* timestamp_pl_interval()
2036
 * Add a interval to a timestamp data type.
2037
 * Note that interval has provisions for qualitative year/month and day
2038 2039 2040 2041
 *	units, so try to do the right thing with them.
 * To add a month, increment the month, and use the same day of month.
 * Then, if the next month has fewer days, set the day of month
 *	to the last day of month.
2042
 * To add a day, increment the mday, and use the same time of day.
2043 2044
 * Lastly, add in the "quantitative time".
 */
2045
Datum
2046
timestamp_pl_interval(PG_FUNCTION_ARGS)
2047
{
B
Bruce Momjian 已提交
2048
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
2049 2050
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Timestamp	result;
2051 2052 2053 2054 2055 2056 2057

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
		if (span->month != 0)
		{
B
Bruce Momjian 已提交
2058
			struct pg_tm tt,
2059
					   *tm = &tt;
2060
			fsec_t		fsec;
2061

2062
			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2063 2064 2065 2066 2067
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));

			tm->tm_mon += span->month;
2068
			if (tm->tm_mon > MONTHS_PER_YEAR)
2069
			{
2070 2071
				tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
				tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2072
			}
2073
			else if (tm->tm_mon < 1)
2074
			{
2075 2076
				tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
				tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2077
			}
2078 2079 2080 2081 2082

			/* adjust for end of month boundary problems... */
			if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
				tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);

2083
			if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2084 2085 2086
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2087 2088
		}

2089 2090 2091 2092 2093 2094
		if (span->day != 0)
		{
			struct pg_tm tt,
					   *tm = &tt;
			fsec_t		fsec;
			int			julian;
B
Bruce Momjian 已提交
2095

2096
			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2097 2098 2099 2100 2101 2102 2103 2104
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));

			/* Add days by converting to and from julian */
			julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
			j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);

2105
			if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2106 2107 2108 2109 2110 2111
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
		}

		timestamp += span->time;
2112 2113 2114 2115 2116 2117 2118
		result = timestamp;
	}

	PG_RETURN_TIMESTAMP(result);
}

Datum
2119
timestamp_mi_interval(PG_FUNCTION_ARGS)
2120
{
B
Bruce Momjian 已提交
2121
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
2122 2123 2124 2125
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Interval	tspan;

	tspan.month = -span->month;
2126
	tspan.day = -span->day;
2127 2128
	tspan.time = -span->time;

2129
	return DirectFunctionCall2(timestamp_pl_interval,
2130 2131 2132 2133 2134
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}


2135
/* timestamptz_pl_interval()
2136 2137 2138 2139 2140 2141 2142 2143 2144
 * Add a interval to a timestamp with time zone data type.
 * Note that interval has provisions for qualitative year/month
 *	units, so try to do the right thing with them.
 * To add a month, increment the month, and use the same day of month.
 * Then, if the next month has fewer days, set the day of month
 *	to the last day of month.
 * Lastly, add in the "quantitative time".
 */
Datum
2145
timestamptz_pl_interval(PG_FUNCTION_ARGS)
2146
{
2147
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2148
	Interval   *span = PG_GETARG_INTERVAL_P(1);
2149
	TimestampTz result;
2150 2151 2152
	int			tz;
	char	   *tzn;

2153 2154
	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
2155 2156 2157 2158
	else
	{
		if (span->month != 0)
		{
B
Bruce Momjian 已提交
2159
			struct pg_tm tt,
2160
					   *tm = &tt;
2161
			fsec_t		fsec;
2162

2163
			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2164 2165 2166
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2167

2168
			tm->tm_mon += span->month;
2169
			if (tm->tm_mon > MONTHS_PER_YEAR)
2170
			{
2171 2172
				tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
				tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2173
			}
2174
			else if (tm->tm_mon < 1)
2175
			{
2176 2177
				tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
				tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2178
			}
2179 2180 2181 2182 2183

			/* adjust for end of month boundary problems... */
			if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
				tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);

2184
			tz = DetermineTimeZoneOffset(tm, global_timezone);
2185

2186
			if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2187 2188 2189
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2190 2191
		}

2192 2193 2194 2195 2196 2197 2198
		if (span->day != 0)
		{
			struct pg_tm tt,
					   *tm = &tt;
			fsec_t		fsec;
			int			julian;

2199
			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2200 2201 2202 2203 2204 2205 2206 2207
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));

			/* Add days by converting to and from julian */
			julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day;
			j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);

2208
			tz = DetermineTimeZoneOffset(tm, global_timezone);
2209

2210
			if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2211 2212 2213 2214 2215 2216
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
		}

		timestamp += span->time;
2217
		result = timestamp;
2218 2219
	}

2220 2221
	PG_RETURN_TIMESTAMP(result);
}
2222

2223
Datum
2224
timestamptz_mi_interval(PG_FUNCTION_ARGS)
2225
{
2226
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2227
	Interval   *span = PG_GETARG_INTERVAL_P(1);
2228 2229
	Interval	tspan;

B
Bruce Momjian 已提交
2230
	tspan.month = -span->month;
2231
	tspan.day = -span->day;
B
Bruce Momjian 已提交
2232
	tspan.time = -span->time;
2233

2234
	return DirectFunctionCall2(timestamptz_pl_interval,
2235 2236 2237
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}
2238 2239


2240 2241
Datum
interval_um(PG_FUNCTION_ARGS)
2242
{
2243
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2244 2245
	Interval   *result;

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

2248 2249 2250
	result->time = -interval->time;
	result->day = -interval->day;
	result->month = -interval->month;
2251

2252 2253
	PG_RETURN_INTERVAL_P(result);
}
2254 2255


2256 2257
Datum
interval_smaller(PG_FUNCTION_ARGS)
2258
{
2259 2260
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
2261
	Interval   *result;
B
Bruce Momjian 已提交
2262

2263 2264 2265
	/* use interval_cmp_internal to be sure this agrees with comparisons */
	if (interval_cmp_internal(interval1, interval2) < 0)
		result = interval1;
2266
	else
2267
		result = interval2;
2268 2269
	PG_RETURN_INTERVAL_P(result);
}
2270

2271 2272
Datum
interval_larger(PG_FUNCTION_ARGS)
2273
{
2274 2275
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
2276
	Interval   *result;
B
Bruce Momjian 已提交
2277

2278 2279
	if (interval_cmp_internal(interval1, interval2) > 0)
		result = interval1;
2280
	else
2281
		result = interval2;
2282 2283
	PG_RETURN_INTERVAL_P(result);
}
2284

2285 2286
Datum
interval_pl(PG_FUNCTION_ARGS)
2287
{
2288 2289
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
2290 2291
	Interval   *result;

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

2294 2295 2296
	result->month = span1->month + span2->month;
	result->day = span1->day + span2->day;
	result->time = span1->time + span2->time;
2297

2298 2299
	PG_RETURN_INTERVAL_P(result);
}
2300

2301 2302
Datum
interval_mi(PG_FUNCTION_ARGS)
2303
{
2304 2305
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
2306 2307
	Interval   *result;

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

2310 2311 2312
	result->month = span1->month - span2->month;
	result->day = span1->day - span2->day;
	result->time = span1->time - span2->time;
2313

2314 2315
	PG_RETURN_INTERVAL_P(result);
}
2316

2317 2318
Datum
interval_mul(PG_FUNCTION_ARGS)
2319
{
2320
	Interval   *span = PG_GETARG_INTERVAL_P(0);
2321
	float8		factor = PG_GETARG_FLOAT8(1);
B
Bruce Momjian 已提交
2322 2323 2324
	double		month_remainder,
				day_remainder,
				month_remainder_days;
2325
	Interval   *result;
B
Bruce Momjian 已提交
2326

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

2329 2330 2331 2332 2333 2334
	month_remainder = span->month * factor;
	day_remainder = span->day * factor;
	result->month = (int32) month_remainder;
	result->day = (int32) day_remainder;
	month_remainder -= result->month;
	day_remainder -= result->day;
2335

2336 2337 2338 2339 2340 2341 2342 2343 2344
	/*
	 * The above correctly handles the whole-number part of the month and
	 * day products, but we have to do something with any fractional part
	 * resulting when the factor is nonintegral.  We cascade the fractions
	 * down to lower units using the conversion factors DAYS_PER_MONTH and
	 * SECS_PER_DAY.  Note we do NOT cascade up, since we are not forced to
	 * do so by the representation.  The user can choose to cascade up later,
	 * using justify_hours and/or justify_days.
	 */
2345 2346

	/* fractional months full days into days */
2347
	month_remainder_days = month_remainder * DAYS_PER_MONTH;
2348
	result->day += (int32) month_remainder_days;
2349
	/* fractional months partial days into time */
2350
	day_remainder += month_remainder_days - (int32) month_remainder_days;
2351

2352
#ifdef HAVE_INT64_TIMESTAMP
2353
	result->time = rint(span->time * factor + day_remainder * USECS_PER_DAY);
2354
#else
2355
	result->time = span->time * factor + day_remainder * SECS_PER_DAY;
2356
#endif
2357

2358
	PG_RETURN_INTERVAL_P(result);
2359
}
2360

2361 2362
Datum
mul_d_interval(PG_FUNCTION_ARGS)
2363
{
2364 2365
	/* Args are float8 and Interval *, but leave them as generic Datum */
	Datum		factor = PG_GETARG_DATUM(0);
2366
	Datum		span = PG_GETARG_DATUM(1);
2367

2368
	return DirectFunctionCall2(interval_mul, span, factor);
2369 2370 2371 2372
}

Datum
interval_div(PG_FUNCTION_ARGS)
2373
{
2374
	Interval   *span = PG_GETARG_INTERVAL_P(0);
2375
	float8		factor = PG_GETARG_FLOAT8(1);
B
Bruce Momjian 已提交
2376 2377 2378
	double		month_remainder,
				day_remainder,
				month_remainder_days;
2379
	Interval   *result;
B
Bruce Momjian 已提交
2380

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

2383
	if (factor == 0.0)
2384 2385 2386
		ereport(ERROR,
				(errcode(ERRCODE_DIVISION_BY_ZERO),
				 errmsg("division by zero")));
2387

2388 2389 2390 2391 2392 2393
	month_remainder = span->month / factor;
	day_remainder = span->day / factor;
	result->month = (int32) month_remainder;
	result->day = (int32) day_remainder;
	month_remainder -= result->month;
	day_remainder -= result->day;
2394

2395 2396 2397
	/*
	 * Handle any fractional parts the same way as in interval_mul.
	 */
2398 2399

	/* fractional months full days into days */
2400
	month_remainder_days = month_remainder * DAYS_PER_MONTH;
2401
	result->day += (int32) month_remainder_days;
2402
	/* fractional months partial days into time */
2403
	day_remainder += month_remainder_days - (int32) month_remainder_days;
2404

2405
#ifdef HAVE_INT64_TIMESTAMP
2406
	result->time = rint(span->time / factor + day_remainder * USECS_PER_DAY);
2407
#else
2408
	result->time = span->time / factor + day_remainder * SECS_PER_DAY;
2409
#endif
2410

2411
	PG_RETURN_INTERVAL_P(result);
2412
}
2413

2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435
/*
 * interval_accum and interval_avg implement the AVG(interval) aggregate.
 *
 * The transition datatype for this aggregate is a 2-element array of
 * intervals, where the first is the running sum and the second contains
 * the number of values so far in its 'time' field.  This is a bit ugly
 * but it beats inventing a specialized datatype for the purpose.
 */

Datum
interval_accum(PG_FUNCTION_ARGS)
{
	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
	Interval   *newval = PG_GETARG_INTERVAL_P(1);
	Datum	   *transdatums;
	int			ndatums;
	Interval	sumX,
				N;
	Interval   *newsum;
	ArrayType  *result;

	deconstruct_array(transarray,
2436
					  INTERVALOID, sizeof(Interval), false, 'd',
2437 2438
					  &transdatums, &ndatums);
	if (ndatums != 2)
2439
		elog(ERROR, "expected 2-element interval array");
B
Bruce Momjian 已提交
2440

2441
	/*
B
Bruce Momjian 已提交
2442 2443 2444 2445
	 * XXX memcpy, instead of just extracting a pointer, to work around buggy
	 * array code: it won't ensure proper alignment of Interval objects on
	 * machines where double requires 8-byte alignment. That should be fixed,
	 * but in the meantime...
2446
	 *
B
Bruce Momjian 已提交
2447 2448
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2449
	 */
2450 2451
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2452 2453

	newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
B
Bruce Momjian 已提交
2454 2455
												   IntervalPGetDatum(&sumX),
												 IntervalPGetDatum(newval)));
2456 2457 2458 2459 2460 2461
	N.time += 1;

	transdatums[0] = IntervalPGetDatum(newsum);
	transdatums[1] = IntervalPGetDatum(&N);

	result = construct_array(transdatums, 2,
2462
							 INTERVALOID, sizeof(Interval), false, 'd');
2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476

	PG_RETURN_ARRAYTYPE_P(result);
}

Datum
interval_avg(PG_FUNCTION_ARGS)
{
	ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
	Datum	   *transdatums;
	int			ndatums;
	Interval	sumX,
				N;

	deconstruct_array(transarray,
2477
					  INTERVALOID, sizeof(Interval), false, 'd',
2478 2479
					  &transdatums, &ndatums);
	if (ndatums != 2)
2480
		elog(ERROR, "expected 2-element interval array");
B
Bruce Momjian 已提交
2481

2482
	/*
B
Bruce Momjian 已提交
2483 2484 2485 2486
	 * XXX memcpy, instead of just extracting a pointer, to work around buggy
	 * array code: it won't ensure proper alignment of Interval objects on
	 * machines where double requires 8-byte alignment. That should be fixed,
	 * but in the meantime...
2487
	 *
B
Bruce Momjian 已提交
2488 2489
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2490
	 */
2491 2492
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503

	/* SQL92 defines AVG of no values to be NULL */
	if (N.time == 0)
		PG_RETURN_NULL();

	return DirectFunctionCall2(interval_div,
							   IntervalPGetDatum(&sumX),
							   Float8GetDatum(N.time));
}


2504 2505 2506 2507 2508 2509
/* timestamp_age()
 * Calculate time difference while retaining year/month fields.
 * Note that this does not result in an accurate absolute time span
 *	since year and month are out of context once the arithmetic
 *	is done.
 */
2510 2511
Datum
timestamp_age(PG_FUNCTION_ARGS)
2512
{
2513 2514
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
2515
	Interval   *result;
2516
	fsec_t		fsec,
2517 2518
				fsec1,
				fsec2;
B
Bruce Momjian 已提交
2519
	struct pg_tm tt,
2520
			   *tm = &tt;
B
Bruce Momjian 已提交
2521
	struct pg_tm tt1,
2522
			   *tm1 = &tt1;
B
Bruce Momjian 已提交
2523
	struct pg_tm tt2,
2524 2525
			   *tm2 = &tt2;

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

2528 2529
	if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 &&
		timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0)
2530 2531
	{
		fsec = (fsec1 - fsec2);
2532 2533 2534 2535 2536 2537
		tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
		tm->tm_min = tm1->tm_min - tm2->tm_min;
		tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
		tm->tm_mday = tm1->tm_mday - tm2->tm_mday;
		tm->tm_mon = tm1->tm_mon - tm2->tm_mon;
		tm->tm_year = tm1->tm_year - tm2->tm_year;
2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550

		/* flip sign if necessary... */
		if (dt1 < dt2)
		{
			fsec = -fsec;
			tm->tm_sec = -tm->tm_sec;
			tm->tm_min = -tm->tm_min;
			tm->tm_hour = -tm->tm_hour;
			tm->tm_mday = -tm->tm_mday;
			tm->tm_mon = -tm->tm_mon;
			tm->tm_year = -tm->tm_year;
		}

2551
		while (tm->tm_sec < 0)
2552
		{
2553
			tm->tm_sec += SECS_PER_MINUTE;
2554 2555 2556
			tm->tm_min--;
		}

2557
		while (tm->tm_min < 0)
2558
		{
B
Bruce Momjian 已提交
2559
			tm->tm_min += MINS_PER_HOUR;
2560 2561 2562
			tm->tm_hour--;
		}

2563
		while (tm->tm_hour < 0)
2564
		{
2565
			tm->tm_hour += HOURS_PER_DAY;
2566 2567 2568
			tm->tm_mday--;
		}

2569
		while (tm->tm_mday < 0)
2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582
		{
			if (dt1 < dt2)
			{
				tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
				tm->tm_mon--;
			}
			else
			{
				tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
				tm->tm_mon--;
			}
		}

2583
		while (tm->tm_mon < 0)
2584
		{
2585
			tm->tm_mon += MONTHS_PER_YEAR;
2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601
			tm->tm_year--;
		}

		/* recover sign if necessary... */
		if (dt1 < dt2)
		{
			fsec = -fsec;
			tm->tm_sec = -tm->tm_sec;
			tm->tm_min = -tm->tm_min;
			tm->tm_hour = -tm->tm_hour;
			tm->tm_mday = -tm->tm_mday;
			tm->tm_mon = -tm->tm_mon;
			tm->tm_year = -tm->tm_year;
		}

		if (tm2interval(tm, fsec, result) != 0)
2602 2603 2604
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("interval out of range")));
2605 2606
	}
	else
2607 2608 2609
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2610

2611 2612
	PG_RETURN_INTERVAL_P(result);
}
2613 2614


2615 2616 2617 2618 2619
/* timestamptz_age()
 * Calculate time difference while retaining year/month fields.
 * Note that this does not result in an accurate absolute time span
 *	since year and month are out of context once the arithmetic
 *	is done.
2620
 */
2621
Datum
2622
timestamptz_age(PG_FUNCTION_ARGS)
2623
{
2624 2625
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2626
	Interval   *result;
2627
	fsec_t		fsec,
2628 2629
				fsec1,
				fsec2;
B
Bruce Momjian 已提交
2630
	struct pg_tm tt,
2631
			   *tm = &tt;
B
Bruce Momjian 已提交
2632
	struct pg_tm tt1,
2633
			   *tm1 = &tt1;
B
Bruce Momjian 已提交
2634
	struct pg_tm tt2,
2635
			   *tm2 = &tt2;
2636 2637 2638
	int			tz1;
	int			tz2;
	char	   *tzn;
2639 2640 2641

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

2642 2643
	if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn, NULL) == 0 &&
		timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn, NULL) == 0)
2644
	{
2645 2646 2647 2648 2649 2650 2651
		fsec = fsec1 - fsec2;
		tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
		tm->tm_min = tm1->tm_min - tm2->tm_min;
		tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
		tm->tm_mday = tm1->tm_mday - tm2->tm_mday;
		tm->tm_mon = tm1->tm_mon - tm2->tm_mon;
		tm->tm_year = tm1->tm_year - tm2->tm_year;
2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664

		/* flip sign if necessary... */
		if (dt1 < dt2)
		{
			fsec = -fsec;
			tm->tm_sec = -tm->tm_sec;
			tm->tm_min = -tm->tm_min;
			tm->tm_hour = -tm->tm_hour;
			tm->tm_mday = -tm->tm_mday;
			tm->tm_mon = -tm->tm_mon;
			tm->tm_year = -tm->tm_year;
		}

2665
		while (tm->tm_sec < 0)
2666
		{
2667
			tm->tm_sec += SECS_PER_MINUTE;
2668 2669 2670
			tm->tm_min--;
		}

2671
		while (tm->tm_min < 0)
2672
		{
B
Bruce Momjian 已提交
2673
			tm->tm_min += MINS_PER_HOUR;
2674 2675 2676
			tm->tm_hour--;
		}

2677
		while (tm->tm_hour < 0)
2678
		{
2679
			tm->tm_hour += HOURS_PER_DAY;
2680 2681 2682
			tm->tm_mday--;
		}

2683
		while (tm->tm_mday < 0)
2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696
		{
			if (dt1 < dt2)
			{
				tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
				tm->tm_mon--;
			}
			else
			{
				tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
				tm->tm_mon--;
			}
		}

2697
		while (tm->tm_mon < 0)
2698
		{
2699
			tm->tm_mon += MONTHS_PER_YEAR;
2700 2701 2702
			tm->tm_year--;
		}

2703 2704 2705 2706
		/*
		 * Note: we deliberately ignore any difference between tz1 and tz2.
		 */

2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719
		/* recover sign if necessary... */
		if (dt1 < dt2)
		{
			fsec = -fsec;
			tm->tm_sec = -tm->tm_sec;
			tm->tm_min = -tm->tm_min;
			tm->tm_hour = -tm->tm_hour;
			tm->tm_mday = -tm->tm_mday;
			tm->tm_mon = -tm->tm_mon;
			tm->tm_year = -tm->tm_year;
		}

		if (tm2interval(tm, fsec, result) != 0)
2720 2721 2722
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("interval out of range")));
2723 2724
	}
	else
2725 2726 2727
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744

	PG_RETURN_INTERVAL_P(result);
}


/*----------------------------------------------------------
 *	Conversion operators.
 *---------------------------------------------------------*/


/* timestamp_text()
 * Convert timestamp to text data type.
 */
Datum
timestamp_text(PG_FUNCTION_ARGS)
{
	/* Input is a Timestamp, but may as well leave it in Datum form */
B
Bruce Momjian 已提交
2745
	Datum		timestamp = PG_GETARG_DATUM(0);
2746 2747 2748
	text	   *result;
	char	   *str;
	int			len;
2749

2750
	str = DatumGetCString(DirectFunctionCall1(timestamp_out, timestamp));
2751 2752 2753 2754 2755

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2756
	VARATT_SIZEP(result) = len;
2757
	memmove(VARDATA(result), str, len - VARHDRSZ);
2758 2759 2760

	pfree(str);

2761 2762
	PG_RETURN_TEXT_P(result);
}
2763 2764 2765 2766 2767 2768 2769


/* text_timestamp()
 * Convert text string to timestamp.
 * Text type is not null terminated, so use temporary string
 *	then call the standard input routine.
 */
2770 2771
Datum
text_timestamp(PG_FUNCTION_ARGS)
2772
{
2773
	text	   *str = PG_GETARG_TEXT_P(0);
2774 2775 2776 2777 2778
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2779
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2780 2781
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2782
				 errmsg("invalid input syntax for type timestamp: \"%s\"",
2783
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2784
												   PointerGetDatum(str))))));
2785 2786 2787

	sp = VARDATA(str);
	dp = dstr;
2788
	for (i = 0; i < VARSIZE(str) - VARHDRSZ; i++)
2789 2790 2791
		*dp++ = *sp++;
	*dp = '\0';

2792 2793 2794 2795
	return DirectFunctionCall3(timestamp_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2796
}
2797 2798


2799 2800 2801 2802 2803 2804 2805
/* timestamptz_text()
 * Convert timestamp with time zone to text data type.
 */
Datum
timestamptz_text(PG_FUNCTION_ARGS)
{
	/* Input is a Timestamp, but may as well leave it in Datum form */
B
Bruce Momjian 已提交
2806
	Datum		timestamp = PG_GETARG_DATUM(0);
2807 2808 2809 2810 2811 2812
	text	   *result;
	char	   *str;
	int			len;

	str = DatumGetCString(DirectFunctionCall1(timestamptz_out, timestamp));

2813
	len = strlen(str) + VARHDRSZ;
2814 2815 2816 2817

	result = palloc(len);

	VARATT_SIZEP(result) = len;
2818
	memmove(VARDATA(result), str, len - VARHDRSZ);
2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839

	pfree(str);

	PG_RETURN_TEXT_P(result);
}

/* text_timestamptz()
 * Convert text string to timestamp with time zone.
 * Text type is not null terminated, so use temporary string
 *	then call the standard input routine.
 */
Datum
text_timestamptz(PG_FUNCTION_ARGS)
{
	text	   *str = PG_GETARG_TEXT_P(0);
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2840 2841
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2842
				 errmsg("invalid input syntax for type timestamp with time zone: \"%s\"",
2843
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2844
												   PointerGetDatum(str))))));
2845 2846 2847

	sp = VARDATA(str);
	dp = dstr;
2848
	for (i = 0; i < VARSIZE(str) - VARHDRSZ; i++)
2849 2850 2851
		*dp++ = *sp++;
	*dp = '\0';

2852 2853 2854 2855
	return DirectFunctionCall3(timestamptz_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2856 2857 2858
}


2859 2860 2861
/* interval_text()
 * Convert interval to text data type.
 */
2862 2863
Datum
interval_text(PG_FUNCTION_ARGS)
2864
{
2865
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2866 2867 2868 2869
	text	   *result;
	char	   *str;
	int			len;

2870
	str = DatumGetCString(DirectFunctionCall1(interval_out,
B
Bruce Momjian 已提交
2871
											  IntervalPGetDatum(interval)));
2872

2873
	len = strlen(str) + VARHDRSZ;
2874 2875 2876

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2877
	VARATT_SIZEP(result) = len;
2878
	memmove(VARDATA(result), str, len - VARHDRSZ);
2879 2880 2881

	pfree(str);

2882 2883
	PG_RETURN_TEXT_P(result);
}
2884 2885 2886 2887 2888 2889 2890


/* text_interval()
 * Convert text string to interval.
 * Text type may not be null terminated, so copy to temporary string
 *	then call the standard input routine.
 */
2891 2892
Datum
text_interval(PG_FUNCTION_ARGS)
2893
{
2894
	text	   *str = PG_GETARG_TEXT_P(0);
2895 2896 2897 2898 2899
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2900
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2901 2902
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2903
				 errmsg("invalid input syntax for type interval: \"%s\"",
2904
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2905
												   PointerGetDatum(str))))));
2906

2907 2908 2909 2910 2911 2912
	sp = VARDATA(str);
	dp = dstr;
	for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
		*dp++ = *sp++;
	*dp = '\0';

2913 2914 2915 2916
	return DirectFunctionCall3(interval_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2917
}
2918 2919

/* timestamp_trunc()
2920
 * Truncate timestamp to specified units.
2921
 */
2922 2923
Datum
timestamp_trunc(PG_FUNCTION_ARGS)
2924
{
2925
	text	   *units = PG_GETARG_TEXT_P(0);
B
Bruce Momjian 已提交
2926
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
2927
	Timestamp	result;
2928 2929
	int			type,
				val;
2930
	char	   *lowunits;
2931
	fsec_t		fsec;
B
Bruce Momjian 已提交
2932
	struct pg_tm tt,
2933 2934
			   *tm = &tt;

2935 2936
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMP(timestamp);
2937

2938 2939 2940
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
2941 2942 2943

	type = DecodeUnits(0, lowunits, &val);

2944
	if (type == UNITS)
2945
	{
2946
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2947 2948 2949 2950
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

2951 2952
		switch (val)
		{
2953
			case DTK_WEEK:
B
Bruce Momjian 已提交
2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974
				{
					int			woy;

					woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);

					/*
					 * If it is week 52/53 and the month is January, then the
					 * week must belong to the previous year. Also, some
					 * December dates belong to the next year.
					 */
					if (woy >= 52 && tm->tm_mon == 1)
						--tm->tm_year;
					if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
						++tm->tm_year;
					isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
					tm->tm_hour = 0;
					tm->tm_min = 0;
					tm->tm_sec = 0;
					fsec = 0;
					break;
				}
2975
			case DTK_MILLENNIUM:
2976 2977
				/* see comments in timestamptz_trunc */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
2978
					tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
2979
				else
B
Bruce Momjian 已提交
2980
					tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
2981
			case DTK_CENTURY:
2982 2983
				/* see comments in timestamptz_trunc */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
2984
					tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
2985
				else
B
Bruce Momjian 已提交
2986
					tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
2987
			case DTK_DECADE:
2988 2989 2990 2991 2992 2993
				/* see comments in timestamptz_trunc */
				if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
				{
					if (tm->tm_year > 0)
						tm->tm_year = (tm->tm_year / 10) * 10;
					else
B
Bruce Momjian 已提交
2994
						tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
2995
				}
2996 2997 2998
			case DTK_YEAR:
				tm->tm_mon = 1;
			case DTK_QUARTER:
2999
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012
			case DTK_MONTH:
				tm->tm_mday = 1;
			case DTK_DAY:
				tm->tm_hour = 0;
			case DTK_HOUR:
				tm->tm_min = 0;
			case DTK_MINUTE:
				tm->tm_sec = 0;
			case DTK_SECOND:
				fsec = 0;
				break;

			case DTK_MILLISEC:
3013
#ifdef HAVE_INT64_TIMESTAMP
3014
				fsec = (fsec / 1000) * 1000;
3015
#else
3016
				fsec = rint(fsec * 1000) / 1000;
3017
#endif
3018 3019 3020
				break;

			case DTK_MICROSEC:
3021
#ifndef HAVE_INT64_TIMESTAMP
3022
				fsec = rint(fsec * 1000000) / 1000000;
3023
#endif
3024 3025 3026
				break;

			default:
3027 3028 3029 3030
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
								lowunits)));
3031 3032 3033 3034
				result = 0;
		}

		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
3035 3036 3037
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3038
	}
3039 3040
	else
	{
3041 3042 3043 3044
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("timestamp units \"%s\" not recognized",
						lowunits)));
3045 3046
		result = 0;
	}
3047

3048 3049
	PG_RETURN_TIMESTAMP(result);
}
3050

3051 3052 3053 3054 3055 3056 3057
/* timestamptz_trunc()
 * Truncate timestamp to specified units.
 */
Datum
timestamptz_trunc(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
3058
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3059
	TimestampTz result;
3060 3061 3062
	int			tz;
	int			type,
				val;
3063
	bool		redotz = false;
3064
	char	   *lowunits;
3065
	fsec_t		fsec;
3066
	char	   *tzn;
B
Bruce Momjian 已提交
3067
	struct pg_tm tt,
3068
			   *tm = &tt;
3069

3070 3071
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMPTZ(timestamp);
3072

3073 3074 3075 3076 3077 3078
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

	type = DecodeUnits(0, lowunits, &val);

3079
	if (type == UNITS)
3080
	{
3081
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3082 3083 3084 3085
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3086
		switch (val)
3087
		{
3088
			case DTK_WEEK:
B
Bruce Momjian 已提交
3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110
				{
					int			woy;

					woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);

					/*
					 * If it is week 52/53 and the month is January, then the
					 * week must belong to the previous year. Also, some
					 * December dates belong to the next year.
					 */
					if (woy >= 52 && tm->tm_mon == 1)
						--tm->tm_year;
					if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
						++tm->tm_year;
					isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
					tm->tm_hour = 0;
					tm->tm_min = 0;
					tm->tm_sec = 0;
					fsec = 0;
					redotz = true;
					break;
				}
3111
				/* one may consider DTK_THOUSAND and DTK_HUNDRED... */
3112
			case DTK_MILLENNIUM:
B
Bruce Momjian 已提交
3113 3114 3115

				/*
				 * truncating to the millennium? what is this supposed to
B
Bruce Momjian 已提交
3116 3117
				 * mean? let us put the first year of the millennium... i.e.
				 * -1000, 1, 1001, 2001...
3118 3119
				 */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3120
					tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
3121
				else
B
Bruce Momjian 已提交
3122
					tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
3123
				/* FALL THRU */
3124
			case DTK_CENTURY:
3125 3126
				/* truncating to the century? as above: -100, 1, 101... */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3127
					tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
3128
				else
B
Bruce Momjian 已提交
3129
					tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
3130
				/* FALL THRU */
3131
			case DTK_DECADE:
B
Bruce Momjian 已提交
3132 3133

				/*
B
Bruce Momjian 已提交
3134 3135
				 * truncating to the decade? first year of the decade. must
				 * not be applied if year was truncated before!
3136 3137 3138 3139 3140 3141
				 */
				if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
				{
					if (tm->tm_year > 0)
						tm->tm_year = (tm->tm_year / 10) * 10;
					else
B
Bruce Momjian 已提交
3142
						tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
3143
				}
3144
				/* FALL THRU */
3145 3146
			case DTK_YEAR:
				tm->tm_mon = 1;
3147
				/* FALL THRU */
3148
			case DTK_QUARTER:
3149
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
3150
				/* FALL THRU */
3151 3152
			case DTK_MONTH:
				tm->tm_mday = 1;
3153
				/* FALL THRU */
3154 3155
			case DTK_DAY:
				tm->tm_hour = 0;
3156 3157
				redotz = true;	/* for all cases >= DAY */
				/* FALL THRU */
3158 3159
			case DTK_HOUR:
				tm->tm_min = 0;
3160
				/* FALL THRU */
3161 3162
			case DTK_MINUTE:
				tm->tm_sec = 0;
3163
				/* FALL THRU */
3164 3165 3166 3167 3168
			case DTK_SECOND:
				fsec = 0;
				break;

			case DTK_MILLISEC:
3169
#ifdef HAVE_INT64_TIMESTAMP
3170
				fsec = (fsec / 1000) * 1000;
3171
#else
3172
				fsec = rint(fsec * 1000) / 1000;
3173
#endif
3174 3175
				break;
			case DTK_MICROSEC:
3176
#ifndef HAVE_INT64_TIMESTAMP
3177
				fsec = rint(fsec * 1000000) / 1000000;
3178
#endif
3179 3180 3181
				break;

			default:
3182 3183
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
3184 3185
						 errmsg("timestamp with time zone units \"%s\" not "
								"supported", lowunits)));
3186
				result = 0;
3187
		}
3188

3189
		if (redotz)
3190
			tz = DetermineTimeZoneOffset(tm, global_timezone);
3191 3192

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
3193 3194 3195
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3196 3197 3198
	}
	else
	{
3199 3200
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3201 3202
			   errmsg("timestamp with time zone units \"%s\" not recognized",
					  lowunits)));
3203
		result = 0;
3204 3205
	}

3206
	PG_RETURN_TIMESTAMPTZ(result);
3207
}
3208 3209 3210 3211

/* interval_trunc()
 * Extract specified field from interval.
 */
3212 3213
Datum
interval_trunc(PG_FUNCTION_ARGS)
3214
{
3215 3216
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
3217 3218 3219
	Interval   *result;
	int			type,
				val;
3220
	char	   *lowunits;
3221
	fsec_t		fsec;
B
Bruce Momjian 已提交
3222
	struct pg_tm tt,
3223 3224
			   *tm = &tt;

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

3227 3228 3229
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
3230 3231 3232

	type = DecodeUnits(0, lowunits, &val);

3233
	if (type == UNITS)
3234 3235 3236 3237 3238
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
B
Bruce Momjian 已提交
3239
					/* fall through */
3240
				case DTK_MILLENNIUM:
3241
					/* caution: C division may have negative remainder */
3242 3243
					tm->tm_year = (tm->tm_year / 1000) * 1000;
				case DTK_CENTURY:
3244
					/* caution: C division may have negative remainder */
3245 3246
					tm->tm_year = (tm->tm_year / 100) * 100;
				case DTK_DECADE:
3247
					/* caution: C division may have negative remainder */
3248 3249 3250 3251
					tm->tm_year = (tm->tm_year / 10) * 10;
				case DTK_YEAR:
					tm->tm_mon = 0;
				case DTK_QUARTER:
3252
					tm->tm_mon = 3 * (tm->tm_mon / 3);
3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265
				case DTK_MONTH:
					tm->tm_mday = 0;
				case DTK_DAY:
					tm->tm_hour = 0;
				case DTK_HOUR:
					tm->tm_min = 0;
				case DTK_MINUTE:
					tm->tm_sec = 0;
				case DTK_SECOND:
					fsec = 0;
					break;

				case DTK_MILLISEC:
3266
#ifdef HAVE_INT64_TIMESTAMP
3267
					fsec = (fsec / 1000) * 1000;
3268
#else
3269
					fsec = rint(fsec * 1000) / 1000;
3270
#endif
3271 3272
					break;
				case DTK_MICROSEC:
3273
#ifndef HAVE_INT64_TIMESTAMP
3274
					fsec = rint(fsec * 1000000) / 1000000;
3275
#endif
3276 3277 3278
					break;

				default:
3279 3280 3281
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("interval units \"%s\" not supported",
B
Bruce Momjian 已提交
3282
									lowunits)));
3283 3284 3285
			}

			if (tm2interval(tm, fsec, result) != 0)
3286 3287 3288
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("interval out of range")));
3289 3290
		}
		else
3291
			elog(ERROR, "could not convert interval to tm");
3292 3293 3294
	}
	else
	{
3295 3296 3297
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval units \"%s\" not recognized",
B
Bruce Momjian 已提交
3298
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
3299
												 PointerGetDatum(units))))));
3300
		*result = *interval;
3301 3302
	}

3303 3304
	PG_RETURN_INTERVAL_P(result);
}
3305

B
Bruce Momjian 已提交
3306
/* isoweek2date()
3307
 * Convert ISO week of year number to date.
3308
 * The year field must be specified with the ISO year!
3309
 * karel 2000/08/07
B
Bruce Momjian 已提交
3310 3311
 */
void
B
Bruce Momjian 已提交
3312
isoweek2date(int woy, int *year, int *mon, int *mday)
B
Bruce Momjian 已提交
3313
{
B
Bruce Momjian 已提交
3314 3315 3316 3317
	int			day0,
				day4,
				dayn;

B
Bruce Momjian 已提交
3318
	if (!*year)
3319 3320
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3321
		   errmsg("cannot calculate week number without year information")));
B
Bruce Momjian 已提交
3322 3323 3324

	/* fourth day of current year */
	day4 = date2j(*year, 1, 4);
B
Bruce Momjian 已提交
3325

B
Bruce Momjian 已提交
3326
	/* day0 == offset to first day of week (Monday) */
3327
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3328 3329

	dayn = ((woy - 1) * 7) + (day4 - day0);
B
Bruce Momjian 已提交
3330

B
Bruce Momjian 已提交
3331 3332 3333 3334
	j2date(dayn, year, mon, mday);
}

/* date2isoweek()
B
Bruce Momjian 已提交
3335
 *
B
Bruce Momjian 已提交
3336 3337 3338
 *	Returns ISO week number of year.
 */
int
B
Bruce Momjian 已提交
3339
date2isoweek(int year, int mon, int mday)
B
Bruce Momjian 已提交
3340
{
B
Bruce Momjian 已提交
3341 3342 3343 3344 3345 3346
	float8		result;
	int			day0,
				day4,
				dayn;

	/* current day */
B
Bruce Momjian 已提交
3347
	dayn = date2j(year, mon, mday);
B
Bruce Momjian 已提交
3348

B
Bruce Momjian 已提交
3349 3350
	/* fourth day of current year */
	day4 = date2j(year, 1, 4);
B
Bruce Momjian 已提交
3351

B
Bruce Momjian 已提交
3352
	/* day0 == offset to first day of week (Monday) */
3353
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3354 3355

	/*
B
Bruce Momjian 已提交
3356 3357
	 * We need the first week containing a Thursday, otherwise this day falls
	 * into the previous year for purposes of counting weeks
B
Bruce Momjian 已提交
3358
	 */
3359
	if (dayn < day4 - day0)
B
Bruce Momjian 已提交
3360
	{
3361
		day4 = date2j(year - 1, 1, 4);
B
Bruce Momjian 已提交
3362

B
Bruce Momjian 已提交
3363
		/* day0 == offset to first day of week (Monday) */
3364
		day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3365
	}
B
Bruce Momjian 已提交
3366

3367
	result = (dayn - (day4 - day0)) / 7 + 1;
B
Bruce Momjian 已提交
3368 3369

	/*
B
Bruce Momjian 已提交
3370 3371
	 * Sometimes the last few days in a year will fall into the first week of
	 * the next year, so check for this.
B
Bruce Momjian 已提交
3372
	 */
3373
	if (result >= 52)
B
Bruce Momjian 已提交
3374
	{
3375
		day4 = date2j(year + 1, 1, 4);
B
Bruce Momjian 已提交
3376

B
Bruce Momjian 已提交
3377
		/* day0 == offset to first day of week (Monday) */
3378
		day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3379

3380 3381
		if (dayn >= day4 - day0)
			result = (dayn - (day4 - day0)) / 7 + 1;
B
Bruce Momjian 已提交
3382
	}
3383

B
Bruce Momjian 已提交
3384
	return (int) result;
B
Bruce Momjian 已提交
3385 3386 3387
}


3388 3389 3390 3391 3392 3393 3394
/* date2isoyear()
 *
 *	Returns ISO 8601 year number.
 */
int
date2isoyear(int year, int mon, int mday)
{
B
Bruce Momjian 已提交
3395 3396 3397 3398
	float8		result;
	int			day0,
				day4,
				dayn;
3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409

	/* current day */
	dayn = date2j(year, mon, mday);

	/* fourth day of current year */
	day4 = date2j(year, 1, 4);

	/* day0 == offset to first day of week (Monday) */
	day0 = j2day(day4 - 1);

	/*
B
Bruce Momjian 已提交
3410 3411
	 * We need the first week containing a Thursday, otherwise this day falls
	 * into the previous year for purposes of counting weeks
3412
	 */
3413
	if (dayn < day4 - day0)
3414 3415 3416 3417 3418 3419 3420 3421 3422
	{
		day4 = date2j(year - 1, 1, 4);

		/* day0 == offset to first day of week (Monday) */
		day0 = j2day(day4 - 1);

		year--;
	}

3423
	result = (dayn - (day4 - day0)) / 7 + 1;
3424 3425

	/*
B
Bruce Momjian 已提交
3426 3427
	 * Sometimes the last few days in a year will fall into the first week of
	 * the next year, so check for this.
3428
	 */
3429
	if (result >= 52)
3430 3431 3432 3433 3434 3435
	{
		day4 = date2j(year + 1, 1, 4);

		/* day0 == offset to first day of week (Monday) */
		day0 = j2day(day4 - 1);

3436
		if (dayn >= day4 - day0)
3437 3438 3439 3440 3441 3442 3443
			year++;
	}

	return year;
}


3444 3445 3446
/* timestamp_part()
 * Extract specified field from timestamp.
 */
3447 3448
Datum
timestamp_part(PG_FUNCTION_ARGS)
3449
{
3450
	text	   *units = PG_GETARG_TEXT_P(0);
B
Bruce Momjian 已提交
3451
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
3452
	float8		result;
3453 3454
	int			type,
				val;
3455
	char	   *lowunits;
3456
	fsec_t		fsec;
B
Bruce Momjian 已提交
3457
	struct pg_tm tt,
3458 3459
			   *tm = &tt;

3460
	if (TIMESTAMP_NOT_FINITE(timestamp))
3461
	{
3462 3463 3464
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3465

3466 3467 3468 3469 3470 3471 3472 3473
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3474
	if (type == UNITS)
3475
	{
3476
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3477 3478 3479 3480
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3481
		switch (val)
3482
		{
3483
			case DTK_MICROSEC:
3484
#ifdef HAVE_INT64_TIMESTAMP
3485
				result = tm->tm_sec * 1000000.0 + fsec;
3486
#else
3487
				result = (tm->tm_sec + fsec) * 1000000;
3488
#endif
3489 3490 3491
				break;

			case DTK_MILLISEC:
3492
#ifdef HAVE_INT64_TIMESTAMP
3493
				result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3494
#else
3495
				result = (tm->tm_sec + fsec) * 1000;
3496
#endif
3497 3498 3499
				break;

			case DTK_SECOND:
3500
#ifdef HAVE_INT64_TIMESTAMP
3501
				result = tm->tm_sec + fsec / 1000000.0;
3502
#else
3503
				result = tm->tm_sec + fsec;
3504
#endif
3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523
				break;

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

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

			case DTK_DAY:
				result = tm->tm_mday;
				break;

			case DTK_MONTH:
				result = tm->tm_mon;
				break;

			case DTK_QUARTER:
3524
				result = (tm->tm_mon - 1) / 3 + 1;
3525 3526 3527 3528 3529 3530 3531
				break;

			case DTK_WEEK:
				result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
				break;

			case DTK_YEAR:
3532 3533 3534
				if (tm->tm_year > 0)
					result = tm->tm_year;
				else
B
Bruce Momjian 已提交
3535 3536
					/* there is no year 0, just 1 BC and 1 AD */
					result = tm->tm_year - 1;
3537 3538 3539
				break;

			case DTK_DECADE:
B
Bruce Momjian 已提交
3540 3541

				/*
B
Bruce Momjian 已提交
3542 3543 3544
				 * what is a decade wrt dates? let us assume that decade 199
				 * is 1990 thru 1999... decade 0 starts on year 1 BC, and -1
				 * is 11 BC thru 2 BC...
3545
				 */
B
Bruce Momjian 已提交
3546
				if (tm->tm_year >= 0)
3547
					result = tm->tm_year / 10;
3548
				else
B
Bruce Momjian 已提交
3549
					result = -((8 - (tm->tm_year - 1)) / 10);
3550 3551 3552
				break;

			case DTK_CENTURY:
B
Bruce Momjian 已提交
3553

3554 3555 3556 3557 3558
				/* ----
				 * centuries AD, c>0: year in [ (c-1)* 100 + 1 : c*100 ]
				 * centuries BC, c<0: year in [ c*100 : (c+1) * 100 - 1]
				 * there is no number 0 century.
				 * ----
3559 3560
				 */
				if (tm->tm_year > 0)
3561
					result = (tm->tm_year + 99) / 100;
3562
				else
3563
					/* caution: C division may have negative remainder */
B
Bruce Momjian 已提交
3564
					result = -((99 - (tm->tm_year - 1)) / 100);
3565 3566 3567
				break;

			case DTK_MILLENNIUM:
3568 3569
				/* see comments above. */
				if (tm->tm_year > 0)
3570
					result = (tm->tm_year + 999) / 1000;
3571
				else
B
Bruce Momjian 已提交
3572
					result = -((999 - (tm->tm_year - 1)) / 1000);
3573 3574
				break;

3575 3576
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3577
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3578
				result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
B
Bruce Momjian 已提交
3579
					tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY;
3580
#else
B
Bruce Momjian 已提交
3581
				result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
B
Bruce Momjian 已提交
3582
						   tm->tm_sec + fsec) / (double) SECS_PER_DAY;
3583
#endif
3584 3585
				break;

3586 3587 3588
			case DTK_TZ:
			case DTK_TZ_MINUTE:
			case DTK_TZ_HOUR:
3589
			default:
3590 3591 3592
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
B
Bruce Momjian 已提交
3593
								lowunits)));
3594 3595 3596 3597 3598 3599 3600 3601
				result = 0;
		}
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
B
Bruce Momjian 已提交
3602 3603 3604
				{
					int			tz;
					TimestampTz timestamptz;
3605

B
Bruce Momjian 已提交
3606
					/*
B
Bruce Momjian 已提交
3607
					 * convert to timestamptz to produce consistent results
B
Bruce Momjian 已提交
3608
					 */
3609
					if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
B
Bruce Momjian 已提交
3610
						ereport(ERROR,
B
Bruce Momjian 已提交
3611 3612
								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
								 errmsg("timestamp out of range")));
3613

3614
					tz = DetermineTimeZoneOffset(tm, global_timezone);
3615

B
Bruce Momjian 已提交
3616 3617
					if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
						ereport(ERROR,
B
Bruce Momjian 已提交
3618 3619
								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
								 errmsg("timestamp out of range")));
3620

3621
#ifdef HAVE_INT64_TIMESTAMP
3622
					result = (timestamptz - SetEpochTimestamp()) / 1000000.0;
3623
#else
B
Bruce Momjian 已提交
3624
					result = timestamptz - SetEpochTimestamp();
3625
#endif
B
Bruce Momjian 已提交
3626 3627
					break;
				}
3628
			case DTK_DOW:
3629
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3630 3631 3632
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3633 3634
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3635

3636
			case DTK_DOY:
3637
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3638 3639 3640
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3641 3642 3643
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3644

3645
			default:
3646 3647 3648
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
B
Bruce Momjian 已提交
3649
								lowunits)));
3650 3651
				result = 0;
		}
3652

3653 3654 3655
	}
	else
	{
3656 3657
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3658
				 errmsg("timestamp units \"%s\" not recognized", lowunits)));
3659 3660
		result = 0;
	}
3661

3662 3663
	PG_RETURN_FLOAT8(result);
}
3664

3665 3666 3667 3668 3669 3670 3671
/* timestamptz_part()
 * Extract specified field from timestamp with time zone.
 */
Datum
timestamptz_part(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
3672
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3673 3674 3675 3676
	float8		result;
	int			tz;
	int			type,
				val;
3677
	char	   *lowunits;
3678
	double		dummy;
3679
	fsec_t		fsec;
3680
	char	   *tzn;
B
Bruce Momjian 已提交
3681
	struct pg_tm tt,
3682
			   *tm = &tt;
3683

3684 3685 3686 3687 3688
	if (TIMESTAMP_NOT_FINITE(timestamp))
	{
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3689

3690 3691 3692 3693 3694 3695 3696 3697
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3698
	if (type == UNITS)
3699
	{
3700
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3701 3702 3703 3704
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3705
		switch (val)
3706
		{
3707
			case DTK_TZ:
3708
				result = -tz;
3709 3710 3711
				break;

			case DTK_TZ_MINUTE:
3712
				result = -tz;
B
Bruce Momjian 已提交
3713
				result /= MINS_PER_HOUR;
B
Bruce Momjian 已提交
3714
				FMODULO(result, dummy, (double) MINS_PER_HOUR);
3715 3716 3717
				break;

			case DTK_TZ_HOUR:
3718
				dummy = -tz;
B
Bruce Momjian 已提交
3719
				FMODULO(dummy, result, (double) SECS_PER_HOUR);
3720 3721 3722
				break;

			case DTK_MICROSEC:
3723
#ifdef HAVE_INT64_TIMESTAMP
3724
				result = tm->tm_sec * 1000000.0 + fsec;
3725
#else
3726
				result = (tm->tm_sec + fsec) * 1000000;
3727
#endif
3728 3729 3730
				break;

			case DTK_MILLISEC:
3731
#ifdef HAVE_INT64_TIMESTAMP
3732
				result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3733
#else
3734
				result = (tm->tm_sec + fsec) * 1000;
3735
#endif
3736 3737 3738
				break;

			case DTK_SECOND:
3739
#ifdef HAVE_INT64_TIMESTAMP
3740
				result = tm->tm_sec + fsec / 1000000.0;
3741
#else
3742
				result = tm->tm_sec + fsec;
3743
#endif
3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762
				break;

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

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

			case DTK_DAY:
				result = tm->tm_mday;
				break;

			case DTK_MONTH:
				result = tm->tm_mon;
				break;

			case DTK_QUARTER:
3763
				result = (tm->tm_mon - 1) / 3 + 1;
3764 3765 3766 3767 3768 3769 3770
				break;

			case DTK_WEEK:
				result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
				break;

			case DTK_YEAR:
3771 3772 3773 3774 3775
				if (tm->tm_year > 0)
					result = tm->tm_year;
				else
					/* there is no year 0, just 1 BC and 1 AD */
					result = tm->tm_year - 1;
3776 3777 3778
				break;

			case DTK_DECADE:
3779
				/* see comments in timestamp_part */
B
Bruce Momjian 已提交
3780
				if (tm->tm_year > 0)
3781
					result = tm->tm_year / 10;
3782
				else
B
Bruce Momjian 已提交
3783
					result = -((8 - (tm->tm_year - 1)) / 10);
3784 3785 3786
				break;

			case DTK_CENTURY:
3787 3788
				/* see comments in timestamp_part */
				if (tm->tm_year > 0)
3789
					result = (tm->tm_year + 99) / 100;
3790
				else
B
Bruce Momjian 已提交
3791
					result = -((99 - (tm->tm_year - 1)) / 100);
3792 3793 3794
				break;

			case DTK_MILLENNIUM:
3795 3796
				/* see comments in timestamp_part */
				if (tm->tm_year > 0)
3797
					result = (tm->tm_year + 999) / 1000;
3798
				else
B
Bruce Momjian 已提交
3799
					result = -((999 - (tm->tm_year - 1)) / 1000);
3800 3801
				break;

3802 3803
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3804
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3805
				result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
B
Bruce Momjian 已提交
3806
					tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY;
3807
#else
B
Bruce Momjian 已提交
3808
				result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
B
Bruce Momjian 已提交
3809
						   tm->tm_sec + fsec) / (double) SECS_PER_DAY;
3810
#endif
3811 3812
				break;

3813
			default:
3814 3815
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
3816 3817
				errmsg("timestamp with time zone units \"%s\" not supported",
					   lowunits)));
3818 3819
				result = 0;
		}
3820

3821 3822 3823 3824 3825 3826
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
3827
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3828
				result = (timestamp - SetEpochTimestamp()) / 1000000.0;
3829
#else
3830
				result = timestamp - SetEpochTimestamp();
3831
#endif
3832
				break;
3833

3834
			case DTK_DOW:
3835
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3836 3837 3838
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3839 3840
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3841

3842
			case DTK_DOY:
3843
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3844 3845 3846
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3847 3848 3849
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3850

3851
			default:
3852 3853
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
3854 3855
				errmsg("timestamp with time zone units \"%s\" not supported",
					   lowunits)));
3856
				result = 0;
3857 3858
		}
	}
3859 3860
	else
	{
3861 3862
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3863 3864
			   errmsg("timestamp with time zone units \"%s\" not recognized",
					  lowunits)));
3865

3866 3867
		result = 0;
	}
3868

3869 3870
	PG_RETURN_FLOAT8(result);
}
3871 3872 3873 3874 3875


/* interval_part()
 * Extract specified field from interval.
 */
3876 3877
Datum
interval_part(PG_FUNCTION_ARGS)
3878
{
3879 3880 3881
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
	float8		result;
3882 3883
	int			type,
				val;
3884
	char	   *lowunits;
3885
	fsec_t		fsec;
B
Bruce Momjian 已提交
3886
	struct pg_tm tt,
3887 3888
			   *tm = &tt;

3889 3890 3891
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
3892 3893

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

3897
	if (type == UNITS)
3898 3899 3900 3901 3902
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
B
Bruce Momjian 已提交
3903
				case DTK_MICROSEC:
3904
#ifdef HAVE_INT64_TIMESTAMP
3905
					result = tm->tm_sec * 1000000.0 + fsec;
3906
#else
B
Bruce Momjian 已提交
3907
					result = (tm->tm_sec + fsec) * 1000000;
3908
#endif
B
Bruce Momjian 已提交
3909
					break;
3910

B
Bruce Momjian 已提交
3911
				case DTK_MILLISEC:
3912
#ifdef HAVE_INT64_TIMESTAMP
3913
					result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3914
#else
B
Bruce Momjian 已提交
3915
					result = (tm->tm_sec + fsec) * 1000;
3916
#endif
B
Bruce Momjian 已提交
3917
					break;
3918

B
Bruce Momjian 已提交
3919
				case DTK_SECOND:
3920
#ifdef HAVE_INT64_TIMESTAMP
3921
					result = tm->tm_sec + fsec / 1000000.0;
3922
#else
3923
					result = tm->tm_sec + fsec;
3924
#endif
B
Bruce Momjian 已提交
3925
					break;
3926 3927

				case DTK_MINUTE:
3928
					result = tm->tm_min;
3929 3930 3931
					break;

				case DTK_HOUR:
3932
					result = tm->tm_hour;
3933 3934 3935
					break;

				case DTK_DAY:
3936
					result = tm->tm_mday;
3937 3938 3939
					break;

				case DTK_MONTH:
3940
					result = tm->tm_mon;
3941 3942 3943
					break;

				case DTK_QUARTER:
3944
					result = (tm->tm_mon / 3) + 1;
3945 3946 3947
					break;

				case DTK_YEAR:
3948
					result = tm->tm_year;
3949 3950 3951
					break;

				case DTK_DECADE:
3952
					/* caution: C division may have negative remainder */
3953
					result = tm->tm_year / 10;
3954 3955 3956
					break;

				case DTK_CENTURY:
3957
					/* caution: C division may have negative remainder */
3958
					result = tm->tm_year / 100;
3959 3960
					break;

3961
				case DTK_MILLENNIUM:
3962
					/* caution: C division may have negative remainder */
3963
					result = tm->tm_year / 1000;
3964 3965 3966
					break;

				default:
3967 3968 3969
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("interval units \"%s\" not supported",
B
Bruce Momjian 已提交
3970 3971
								 DatumGetCString(DirectFunctionCall1(textout,
												 PointerGetDatum(units))))));
3972
					result = 0;
3973 3974 3975 3976 3977
			}

		}
		else
		{
3978
			elog(ERROR, "could not convert interval to tm");
3979
			result = 0;
3980 3981
		}
	}
3982
	else if (type == RESERV && val == DTK_EPOCH)
3983
	{
3984
#ifdef HAVE_INT64_TIMESTAMP
3985
		result = interval->time / 1000000.0;
3986
#else
3987
		result = interval->time;
3988
#endif
3989
		result += (DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR);
B
Bruce Momjian 已提交
3990
		result += ((double) DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR);
3991
		result += interval->day * SECS_PER_DAY;
3992 3993 3994
	}
	else
	{
3995 3996 3997
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval units \"%s\" not recognized",
B
Bruce Momjian 已提交
3998
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
3999
												 PointerGetDatum(units))))));
4000
		result = 0;
4001 4002
	}

4003 4004
	PG_RETURN_FLOAT8(result);
}
4005 4006


B
Bruce Momjian 已提交
4007 4008 4009
/*	timestamp_zone()
 *	Encode timestamp type with specified time zone.
 *	This function is just timestamp2timestamptz() except instead of
4010 4011 4012 4013
 *	shifting to the global timezone, we shift to the specified timezone.
 *	This is different from the other AT TIME ZONE cases because instead
 *	of shifting to a _to_ a new time zone, it sets the time to _be_ the
 *	specified timezone.
4014
 */
4015 4016
Datum
timestamp_zone(PG_FUNCTION_ARGS)
4017
{
4018
	text	   *zone = PG_GETARG_TEXT_P(0);
4019
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
4020
	TimestampTz result;
4021
	int			tz;
B
Bruce Momjian 已提交
4022 4023 4024 4025
	pg_tz	   *tzp;
	char		tzname[TZ_STRLEN_MAX + 1];
	int			len;

4026 4027 4028
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMPTZ(timestamp);

4029
	/*
B
Bruce Momjian 已提交
4030 4031 4032 4033
	 * Look up the requested timezone.	First we look in the timezone database
	 * (to handle cases like "America/New_York"), and if that fails, we look
	 * in the date token table (to handle cases like "EST").
	 */
4034
	len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
B
Bruce Momjian 已提交
4035
	memcpy(tzname, VARDATA(zone), len);
4036
	tzname[len] = '\0';
4037
	tzp = pg_tzset(tzname);
4038
	if (tzp)
B
Bruce Momjian 已提交
4039
	{
4040 4041
		/* Apply the timezone change */
		struct pg_tm tm;
B
Bruce Momjian 已提交
4042
		fsec_t		fsec;
4043

4044 4045 4046 4047
		if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4048
		tz = DetermineTimeZoneOffset(&tm, tzp);
4049 4050 4051 4052 4053
		if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("could not convert to time zone \"%s\"",
							tzname)));
4054
	}
4055
	else
B
Bruce Momjian 已提交
4056
	{
4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076
		char	   *lowzone;
		int			type,
					val;

		lowzone = downcase_truncate_identifier(VARDATA(zone),
											   VARSIZE(zone) - VARHDRSZ,
											   false);
		type = DecodeSpecial(0, lowzone, &val);

		if (type == TZ || type == DTZ)
			tz = -(val * 60);
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("time zone \"%s\" not recognized", tzname)));
			tz = 0;				/* keep compiler quiet */
		}

		result = dt2local(timestamp, tz);
4077
	}
4078

4079
	PG_RETURN_TIMESTAMPTZ(result);
B
Bruce Momjian 已提交
4080
}
4081 4082 4083 4084 4085 4086 4087 4088

/* timestamp_izone()
 * Encode timestamp type with specified time interval as time zone.
 */
Datum
timestamp_izone(PG_FUNCTION_ARGS)
{
	Interval   *zone = PG_GETARG_INTERVAL_P(0);
B
Bruce Momjian 已提交
4089
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
4090
	TimestampTz result;
4091 4092 4093 4094 4095 4096
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMPTZ(timestamp);

	if (zone->month != 0)
4097 4098
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
4099 4100 4101
				 errmsg("interval time zone \"%s\" must not specify month",
						DatumGetCString(DirectFunctionCall1(interval_out,
												  PointerGetDatum(zone))))));
4102

4103
#ifdef HAVE_INT64_TIMESTAMP
4104
	tz = zone->time / USECS_PER_SEC;
4105
#else
4106
	tz = zone->time;
4107 4108 4109
#endif

	result = dt2local(timestamp, tz);
4110 4111 4112 4113 4114 4115 4116 4117 4118 4119

	PG_RETURN_TIMESTAMPTZ(result);
}	/* timestamp_izone() */

/* timestamp_timestamptz()
 * Convert local timestamp to timestamp at GMT
 */
Datum
timestamp_timestamptz(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
4120
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
4121 4122 4123 4124 4125 4126 4127

	PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
}

static TimestampTz
timestamp2timestamptz(Timestamp timestamp)
{
4128
	TimestampTz result;
B
Bruce Momjian 已提交
4129
	struct pg_tm tt,
4130
			   *tm = &tt;
4131
	fsec_t		fsec;
4132 4133 4134 4135 4136 4137
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
4138
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
4139 4140 4141
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4142

4143
		tz = DetermineTimeZoneOffset(tm, global_timezone);
4144 4145

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
4146 4147 4148
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4149 4150
	}

4151
	return result;
4152 4153 4154 4155 4156 4157 4158 4159
}

/* timestamptz_timestamp()
 * Convert timestamp at GMT to local timestamp
 */
Datum
timestamptz_timestamp(PG_FUNCTION_ARGS)
{
4160
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
4161
	Timestamp	result;
B
Bruce Momjian 已提交
4162
	struct pg_tm tt,
4163
			   *tm = &tt;
4164
	fsec_t		fsec;
4165 4166 4167 4168 4169 4170 4171
	char	   *tzn;
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
4172
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
4173 4174 4175
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4176
		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
4177 4178 4179
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4180 4181 4182 4183 4184
	}
	PG_RETURN_TIMESTAMP(result);
}

/* timestamptz_zone()
4185 4186
 * Evaluate timestamp with time zone type at the specified time zone.
 * Returns a timestamp without time zone.
4187 4188 4189 4190 4191
 */
Datum
timestamptz_zone(PG_FUNCTION_ARGS)
{
	text	   *zone = PG_GETARG_TEXT_P(0);
4192
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4193
	Timestamp	result;
4194
	int			tz;
4195
	pg_tz	   *tzp;
B
Bruce Momjian 已提交
4196 4197
	char		tzname[TZ_STRLEN_MAX + 1];
	int			len;
4198

4199
	if (TIMESTAMP_NOT_FINITE(timestamp))
4200
		PG_RETURN_TIMESTAMP(timestamp);
4201

4202
	/*
B
Bruce Momjian 已提交
4203 4204 4205 4206
	 * Look up the requested timezone.	First we look in the timezone database
	 * (to handle cases like "America/New_York"), and if that fails, we look
	 * in the date token table (to handle cases like "EST").
	 */
4207
	len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
B
Bruce Momjian 已提交
4208
	memcpy(tzname, VARDATA(zone), len);
4209
	tzname[len] = '\0';
4210
	tzp = pg_tzset(tzname);
4211
	if (tzp)
B
Bruce Momjian 已提交
4212
	{
4213 4214
		/* Apply the timezone change */
		struct pg_tm tm;
B
Bruce Momjian 已提交
4215
		fsec_t		fsec;
4216

4217 4218 4219 4220 4221 4222 4223 4224 4225
		if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
		if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("could not convert to time zone \"%s\"",
							tzname)));
4226
	}
4227 4228 4229 4230 4231
	else
	{
		char	   *lowzone;
		int			type,
					val;
4232

4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248
		lowzone = downcase_truncate_identifier(VARDATA(zone),
											   VARSIZE(zone) - VARHDRSZ,
											   false);
		type = DecodeSpecial(0, lowzone, &val);

		if (type == TZ || type == DTZ)
			tz = val * 60;
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("time zone \"%s\" not recognized", tzname)));
			tz = 0;				/* keep compiler quiet */
		}

		result = dt2local(timestamp, tz);
4249 4250
	}

4251
	PG_RETURN_TIMESTAMP(result);
B
Bruce Momjian 已提交
4252
}
4253

4254 4255
/* timestamptz_izone()
 * Encode timestamp with time zone type with specified time interval as time zone.
4256
 * Returns a timestamp without time zone.
4257 4258
 */
Datum
4259
timestamptz_izone(PG_FUNCTION_ARGS)
4260 4261
{
	Interval   *zone = PG_GETARG_INTERVAL_P(0);
4262
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4263
	Timestamp	result;
4264 4265 4266
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
4267
		PG_RETURN_TIMESTAMP(timestamp);
4268 4269

	if (zone->month != 0)
4270 4271
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
4272 4273 4274
				 errmsg("interval time zone \"%s\" must not specify month",
						DatumGetCString(DirectFunctionCall1(interval_out,
												  PointerGetDatum(zone))))));
4275

4276
#ifdef HAVE_INT64_TIMESTAMP
4277
	tz = -(zone->time / USECS_PER_SEC);
4278
#else
4279
	tz = -zone->time;
4280
#endif
4281

4282
	result = dt2local(timestamp, tz);
4283

4284
	PG_RETURN_TIMESTAMP(result);
B
Bruce Momjian 已提交
4285
}