timestamp.c 100.4 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * timestamp.c
4
 *	  Functions for the built-in SQL92 types "timestamp" and "interval".
5
 *
6
 * Portions Copyright (c) 1996-2006, 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.162 2006/03/06 22:49:16 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
	/*----------
	 *	This is wrong, but removing it breaks a lot of regression tests.
	 *	For example:
1949
	 *
1950 1951 1952 1953 1954 1955 1956 1957
	 *	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)
1958
	 *
1959
	 *	so adding that to the first timestamp gets:
1960
	 *
1961 1962 1963 1964 1965 1966 1967 1968 1969
	 *	 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)
	 *----------
1970
	 */
1971
	result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
B
Bruce Momjian 已提交
1972
												 IntervalPGetDatum(result)));
1973

1974
	PG_RETURN_INTERVAL_P(result);
1975 1976
}

1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052
/*
 *  interval_justify_interval()
 *
 *  Adjust interval so 'month', 'day', and 'time' portions are within
 *  customary bounds.  Specifically:
 *
 *  	0 <= abs(time) < 24 hours
 *  	0 <= abs(day)  < 30 days
 *
 *  Also, the sign bit on all three fields is made equal, so either
 *  all three fields are negative or all are positive.
 */
Datum
interval_justify_interval(PG_FUNCTION_ARGS)
{
	Interval   *span = PG_GETARG_INTERVAL_P(0);
	Interval   *result;
	
#ifdef HAVE_INT64_TIMESTAMP
	int64		wholeday;
#else
	double		wholeday;
#endif
	int32		wholemonth;

	result = (Interval *) palloc(sizeof(Interval));
	result->month = span->month;
	result->day = span->day;
	result->time = span->time;

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

	wholemonth = result->day / DAYS_PER_MONTH;
	result->day -= wholemonth * DAYS_PER_MONTH;
	result->month += wholemonth;

	if (result->month > 0 &&
		(result->day < 0 || (result->day == 0 && result->time < 0)))
	{
		result->day += DAYS_PER_MONTH;
		result->month--;
	}
	else if (result->month < 0 &&
		(result->day > 0 || (result->day == 0 && result->time > 0)))
	{
		result->day -= DAYS_PER_MONTH;
		result->month++;
	}

	if (result->day > 0 && result->time < 0)
	{
#ifdef HAVE_INT64_TIMESTAMP
		result->time += USECS_PER_DAY;
#else
		result->time += (double) SECS_PER_DAY;
#endif
		result->day--;
	}
	else if (result->day < 0 && result->time > 0)
 	{
#ifdef HAVE_INT64_TIMESTAMP
		result->time -= USECS_PER_DAY;
#else
		result->time -= (double) SECS_PER_DAY;
#endif
		result->day++;
	}

	PG_RETURN_INTERVAL_P(result);
}

2053 2054 2055 2056 2057
/*
 *	interval_justify_hours()
 *
 *	Adjust interval so 'time' contains less than a whole day, adding
 *	the excess to 'day'.  This is useful for
2058
 *	situations (such as non-TZ) where '1 day' = '24 hours' is valid,
2059
 *	e.g. interval subtraction and division.
2060 2061 2062 2063
 */
Datum
interval_justify_hours(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
2064 2065
	Interval   *span = PG_GETARG_INTERVAL_P(0);
	Interval   *result;
2066

2067 2068 2069 2070 2071
#ifdef HAVE_INT64_TIMESTAMP
	int64		wholeday;
#else
	double		wholeday;
#endif
2072 2073 2074

	result = (Interval *) palloc(sizeof(Interval));
	result->month = span->month;
2075
	result->day = span->day;
2076 2077 2078
	result->time = span->time;

#ifdef HAVE_INT64_TIMESTAMP
2079
	TMODULO(result->time, wholeday, USECS_PER_DAY);
2080
#else
2081
	TMODULO(result->time, wholeday, (double) SECS_PER_DAY);
2082
#endif
2083
	result->day += wholeday;	/* could overflow... */
2084

2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103
	if (result->day > 0 && result->time < 0)
	{
#ifdef HAVE_INT64_TIMESTAMP
		result->time += USECS_PER_DAY;
#else
		result->time += (double) SECS_PER_DAY;
#endif
		result->day--;
	}
	else if (result->day < 0 && result->time > 0)
	{
#ifdef HAVE_INT64_TIMESTAMP
		result->time -= USECS_PER_DAY;
#else
		result->time -= (double) SECS_PER_DAY;
#endif
		result->day++;
	}

2104 2105
	PG_RETURN_INTERVAL_P(result);
}
2106

2107 2108 2109 2110 2111
/*
 *	interval_justify_days()
 *
 *	Adjust interval so 'day' contains less than 30 days, adding
 *	the excess to 'month'.
2112 2113 2114 2115
 */
Datum
interval_justify_days(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
2116 2117
	Interval   *span = PG_GETARG_INTERVAL_P(0);
	Interval   *result;
2118
	int32		wholemonth;
2119 2120

	result = (Interval *) palloc(sizeof(Interval));
2121
	result->month = span->month;
2122 2123 2124
	result->day = span->day;
	result->time = span->time;

2125 2126 2127
	wholemonth = result->day / DAYS_PER_MONTH;
	result->day -= wholemonth * DAYS_PER_MONTH;
	result->month += wholemonth;
2128

2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139
	if (result->month > 0 && result->day < 0)
	{
		result->day += DAYS_PER_MONTH;
		result->month--;
	}
	else if (result->month < 0 && result->day > 0)
	{
		result->day -= DAYS_PER_MONTH;
		result->month++;
	}

2140 2141
	PG_RETURN_INTERVAL_P(result);
}
2142

2143
/* timestamp_pl_interval()
2144
 * Add a interval to a timestamp data type.
2145
 * Note that interval has provisions for qualitative year/month and day
2146 2147 2148 2149
 *	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.
2150
 * To add a day, increment the mday, and use the same time of day.
2151 2152
 * Lastly, add in the "quantitative time".
 */
2153
Datum
2154
timestamp_pl_interval(PG_FUNCTION_ARGS)
2155
{
B
Bruce Momjian 已提交
2156
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
2157 2158
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Timestamp	result;
2159 2160 2161 2162 2163 2164 2165

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
		if (span->month != 0)
		{
B
Bruce Momjian 已提交
2166
			struct pg_tm tt,
2167
					   *tm = &tt;
2168
			fsec_t		fsec;
2169

2170
			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2171 2172 2173 2174 2175
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));

			tm->tm_mon += span->month;
2176
			if (tm->tm_mon > MONTHS_PER_YEAR)
2177
			{
2178 2179
				tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
				tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2180
			}
2181
			else if (tm->tm_mon < 1)
2182
			{
2183 2184
				tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
				tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2185
			}
2186 2187 2188 2189 2190

			/* 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]);

2191
			if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2192 2193 2194
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2195 2196
		}

2197 2198 2199 2200 2201 2202
		if (span->day != 0)
		{
			struct pg_tm tt,
					   *tm = &tt;
			fsec_t		fsec;
			int			julian;
B
Bruce Momjian 已提交
2203

2204
			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2205 2206 2207 2208 2209 2210 2211 2212
				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);

2213
			if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2214 2215 2216 2217 2218 2219
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
		}

		timestamp += span->time;
2220 2221 2222 2223 2224 2225 2226
		result = timestamp;
	}

	PG_RETURN_TIMESTAMP(result);
}

Datum
2227
timestamp_mi_interval(PG_FUNCTION_ARGS)
2228
{
B
Bruce Momjian 已提交
2229
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
2230 2231 2232 2233
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Interval	tspan;

	tspan.month = -span->month;
2234
	tspan.day = -span->day;
2235 2236
	tspan.time = -span->time;

2237
	return DirectFunctionCall2(timestamp_pl_interval,
2238 2239 2240 2241 2242
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}


2243
/* timestamptz_pl_interval()
2244 2245 2246 2247 2248 2249 2250 2251 2252
 * 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
2253
timestamptz_pl_interval(PG_FUNCTION_ARGS)
2254
{
2255
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2256
	Interval   *span = PG_GETARG_INTERVAL_P(1);
2257
	TimestampTz result;
2258 2259 2260
	int			tz;
	char	   *tzn;

2261 2262
	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
2263 2264 2265 2266
	else
	{
		if (span->month != 0)
		{
B
Bruce Momjian 已提交
2267
			struct pg_tm tt,
2268
					   *tm = &tt;
2269
			fsec_t		fsec;
2270

2271
			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2272 2273 2274
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2275

2276
			tm->tm_mon += span->month;
2277
			if (tm->tm_mon > MONTHS_PER_YEAR)
2278
			{
2279 2280
				tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
				tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2281
			}
2282
			else if (tm->tm_mon < 1)
2283
			{
2284 2285
				tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
				tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2286
			}
2287 2288 2289 2290 2291

			/* 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]);

2292
			tz = DetermineTimeZoneOffset(tm, global_timezone);
2293

2294
			if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2295 2296 2297
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2298 2299
		}

2300 2301 2302 2303 2304 2305 2306
		if (span->day != 0)
		{
			struct pg_tm tt,
					   *tm = &tt;
			fsec_t		fsec;
			int			julian;

2307
			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2308 2309 2310 2311 2312 2313 2314 2315
				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);

2316
			tz = DetermineTimeZoneOffset(tm, global_timezone);
2317

2318
			if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2319 2320 2321 2322 2323 2324
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
		}

		timestamp += span->time;
2325
		result = timestamp;
2326 2327
	}

2328 2329
	PG_RETURN_TIMESTAMP(result);
}
2330

2331
Datum
2332
timestamptz_mi_interval(PG_FUNCTION_ARGS)
2333
{
2334
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2335
	Interval   *span = PG_GETARG_INTERVAL_P(1);
2336 2337
	Interval	tspan;

B
Bruce Momjian 已提交
2338
	tspan.month = -span->month;
2339
	tspan.day = -span->day;
B
Bruce Momjian 已提交
2340
	tspan.time = -span->time;
2341

2342
	return DirectFunctionCall2(timestamptz_pl_interval,
2343 2344 2345
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}
2346 2347


2348 2349
Datum
interval_um(PG_FUNCTION_ARGS)
2350
{
2351
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2352 2353
	Interval   *result;

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

2356 2357 2358
	result->time = -interval->time;
	result->day = -interval->day;
	result->month = -interval->month;
2359

2360 2361
	PG_RETURN_INTERVAL_P(result);
}
2362 2363


2364 2365
Datum
interval_smaller(PG_FUNCTION_ARGS)
2366
{
2367 2368
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
2369
	Interval   *result;
B
Bruce Momjian 已提交
2370

2371 2372 2373
	/* use interval_cmp_internal to be sure this agrees with comparisons */
	if (interval_cmp_internal(interval1, interval2) < 0)
		result = interval1;
2374
	else
2375
		result = interval2;
2376 2377
	PG_RETURN_INTERVAL_P(result);
}
2378

2379 2380
Datum
interval_larger(PG_FUNCTION_ARGS)
2381
{
2382 2383
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
2384
	Interval   *result;
B
Bruce Momjian 已提交
2385

2386 2387
	if (interval_cmp_internal(interval1, interval2) > 0)
		result = interval1;
2388
	else
2389
		result = interval2;
2390 2391
	PG_RETURN_INTERVAL_P(result);
}
2392

2393 2394
Datum
interval_pl(PG_FUNCTION_ARGS)
2395
{
2396 2397
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
2398 2399
	Interval   *result;

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

2402 2403 2404
	result->month = span1->month + span2->month;
	result->day = span1->day + span2->day;
	result->time = span1->time + span2->time;
2405

2406 2407
	PG_RETURN_INTERVAL_P(result);
}
2408

2409 2410
Datum
interval_mi(PG_FUNCTION_ARGS)
2411
{
2412 2413
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
2414 2415
	Interval   *result;

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

2418 2419 2420
	result->month = span1->month - span2->month;
	result->day = span1->day - span2->day;
	result->time = span1->time - span2->time;
2421

2422 2423
	PG_RETURN_INTERVAL_P(result);
}
2424

2425 2426
Datum
interval_mul(PG_FUNCTION_ARGS)
2427
{
2428
	Interval   *span = PG_GETARG_INTERVAL_P(0);
2429
	float8		factor = PG_GETARG_FLOAT8(1);
B
Bruce Momjian 已提交
2430 2431 2432
	double		month_remainder,
				day_remainder,
				month_remainder_days;
2433
	Interval   *result;
B
Bruce Momjian 已提交
2434

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

2437 2438 2439 2440 2441 2442
	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;
2443

2444
	/*
2445 2446
	 * The above correctly handles the whole-number part of the month and day
	 * products, but we have to do something with any fractional part
2447 2448
	 * resulting when the factor is nonintegral.  We cascade the fractions
	 * down to lower units using the conversion factors DAYS_PER_MONTH and
2449 2450
	 * 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,
2451 2452
	 * using justify_hours and/or justify_days.
	 */
2453 2454

	/* fractional months full days into days */
2455
	month_remainder_days = month_remainder * DAYS_PER_MONTH;
2456
	result->day += (int32) month_remainder_days;
2457
	/* fractional months partial days into time */
2458
	day_remainder += month_remainder_days - (int32) month_remainder_days;
2459

2460
#ifdef HAVE_INT64_TIMESTAMP
2461
	result->time = rint(span->time * factor + day_remainder * USECS_PER_DAY);
2462
#else
2463
	result->time = span->time * factor + day_remainder * SECS_PER_DAY;
2464
#endif
2465

2466
	PG_RETURN_INTERVAL_P(result);
2467
}
2468

2469 2470
Datum
mul_d_interval(PG_FUNCTION_ARGS)
2471
{
2472 2473
	/* Args are float8 and Interval *, but leave them as generic Datum */
	Datum		factor = PG_GETARG_DATUM(0);
2474
	Datum		span = PG_GETARG_DATUM(1);
2475

2476
	return DirectFunctionCall2(interval_mul, span, factor);
2477 2478 2479 2480
}

Datum
interval_div(PG_FUNCTION_ARGS)
2481
{
2482
	Interval   *span = PG_GETARG_INTERVAL_P(0);
2483
	float8		factor = PG_GETARG_FLOAT8(1);
B
Bruce Momjian 已提交
2484 2485 2486
	double		month_remainder,
				day_remainder,
				month_remainder_days;
2487
	Interval   *result;
B
Bruce Momjian 已提交
2488

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

2491
	if (factor == 0.0)
2492 2493 2494
		ereport(ERROR,
				(errcode(ERRCODE_DIVISION_BY_ZERO),
				 errmsg("division by zero")));
2495

2496 2497 2498 2499 2500 2501
	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;
2502

2503 2504 2505
	/*
	 * Handle any fractional parts the same way as in interval_mul.
	 */
2506 2507

	/* fractional months full days into days */
2508
	month_remainder_days = month_remainder * DAYS_PER_MONTH;
2509
	result->day += (int32) month_remainder_days;
2510
	/* fractional months partial days into time */
2511
	day_remainder += month_remainder_days - (int32) month_remainder_days;
2512

2513
#ifdef HAVE_INT64_TIMESTAMP
2514
	result->time = rint(span->time / factor + day_remainder * USECS_PER_DAY);
2515
#else
2516
	result->time = span->time / factor + day_remainder * SECS_PER_DAY;
2517
#endif
2518

2519
	PG_RETURN_INTERVAL_P(result);
2520
}
2521

2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543
/*
 * 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,
2544
					  INTERVALOID, sizeof(Interval), false, 'd',
2545
					  &transdatums, NULL, &ndatums);
2546
	if (ndatums != 2)
2547
		elog(ERROR, "expected 2-element interval array");
B
Bruce Momjian 已提交
2548

2549
	/*
B
Bruce Momjian 已提交
2550 2551 2552 2553
	 * 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...
2554
	 *
B
Bruce Momjian 已提交
2555 2556
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2557
	 */
2558 2559
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2560 2561

	newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
B
Bruce Momjian 已提交
2562 2563
												   IntervalPGetDatum(&sumX),
												 IntervalPGetDatum(newval)));
2564 2565 2566 2567 2568 2569
	N.time += 1;

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

	result = construct_array(transdatums, 2,
2570
							 INTERVALOID, sizeof(Interval), false, 'd');
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584

	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,
2585
					  INTERVALOID, sizeof(Interval), false, 'd',
2586
					  &transdatums, NULL, &ndatums);
2587
	if (ndatums != 2)
2588
		elog(ERROR, "expected 2-element interval array");
B
Bruce Momjian 已提交
2589

2590
	/*
B
Bruce Momjian 已提交
2591 2592 2593 2594
	 * 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...
2595
	 *
B
Bruce Momjian 已提交
2596 2597
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2598
	 */
2599 2600
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611

	/* 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));
}


2612 2613 2614 2615 2616 2617
/* 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.
 */
2618 2619
Datum
timestamp_age(PG_FUNCTION_ARGS)
2620
{
2621 2622
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
2623
	Interval   *result;
2624
	fsec_t		fsec,
2625 2626
				fsec1,
				fsec2;
B
Bruce Momjian 已提交
2627
	struct pg_tm tt,
2628
			   *tm = &tt;
B
Bruce Momjian 已提交
2629
	struct pg_tm tt1,
2630
			   *tm1 = &tt1;
B
Bruce Momjian 已提交
2631
	struct pg_tm tt2,
2632 2633
			   *tm2 = &tt2;

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

2636 2637
	if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 &&
		timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0)
2638 2639
	{
		fsec = (fsec1 - fsec2);
2640 2641 2642 2643 2644 2645
		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;
2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658

		/* 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;
		}

2659
		while (tm->tm_sec < 0)
2660
		{
2661
			tm->tm_sec += SECS_PER_MINUTE;
2662 2663 2664
			tm->tm_min--;
		}

2665
		while (tm->tm_min < 0)
2666
		{
B
Bruce Momjian 已提交
2667
			tm->tm_min += MINS_PER_HOUR;
2668 2669 2670
			tm->tm_hour--;
		}

2671
		while (tm->tm_hour < 0)
2672
		{
2673
			tm->tm_hour += HOURS_PER_DAY;
2674 2675 2676
			tm->tm_mday--;
		}

2677
		while (tm->tm_mday < 0)
2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690
		{
			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--;
			}
		}

2691
		while (tm->tm_mon < 0)
2692
		{
2693
			tm->tm_mon += MONTHS_PER_YEAR;
2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709
			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)
2710 2711 2712
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("interval out of range")));
2713 2714
	}
	else
2715 2716 2717
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2718

2719 2720
	PG_RETURN_INTERVAL_P(result);
}
2721 2722


2723 2724 2725 2726 2727
/* 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.
2728
 */
2729
Datum
2730
timestamptz_age(PG_FUNCTION_ARGS)
2731
{
2732 2733
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2734
	Interval   *result;
2735
	fsec_t		fsec,
2736 2737
				fsec1,
				fsec2;
B
Bruce Momjian 已提交
2738
	struct pg_tm tt,
2739
			   *tm = &tt;
B
Bruce Momjian 已提交
2740
	struct pg_tm tt1,
2741
			   *tm1 = &tt1;
B
Bruce Momjian 已提交
2742
	struct pg_tm tt2,
2743
			   *tm2 = &tt2;
2744 2745 2746
	int			tz1;
	int			tz2;
	char	   *tzn;
2747 2748 2749

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

2750 2751
	if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn, NULL) == 0 &&
		timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn, NULL) == 0)
2752
	{
2753 2754 2755 2756 2757 2758 2759
		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;
2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772

		/* 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;
		}

2773
		while (tm->tm_sec < 0)
2774
		{
2775
			tm->tm_sec += SECS_PER_MINUTE;
2776 2777 2778
			tm->tm_min--;
		}

2779
		while (tm->tm_min < 0)
2780
		{
B
Bruce Momjian 已提交
2781
			tm->tm_min += MINS_PER_HOUR;
2782 2783 2784
			tm->tm_hour--;
		}

2785
		while (tm->tm_hour < 0)
2786
		{
2787
			tm->tm_hour += HOURS_PER_DAY;
2788 2789 2790
			tm->tm_mday--;
		}

2791
		while (tm->tm_mday < 0)
2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804
		{
			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--;
			}
		}

2805
		while (tm->tm_mon < 0)
2806
		{
2807
			tm->tm_mon += MONTHS_PER_YEAR;
2808 2809 2810
			tm->tm_year--;
		}

2811 2812 2813 2814
		/*
		 * Note: we deliberately ignore any difference between tz1 and tz2.
		 */

2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827
		/* 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)
2828 2829 2830
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("interval out of range")));
2831 2832
	}
	else
2833 2834 2835
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852

	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 已提交
2853
	Datum		timestamp = PG_GETARG_DATUM(0);
2854 2855 2856
	text	   *result;
	char	   *str;
	int			len;
2857

2858
	str = DatumGetCString(DirectFunctionCall1(timestamp_out, timestamp));
2859 2860 2861 2862 2863

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2864
	VARATT_SIZEP(result) = len;
2865
	memmove(VARDATA(result), str, len - VARHDRSZ);
2866 2867 2868

	pfree(str);

2869 2870
	PG_RETURN_TEXT_P(result);
}
2871 2872 2873 2874 2875 2876 2877


/* text_timestamp()
 * Convert text string to timestamp.
 * Text type is not null terminated, so use temporary string
 *	then call the standard input routine.
 */
2878 2879
Datum
text_timestamp(PG_FUNCTION_ARGS)
2880
{
2881
	text	   *str = PG_GETARG_TEXT_P(0);
2882 2883 2884 2885 2886
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2887
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2888 2889
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2890
				 errmsg("invalid input syntax for type timestamp: \"%s\"",
2891
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2892
												   PointerGetDatum(str))))));
2893 2894 2895

	sp = VARDATA(str);
	dp = dstr;
2896
	for (i = 0; i < VARSIZE(str) - VARHDRSZ; i++)
2897 2898 2899
		*dp++ = *sp++;
	*dp = '\0';

2900 2901 2902 2903
	return DirectFunctionCall3(timestamp_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2904
}
2905 2906


2907 2908 2909 2910 2911 2912 2913
/* 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 已提交
2914
	Datum		timestamp = PG_GETARG_DATUM(0);
2915 2916 2917 2918 2919 2920
	text	   *result;
	char	   *str;
	int			len;

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

2921
	len = strlen(str) + VARHDRSZ;
2922 2923 2924 2925

	result = palloc(len);

	VARATT_SIZEP(result) = len;
2926
	memmove(VARDATA(result), str, len - VARHDRSZ);
2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947

	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)
2948 2949
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2950
				 errmsg("invalid input syntax for type timestamp with time zone: \"%s\"",
2951
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2952
												   PointerGetDatum(str))))));
2953 2954 2955

	sp = VARDATA(str);
	dp = dstr;
2956
	for (i = 0; i < VARSIZE(str) - VARHDRSZ; i++)
2957 2958 2959
		*dp++ = *sp++;
	*dp = '\0';

2960 2961 2962 2963
	return DirectFunctionCall3(timestamptz_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2964 2965 2966
}


2967 2968 2969
/* interval_text()
 * Convert interval to text data type.
 */
2970 2971
Datum
interval_text(PG_FUNCTION_ARGS)
2972
{
2973
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2974 2975 2976 2977
	text	   *result;
	char	   *str;
	int			len;

2978
	str = DatumGetCString(DirectFunctionCall1(interval_out,
B
Bruce Momjian 已提交
2979
											  IntervalPGetDatum(interval)));
2980

2981
	len = strlen(str) + VARHDRSZ;
2982 2983 2984

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2985
	VARATT_SIZEP(result) = len;
2986
	memmove(VARDATA(result), str, len - VARHDRSZ);
2987 2988 2989

	pfree(str);

2990 2991
	PG_RETURN_TEXT_P(result);
}
2992 2993 2994 2995 2996 2997 2998


/* 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.
 */
2999 3000
Datum
text_interval(PG_FUNCTION_ARGS)
3001
{
3002
	text	   *str = PG_GETARG_TEXT_P(0);
3003 3004 3005 3006 3007
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

3008
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
3009 3010
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3011
				 errmsg("invalid input syntax for type interval: \"%s\"",
3012
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
3013
												   PointerGetDatum(str))))));
3014

3015 3016 3017 3018 3019 3020
	sp = VARDATA(str);
	dp = dstr;
	for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
		*dp++ = *sp++;
	*dp = '\0';

3021 3022 3023 3024
	return DirectFunctionCall3(interval_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
3025
}
3026 3027

/* timestamp_trunc()
3028
 * Truncate timestamp to specified units.
3029
 */
3030 3031
Datum
timestamp_trunc(PG_FUNCTION_ARGS)
3032
{
3033
	text	   *units = PG_GETARG_TEXT_P(0);
B
Bruce Momjian 已提交
3034
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
3035
	Timestamp	result;
3036 3037
	int			type,
				val;
3038
	char	   *lowunits;
3039
	fsec_t		fsec;
B
Bruce Momjian 已提交
3040
	struct pg_tm tt,
3041 3042
			   *tm = &tt;

3043 3044
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMP(timestamp);
3045

3046 3047 3048
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
3049 3050 3051

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

3052
	if (type == UNITS)
3053
	{
3054
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3055 3056 3057 3058
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3059 3060
		switch (val)
		{
3061
			case DTK_WEEK:
B
Bruce Momjian 已提交
3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082
				{
					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;
				}
3083
			case DTK_MILLENNIUM:
3084 3085
				/* see comments in timestamptz_trunc */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3086
					tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
3087
				else
B
Bruce Momjian 已提交
3088
					tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
3089
			case DTK_CENTURY:
3090 3091
				/* see comments in timestamptz_trunc */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3092
					tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
3093
				else
B
Bruce Momjian 已提交
3094
					tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
3095
			case DTK_DECADE:
3096 3097 3098 3099 3100 3101
				/* 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 已提交
3102
						tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
3103
				}
3104 3105 3106
			case DTK_YEAR:
				tm->tm_mon = 1;
			case DTK_QUARTER:
3107
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120
			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:
3121
#ifdef HAVE_INT64_TIMESTAMP
3122
				fsec = (fsec / 1000) * 1000;
3123
#else
3124
				fsec = rint(fsec * 1000) / 1000;
3125
#endif
3126 3127 3128
				break;

			case DTK_MICROSEC:
3129
#ifndef HAVE_INT64_TIMESTAMP
3130
				fsec = rint(fsec * 1000000) / 1000000;
3131
#endif
3132 3133 3134
				break;

			default:
3135 3136 3137 3138
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
								lowunits)));
3139 3140 3141 3142
				result = 0;
		}

		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
3143 3144 3145
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3146
	}
3147 3148
	else
	{
3149 3150 3151 3152
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("timestamp units \"%s\" not recognized",
						lowunits)));
3153 3154
		result = 0;
	}
3155

3156 3157
	PG_RETURN_TIMESTAMP(result);
}
3158

3159 3160 3161 3162 3163 3164 3165
/* timestamptz_trunc()
 * Truncate timestamp to specified units.
 */
Datum
timestamptz_trunc(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
3166
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3167
	TimestampTz result;
3168 3169 3170
	int			tz;
	int			type,
				val;
3171
	bool		redotz = false;
3172
	char	   *lowunits;
3173
	fsec_t		fsec;
3174
	char	   *tzn;
B
Bruce Momjian 已提交
3175
	struct pg_tm tt,
3176
			   *tm = &tt;
3177

3178 3179
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMPTZ(timestamp);
3180

3181 3182 3183 3184 3185 3186
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3187
	if (type == UNITS)
3188
	{
3189
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3190 3191 3192 3193
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3194
		switch (val)
3195
		{
3196
			case DTK_WEEK:
B
Bruce Momjian 已提交
3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218
				{
					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;
				}
3219
				/* one may consider DTK_THOUSAND and DTK_HUNDRED... */
3220
			case DTK_MILLENNIUM:
B
Bruce Momjian 已提交
3221 3222 3223

				/*
				 * truncating to the millennium? what is this supposed to
B
Bruce Momjian 已提交
3224 3225
				 * mean? let us put the first year of the millennium... i.e.
				 * -1000, 1, 1001, 2001...
3226 3227
				 */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3228
					tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
3229
				else
B
Bruce Momjian 已提交
3230
					tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
3231
				/* FALL THRU */
3232
			case DTK_CENTURY:
3233 3234
				/* truncating to the century? as above: -100, 1, 101... */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3235
					tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
3236
				else
B
Bruce Momjian 已提交
3237
					tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
3238
				/* FALL THRU */
3239
			case DTK_DECADE:
B
Bruce Momjian 已提交
3240 3241

				/*
B
Bruce Momjian 已提交
3242 3243
				 * truncating to the decade? first year of the decade. must
				 * not be applied if year was truncated before!
3244 3245 3246 3247 3248 3249
				 */
				if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
				{
					if (tm->tm_year > 0)
						tm->tm_year = (tm->tm_year / 10) * 10;
					else
B
Bruce Momjian 已提交
3250
						tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
3251
				}
3252
				/* FALL THRU */
3253 3254
			case DTK_YEAR:
				tm->tm_mon = 1;
3255
				/* FALL THRU */
3256
			case DTK_QUARTER:
3257
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
3258
				/* FALL THRU */
3259 3260
			case DTK_MONTH:
				tm->tm_mday = 1;
3261
				/* FALL THRU */
3262 3263
			case DTK_DAY:
				tm->tm_hour = 0;
3264 3265
				redotz = true;	/* for all cases >= DAY */
				/* FALL THRU */
3266 3267
			case DTK_HOUR:
				tm->tm_min = 0;
3268
				/* FALL THRU */
3269 3270
			case DTK_MINUTE:
				tm->tm_sec = 0;
3271
				/* FALL THRU */
3272 3273 3274 3275 3276
			case DTK_SECOND:
				fsec = 0;
				break;

			case DTK_MILLISEC:
3277
#ifdef HAVE_INT64_TIMESTAMP
3278
				fsec = (fsec / 1000) * 1000;
3279
#else
3280
				fsec = rint(fsec * 1000) / 1000;
3281
#endif
3282 3283
				break;
			case DTK_MICROSEC:
3284
#ifndef HAVE_INT64_TIMESTAMP
3285
				fsec = rint(fsec * 1000000) / 1000000;
3286
#endif
3287 3288 3289
				break;

			default:
3290 3291
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
3292 3293
						 errmsg("timestamp with time zone units \"%s\" not "
								"supported", lowunits)));
3294
				result = 0;
3295
		}
3296

3297
		if (redotz)
3298
			tz = DetermineTimeZoneOffset(tm, global_timezone);
3299 3300

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
3301 3302 3303
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3304 3305 3306
	}
	else
	{
3307 3308
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3309 3310
			   errmsg("timestamp with time zone units \"%s\" not recognized",
					  lowunits)));
3311
		result = 0;
3312 3313
	}

3314
	PG_RETURN_TIMESTAMPTZ(result);
3315
}
3316 3317 3318 3319

/* interval_trunc()
 * Extract specified field from interval.
 */
3320 3321
Datum
interval_trunc(PG_FUNCTION_ARGS)
3322
{
3323 3324
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
3325 3326 3327
	Interval   *result;
	int			type,
				val;
3328
	char	   *lowunits;
3329
	fsec_t		fsec;
B
Bruce Momjian 已提交
3330
	struct pg_tm tt,
3331 3332
			   *tm = &tt;

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

3335 3336 3337
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
3338 3339 3340

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

3341
	if (type == UNITS)
3342 3343 3344 3345 3346
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
B
Bruce Momjian 已提交
3347
					/* fall through */
3348
				case DTK_MILLENNIUM:
3349
					/* caution: C division may have negative remainder */
3350 3351
					tm->tm_year = (tm->tm_year / 1000) * 1000;
				case DTK_CENTURY:
3352
					/* caution: C division may have negative remainder */
3353 3354
					tm->tm_year = (tm->tm_year / 100) * 100;
				case DTK_DECADE:
3355
					/* caution: C division may have negative remainder */
3356 3357 3358 3359
					tm->tm_year = (tm->tm_year / 10) * 10;
				case DTK_YEAR:
					tm->tm_mon = 0;
				case DTK_QUARTER:
3360
					tm->tm_mon = 3 * (tm->tm_mon / 3);
3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373
				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:
3374
#ifdef HAVE_INT64_TIMESTAMP
3375
					fsec = (fsec / 1000) * 1000;
3376
#else
3377
					fsec = rint(fsec * 1000) / 1000;
3378
#endif
3379 3380
					break;
				case DTK_MICROSEC:
3381
#ifndef HAVE_INT64_TIMESTAMP
3382
					fsec = rint(fsec * 1000000) / 1000000;
3383
#endif
3384 3385 3386
					break;

				default:
3387 3388 3389
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("interval units \"%s\" not supported",
B
Bruce Momjian 已提交
3390
									lowunits)));
3391 3392 3393
			}

			if (tm2interval(tm, fsec, result) != 0)
3394 3395 3396
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("interval out of range")));
3397 3398
		}
		else
3399
			elog(ERROR, "could not convert interval to tm");
3400 3401 3402
	}
	else
	{
3403 3404 3405
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval units \"%s\" not recognized",
B
Bruce Momjian 已提交
3406
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
3407
												 PointerGetDatum(units))))));
3408
		*result = *interval;
3409 3410
	}

3411 3412
	PG_RETURN_INTERVAL_P(result);
}
3413

B
Bruce Momjian 已提交
3414
/* isoweek2date()
3415
 * Convert ISO week of year number to date.
3416
 * The year field must be specified with the ISO year!
3417
 * karel 2000/08/07
B
Bruce Momjian 已提交
3418 3419
 */
void
B
Bruce Momjian 已提交
3420
isoweek2date(int woy, int *year, int *mon, int *mday)
B
Bruce Momjian 已提交
3421
{
B
Bruce Momjian 已提交
3422 3423 3424 3425
	int			day0,
				day4,
				dayn;

B
Bruce Momjian 已提交
3426
	if (!*year)
3427 3428
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3429
		   errmsg("cannot calculate week number without year information")));
B
Bruce Momjian 已提交
3430 3431 3432

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

B
Bruce Momjian 已提交
3434
	/* day0 == offset to first day of week (Monday) */
3435
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3436 3437

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

B
Bruce Momjian 已提交
3439 3440 3441 3442
	j2date(dayn, year, mon, mday);
}

/* date2isoweek()
B
Bruce Momjian 已提交
3443
 *
B
Bruce Momjian 已提交
3444 3445 3446
 *	Returns ISO week number of year.
 */
int
B
Bruce Momjian 已提交
3447
date2isoweek(int year, int mon, int mday)
B
Bruce Momjian 已提交
3448
{
B
Bruce Momjian 已提交
3449 3450 3451 3452 3453 3454
	float8		result;
	int			day0,
				day4,
				dayn;

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

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

B
Bruce Momjian 已提交
3460
	/* day0 == offset to first day of week (Monday) */
3461
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3462 3463

	/*
B
Bruce Momjian 已提交
3464 3465
	 * We need the first week containing a Thursday, otherwise this day falls
	 * into the previous year for purposes of counting weeks
B
Bruce Momjian 已提交
3466
	 */
3467
	if (dayn < day4 - day0)
B
Bruce Momjian 已提交
3468
	{
3469
		day4 = date2j(year - 1, 1, 4);
B
Bruce Momjian 已提交
3470

B
Bruce Momjian 已提交
3471
		/* day0 == offset to first day of week (Monday) */
3472
		day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3473
	}
B
Bruce Momjian 已提交
3474

3475
	result = (dayn - (day4 - day0)) / 7 + 1;
B
Bruce Momjian 已提交
3476 3477

	/*
B
Bruce Momjian 已提交
3478 3479
	 * 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 已提交
3480
	 */
3481
	if (result >= 52)
B
Bruce Momjian 已提交
3482
	{
3483
		day4 = date2j(year + 1, 1, 4);
B
Bruce Momjian 已提交
3484

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

3488 3489
		if (dayn >= day4 - day0)
			result = (dayn - (day4 - day0)) / 7 + 1;
B
Bruce Momjian 已提交
3490
	}
3491

B
Bruce Momjian 已提交
3492
	return (int) result;
B
Bruce Momjian 已提交
3493 3494 3495
}


3496 3497 3498 3499 3500 3501 3502
/* date2isoyear()
 *
 *	Returns ISO 8601 year number.
 */
int
date2isoyear(int year, int mon, int mday)
{
B
Bruce Momjian 已提交
3503 3504 3505 3506
	float8		result;
	int			day0,
				day4,
				dayn;
3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517

	/* 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 已提交
3518 3519
	 * We need the first week containing a Thursday, otherwise this day falls
	 * into the previous year for purposes of counting weeks
3520
	 */
3521
	if (dayn < day4 - day0)
3522 3523 3524 3525 3526 3527 3528 3529 3530
	{
		day4 = date2j(year - 1, 1, 4);

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

		year--;
	}

3531
	result = (dayn - (day4 - day0)) / 7 + 1;
3532 3533

	/*
B
Bruce Momjian 已提交
3534 3535
	 * Sometimes the last few days in a year will fall into the first week of
	 * the next year, so check for this.
3536
	 */
3537
	if (result >= 52)
3538 3539 3540 3541 3542 3543
	{
		day4 = date2j(year + 1, 1, 4);

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

3544
		if (dayn >= day4 - day0)
3545 3546 3547 3548 3549 3550 3551
			year++;
	}

	return year;
}


3552 3553 3554
/* timestamp_part()
 * Extract specified field from timestamp.
 */
3555 3556
Datum
timestamp_part(PG_FUNCTION_ARGS)
3557
{
3558
	text	   *units = PG_GETARG_TEXT_P(0);
B
Bruce Momjian 已提交
3559
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
3560
	float8		result;
3561 3562
	int			type,
				val;
3563
	char	   *lowunits;
3564
	fsec_t		fsec;
B
Bruce Momjian 已提交
3565
	struct pg_tm tt,
3566 3567
			   *tm = &tt;

3568
	if (TIMESTAMP_NOT_FINITE(timestamp))
3569
	{
3570 3571 3572
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3573

3574 3575 3576 3577 3578 3579 3580 3581
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3582
	if (type == UNITS)
3583
	{
3584
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3585 3586 3587 3588
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3589
		switch (val)
3590
		{
3591
			case DTK_MICROSEC:
3592
#ifdef HAVE_INT64_TIMESTAMP
3593
				result = tm->tm_sec * 1000000.0 + fsec;
3594
#else
3595
				result = (tm->tm_sec + fsec) * 1000000;
3596
#endif
3597 3598 3599
				break;

			case DTK_MILLISEC:
3600
#ifdef HAVE_INT64_TIMESTAMP
3601
				result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3602
#else
3603
				result = (tm->tm_sec + fsec) * 1000;
3604
#endif
3605 3606 3607
				break;

			case DTK_SECOND:
3608
#ifdef HAVE_INT64_TIMESTAMP
3609
				result = tm->tm_sec + fsec / 1000000.0;
3610
#else
3611
				result = tm->tm_sec + fsec;
3612
#endif
3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631
				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:
3632
				result = (tm->tm_mon - 1) / 3 + 1;
3633 3634 3635 3636 3637 3638 3639
				break;

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

			case DTK_YEAR:
3640 3641 3642
				if (tm->tm_year > 0)
					result = tm->tm_year;
				else
B
Bruce Momjian 已提交
3643 3644
					/* there is no year 0, just 1 BC and 1 AD */
					result = tm->tm_year - 1;
3645 3646 3647
				break;

			case DTK_DECADE:
B
Bruce Momjian 已提交
3648 3649

				/*
B
Bruce Momjian 已提交
3650 3651 3652
				 * 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...
3653
				 */
B
Bruce Momjian 已提交
3654
				if (tm->tm_year >= 0)
3655
					result = tm->tm_year / 10;
3656
				else
B
Bruce Momjian 已提交
3657
					result = -((8 - (tm->tm_year - 1)) / 10);
3658 3659 3660
				break;

			case DTK_CENTURY:
B
Bruce Momjian 已提交
3661

3662 3663 3664 3665 3666
				/* ----
				 * 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.
				 * ----
3667 3668
				 */
				if (tm->tm_year > 0)
3669
					result = (tm->tm_year + 99) / 100;
3670
				else
3671
					/* caution: C division may have negative remainder */
B
Bruce Momjian 已提交
3672
					result = -((99 - (tm->tm_year - 1)) / 100);
3673 3674 3675
				break;

			case DTK_MILLENNIUM:
3676 3677
				/* see comments above. */
				if (tm->tm_year > 0)
3678
					result = (tm->tm_year + 999) / 1000;
3679
				else
B
Bruce Momjian 已提交
3680
					result = -((999 - (tm->tm_year - 1)) / 1000);
3681 3682
				break;

3683 3684
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3685
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3686
				result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
B
Bruce Momjian 已提交
3687
					tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY;
3688
#else
B
Bruce Momjian 已提交
3689
				result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
B
Bruce Momjian 已提交
3690
						   tm->tm_sec + fsec) / (double) SECS_PER_DAY;
3691
#endif
3692 3693
				break;

3694 3695 3696
			case DTK_TZ:
			case DTK_TZ_MINUTE:
			case DTK_TZ_HOUR:
3697
			default:
3698 3699 3700
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
B
Bruce Momjian 已提交
3701
								lowunits)));
3702 3703 3704 3705 3706 3707 3708 3709
				result = 0;
		}
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
B
Bruce Momjian 已提交
3710 3711 3712
				{
					int			tz;
					TimestampTz timestamptz;
3713

B
Bruce Momjian 已提交
3714
					/*
B
Bruce Momjian 已提交
3715
					 * convert to timestamptz to produce consistent results
B
Bruce Momjian 已提交
3716
					 */
3717
					if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
B
Bruce Momjian 已提交
3718
						ereport(ERROR,
B
Bruce Momjian 已提交
3719 3720
								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
								 errmsg("timestamp out of range")));
3721

3722
					tz = DetermineTimeZoneOffset(tm, global_timezone);
3723

B
Bruce Momjian 已提交
3724 3725
					if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
						ereport(ERROR,
B
Bruce Momjian 已提交
3726 3727
								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
								 errmsg("timestamp out of range")));
3728

3729
#ifdef HAVE_INT64_TIMESTAMP
3730
					result = (timestamptz - SetEpochTimestamp()) / 1000000.0;
3731
#else
B
Bruce Momjian 已提交
3732
					result = timestamptz - SetEpochTimestamp();
3733
#endif
B
Bruce Momjian 已提交
3734 3735
					break;
				}
3736
			case DTK_DOW:
3737
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3738 3739 3740
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3741 3742
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3743

3744
			case DTK_DOY:
3745
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3746 3747 3748
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3749 3750 3751
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3752

3753
			default:
3754 3755 3756
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
B
Bruce Momjian 已提交
3757
								lowunits)));
3758 3759
				result = 0;
		}
3760

3761 3762 3763
	}
	else
	{
3764 3765
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3766
				 errmsg("timestamp units \"%s\" not recognized", lowunits)));
3767 3768
		result = 0;
	}
3769

3770 3771
	PG_RETURN_FLOAT8(result);
}
3772

3773 3774 3775 3776 3777 3778 3779
/* timestamptz_part()
 * Extract specified field from timestamp with time zone.
 */
Datum
timestamptz_part(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
3780
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3781 3782 3783 3784
	float8		result;
	int			tz;
	int			type,
				val;
3785
	char	   *lowunits;
3786
	double		dummy;
3787
	fsec_t		fsec;
3788
	char	   *tzn;
B
Bruce Momjian 已提交
3789
	struct pg_tm tt,
3790
			   *tm = &tt;
3791

3792 3793 3794 3795 3796
	if (TIMESTAMP_NOT_FINITE(timestamp))
	{
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3797

3798 3799 3800 3801 3802 3803 3804 3805
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3806
	if (type == UNITS)
3807
	{
3808
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3809 3810 3811 3812
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3813
		switch (val)
3814
		{
3815
			case DTK_TZ:
3816
				result = -tz;
3817 3818 3819
				break;

			case DTK_TZ_MINUTE:
3820
				result = -tz;
B
Bruce Momjian 已提交
3821
				result /= MINS_PER_HOUR;
B
Bruce Momjian 已提交
3822
				FMODULO(result, dummy, (double) MINS_PER_HOUR);
3823 3824 3825
				break;

			case DTK_TZ_HOUR:
3826
				dummy = -tz;
B
Bruce Momjian 已提交
3827
				FMODULO(dummy, result, (double) SECS_PER_HOUR);
3828 3829 3830
				break;

			case DTK_MICROSEC:
3831
#ifdef HAVE_INT64_TIMESTAMP
3832
				result = tm->tm_sec * 1000000.0 + fsec;
3833
#else
3834
				result = (tm->tm_sec + fsec) * 1000000;
3835
#endif
3836 3837 3838
				break;

			case DTK_MILLISEC:
3839
#ifdef HAVE_INT64_TIMESTAMP
3840
				result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3841
#else
3842
				result = (tm->tm_sec + fsec) * 1000;
3843
#endif
3844 3845 3846
				break;

			case DTK_SECOND:
3847
#ifdef HAVE_INT64_TIMESTAMP
3848
				result = tm->tm_sec + fsec / 1000000.0;
3849
#else
3850
				result = tm->tm_sec + fsec;
3851
#endif
3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870
				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:
3871
				result = (tm->tm_mon - 1) / 3 + 1;
3872 3873 3874 3875 3876 3877 3878
				break;

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

			case DTK_YEAR:
3879 3880 3881 3882 3883
				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;
3884 3885 3886
				break;

			case DTK_DECADE:
3887
				/* see comments in timestamp_part */
B
Bruce Momjian 已提交
3888
				if (tm->tm_year > 0)
3889
					result = tm->tm_year / 10;
3890
				else
B
Bruce Momjian 已提交
3891
					result = -((8 - (tm->tm_year - 1)) / 10);
3892 3893 3894
				break;

			case DTK_CENTURY:
3895 3896
				/* see comments in timestamp_part */
				if (tm->tm_year > 0)
3897
					result = (tm->tm_year + 99) / 100;
3898
				else
B
Bruce Momjian 已提交
3899
					result = -((99 - (tm->tm_year - 1)) / 100);
3900 3901 3902
				break;

			case DTK_MILLENNIUM:
3903 3904
				/* see comments in timestamp_part */
				if (tm->tm_year > 0)
3905
					result = (tm->tm_year + 999) / 1000;
3906
				else
B
Bruce Momjian 已提交
3907
					result = -((999 - (tm->tm_year - 1)) / 1000);
3908 3909
				break;

3910 3911
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3912
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3913
				result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
B
Bruce Momjian 已提交
3914
					tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY;
3915
#else
B
Bruce Momjian 已提交
3916
				result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
B
Bruce Momjian 已提交
3917
						   tm->tm_sec + fsec) / (double) SECS_PER_DAY;
3918
#endif
3919 3920
				break;

3921
			default:
3922 3923
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
3924 3925
				errmsg("timestamp with time zone units \"%s\" not supported",
					   lowunits)));
3926 3927
				result = 0;
		}
3928

3929 3930 3931 3932 3933 3934
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
3935
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3936
				result = (timestamp - SetEpochTimestamp()) / 1000000.0;
3937
#else
3938
				result = timestamp - SetEpochTimestamp();
3939
#endif
3940
				break;
3941

3942
			case DTK_DOW:
3943
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3944 3945 3946
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3947 3948
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3949

3950
			case DTK_DOY:
3951
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3952 3953 3954
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3955 3956 3957
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3958

3959
			default:
3960 3961
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
3962 3963
				errmsg("timestamp with time zone units \"%s\" not supported",
					   lowunits)));
3964
				result = 0;
3965 3966
		}
	}
3967 3968
	else
	{
3969 3970
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3971 3972
			   errmsg("timestamp with time zone units \"%s\" not recognized",
					  lowunits)));
3973

3974 3975
		result = 0;
	}
3976

3977 3978
	PG_RETURN_FLOAT8(result);
}
3979 3980 3981 3982 3983


/* interval_part()
 * Extract specified field from interval.
 */
3984 3985
Datum
interval_part(PG_FUNCTION_ARGS)
3986
{
3987 3988 3989
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
	float8		result;
3990 3991
	int			type,
				val;
3992
	char	   *lowunits;
3993
	fsec_t		fsec;
B
Bruce Momjian 已提交
3994
	struct pg_tm tt,
3995 3996
			   *tm = &tt;

3997 3998 3999
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
4000 4001

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

4005
	if (type == UNITS)
4006 4007 4008 4009 4010
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
B
Bruce Momjian 已提交
4011
				case DTK_MICROSEC:
4012
#ifdef HAVE_INT64_TIMESTAMP
4013
					result = tm->tm_sec * 1000000.0 + fsec;
4014
#else
B
Bruce Momjian 已提交
4015
					result = (tm->tm_sec + fsec) * 1000000;
4016
#endif
B
Bruce Momjian 已提交
4017
					break;
4018

B
Bruce Momjian 已提交
4019
				case DTK_MILLISEC:
4020
#ifdef HAVE_INT64_TIMESTAMP
4021
					result = tm->tm_sec * 1000.0 + fsec / 1000.0;
4022
#else
B
Bruce Momjian 已提交
4023
					result = (tm->tm_sec + fsec) * 1000;
4024
#endif
B
Bruce Momjian 已提交
4025
					break;
4026

B
Bruce Momjian 已提交
4027
				case DTK_SECOND:
4028
#ifdef HAVE_INT64_TIMESTAMP
4029
					result = tm->tm_sec + fsec / 1000000.0;
4030
#else
4031
					result = tm->tm_sec + fsec;
4032
#endif
B
Bruce Momjian 已提交
4033
					break;
4034 4035

				case DTK_MINUTE:
4036
					result = tm->tm_min;
4037 4038 4039
					break;

				case DTK_HOUR:
4040
					result = tm->tm_hour;
4041 4042 4043
					break;

				case DTK_DAY:
4044
					result = tm->tm_mday;
4045 4046 4047
					break;

				case DTK_MONTH:
4048
					result = tm->tm_mon;
4049 4050 4051
					break;

				case DTK_QUARTER:
4052
					result = (tm->tm_mon / 3) + 1;
4053 4054 4055
					break;

				case DTK_YEAR:
4056
					result = tm->tm_year;
4057 4058 4059
					break;

				case DTK_DECADE:
4060
					/* caution: C division may have negative remainder */
4061
					result = tm->tm_year / 10;
4062 4063 4064
					break;

				case DTK_CENTURY:
4065
					/* caution: C division may have negative remainder */
4066
					result = tm->tm_year / 100;
4067 4068
					break;

4069
				case DTK_MILLENNIUM:
4070
					/* caution: C division may have negative remainder */
4071
					result = tm->tm_year / 1000;
4072 4073 4074
					break;

				default:
4075 4076 4077
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("interval units \"%s\" not supported",
B
Bruce Momjian 已提交
4078 4079
								 DatumGetCString(DirectFunctionCall1(textout,
												 PointerGetDatum(units))))));
4080
					result = 0;
4081 4082 4083 4084 4085
			}

		}
		else
		{
4086
			elog(ERROR, "could not convert interval to tm");
4087
			result = 0;
4088 4089
		}
	}
4090
	else if (type == RESERV && val == DTK_EPOCH)
4091
	{
4092
#ifdef HAVE_INT64_TIMESTAMP
4093
		result = interval->time / 1000000.0;
4094
#else
4095
		result = interval->time;
4096
#endif
4097
		result += (DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR);
B
Bruce Momjian 已提交
4098
		result += ((double) DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR);
4099
		result += interval->day * SECS_PER_DAY;
4100 4101 4102
	}
	else
	{
4103 4104 4105
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval units \"%s\" not recognized",
B
Bruce Momjian 已提交
4106
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
4107
												 PointerGetDatum(units))))));
4108
		result = 0;
4109 4110
	}

4111 4112
	PG_RETURN_FLOAT8(result);
}
4113 4114


B
Bruce Momjian 已提交
4115 4116 4117
/*	timestamp_zone()
 *	Encode timestamp type with specified time zone.
 *	This function is just timestamp2timestamptz() except instead of
4118 4119 4120 4121
 *	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.
4122
 */
4123 4124
Datum
timestamp_zone(PG_FUNCTION_ARGS)
4125
{
4126
	text	   *zone = PG_GETARG_TEXT_P(0);
4127
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
4128
	TimestampTz result;
4129
	int			tz;
B
Bruce Momjian 已提交
4130 4131 4132 4133
	pg_tz	   *tzp;
	char		tzname[TZ_STRLEN_MAX + 1];
	int			len;

4134 4135 4136
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMPTZ(timestamp);

4137
	/*
B
Bruce Momjian 已提交
4138 4139 4140 4141
	 * 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").
	 */
4142
	len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
B
Bruce Momjian 已提交
4143
	memcpy(tzname, VARDATA(zone), len);
4144
	tzname[len] = '\0';
4145
	tzp = pg_tzset(tzname);
4146
	if (tzp)
B
Bruce Momjian 已提交
4147
	{
4148 4149
		/* Apply the timezone change */
		struct pg_tm tm;
B
Bruce Momjian 已提交
4150
		fsec_t		fsec;
4151

4152 4153 4154 4155
		if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4156
		tz = DetermineTimeZoneOffset(&tm, tzp);
4157 4158 4159 4160 4161
		if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("could not convert to time zone \"%s\"",
							tzname)));
4162
	}
4163
	else
B
Bruce Momjian 已提交
4164
	{
4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184
		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);
4185
	}
4186

4187
	PG_RETURN_TIMESTAMPTZ(result);
B
Bruce Momjian 已提交
4188
}
4189 4190 4191 4192 4193 4194 4195 4196

/* 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 已提交
4197
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
4198
	TimestampTz result;
4199 4200 4201 4202 4203 4204
	int			tz;

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

	if (zone->month != 0)
4205 4206
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
4207 4208 4209
				 errmsg("interval time zone \"%s\" must not specify month",
						DatumGetCString(DirectFunctionCall1(interval_out,
												  PointerGetDatum(zone))))));
4210

4211
#ifdef HAVE_INT64_TIMESTAMP
4212
	tz = zone->time / USECS_PER_SEC;
4213
#else
4214
	tz = zone->time;
4215 4216 4217
#endif

	result = dt2local(timestamp, tz);
4218 4219 4220 4221 4222 4223 4224 4225 4226 4227

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

/* timestamp_timestamptz()
 * Convert local timestamp to timestamp at GMT
 */
Datum
timestamp_timestamptz(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
4228
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
4229 4230 4231 4232 4233 4234 4235

	PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
}

static TimestampTz
timestamp2timestamptz(Timestamp timestamp)
{
4236
	TimestampTz result;
B
Bruce Momjian 已提交
4237
	struct pg_tm tt,
4238
			   *tm = &tt;
4239
	fsec_t		fsec;
4240 4241 4242 4243 4244 4245
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
4246
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
4247 4248 4249
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4250

4251
		tz = DetermineTimeZoneOffset(tm, global_timezone);
4252 4253

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
4254 4255 4256
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4257 4258
	}

4259
	return result;
4260 4261 4262 4263 4264 4265 4266 4267
}

/* timestamptz_timestamp()
 * Convert timestamp at GMT to local timestamp
 */
Datum
timestamptz_timestamp(PG_FUNCTION_ARGS)
{
4268
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
4269
	Timestamp	result;
B
Bruce Momjian 已提交
4270
	struct pg_tm tt,
4271
			   *tm = &tt;
4272
	fsec_t		fsec;
4273 4274 4275 4276 4277 4278 4279
	char	   *tzn;
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
4280
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
4281 4282 4283
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4284
		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
4285 4286 4287
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4288 4289 4290 4291 4292
	}
	PG_RETURN_TIMESTAMP(result);
}

/* timestamptz_zone()
4293 4294
 * Evaluate timestamp with time zone type at the specified time zone.
 * Returns a timestamp without time zone.
4295 4296 4297 4298 4299
 */
Datum
timestamptz_zone(PG_FUNCTION_ARGS)
{
	text	   *zone = PG_GETARG_TEXT_P(0);
4300
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4301
	Timestamp	result;
4302
	int			tz;
4303
	pg_tz	   *tzp;
B
Bruce Momjian 已提交
4304 4305
	char		tzname[TZ_STRLEN_MAX + 1];
	int			len;
4306

4307
	if (TIMESTAMP_NOT_FINITE(timestamp))
4308
		PG_RETURN_TIMESTAMP(timestamp);
4309

4310
	/*
B
Bruce Momjian 已提交
4311 4312 4313 4314
	 * 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").
	 */
4315
	len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
B
Bruce Momjian 已提交
4316
	memcpy(tzname, VARDATA(zone), len);
4317
	tzname[len] = '\0';
4318
	tzp = pg_tzset(tzname);
4319
	if (tzp)
B
Bruce Momjian 已提交
4320
	{
4321 4322
		/* Apply the timezone change */
		struct pg_tm tm;
B
Bruce Momjian 已提交
4323
		fsec_t		fsec;
4324

4325 4326 4327 4328 4329 4330 4331 4332 4333
		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)));
4334
	}
4335 4336 4337 4338 4339
	else
	{
		char	   *lowzone;
		int			type,
					val;
4340

4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356
		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);
4357 4358
	}

4359
	PG_RETURN_TIMESTAMP(result);
B
Bruce Momjian 已提交
4360
}
4361

4362 4363
/* timestamptz_izone()
 * Encode timestamp with time zone type with specified time interval as time zone.
4364
 * Returns a timestamp without time zone.
4365 4366
 */
Datum
4367
timestamptz_izone(PG_FUNCTION_ARGS)
4368 4369
{
	Interval   *zone = PG_GETARG_INTERVAL_P(0);
4370
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4371
	Timestamp	result;
4372 4373 4374
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
4375
		PG_RETURN_TIMESTAMP(timestamp);
4376 4377

	if (zone->month != 0)
4378 4379
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
4380 4381 4382
				 errmsg("interval time zone \"%s\" must not specify month",
						DatumGetCString(DirectFunctionCall1(interval_out,
												  PointerGetDatum(zone))))));
4383

4384
#ifdef HAVE_INT64_TIMESTAMP
4385
	tz = -(zone->time / USECS_PER_SEC);
4386
#else
4387
	tz = -zone->time;
4388
#endif
4389

4390
	result = dt2local(timestamp, tz);
4391

4392
	PG_RETURN_TIMESTAMP(result);
B
Bruce Momjian 已提交
4393
}