timestamp.c 88.6 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * timestamp.c
4
 *	  Functions for the built-in SQL92 types "timestamp" and "interval".
5
 *
P
 
PostgreSQL Daemon 已提交
6
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7 8 9 10
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.125 2005/06/14 21:04:40 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

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

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

40

41 42
#ifdef HAVE_INT64_TIMESTAMP
static int64 time2t(const int hour, const int min, const int sec, const fsec_t fsec);
B
Bruce Momjian 已提交
43

44 45 46
#else
static double time2t(const int hour, const int min, const int sec, const fsec_t fsec);
#endif
47 48
static int	EncodeSpecialTimestamp(Timestamp dt, char *str);
static Timestamp dt2local(Timestamp dt, int timezone);
49
static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
50
static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
51
static TimestampTz timestamp2timestamptz(Timestamp timestamp);
52

53 54 55 56 57 58 59 60

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

/* timestamp_in()
 * Convert a string to internal form.
 */
61 62
Datum
timestamp_in(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
63
{
64
	char	   *str = PG_GETARG_CSTRING(0);
65

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

82 83
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
						  field, ftype, MAXDATEFIELDS, &nf);
84 85 86 87
	if (dterr == 0)
		dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
	if (dterr != 0)
		DateTimeParseError(dterr, str, "timestamp");
88 89 90 91

	switch (dtype)
	{
		case DTK_DATE:
92
			if (tm2timestamp(tm, fsec, NULL, &result) != 0)
93 94 95
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range: \"%s\"", str)));
96 97 98
			break;

		case DTK_EPOCH:
99
			result = SetEpochTimestamp();
100 101 102
			break;

		case DTK_LATE:
103
			TIMESTAMP_NOEND(result);
104 105 106
			break;

		case DTK_EARLY:
107
			TIMESTAMP_NOBEGIN(result);
108 109 110
			break;

		case DTK_INVALID:
111 112
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
113
					 errmsg("date/time value \"%s\" is no longer supported", str)));
114

115
			TIMESTAMP_NOEND(result);
116
			break;
117

118
		default:
119 120
			elog(ERROR, "unexpected dtype %d while parsing timestamp \"%s\"",
				 dtype, str);
121
			TIMESTAMP_NOEND(result);
122
	}
M
Marc G. Fournier 已提交
123

124 125
	AdjustTimestampForTypmod(&result, typmod);

126 127
	PG_RETURN_TIMESTAMP(result);
}
M
Marc G. Fournier 已提交
128

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

143 144 145
	if (TIMESTAMP_NOT_FINITE(timestamp))
		EncodeSpecialTimestamp(timestamp, buf);
	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0)
146 147
		EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf);
	else
148 149 150
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
151 152 153 154 155

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

156 157 158 159 160 161 162 163 164 165
/*
 *		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);
166
	Timestamp timestamp;
B
Bruce Momjian 已提交
167
	struct pg_tm tt,
168 169
			   *tm = &tt;
	fsec_t		fsec;
170 171

#ifdef HAVE_INT64_TIMESTAMP
172
	timestamp = (Timestamp) pq_getmsgint64(buf);
173

174
#else
175
	timestamp = (Timestamp) pq_getmsgfloat8(buf);
176
#endif
177 178 179

	/* rangecheck: see if timestamp_out would like it */
	if (TIMESTAMP_NOT_FINITE(timestamp))
B
Bruce Momjian 已提交
180
		 /* ok */ ;
181
	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
182 183 184 185 186
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));

	PG_RETURN_TIMESTAMP(timestamp);
187 188 189 190 191 192 193 194
}

/*
 *		timestamp_send			- converts timestamp to binary format
 */
Datum
timestamp_send(PG_FUNCTION_ARGS)
{
195
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
196 197 198 199 200 201 202 203 204 205 206 207
	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));
}


208 209 210 211 212 213 214
/* timestamp_scale()
 * Adjust time type for specified scale factor.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
timestamp_scale(PG_FUNCTION_ARGS)
{
215
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
216 217 218 219 220
	int32		typmod = PG_GETARG_INT32(1);
	Timestamp	result;

	result = timestamp;

221
	AdjustTimestampForTypmod(&result, typmod);
222 223 224 225 226 227 228

	PG_RETURN_TIMESTAMP(result);
}

static void
AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
{
229
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
230
	static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
231 232 233 234 235 236 237 238 239
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

B
Bruce Momjian 已提交
240
	static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
241 242 243 244 245 246
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
247 248
		INT64CONST(0)
	};
B
Bruce Momjian 已提交
249

250
#else
B
Bruce Momjian 已提交
251
	static const double TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
252 253 254 255 256 257 258 259 260 261 262 263
		1,
		10,
		100,
		1000,
		10000,
		100000,
		1000000
	};
#endif

	if (!TIMESTAMP_NOT_FINITE(*time)
		&& (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
264
	{
265
		if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION)
266 267
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
268 269
			  errmsg("timestamp(%d) precision must be between %d and %d",
					 typmod, 0, MAX_TIMESTAMP_PRECISION)));
270

271 272 273
		/*
		 * Note: this round-to-nearest code is not completely consistent
		 * about rounding values that are exactly halfway between integral
B
Bruce Momjian 已提交
274 275 276
		 * 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?
277
		 */
278 279 280
#ifdef HAVE_INT64_TIMESTAMP
		if (*time >= INT64CONST(0))
		{
281 282
			*time = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) *
					TimestampScales[typmod];
283 284 285
		}
		else
		{
B
Bruce Momjian 已提交
286 287
			*time = -((((-*time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
					  * TimestampScales[typmod]);
288
		}
289
#else
290
		*time = rint((double)*time * TimestampScales[typmod]) / TimestampScales[typmod];
291
#endif
292 293 294
	}
}

295 296 297 298 299 300 301 302

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

304 305 306 307
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
308
	TimestampTz result;
309
	fsec_t		fsec;
B
Bruce Momjian 已提交
310
	struct pg_tm tt,
311 312 313 314
			   *tm = &tt;
	int			tz;
	int			dtype;
	int			nf;
315
	int			dterr;
316 317
	char	   *field[MAXDATEFIELDS];
	int			ftype[MAXDATEFIELDS];
318
	char		workbuf[MAXDATELEN + MAXDATEFIELDS];
319

320 321
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
						  field, ftype, MAXDATEFIELDS, &nf);
322 323 324 325
	if (dterr == 0)
		dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
	if (dterr != 0)
		DateTimeParseError(dterr, str, "timestamp with time zone");
326 327 328 329 330

	switch (dtype)
	{
		case DTK_DATE:
			if (tm2timestamp(tm, fsec, &tz, &result) != 0)
331 332 333
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range: \"%s\"", str)));
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
			break;

		case DTK_EPOCH:
			result = SetEpochTimestamp();
			break;

		case DTK_LATE:
			TIMESTAMP_NOEND(result);
			break;

		case DTK_EARLY:
			TIMESTAMP_NOBEGIN(result);
			break;

		case DTK_INVALID:
349 350
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
351
					 errmsg("date/time value \"%s\" is no longer supported", str)));
352

353 354 355 356
			TIMESTAMP_NOEND(result);
			break;

		default:
357 358
			elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"",
				 dtype, str);
359 360 361
			TIMESTAMP_NOEND(result);
	}

362 363
	AdjustTimestampForTypmod(&result, typmod);

364 365 366 367 368 369 370 371 372
	PG_RETURN_TIMESTAMPTZ(result);
}

/* timestamptz_out()
 * Convert a timestamp to external form.
 */
Datum
timestamptz_out(PG_FUNCTION_ARGS)
{
373
	TimestampTz dt = PG_GETARG_TIMESTAMPTZ(0);
374
	char	   *result;
375
	int			tz;
B
Bruce Momjian 已提交
376
	struct pg_tm tt,
377
			   *tm = &tt;
378
	fsec_t		fsec;
379
	char	   *tzn;
380
	char		buf[MAXDATELEN + 1];
M
Marc G. Fournier 已提交
381

382
	if (TIMESTAMP_NOT_FINITE(dt))
383 384
		EncodeSpecialTimestamp(dt, buf);
	else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)
385 386
		EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf);
	else
387 388 389
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
390

391 392 393
	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}
394

395 396 397 398 399 400 401 402 403 404
/*
 *		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 已提交
405
	TimestampTz timestamp;
406
	int			tz;
B
Bruce Momjian 已提交
407
	struct pg_tm tt,
408 409 410
			   *tm = &tt;
	fsec_t		fsec;
	char	   *tzn;
411 412

#ifdef HAVE_INT64_TIMESTAMP
413
	timestamp = (TimestampTz) pq_getmsgint64(buf);
414

415
#else
416
	timestamp = (TimestampTz) pq_getmsgfloat8(buf);
417
#endif
418 419 420

	/* rangecheck: see if timestamptz_out would like it */
	if (TIMESTAMP_NOT_FINITE(timestamp))
B
Bruce Momjian 已提交
421
		 /* ok */ ;
422
	else if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
423 424 425 426 427
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));

	PG_RETURN_TIMESTAMPTZ(timestamp);
428 429 430 431 432 433 434 435
}

/*
 *		timestamptz_send			- converts timestamptz to binary format
 */
Datum
timestamptz_send(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
436
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
437 438 439 440 441 442 443 444 445 446 447 448
	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));
}


449 450 451 452 453 454 455
/* timestamptz_scale()
 * Adjust time type for specified scale factor.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
timestamptz_scale(PG_FUNCTION_ARGS)
{
456
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
457
	int32		typmod = PG_GETARG_INT32(1);
458
	TimestampTz result;
459 460 461

	result = timestamp;

462
	AdjustTimestampForTypmod(&result, typmod);
463 464 465 466

	PG_RETURN_TIMESTAMPTZ(result);
}

467 468 469 470 471 472 473

/* interval_in()
 * Convert a string to internal form.
 *
 * External format(s):
 *	Uses the generic date/time parsing and decoding routines.
 */
474 475
Datum
interval_in(PG_FUNCTION_ARGS)
476
{
477
	char	   *str = PG_GETARG_CSTRING(0);
478

479 480 481 482
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
483
	Interval   *result;
484
	fsec_t		fsec;
B
Bruce Momjian 已提交
485
	struct pg_tm tt,
486 487 488
			   *tm = &tt;
	int			dtype;
	int			nf;
489
	int			dterr;
490 491
	char	   *field[MAXDATEFIELDS];
	int			ftype[MAXDATEFIELDS];
492
	char		workbuf[256];
493 494 495 496 497 498 499 500 501

	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;

502 503
	dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field,
						  ftype, MAXDATEFIELDS, &nf);
504 505 506 507 508 509 510 511
	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");
	}
512

513
	result = (Interval *) palloc(sizeof(Interval));
514 515 516 517

	switch (dtype)
	{
		case DTK_DELTA:
518
			if (tm2interval(tm, fsec, result) != 0)
519 520 521
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("interval out of range")));
522
			AdjustIntervalForTypmod(result, typmod);
523 524 525
			break;

		case DTK_INVALID:
526 527
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
528
					 errmsg("date/time value \"%s\" is no longer supported", str)));
529
			break;
530

531
		default:
532 533
			elog(ERROR, "unexpected dtype %d while parsing interval \"%s\"",
				 dtype, str);
534
	}
535

536
	PG_RETURN_INTERVAL_P(result);
537
}
538 539 540 541

/* interval_out()
 * Convert a time span to external form.
 */
542 543
Datum
interval_out(PG_FUNCTION_ARGS)
544
{
545
	Interval   *span = PG_GETARG_INTERVAL_P(0);
546
	char	   *result;
B
Bruce Momjian 已提交
547
	struct pg_tm tt,
548
			   *tm = &tt;
549
	fsec_t		fsec;
550 551 552
	char		buf[MAXDATELEN + 1];

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

555
	if (EncodeInterval(tm, fsec, DateStyle, buf) != 0)
556
		elog(ERROR, "could not format interval");
557

558 559 560
	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}
561

562 563 564 565 566 567 568 569 570 571 572 573
/*
 *		interval_recv			- converts external binary format to interval
 */
Datum
interval_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
	Interval   *interval;

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

#ifdef HAVE_INT64_TIMESTAMP
574
	interval->time = pq_getmsgint64(buf);
575

576
#else
577
	interval->time = pq_getmsgfloat8(buf);
578
#endif
579
	interval->month = pq_getmsgint(buf, sizeof(interval->month));
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603

	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
	pq_sendint(&buf, interval->month, sizeof(interval->month));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}


604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
/* 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);
}

static void
AdjustIntervalForTypmod(Interval *interval, int32 typmod)
{
626
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
627
	static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
628 629 630 631 632 633 634 635 636
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

B
Bruce Momjian 已提交
637
	static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
638 639 640 641 642 643
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
644 645
		INT64CONST(0)
	};
B
Bruce Momjian 已提交
646

647
#else
B
Bruce Momjian 已提交
648
	static const double IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
649
		1,
650
		10,
651 652 653 654 655
		100,
		1000,
		10000,
		100000,
		1000000
656 657 658
	};
#endif

B
Bruce Momjian 已提交
659 660
	/*
	 * Unspecified range and precision? Then not necessary to adjust.
661 662
	 * Setting typmod to -1 is the convention for all types.
	 */
663 664
	if (typmod != -1)
	{
665 666
		int			range = INTERVAL_RANGE(typmod);
		int			precision = INTERVAL_PRECISION(typmod);
667

668
		if (range == INTERVAL_FULL_RANGE)
669 670 671
		{
			/* Do nothing... */
		}
672
		else if (range == INTERVAL_MASK(YEAR))
673
		{
674 675
			interval->month = (interval->month / 12) * 12;
			interval->time = 0;
676
		}
677
		else if (range == INTERVAL_MASK(MONTH))
678
		{
679 680
			interval->month %= 12;
			interval->time = 0;
681 682
		}
		/* YEAR TO MONTH */
683
		else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
684
			interval->time = 0;
685

686
		else if (range == INTERVAL_MASK(DAY))
687
		{
688
			interval->month = 0;
689

690
#ifdef HAVE_INT64_TIMESTAMP
691 692
			interval->time = ((int) (interval->time / USECS_PER_DAY)) *
								USECS_PER_DAY;
693

694
#else
695
			interval->time = ((int) (interval->time / SECS_PER_DAY)) * SECS_PER_DAY;
696
#endif
697
		}
698
		else if (range == INTERVAL_MASK(HOUR))
699
		{
700 701
#ifdef HAVE_INT64_TIMESTAMP
			int64		day;
B
Bruce Momjian 已提交
702

703
#else
704
			double		day;
705
#endif
706

707
			interval->month = 0;
708

709
#ifdef HAVE_INT64_TIMESTAMP
710 711 712 713
			day = interval->time / USECS_PER_DAY;
			interval->time -= day * USECS_PER_DAY;
			interval->time = (interval->time / USECS_PER_HOUR) *
								USECS_PER_HOUR;
714

715
#else
716
			TMODULO(interval->time, day, (double)SECS_PER_DAY);
717
			interval->time = ((int) (interval->time / 3600)) * 3600.0;
718
#endif
719
		}
720
		else if (range == INTERVAL_MASK(MINUTE))
721
		{
722 723
#ifdef HAVE_INT64_TIMESTAMP
			int64		hour;
B
Bruce Momjian 已提交
724

725
#else
726
			double		hour;
727
#endif
728

729
			interval->month = 0;
730

731
#ifdef HAVE_INT64_TIMESTAMP
732 733 734 735
			hour = interval->time / USECS_PER_HOUR;
			interval->time -= hour * USECS_PER_HOUR;
			interval->time = (interval->time / USECS_PER_MINUTE) *
								USECS_PER_MINUTE;
736

737
#else
738
			TMODULO(interval->time, hour, 3600.0);
739
			interval->time = ((int) (interval->time / 60)) * 60;
740
#endif
741
		}
742
		else if (range == INTERVAL_MASK(SECOND))
743
		{
744 745
#ifdef HAVE_INT64_TIMESTAMP
			int64		minute;
B
Bruce Momjian 已提交
746

747 748 749
#else
			double		minute;
#endif
750

751
			interval->month = 0;
752

753
#ifdef HAVE_INT64_TIMESTAMP
754 755
			minute = interval->time / USECS_PER_MINUTE;
			interval->time -= minute * USECS_PER_MINUTE;
756

757 758
#else
			TMODULO(interval->time, minute, 60.0);
759
/*			interval->time = (int)(interval->time); */
760
#endif
761 762
		}
		/* DAY TO HOUR */
763 764
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR)))
765
		{
766
			interval->month = 0;
767

768
#ifdef HAVE_INT64_TIMESTAMP
769 770
			interval->time = (interval->time / USECS_PER_HOUR) *
								USECS_PER_HOUR;
771

772
#else
773
			interval->time = ((int) (interval->time / 3600)) * 3600;
774
#endif
775 776
		}
		/* DAY TO MINUTE */
777 778 779
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE)))
780
		{
781
			interval->month = 0;
782

783
#ifdef HAVE_INT64_TIMESTAMP
784 785
			interval->time = (interval->time / USECS_PER_MINUTE) *
								USECS_PER_MINUTE;
786

787
#else
788
			interval->time = ((int) (interval->time / 60)) * 60;
789
#endif
790 791
		}
		/* DAY TO SECOND */
792 793 794 795
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
796
			interval->month = 0;
797

798
		/* HOUR TO MINUTE */
799 800
		else if (range == (INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE)))
801
		{
802 803
#ifdef HAVE_INT64_TIMESTAMP
			int64		day;
B
Bruce Momjian 已提交
804

805
#else
806
			double		day;
807
#endif
808

809
			interval->month = 0;
810

811
#ifdef HAVE_INT64_TIMESTAMP
812 813 814 815
			day = (interval->time / USECS_PER_DAY);
			interval->time -= day * USECS_PER_DAY;
			interval->time = (interval->time / USECS_PER_MINUTE) *
								USECS_PER_MINUTE;
816

817
#else
818
			TMODULO(interval->time, day, (double)SECS_PER_DAY);
819
			interval->time = ((int) (interval->time / 60)) * 60;
820
#endif
821 822
		}
		/* HOUR TO SECOND */
823 824 825
		else if (range == (INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
826
		{
827 828
#ifdef HAVE_INT64_TIMESTAMP
			int64		day;
B
Bruce Momjian 已提交
829

830
#else
831
			double		day;
832
#endif
833

834
			interval->month = 0;
835

836
#ifdef HAVE_INT64_TIMESTAMP
837 838
			day = interval->time / USECS_PER_DAY;
			interval->time -= day * USECS_PER_DAY;
839

840
#else
841
			TMODULO(interval->time, day, (double)SECS_PER_DAY);
842
#endif
843 844
		}
		/* MINUTE TO SECOND */
845 846
		else if (range == (INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
847
		{
848 849
#ifdef HAVE_INT64_TIMESTAMP
			int64		hour;
B
Bruce Momjian 已提交
850

851
#else
852
			double		hour;
853
#endif
854

855
			interval->month = 0;
856

857
#ifdef HAVE_INT64_TIMESTAMP
858 859
			hour = interval->time / USECS_PER_HOUR;
			interval->time -= hour * USECS_PER_HOUR;
860
#else
861
			TMODULO(interval->time, hour, 3600.0);
862
#endif
863 864
		}
		else
865
			elog(ERROR, "unrecognized interval typmod: %d", typmod);
866

867
		/* Need to adjust precision? If not, don't even try! */
868
		if (precision != INTERVAL_FULL_PRECISION)
869
		{
870
			if (precision < 0 || precision > MAX_INTERVAL_PRECISION)
871 872
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
873 874
				errmsg("interval(%d) precision must be between %d and %d",
					   precision, 0, MAX_INTERVAL_PRECISION)));
875

876
			/*
B
Bruce Momjian 已提交
877 878 879 880 881 882
			 * 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?
883
			 */
884 885 886
#ifdef HAVE_INT64_TIMESTAMP
			if (interval->time >= INT64CONST(0))
			{
887
				interval->time = ((interval->time +
888 889 890
									IntervalOffsets[precision]) /
									IntervalScales[precision]) *
									IntervalScales[precision];
891 892 893
			}
			else
			{
894 895 896 897
				interval->time = -(((-interval->time +
									IntervalOffsets[precision]) /
									IntervalScales[precision]) *
									IntervalScales[precision]);
898
			}
899
#else
900 901 902
			interval->time = rint(((double) interval->time) *
									IntervalScales[precision]) /
									IntervalScales[precision];
903
#endif
904 905 906 907 908 909
		}
	}

	return;
}

910 911 912 913

/* EncodeSpecialTimestamp()
 * Convert reserved timestamp data type to string.
 */
914
static int
915 916
EncodeSpecialTimestamp(Timestamp dt, char *str)
{
917 918 919 920 921 922
	if (TIMESTAMP_IS_NOBEGIN(dt))
		strcpy(str, EARLY);
	else if (TIMESTAMP_IS_NOEND(dt))
		strcpy(str, LATE);
	else
		return FALSE;
M
Marc G. Fournier 已提交
923

924
	return TRUE;
925 926
}	/* EncodeSpecialTimestamp() */

927 928
Datum
now(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
929
{
930
	TimestampTz result;
931
	AbsoluteTime sec;
932
	int			usec;
933

934
	sec = GetCurrentTransactionStartTimeUsec(&usec);
935

936
	result = AbsoluteTimeUsecToTimestampTz(sec, usec);
937

938
	PG_RETURN_TIMESTAMPTZ(result);
M
Marc G. Fournier 已提交
939 940
}

941 942 943 944 945 946
Datum
pgsql_postmaster_start_time(PG_FUNCTION_ARGS)
{
	PG_RETURN_TIMESTAMPTZ(StartTime);
}

947
void
948
dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
949
{
950 951
#ifdef HAVE_INT64_TIMESTAMP
	int64		time;
B
Bruce Momjian 已提交
952

953
#else
954
	double		time;
955
#endif
956 957 958

	time = jd;

959
#ifdef HAVE_INT64_TIMESTAMP
960 961 962 963 964 965
	*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);
966
#else
967 968 969 970
	*hour = time / 3600;
	time -= (*hour) * 3600;
	*min = time / 60;
	time -= (*min) * 60;
971 972 973
	*sec = time;
	*fsec = JROUND(time - *sec);
#endif
974 975 976 977 978

	return;
}	/* dt2time() */


979 980 981
/*
 * timestamp2tm() - Convert timestamp data type to POSIX time structure.
 *
982 983 984 985 986 987 988
 * 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
 */
int
B
Bruce Momjian 已提交
989
timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char **tzn)
990
{
991
	Timestamp date;
992
	Timestamp	time;
993
	pg_time_t	utime;
994

995 996 997 998
	/*
	 * 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.
999 1000
	 */
	if (HasCTZSet && (tzp != NULL))
1001
	{
1002
#ifdef HAVE_INT64_TIMESTAMP
1003
		dt -= CTimeZone * USECS_PER_SEC;
1004
#else
1005
		dt -= CTimeZone;
1006
#endif
1007
	}
1008

1009
	time = dt;
1010
#ifdef HAVE_INT64_TIMESTAMP
1011
	TMODULO(time, date, USECS_PER_DAY);
1012 1013 1014

	if (time < INT64CONST(0))
	{
1015
		time += USECS_PER_DAY;
1016
		date -= 1;
1017 1018
	}
#else
1019
	TMODULO(time, date, (double)SECS_PER_DAY);
1020 1021 1022

	if (time < 0)
	{
1023
		time += SECS_PER_DAY;
1024
		date -=1;
1025
	}
1026
#endif
1027 1028

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

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

1035
	j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1036
	dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1037

1038 1039
	/* Done if no TZ conversion wanted */
	if (tzp == NULL)
1040
	{
1041 1042 1043 1044 1045 1046 1047
		tm->tm_isdst = -1;
		tm->tm_gmtoff = 0;
		tm->tm_zone = NULL;
		if (tzn != NULL)
			*tzn = NULL;
		return 0;
	}
1048

1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
	/*
	 * We have a brute force time zone per SQL99? Then use it without
	 * change since we have already rotated to the time zone.
	 */
	if (HasCTZSet)
	{
		*tzp = CTimeZone;
		tm->tm_isdst = 0;
		tm->tm_gmtoff = CTimeZone;
		tm->tm_zone = NULL;
		if (tzn != NULL)
			*tzn = NULL;
		return 0;
	}

	/*
	 * If the time falls within the range of pg_time_t, use pg_localtime()
	 * to rotate to the local time zone.
	 *
	 * First, convert to an integral timestamp, avoiding possibly
	 * platform-specific roundoff-in-wrong-direction errors, and adjust to
B
Bruce Momjian 已提交
1070
	 * Unix epoch.	Then see if we can convert to pg_time_t without loss.
1071 1072 1073
	 * This coding avoids hardwiring any assumptions about the width of
	 * pg_time_t, so it should behave sanely on machines without int64.
	 */
1074
#ifdef HAVE_INT64_TIMESTAMP
1075
	dt = (dt - *fsec) / USECS_PER_SEC +
1076
		(POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
1077
#else
1078
	dt = rint(dt - *fsec +
1079
			  (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
1080
#endif
1081 1082 1083
	utime = (pg_time_t) dt;
	if ((Timestamp) utime == dt)
	{
1084
		struct pg_tm *tx = pg_localtime(&utime, global_timezone);
1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097

		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;
		*tzp = -(tm->tm_gmtoff);
		if (tzn != NULL)
			*tzn = (char *) tm->tm_zone;
1098 1099 1100
	}
	else
	{
1101 1102 1103 1104 1105
		/*
		 * When out of range of pg_time_t, treat as GMT
		 */
		*tzp = 0;
		/* Mark this as *no* time zone available */
1106
		tm->tm_isdst = -1;
1107 1108
		tm->tm_gmtoff = 0;
		tm->tm_zone = NULL;
1109 1110 1111 1112 1113
		if (tzn != NULL)
			*tzn = NULL;
	}

	return 0;
1114
}
1115 1116 1117 1118 1119 1120


/* 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.
1121
 *
1122
 * Returns -1 on failure (value out of range).
1123 1124
 */
int
1125
tm2timestamp(struct pg_tm * tm, fsec_t fsec, int *tzp, Timestamp *result)
1126
{
1127
#ifdef HAVE_INT64_TIMESTAMP
1128
	int date;
B
Bruce Momjian 已提交
1129
	int64		time;
B
Bruce Momjian 已提交
1130

1131
#else
1132
	double date,
1133
				time;
1134
#endif
1135 1136 1137 1138 1139

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

1140
	date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1141

1142 1143
	time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
#ifdef HAVE_INT64_TIMESTAMP
1144
	*result = date * USECS_PER_DAY + time;
1145
	/* check for major overflow */
1146
	if ((*result - time) / USECS_PER_DAY != date)
1147 1148
		return -1;
	/* check for just-barely overflow (okay except time-of-day wraps) */
1149 1150
	if ((*result < 0 && date >= 0) ||
		(*result >= 0 && date < 0))
1151
		return -1;
1152
#else
1153
	*result = date * SECS_PER_DAY + time;
1154
#endif
1155 1156 1157 1158
	if (tzp != NULL)
		*result = dt2local(*result, -(*tzp));

	return 0;
1159
}
1160 1161 1162 1163 1164


/* interval2tm()
 * Convert a interval data type to a tm structure.
 */
B
Bruce Momjian 已提交
1165
int
1166
interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
1167
{
1168 1169
#ifdef HAVE_INT64_TIMESTAMP
	int64		time;
B
Bruce Momjian 已提交
1170

1171
#else
1172
	double		time;
1173
#endif
1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188

	if (span.month != 0)
	{
		tm->tm_year = span.month / 12;
		tm->tm_mon = span.month % 12;

	}
	else
	{
		tm->tm_year = 0;
		tm->tm_mon = 0;
	}

	time = span.time;

1189
#ifdef HAVE_INT64_TIMESTAMP
1190 1191 1192 1193 1194 1195 1196 1197
	tm->tm_mday = (time / USECS_PER_DAY);
	time -= (tm->tm_mday * USECS_PER_DAY);
	tm->tm_hour = (time / USECS_PER_HOUR);
	time -= (tm->tm_hour * USECS_PER_HOUR);
	tm->tm_min = (time / USECS_PER_MINUTE);
	time -= (tm->tm_min * USECS_PER_MINUTE);
	tm->tm_sec = (time / USECS_PER_SEC);
	*fsec = (time - (tm->tm_sec * USECS_PER_SEC));
1198
#else
1199
	TMODULO(time, tm->tm_mday, (double)SECS_PER_DAY);
1200 1201 1202 1203
	TMODULO(time, tm->tm_hour, 3600e0);
	TMODULO(time, tm->tm_min, 60e0);
	TMODULO(time, tm->tm_sec, 1e0);
	*fsec = time;
1204
#endif
1205 1206

	return 0;
1207
}
1208

B
Bruce Momjian 已提交
1209
int
1210
tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span)
1211
{
1212
	span->month = tm->tm_year * 12 + tm->tm_mon;
1213
#ifdef HAVE_INT64_TIMESTAMP
1214 1215 1216 1217
	span->time = (((((((tm->tm_mday * INT64CONST(24)) +
						tm->tm_hour) * INT64CONST(60)) +
						tm->tm_min) * INT64CONST(60)) +
						tm->tm_sec) * USECS_PER_SEC) + fsec;
1218
#else
1219 1220 1221 1222
	span->time = (((((tm->tm_mday * 24.0) +
						tm->tm_hour) * 60.0) +
						tm->tm_min) * 60.0) +
						tm->tm_sec;
1223
	span->time = JROUND(span->time + fsec);
1224
#endif
1225 1226

	return 0;
1227
}
1228

1229 1230 1231 1232
#ifdef HAVE_INT64_TIMESTAMP
static int64
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
{
1233
	return (((((hour * 60) + min) * 60) + sec) * USECS_PER_SEC) + fsec;
1234
}	/* time2t() */
B
Bruce Momjian 已提交
1235

1236
#else
1237
static double
1238
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
1239
{
1240
	return (((hour * 60) + min) * 60) + sec + fsec;
1241
}	/* time2t() */
1242
#endif
1243

1244
static Timestamp
1245 1246
dt2local(Timestamp dt, int tz)
{
1247
#ifdef HAVE_INT64_TIMESTAMP
1248
	dt -= (tz * USECS_PER_SEC);
1249
#else
1250 1251
	dt -= tz;
	dt = JROUND(dt);
1252
#endif
1253 1254 1255 1256 1257 1258 1259 1260 1261
	return dt;
}	/* dt2local() */


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


1262 1263
Datum
timestamp_finite(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1264
{
1265
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1266

B
Bruce Momjian 已提交
1267
	PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp));
1268
}
M
Marc G. Fournier 已提交
1269

1270 1271
Datum
interval_finite(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1272
{
1273
	PG_RETURN_BOOL(true);
1274
}
1275 1276 1277 1278 1279 1280


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

1281
void
1282
GetEpochTime(struct pg_tm * tm)
1283
{
B
Bruce Momjian 已提交
1284 1285
	struct pg_tm *t0;
	pg_time_t	epoch = 0;
1286

1287
	t0 = pg_gmtime(&epoch);
1288 1289 1290 1291 1292 1293 1294 1295

	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;

1296
	tm->tm_year += 1900;
1297
	tm->tm_mon++;
1298
}
1299 1300

Timestamp
1301
SetEpochTimestamp(void)
1302
{
1303
	Timestamp	dt;
B
Bruce Momjian 已提交
1304
	struct pg_tm tt,
1305
			   *tm = &tt;
1306

1307
	GetEpochTime(tm);
1308
	/* we don't bother to test for failure ... */
1309
	tm2timestamp(tm, 0, NULL, &dt);
M
Marc G. Fournier 已提交
1310

1311
	return dt;
1312
}	/* SetEpochTimestamp() */
1313

1314
/*
1315 1316 1317
 * We are currently sharing some code between timestamp and timestamptz.
 * The comparison functions are among them. - thomas 2001-09-25
 *
1318
 *		timestamp_relop - is timestamp1 relop timestamp2
1319 1320
 *
 *		collate invalid timestamp at the end
1321
 */
1322
int
1323 1324
timestamp_cmp_internal(Timestamp dt1, Timestamp dt2)
{
1325
#ifdef HAVE_INT64_TIMESTAMP
1326
	return (dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0);
1327
#else
1328

1329 1330 1331
	/*
	 * When using float representation, we have to be wary of NaNs.
	 *
1332 1333
	 * We consider all NANs to be equal and larger than any non-NAN. This is
	 * somewhat arbitrary; the important thing is to have a consistent
1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
	 * sort order.
	 */
	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
1357 1358
}

1359 1360
Datum
timestamp_eq(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1361
{
1362 1363
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1364

1365
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
1366
}
M
Marc G. Fournier 已提交
1367

1368 1369
Datum
timestamp_ne(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1370
{
1371 1372
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1373

1374
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
1375
}
M
Marc G. Fournier 已提交
1376

1377 1378
Datum
timestamp_lt(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1379
{
1380 1381
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1382

1383
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
1384
}
M
Marc G. Fournier 已提交
1385

1386 1387
Datum
timestamp_gt(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1388
{
1389 1390
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1391

1392
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
1393
}
1394

1395 1396
Datum
timestamp_le(PG_FUNCTION_ARGS)
1397
{
1398 1399
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1400

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

1404 1405
Datum
timestamp_ge(PG_FUNCTION_ARGS)
1406
{
1407 1408
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1409

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

1413 1414
Datum
timestamp_cmp(PG_FUNCTION_ARGS)
1415
{
1416 1417
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1418

1419
	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
1420
}
1421 1422


1423 1424 1425 1426 1427 1428 1429 1430
/*
 * Crosstype comparison functions for timestamp vs timestamptz
 */

Datum
timestamp_eq_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1431 1432
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442

	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 已提交
1443 1444
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1445 1446 1447 1448 1449 1450 1451 1452 1453 1454

	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 已提交
1455 1456
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466

	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 已提交
1467 1468
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478

	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 已提交
1479 1480
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1481 1482 1483 1484 1485 1486 1487 1488 1489 1490

	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 已提交
1491 1492
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502

	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 已提交
1503 1504
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1505 1506 1507 1508 1509 1510 1511 1512 1513

	dt1 = timestamp2timestamptz(timestampVal);

	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}

Datum
timestamptz_eq_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1514
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1515
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1516
	TimestampTz dt2;
1517 1518 1519 1520 1521 1522 1523 1524 1525

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_ne_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1526
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1527
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1528
	TimestampTz dt2;
1529 1530 1531 1532 1533 1534 1535 1536 1537

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_lt_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1538
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1539
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1540
	TimestampTz dt2;
1541 1542 1543 1544 1545 1546 1547 1548 1549

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_gt_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1550
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1551
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1552
	TimestampTz dt2;
1553 1554 1555 1556 1557 1558 1559 1560 1561

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_le_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1562
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1563
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1564
	TimestampTz dt2;
1565 1566 1567 1568 1569 1570 1571 1572 1573

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_ge_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1574
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1575
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1576
	TimestampTz dt2;
1577 1578 1579 1580 1581 1582 1583 1584 1585

	dt2 = timestamp2timestamptz(timestampVal);

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

Datum
timestamptz_cmp_timestamp(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
1586
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
1587
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(1);
B
Bruce Momjian 已提交
1588
	TimestampTz dt2;
1589 1590 1591 1592 1593 1594 1595

	dt2 = timestamp2timestamptz(timestampVal);

	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}


1596 1597
/*
 *		interval_relop	- is interval1 relop interval2
1598 1599
 *
 *		collate invalid interval at the end
1600
 */
1601 1602 1603
static int
interval_cmp_internal(Interval *interval1, Interval *interval2)
{
1604 1605 1606
#ifdef HAVE_INT64_TIMESTAMP
	int64		span1,
				span2;
B
Bruce Momjian 已提交
1607

1608
#else
1609 1610
	double		span1,
				span2;
1611
#endif
1612

1613
	span1 = interval1->time;
1614 1615 1616 1617
	span2 = interval2->time;

#ifdef HAVE_INT64_TIMESTAMP
	if (interval1->month != 0)
1618
		span1 += interval1->month * INT64CONST(30) * USECS_PER_DAY;
1619
	if (interval2->month != 0)
1620
		span2 += interval2->month * INT64CONST(30) * USECS_PER_DAY;
1621
#else
1622
	if (interval1->month != 0)
1623
		span1 += interval1->month * (30.0 * SECS_PER_DAY);
1624
	if (interval2->month != 0)
1625
		span2 += interval2->month * (30.0 * SECS_PER_DAY);
1626
#endif
1627

1628
	return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
1629 1630
}

1631 1632
Datum
interval_eq(PG_FUNCTION_ARGS)
1633
{
1634 1635
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1636

1637
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0);
1638
}
1639

1640 1641
Datum
interval_ne(PG_FUNCTION_ARGS)
1642
{
1643 1644
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1645

1646
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0);
1647
}
1648

1649 1650
Datum
interval_lt(PG_FUNCTION_ARGS)
1651
{
1652 1653
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1654

1655
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0);
1656
}
1657

1658 1659
Datum
interval_gt(PG_FUNCTION_ARGS)
1660
{
1661 1662
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1663

1664
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0);
1665
}
1666

1667 1668
Datum
interval_le(PG_FUNCTION_ARGS)
1669
{
1670 1671
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1672

1673
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0);
1674
}
1675

1676 1677
Datum
interval_ge(PG_FUNCTION_ARGS)
1678
{
1679 1680
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1681

1682
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0);
1683
}
1684

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

1691
	PG_RETURN_INT32(interval_cmp_internal(interval1, interval2));
1692
}
1693

1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706
/*
 * 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
	 * sizeof(Interval), so that any garbage pad bytes in the structure
	 * won't be included in the hash!
	 */
1707
	return hash_any((unsigned char *) key, sizeof(key->time) + sizeof(key->month));
1708 1709
}

1710 1711 1712 1713 1714
/* 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.
1715
 */
1716 1717
Datum
overlaps_timestamp(PG_FUNCTION_ARGS)
1718
{
B
Bruce Momjian 已提交
1719 1720
	/*
	 * The arguments are Timestamps, but we leave them as generic Datums
1721 1722
	 * to avoid unnecessary conversions between value and reference forms
	 * --- not to mention possible dereferences of null pointers.
1723 1724 1725 1726 1727
	 */
	Datum		ts1 = PG_GETARG_DATUM(0);
	Datum		te1 = PG_GETARG_DATUM(1);
	Datum		ts2 = PG_GETARG_DATUM(2);
	Datum		te2 = PG_GETARG_DATUM(3);
1728 1729 1730 1731
	bool		ts1IsNull = PG_ARGISNULL(0);
	bool		te1IsNull = PG_ARGISNULL(1);
	bool		ts2IsNull = PG_ARGISNULL(2);
	bool		te2IsNull = PG_ARGISNULL(3);
1732 1733 1734 1735 1736 1737

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

1738
	/*
B
Bruce Momjian 已提交
1739 1740 1741
	 * 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.
1742 1743
	 */
	if (ts1IsNull)
1744
	{
1745 1746 1747
		if (te1IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
1748
		ts1 = te1;
1749
		te1IsNull = true;
1750
	}
1751
	else if (!te1IsNull)
1752
	{
1753 1754
		if (TIMESTAMP_GT(ts1, te1))
		{
B
Bruce Momjian 已提交
1755
			Datum		tt = ts1;
1756

1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767
			ts1 = te1;
			te1 = tt;
		}
	}

	/* Likewise for interval 2. */
	if (ts2IsNull)
	{
		if (te2IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
1768
		ts2 = te2;
1769
		te2IsNull = true;
1770
	}
1771 1772 1773 1774
	else if (!te2IsNull)
	{
		if (TIMESTAMP_GT(ts2, te2))
		{
B
Bruce Momjian 已提交
1775
			Datum		tt = ts2;
1776

1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787
			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 已提交
1788 1789
		/*
		 * This case is ts1 < te2 OR te1 < te2, which may look redundant
1790 1791 1792 1793 1794 1795 1796 1797
		 * but in the presence of nulls it's not quite completely so.
		 */
		if (te2IsNull)
			PG_RETURN_NULL();
		if (TIMESTAMP_LT(ts1, te2))
			PG_RETURN_BOOL(true);
		if (te1IsNull)
			PG_RETURN_NULL();
B
Bruce Momjian 已提交
1798 1799 1800

		/*
		 * If te1 is not null then we had ts1 <= te1 above, and we just
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813
		 * found ts1 >= te2, hence te1 >= te2.
		 */
		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 已提交
1814 1815 1816

		/*
		 * If te2 is not null then we had ts2 <= te2 above, and we just
1817 1818 1819 1820 1821 1822
		 * found ts2 >= te1, hence te2 >= te1.
		 */
		PG_RETURN_BOOL(false);
	}
	else
	{
B
Bruce Momjian 已提交
1823 1824 1825 1826
		/*
		 * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
		 * rather silly way of saying "true if both are nonnull, else
		 * null".
1827 1828 1829 1830 1831
		 */
		if (te1IsNull || te2IsNull)
			PG_RETURN_NULL();
		PG_RETURN_BOOL(true);
	}
1832 1833 1834 1835

#undef TIMESTAMP_GT
#undef TIMESTAMP_LT
}
1836

1837 1838 1839 1840 1841

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

1842 1843
Datum
timestamp_smaller(PG_FUNCTION_ARGS)
1844
{
1845 1846 1847
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
1848

1849 1850 1851 1852 1853
	/* use timestamp_cmp_internal to be sure this agrees with comparisons */
	if (timestamp_cmp_internal(dt1, dt2) < 0)
		result = dt1;
	else
		result = dt2;
1854 1855
	PG_RETURN_TIMESTAMP(result);
}
1856

1857 1858
Datum
timestamp_larger(PG_FUNCTION_ARGS)
1859
{
1860 1861 1862
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
1863

1864 1865 1866 1867
	if (timestamp_cmp_internal(dt1, dt2) > 0)
		result = dt1;
	else
		result = dt2;
1868 1869
	PG_RETURN_TIMESTAMP(result);
}
1870 1871


1872 1873
Datum
timestamp_mi(PG_FUNCTION_ARGS)
1874
{
1875 1876
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1877 1878
	Interval   *result;

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

1881 1882
	if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
	{
1883 1884
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1885
				 errmsg("cannot subtract infinite timestamps")));
1886

1887 1888
		result->time = 0;
	}
1889
	else
1890 1891 1892
#ifdef HAVE_INT64_TIMESTAMP
		result->time = (dt1 - dt2);
#else
1893
		result->time = JROUND(dt1 - dt2);
1894
#endif
1895

1896 1897
	result->month = 0;

1898 1899
	PG_RETURN_INTERVAL_P(result);
}
1900 1901


1902
/* timestamp_pl_interval()
1903 1904 1905 1906 1907 1908 1909 1910
 * Add a interval to a timestamp 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".
 */
1911
Datum
1912
timestamp_pl_interval(PG_FUNCTION_ARGS)
1913
{
1914
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1915 1916
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Timestamp	result;
1917 1918 1919

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
1920

1921 1922 1923 1924
	else
	{
		if (span->month != 0)
		{
B
Bruce Momjian 已提交
1925
			struct pg_tm tt,
1926
					   *tm = &tt;
1927
			fsec_t		fsec;
1928

1929
			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
1930 1931 1932 1933 1934 1935
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));

			tm->tm_mon += span->month;
			if (tm->tm_mon > 12)
1936
			{
1937 1938
				tm->tm_year += (tm->tm_mon - 1) / 12;
				tm->tm_mon = ((tm->tm_mon - 1) % 12) + 1;
1939
			}
1940
			else if (tm->tm_mon < 1)
1941
			{
1942 1943
				tm->tm_year += tm->tm_mon / 12 - 1;
				tm->tm_mon = tm->tm_mon % 12 + 12;
1944
			}
1945 1946 1947 1948 1949

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

1950
			if (tm2timestamp(tm, fsec, NULL, &timestamp) !=0)
1951 1952 1953
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
1954 1955
		}

1956
		timestamp +=span->time;
1957 1958 1959 1960 1961 1962 1963
		result = timestamp;
	}

	PG_RETURN_TIMESTAMP(result);
}

Datum
1964
timestamp_mi_interval(PG_FUNCTION_ARGS)
1965
{
1966
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1967 1968 1969 1970 1971 1972
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Interval	tspan;

	tspan.month = -span->month;
	tspan.time = -span->time;

1973
	return DirectFunctionCall2(timestamp_pl_interval,
1974 1975 1976 1977 1978
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}


1979
/* timestamptz_pl_interval()
1980 1981 1982 1983 1984 1985 1986 1987 1988
 * 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
1989
timestamptz_pl_interval(PG_FUNCTION_ARGS)
1990
{
1991
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
1992
	Interval   *span = PG_GETARG_INTERVAL_P(1);
1993
	TimestampTz result;
1994 1995 1996
	int			tz;
	char	   *tzn;

1997 1998
	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
1999

2000 2001 2002 2003
	else
	{
		if (span->month != 0)
		{
B
Bruce Momjian 已提交
2004
			struct pg_tm tt,
2005
					   *tm = &tt;
2006
			fsec_t		fsec;
2007

2008
			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
2009 2010 2011
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2012

2013 2014 2015
			tm->tm_mon += span->month;
			if (tm->tm_mon > 12)
			{
2016 2017
				tm->tm_year += (tm->tm_mon - 1) / 12;
				tm->tm_mon = ((tm->tm_mon - 1) % 12) + 1;
2018
			}
2019
			else if (tm->tm_mon < 1)
2020
			{
2021 2022
				tm->tm_year += tm->tm_mon / 12 - 1;
				tm->tm_mon = tm->tm_mon % 12 + 12;
2023
			}
2024 2025 2026 2027 2028 2029 2030

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

			tz = DetermineLocalTimeZone(tm);

2031
			if (tm2timestamp(tm, fsec, &tz, &timestamp) !=0)
2032 2033 2034
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2035 2036
		}

2037
		timestamp +=span->time;
2038
		result = timestamp;
2039 2040
	}

2041 2042
	PG_RETURN_TIMESTAMP(result);
}
2043

2044
Datum
2045
timestamptz_mi_interval(PG_FUNCTION_ARGS)
2046
{
2047
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2048
	Interval   *span = PG_GETARG_INTERVAL_P(1);
2049 2050
	Interval	tspan;

B
Bruce Momjian 已提交
2051 2052
	tspan.month = -span->month;
	tspan.time = -span->time;
2053

2054
	return DirectFunctionCall2(timestamptz_pl_interval,
2055 2056 2057
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}
2058 2059


2060 2061
Datum
interval_um(PG_FUNCTION_ARGS)
2062
{
2063
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2064 2065
	Interval   *result;

2066
	result = (Interval *) palloc(sizeof(Interval));
2067 2068 2069 2070

	result->time = -(interval->time);
	result->month = -(interval->month);

2071 2072
	PG_RETURN_INTERVAL_P(result);
}
2073 2074


2075 2076
Datum
interval_smaller(PG_FUNCTION_ARGS)
2077
{
2078 2079
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
2080
	Interval   *result;
B
Bruce Momjian 已提交
2081

2082 2083 2084
	/* use interval_cmp_internal to be sure this agrees with comparisons */
	if (interval_cmp_internal(interval1, interval2) < 0)
		result = interval1;
2085
	else
2086
		result = interval2;
2087 2088
	PG_RETURN_INTERVAL_P(result);
}
2089

2090 2091
Datum
interval_larger(PG_FUNCTION_ARGS)
2092
{
2093 2094
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
2095
	Interval   *result;
B
Bruce Momjian 已提交
2096

2097 2098
	if (interval_cmp_internal(interval1, interval2) > 0)
		result = interval1;
2099
	else
2100
		result = interval2;
2101 2102
	PG_RETURN_INTERVAL_P(result);
}
2103

2104 2105
Datum
interval_pl(PG_FUNCTION_ARGS)
2106
{
2107 2108
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
2109 2110
	Interval   *result;

2111
	result = (Interval *) palloc(sizeof(Interval));
2112 2113

	result->month = (span1->month + span2->month);
2114 2115 2116
#ifdef HAVE_INT64_TIMESTAMP
	result->time = (span1->time + span2->time);
#else
2117
	result->time = JROUND(span1->time + span2->time);
2118
#endif
2119

2120 2121
	PG_RETURN_INTERVAL_P(result);
}
2122

2123 2124
Datum
interval_mi(PG_FUNCTION_ARGS)
2125
{
2126 2127
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
2128 2129
	Interval   *result;

2130
	result = (Interval *) palloc(sizeof(Interval));
2131 2132

	result->month = (span1->month - span2->month);
2133 2134 2135
#ifdef HAVE_INT64_TIMESTAMP
	result->time = (span1->time - span2->time);
#else
2136
	result->time = JROUND(span1->time - span2->time);
2137
#endif
2138

2139 2140
	PG_RETURN_INTERVAL_P(result);
}
2141

2142 2143
Datum
interval_mul(PG_FUNCTION_ARGS)
2144
{
2145 2146
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	float8		factor = PG_GETARG_FLOAT8(1);
2147
	Interval   *result;
B
Bruce Momjian 已提交
2148

2149 2150
#ifdef HAVE_INT64_TIMESTAMP
	int64		months;
B
Bruce Momjian 已提交
2151

2152
#else
2153
	double		months;
2154
#endif
2155

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

2158
	months = (span1->month * factor);
2159 2160 2161
#ifdef HAVE_INT64_TIMESTAMP
	result->month = months;
	result->time = (span1->time * factor);
2162
	result->time += (months - result->month) * INT64CONST(30) *
2163
					USECS_PER_DAY;
2164
#else
2165
	result->month = rint(months);
2166
	result->time = JROUND(span1->time * factor);
2167
	/* evaluate fractional months as 30 days */
2168
	result->time += JROUND((months - result->month) * 30 * SECS_PER_DAY);
2169
#endif
2170

2171 2172
	PG_RETURN_INTERVAL_P(result);
}
2173

2174 2175
Datum
mul_d_interval(PG_FUNCTION_ARGS)
2176
{
2177 2178 2179
	/* Args are float8 and Interval *, but leave them as generic Datum */
	Datum		factor = PG_GETARG_DATUM(0);
	Datum		span1 = PG_GETARG_DATUM(1);
2180

2181 2182 2183 2184 2185
	return DirectFunctionCall2(interval_mul, span1, factor);
}

Datum
interval_div(PG_FUNCTION_ARGS)
2186
{
2187
	Interval   *span = PG_GETARG_INTERVAL_P(0);
2188
	float8		factor = PG_GETARG_FLOAT8(1);
2189
	Interval   *result;
B
Bruce Momjian 已提交
2190

2191
#ifndef HAVE_INT64_TIMESTAMP
2192
	double		months;
2193
#endif
2194

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

2197
	if (factor == 0.0)
2198 2199 2200
		ereport(ERROR,
				(errcode(ERRCODE_DIVISION_BY_ZERO),
				 errmsg("division by zero")));
2201

2202 2203 2204 2205
#ifdef HAVE_INT64_TIMESTAMP
	result->month = (span->month / factor);
	result->time = (span->time / factor);
	/* evaluate fractional months as 30 days */
2206
	result->time += ((span->month - (result->month * factor)) *
2207
					INT64CONST(30) * USECS_PER_DAY) / factor;
2208
#else
2209
	months = span->month / factor;
2210
	result->month = rint(months);
2211
	result->time = JROUND(span->time / factor);
2212
	/* evaluate fractional months as 30 days */
2213
	result->time += JROUND((months - result->month) * 30 * SECS_PER_DAY);
2214
#endif
2215

2216 2217
	PG_RETURN_INTERVAL_P(result);
}
2218

2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241
/*
 * 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;

	/* We assume the input is array of interval */
	deconstruct_array(transarray,
2242
					  INTERVALOID, 12, false, 'd',
2243 2244
					  &transdatums, &ndatums);
	if (ndatums != 2)
2245
		elog(ERROR, "expected 2-element interval array");
B
Bruce Momjian 已提交
2246

2247 2248 2249
	/*
	 * XXX memcpy, instead of just extracting a pointer, to work around
	 * buggy array code: it won't ensure proper alignment of Interval
B
Bruce Momjian 已提交
2250 2251
	 * objects on machines where double requires 8-byte alignment. That
	 * should be fixed, but in the meantime...
2252
	 *
B
Bruce Momjian 已提交
2253 2254
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2255
	 */
2256 2257
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2258 2259

	newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
2260 2261
												IntervalPGetDatum(&sumX),
											 IntervalPGetDatum(newval)));
2262 2263 2264 2265 2266 2267
	N.time += 1;

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

	result = construct_array(transdatums, 2,
2268
							 INTERVALOID, 12, false, 'd');
2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283

	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;

	/* We assume the input is array of interval */
	deconstruct_array(transarray,
2284
					  INTERVALOID, 12, false, 'd',
2285 2286
					  &transdatums, &ndatums);
	if (ndatums != 2)
2287
		elog(ERROR, "expected 2-element interval array");
B
Bruce Momjian 已提交
2288

2289 2290 2291
	/*
	 * XXX memcpy, instead of just extracting a pointer, to work around
	 * buggy array code: it won't ensure proper alignment of Interval
B
Bruce Momjian 已提交
2292 2293
	 * objects on machines where double requires 8-byte alignment. That
	 * should be fixed, but in the meantime...
2294
	 *
B
Bruce Momjian 已提交
2295 2296
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2297
	 */
2298 2299
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310

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


2311 2312 2313 2314 2315 2316
/* 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.
 */
2317 2318
Datum
timestamp_age(PG_FUNCTION_ARGS)
2319
{
2320 2321
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
2322
	Interval   *result;
2323
	fsec_t		fsec,
2324 2325
				fsec1,
				fsec2;
B
Bruce Momjian 已提交
2326
	struct pg_tm tt,
2327
			   *tm = &tt;
B
Bruce Momjian 已提交
2328
	struct pg_tm tt1,
2329
			   *tm1 = &tt1;
B
Bruce Momjian 已提交
2330
	struct pg_tm tt2,
2331 2332
			   *tm2 = &tt2;

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

2335 2336
	if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0 &&
		timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0)
2337 2338
	{
		fsec = (fsec1 - fsec2);
2339 2340 2341 2342 2343 2344
		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;
2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357

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

2358
		while (tm->tm_sec < 0)
2359 2360 2361 2362 2363
		{
			tm->tm_sec += 60;
			tm->tm_min--;
		}

2364
		while (tm->tm_min < 0)
2365 2366 2367 2368 2369
		{
			tm->tm_min += 60;
			tm->tm_hour--;
		}

2370
		while (tm->tm_hour < 0)
2371 2372 2373 2374 2375
		{
			tm->tm_hour += 24;
			tm->tm_mday--;
		}

2376
		while (tm->tm_mday < 0)
2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389
		{
			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--;
			}
		}

2390
		while (tm->tm_mon < 0)
2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408
		{
			tm->tm_mon += 12;
			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)
2409 2410 2411
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("interval out of range")));
2412 2413
	}
	else
2414 2415 2416
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2417

2418 2419
	PG_RETURN_INTERVAL_P(result);
}
2420 2421


2422 2423 2424 2425 2426
/* 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.
2427
 */
2428
Datum
2429
timestamptz_age(PG_FUNCTION_ARGS)
2430
{
2431 2432
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2433
	Interval   *result;
2434
	fsec_t		fsec,
2435 2436
				fsec1,
				fsec2;
B
Bruce Momjian 已提交
2437
	struct pg_tm tt,
2438
			   *tm = &tt;
B
Bruce Momjian 已提交
2439
	struct pg_tm tt1,
2440
			   *tm1 = &tt1;
B
Bruce Momjian 已提交
2441
	struct pg_tm tt2,
2442
			   *tm2 = &tt2;
2443 2444 2445
	int			tz1;
	int			tz2;
	char	   *tzn;
2446 2447 2448

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

2449 2450
	if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn) == 0 &&
		timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn) == 0)
2451
	{
2452 2453 2454 2455 2456 2457 2458
		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;
2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471

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

2472
		while (tm->tm_sec < 0)
2473 2474 2475 2476 2477
		{
			tm->tm_sec += 60;
			tm->tm_min--;
		}

2478
		while (tm->tm_min < 0)
2479 2480 2481 2482 2483
		{
			tm->tm_min += 60;
			tm->tm_hour--;
		}

2484
		while (tm->tm_hour < 0)
2485 2486 2487 2488 2489
		{
			tm->tm_hour += 24;
			tm->tm_mday--;
		}

2490
		while (tm->tm_mday < 0)
2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503
		{
			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--;
			}
		}

2504
		while (tm->tm_mon < 0)
2505 2506 2507 2508 2509
		{
			tm->tm_mon += 12;
			tm->tm_year--;
		}

2510 2511 2512 2513
		/*
		 * Note: we deliberately ignore any difference between tz1 and tz2.
		 */

2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526
		/* 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)
2527 2528 2529
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("interval out of range")));
2530 2531
	}
	else
2532 2533 2534
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551

	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 */
2552
	Datum timestamp = PG_GETARG_DATUM(0);
2553 2554 2555
	text	   *result;
	char	   *str;
	int			len;
2556

2557
	str = DatumGetCString(DirectFunctionCall1(timestamp_out, timestamp));
2558 2559 2560 2561 2562

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2563
	VARATT_SIZEP(result) = len;
2564 2565 2566 2567
	memmove(VARDATA(result), str, (len - VARHDRSZ));

	pfree(str);

2568 2569
	PG_RETURN_TEXT_P(result);
}
2570 2571 2572 2573 2574 2575 2576


/* text_timestamp()
 * Convert text string to timestamp.
 * Text type is not null terminated, so use temporary string
 *	then call the standard input routine.
 */
2577 2578
Datum
text_timestamp(PG_FUNCTION_ARGS)
2579
{
2580
	text	   *str = PG_GETARG_TEXT_P(0);
2581 2582 2583 2584 2585
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2586
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2587 2588
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2589
				 errmsg("invalid input syntax for type timestamp: \"%s\"",
2590
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2591
											   PointerGetDatum(str))))));
2592 2593 2594 2595 2596 2597 2598

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

2599 2600 2601 2602
	return DirectFunctionCall3(timestamp_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2603
}
2604 2605


2606 2607 2608 2609 2610 2611 2612
/* 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 */
2613
	Datum timestamp = PG_GETARG_DATUM(0);
2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646
	text	   *result;
	char	   *str;
	int			len;

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

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

	VARATT_SIZEP(result) = len;
	memmove(VARDATA(result), str, (len - VARHDRSZ));

	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)
2647 2648
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2649
				 errmsg("invalid input syntax for type timestamp with time zone: \"%s\"",
2650
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2651
											   PointerGetDatum(str))))));
2652 2653 2654 2655 2656 2657 2658

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

2659 2660 2661 2662
	return DirectFunctionCall3(timestamptz_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2663 2664 2665
}


2666 2667 2668
/* interval_text()
 * Convert interval to text data type.
 */
2669 2670
Datum
interval_text(PG_FUNCTION_ARGS)
2671
{
2672
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2673 2674 2675 2676
	text	   *result;
	char	   *str;
	int			len;

2677
	str = DatumGetCString(DirectFunctionCall1(interval_out,
2678
										   IntervalPGetDatum(interval)));
2679 2680 2681 2682 2683

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2684
	VARATT_SIZEP(result) = len;
2685 2686 2687 2688
	memmove(VARDATA(result), str, (len - VARHDRSZ));

	pfree(str);

2689 2690
	PG_RETURN_TEXT_P(result);
}
2691 2692 2693 2694 2695 2696 2697


/* 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.
 */
2698 2699
Datum
text_interval(PG_FUNCTION_ARGS)
2700
{
2701
	text	   *str = PG_GETARG_TEXT_P(0);
2702 2703 2704 2705 2706
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2707
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2708 2709
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2710
				 errmsg("invalid input syntax for type interval: \"%s\"",
2711
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2712
											   PointerGetDatum(str))))));
2713

2714 2715 2716 2717 2718 2719
	sp = VARDATA(str);
	dp = dstr;
	for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
		*dp++ = *sp++;
	*dp = '\0';

2720 2721 2722 2723
	return DirectFunctionCall3(interval_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2724
}
2725 2726

/* timestamp_trunc()
2727
 * Truncate timestamp to specified units.
2728
 */
2729 2730
Datum
timestamp_trunc(PG_FUNCTION_ARGS)
2731
{
2732
	text	   *units = PG_GETARG_TEXT_P(0);
2733
	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
2734
	Timestamp	result;
2735 2736
	int			type,
				val;
2737
	char	   *lowunits;
2738
	fsec_t		fsec;
B
Bruce Momjian 已提交
2739
	struct pg_tm tt,
2740 2741
			   *tm = &tt;

2742 2743
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMP(timestamp);
2744

2745 2746 2747
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
2748 2749 2750

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

2751
	if (type == UNITS)
2752
	{
2753
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
2754 2755 2756 2757
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

2758 2759
		switch (val)
		{
2760
			case DTK_WEEK:
2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771
			{
				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.
				 */
				if (woy >= 52 && tm->tm_mon == 1)
					--tm->tm_year;
				isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
2772 2773 2774 2775 2776
				tm->tm_hour = 0;
				tm->tm_min = 0;
				tm->tm_sec = 0;
				fsec = 0;
				break;
2777
			}
2778
			case DTK_MILLENNIUM:
2779 2780
				/* see comments in timestamptz_trunc */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
2781
					tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
2782
				else
B
Bruce Momjian 已提交
2783
					tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
2784
			case DTK_CENTURY:
2785 2786
				/* see comments in timestamptz_trunc */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
2787
					tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
2788
				else
B
Bruce Momjian 已提交
2789
					tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
2790
			case DTK_DECADE:
2791 2792 2793 2794 2795 2796
				/* 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 已提交
2797
						tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
2798
				}
2799 2800 2801
			case DTK_YEAR:
				tm->tm_mon = 1;
			case DTK_QUARTER:
2802
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815
			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:
2816
#ifdef HAVE_INT64_TIMESTAMP
2817
				fsec = (fsec / 1000) * 1000;
2818
#else
2819
				fsec = rint(fsec * 1000) / 1000;
2820
#endif
2821 2822 2823
				break;

			case DTK_MICROSEC:
2824
#ifndef HAVE_INT64_TIMESTAMP
2825
				fsec = rint(fsec * 1000000) / 1000000;
2826
#endif
2827 2828 2829
				break;

			default:
2830 2831 2832 2833
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
								lowunits)));
2834 2835 2836 2837
				result = 0;
		}

		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
2838 2839 2840
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
2841
	}
2842 2843
	else
	{
2844 2845 2846 2847
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("timestamp units \"%s\" not recognized",
						lowunits)));
2848 2849
		result = 0;
	}
2850

2851 2852
	PG_RETURN_TIMESTAMP(result);
}
2853

2854 2855 2856 2857 2858 2859 2860
/* timestamptz_trunc()
 * Truncate timestamp to specified units.
 */
Datum
timestamptz_trunc(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
2861
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
2862
	TimestampTz result;
2863 2864 2865
	int			tz;
	int			type,
				val;
2866
	bool		redotz = false;
2867
	char	   *lowunits;
2868
	fsec_t		fsec;
2869
	char	   *tzn;
B
Bruce Momjian 已提交
2870
	struct pg_tm tt,
2871
			   *tm = &tt;
2872

2873 2874
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMPTZ(timestamp);
2875

2876 2877 2878 2879 2880 2881
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

2882
	if (type == UNITS)
2883
	{
2884
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
2885 2886 2887 2888
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

2889
		switch (val)
2890
		{
2891
			case DTK_WEEK:
2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902
			{
				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.
				 */
				if (woy >= 52 && tm->tm_mon == 1)
					--tm->tm_year;
				isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
2903 2904 2905 2906
				tm->tm_hour = 0;
				tm->tm_min = 0;
				tm->tm_sec = 0;
				fsec = 0;
2907
				redotz = true;
2908
				break;
2909
			}
2910
				/* one may consider DTK_THOUSAND and DTK_HUNDRED... */
2911
			case DTK_MILLENNIUM:
B
Bruce Momjian 已提交
2912 2913 2914 2915

				/*
				 * truncating to the millennium? what is this supposed to
				 * mean? let us put the first year of the millennium...
2916 2917 2918
				 * i.e. -1000, 1, 1001, 2001...
				 */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
2919
					tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
2920
				else
B
Bruce Momjian 已提交
2921
					tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
2922
				/* FALL THRU */
2923
			case DTK_CENTURY:
2924 2925
				/* truncating to the century? as above: -100, 1, 101... */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
2926
					tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
2927
				else
B
Bruce Momjian 已提交
2928
					tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
2929
				/* FALL THRU */
2930
			case DTK_DECADE:
B
Bruce Momjian 已提交
2931 2932 2933

				/*
				 * truncating to the decade? first year of the decade.
2934 2935 2936 2937 2938 2939 2940
				 * must not be applied if year was truncated before!
				 */
				if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
				{
					if (tm->tm_year > 0)
						tm->tm_year = (tm->tm_year / 10) * 10;
					else
B
Bruce Momjian 已提交
2941
						tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
2942
				}
2943
				/* FALL THRU */
2944 2945
			case DTK_YEAR:
				tm->tm_mon = 1;
2946
				/* FALL THRU */
2947
			case DTK_QUARTER:
2948
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
2949
				/* FALL THRU */
2950 2951
			case DTK_MONTH:
				tm->tm_mday = 1;
2952
				/* FALL THRU */
2953 2954
			case DTK_DAY:
				tm->tm_hour = 0;
2955 2956
				redotz = true;	/* for all cases >= DAY */
				/* FALL THRU */
2957 2958
			case DTK_HOUR:
				tm->tm_min = 0;
2959
				/* FALL THRU */
2960 2961
			case DTK_MINUTE:
				tm->tm_sec = 0;
2962
				/* FALL THRU */
2963 2964 2965 2966 2967
			case DTK_SECOND:
				fsec = 0;
				break;

			case DTK_MILLISEC:
2968 2969 2970
#ifdef HAVE_INT64_TIMESTAMP
				fsec = ((fsec / 1000) * 1000);
#else
2971
				fsec = rint(fsec * 1000) / 1000;
2972
#endif
2973 2974
				break;
			case DTK_MICROSEC:
2975
#ifndef HAVE_INT64_TIMESTAMP
2976
				fsec = rint(fsec * 1000000) / 1000000;
2977
#endif
2978 2979 2980
				break;

			default:
2981 2982
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
2983 2984
					  errmsg("timestamp with time zone units \"%s\" not "
							 "supported", lowunits)));
2985
				result = 0;
2986
		}
2987

2988 2989
		if (redotz)
			tz = DetermineLocalTimeZone(tm);
2990 2991

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
2992 2993 2994
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
2995 2996 2997
	}
	else
	{
2998 2999
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3000 3001
		   errmsg("timestamp with time zone units \"%s\" not recognized",
				  lowunits)));
3002
		result = 0;
3003 3004
	}

3005
	PG_RETURN_TIMESTAMPTZ(result);
3006
}
3007 3008 3009 3010

/* interval_trunc()
 * Extract specified field from interval.
 */
3011 3012
Datum
interval_trunc(PG_FUNCTION_ARGS)
3013
{
3014 3015
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
3016 3017 3018
	Interval   *result;
	int			type,
				val;
3019
	char	   *lowunits;
3020
	fsec_t		fsec;
B
Bruce Momjian 已提交
3021
	struct pg_tm tt,
3022 3023
			   *tm = &tt;

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

3026 3027 3028
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
3029 3030 3031

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

3032
	if (type == UNITS)
3033 3034 3035 3036 3037
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
3038
				case DTK_MILLENNIUM:
3039
					/* caution: C division may have negative remainder */
3040 3041
					tm->tm_year = (tm->tm_year / 1000) * 1000;
				case DTK_CENTURY:
3042
					/* caution: C division may have negative remainder */
3043 3044
					tm->tm_year = (tm->tm_year / 100) * 100;
				case DTK_DECADE:
3045
					/* caution: C division may have negative remainder */
3046 3047 3048 3049
					tm->tm_year = (tm->tm_year / 10) * 10;
				case DTK_YEAR:
					tm->tm_mon = 0;
				case DTK_QUARTER:
3050
					tm->tm_mon = 3 * (tm->tm_mon / 3);
3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063
				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:
3064 3065 3066
#ifdef HAVE_INT64_TIMESTAMP
					fsec = ((fsec / 1000) * 1000);
#else
3067
					fsec = rint(fsec * 1000) / 1000;
3068
#endif
3069 3070
					break;
				case DTK_MICROSEC:
3071
#ifndef HAVE_INT64_TIMESTAMP
3072
					fsec = rint(fsec * 1000000) / 1000000;
3073
#endif
3074 3075 3076
					break;

				default:
3077 3078 3079
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("interval units \"%s\" not supported",
B
Bruce Momjian 已提交
3080
									lowunits)));
3081 3082 3083
			}

			if (tm2interval(tm, fsec, result) != 0)
3084 3085 3086
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("interval out of range")));
3087 3088
		}
		else
3089
			elog(ERROR, "could not convert interval to tm");
3090 3091 3092
	}
	else
	{
3093 3094 3095
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval units \"%s\" not recognized",
B
Bruce Momjian 已提交
3096 3097
						DatumGetCString(DirectFunctionCall1(textout,
											 PointerGetDatum(units))))));
3098
		*result = *interval;
3099 3100
	}

3101 3102
	PG_RETURN_INTERVAL_P(result);
}
3103

B
Bruce Momjian 已提交
3104
/* isoweek2date()
3105
 * Convert ISO week of year number to date.
3106
 * The year field must be specified with the ISO year!
3107
 * karel 2000/08/07
B
Bruce Momjian 已提交
3108 3109
 */
void
B
Bruce Momjian 已提交
3110
isoweek2date(int woy, int *year, int *mon, int *mday)
B
Bruce Momjian 已提交
3111
{
B
Bruce Momjian 已提交
3112 3113 3114 3115
	int			day0,
				day4,
				dayn;

B
Bruce Momjian 已提交
3116
	if (!*year)
3117 3118
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3119
		errmsg("cannot calculate week number without year information")));
B
Bruce Momjian 已提交
3120 3121 3122

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

B
Bruce Momjian 已提交
3124
	/* day0 == offset to first day of week (Monday) */
3125
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3126 3127

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

B
Bruce Momjian 已提交
3129 3130 3131 3132
	j2date(dayn, year, mon, mday);
}

/* date2isoweek()
B
Bruce Momjian 已提交
3133
 *
B
Bruce Momjian 已提交
3134 3135 3136
 *	Returns ISO week number of year.
 */
int
B
Bruce Momjian 已提交
3137
date2isoweek(int year, int mon, int mday)
B
Bruce Momjian 已提交
3138
{
B
Bruce Momjian 已提交
3139 3140 3141 3142 3143 3144
	float8		result;
	int			day0,
				day4,
				dayn;

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

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

B
Bruce Momjian 已提交
3150
	/* day0 == offset to first day of week (Monday) */
3151
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3152 3153 3154 3155

	/*
	 * We need the first week containing a Thursday, otherwise this day
	 * falls into the previous year for purposes of counting weeks
B
Bruce Momjian 已提交
3156
	 */
3157
	if (dayn < day4 - day0)
B
Bruce Momjian 已提交
3158
	{
3159
		day4 = date2j(year - 1, 1, 4);
B
Bruce Momjian 已提交
3160

B
Bruce Momjian 已提交
3161
		/* day0 == offset to first day of week (Monday) */
3162
		day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3163
	}
B
Bruce Momjian 已提交
3164

3165
	result = (dayn - (day4 - day0)) / 7 + 1;
B
Bruce Momjian 已提交
3166 3167 3168 3169

	/*
	 * 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 已提交
3170
	 */
3171
	if (result >= 52)
B
Bruce Momjian 已提交
3172
	{
3173
		day4 = date2j(year + 1, 1, 4);
B
Bruce Momjian 已提交
3174

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

3178 3179
		if (dayn >= day4 - day0)
			result = (dayn - (day4 - day0)) / 7 + 1;
B
Bruce Momjian 已提交
3180
	}
3181

B
Bruce Momjian 已提交
3182
	return (int) result;
B
Bruce Momjian 已提交
3183 3184 3185
}


3186 3187 3188 3189 3190 3191 3192
/* date2isoyear()
 *
 *	Returns ISO 8601 year number.
 */
int
date2isoyear(int year, int mon, int mday)
{
B
Bruce Momjian 已提交
3193 3194 3195 3196
	float8		result;
	int			day0,
				day4,
				dayn;
3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210

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

	/*
	 * We need the first week containing a Thursday, otherwise this day
	 * falls into the previous year for purposes of counting weeks
	 */
3211
	if (dayn < day4 - day0)
3212 3213 3214 3215 3216 3217 3218 3219 3220
	{
		day4 = date2j(year - 1, 1, 4);

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

		year--;
	}

3221
	result = (dayn - (day4 - day0)) / 7 + 1;
3222 3223 3224 3225 3226

	/*
	 * Sometimes the last few days in a year will fall into the first week
	 * of the next year, so check for this.
	 */
3227
	if (result >= 52)
3228 3229 3230 3231 3232 3233
	{
		day4 = date2j(year + 1, 1, 4);

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

3234
		if (dayn >= day4 - day0)
3235 3236 3237 3238 3239 3240 3241
			year++;
	}

	return year;
}


3242 3243 3244
/* timestamp_part()
 * Extract specified field from timestamp.
 */
3245 3246
Datum
timestamp_part(PG_FUNCTION_ARGS)
3247
{
3248
	text	   *units = PG_GETARG_TEXT_P(0);
3249
	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3250
	float8		result;
3251 3252
	int			type,
				val;
3253
	char	   *lowunits;
3254
	fsec_t		fsec;
B
Bruce Momjian 已提交
3255
	struct pg_tm tt,
3256 3257
			   *tm = &tt;

3258
	if (TIMESTAMP_NOT_FINITE(timestamp))
3259
	{
3260 3261 3262
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3263

3264 3265 3266 3267 3268 3269 3270 3271
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3272
	if (type == UNITS)
3273
	{
3274
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
3275 3276 3277 3278
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3279
		switch (val)
3280
		{
3281
			case DTK_MICROSEC:
3282
#ifdef HAVE_INT64_TIMESTAMP
3283
				result = tm->tm_sec * 1000000e0 + fsec;
3284
#else
3285
				result = (tm->tm_sec + fsec) * 1000000;
3286
#endif
3287 3288 3289
				break;

			case DTK_MILLISEC:
3290
#ifdef HAVE_INT64_TIMESTAMP
3291
				result = tm->tm_sec * 1000e0 + fsec / 1000e0;
3292
#else
3293
				result = (tm->tm_sec + fsec) * 1000;
3294
#endif
3295 3296 3297
				break;

			case DTK_SECOND:
3298
#ifdef HAVE_INT64_TIMESTAMP
3299
				result = tm->tm_sec + fsec / 1000000e0;
3300
#else
3301
				result = tm->tm_sec + fsec;
3302
#endif
3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321
				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:
3322
				result = (tm->tm_mon - 1) / 3 + 1;
3323 3324 3325 3326 3327 3328 3329
				break;

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

			case DTK_YEAR:
3330 3331 3332
				if (tm->tm_year > 0)
					result = tm->tm_year;
				else
B
Bruce Momjian 已提交
3333 3334
					/* there is no year 0, just 1 BC and 1 AD */
					result = tm->tm_year - 1;
3335 3336 3337
				break;

			case DTK_DECADE:
B
Bruce Momjian 已提交
3338 3339 3340 3341 3342

				/*
				 * 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...
3343
				 */
B
Bruce Momjian 已提交
3344
				if (tm->tm_year >= 0)
3345 3346
					result = (tm->tm_year / 10);
				else
B
Bruce Momjian 已提交
3347
					result = -((8 - (tm->tm_year - 1)) / 10);
3348 3349 3350
				break;

			case DTK_CENTURY:
B
Bruce Momjian 已提交
3351

3352 3353 3354 3355 3356
				/* ----
				 * 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.
				 * ----
3357 3358
				 */
				if (tm->tm_year > 0)
3359
					result = (tm->tm_year + 99) / 100;
3360
				else
3361
					/* caution: C division may have negative remainder */
B
Bruce Momjian 已提交
3362
					result = -((99 - (tm->tm_year - 1)) / 100);
3363 3364 3365
				break;

			case DTK_MILLENNIUM:
3366 3367
				/* see comments above. */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3368
					result = ((tm->tm_year + 999) / 1000);
3369
				else
B
Bruce Momjian 已提交
3370
					result = -((999 - (tm->tm_year - 1)) / 1000);
3371 3372
				break;

3373 3374
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3375
#ifdef HAVE_INT64_TIMESTAMP
3376
				result += ((((tm->tm_hour * 60) + tm->tm_min) * 60) +
3377
							tm->tm_sec + (fsec / 1000000e0)) / (double)SECS_PER_DAY;
3378
#else
3379
				result += ((((tm->tm_hour * 60) + tm->tm_min) * 60) +
3380
							tm->tm_sec + fsec) / (double)SECS_PER_DAY;
3381
#endif
3382 3383
				break;

3384 3385 3386
			case DTK_TZ:
			case DTK_TZ_MINUTE:
			case DTK_TZ_HOUR:
3387
			default:
3388 3389 3390
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
B
Bruce Momjian 已提交
3391
								lowunits)));
3392 3393 3394 3395 3396 3397 3398 3399
				result = 0;
		}
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
B
Bruce Momjian 已提交
3400 3401 3402
				{
					int			tz;
					TimestampTz timestamptz;
3403

B
Bruce Momjian 已提交
3404 3405 3406 3407
					/*
					 * convert to timestamptz to produce consistent
					 * results
					 */
3408
					if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
B
Bruce Momjian 已提交
3409 3410 3411
						ereport(ERROR,
						   (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							errmsg("timestamp out of range")));
3412

B
Bruce Momjian 已提交
3413
					tz = DetermineLocalTimeZone(tm);
3414

B
Bruce Momjian 已提交
3415 3416 3417 3418
					if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
						ereport(ERROR,
						   (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							errmsg("timestamp out of range")));
3419

3420
#ifdef HAVE_INT64_TIMESTAMP
3421
					result = (timestamptz - SetEpochTimestamp()) / 1000000e0;
3422
#else
B
Bruce Momjian 已提交
3423
					result = timestamptz - SetEpochTimestamp();
3424
#endif
B
Bruce Momjian 已提交
3425 3426
					break;
				}
3427
			case DTK_DOW:
3428
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
3429 3430 3431
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3432 3433
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3434

3435
			case DTK_DOY:
3436
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
3437 3438 3439
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3440 3441 3442
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3443

3444
			default:
3445 3446 3447
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
B
Bruce Momjian 已提交
3448
								lowunits)));
3449 3450
				result = 0;
		}
3451

3452 3453 3454
	}
	else
	{
3455 3456
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3457
			 errmsg("timestamp units \"%s\" not recognized", lowunits)));
3458 3459
		result = 0;
	}
3460

3461 3462
	PG_RETURN_FLOAT8(result);
}
3463

3464 3465 3466 3467 3468 3469 3470
/* timestamptz_part()
 * Extract specified field from timestamp with time zone.
 */
Datum
timestamptz_part(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
3471
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3472 3473 3474 3475
	float8		result;
	int			tz;
	int			type,
				val;
3476
	char	   *lowunits;
3477
	double		dummy;
3478
	fsec_t		fsec;
3479
	char	   *tzn;
B
Bruce Momjian 已提交
3480
	struct pg_tm tt,
3481
			   *tm = &tt;
3482

3483 3484 3485 3486 3487
	if (TIMESTAMP_NOT_FINITE(timestamp))
	{
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3488

3489 3490 3491 3492 3493 3494 3495 3496
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3497
	if (type == UNITS)
3498
	{
3499
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
3500 3501 3502 3503
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3504
		switch (val)
3505
		{
3506
			case DTK_TZ:
3507
				result = -tz;
3508 3509 3510
				break;

			case DTK_TZ_MINUTE:
3511 3512 3513
				result = -tz;
				result /= 60;
				FMODULO(result, dummy, 60e0);
3514 3515 3516
				break;

			case DTK_TZ_HOUR:
3517 3518
				dummy = -tz;
				FMODULO(dummy, result, 3600e0);
3519 3520 3521
				break;

			case DTK_MICROSEC:
3522
#ifdef HAVE_INT64_TIMESTAMP
3523
				result = tm->tm_sec * 1000000e0 + fsec;
3524
#else
3525
				result = (tm->tm_sec + fsec) * 1000000;
3526
#endif
3527 3528 3529
				break;

			case DTK_MILLISEC:
3530
#ifdef HAVE_INT64_TIMESTAMP
3531
				result = tm->tm_sec * 1000e0 + fsec / 1000e0;
3532
#else
3533
				result = (tm->tm_sec + fsec) * 1000;
3534
#endif
3535 3536 3537
				break;

			case DTK_SECOND:
3538
#ifdef HAVE_INT64_TIMESTAMP
3539
				result = tm->tm_sec + fsec / 1000000e0;
3540
#else
3541
				result = tm->tm_sec + fsec;
3542
#endif
3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561
				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:
3562
				result = (tm->tm_mon - 1) / 3 + 1;
3563 3564 3565 3566 3567 3568 3569
				break;

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

			case DTK_YEAR:
3570 3571 3572 3573 3574
				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;
3575 3576 3577
				break;

			case DTK_DECADE:
3578
				/* see comments in timestamp_part */
B
Bruce Momjian 已提交
3579
				if (tm->tm_year > 0)
3580 3581
					result = (tm->tm_year / 10);
				else
B
Bruce Momjian 已提交
3582
					result = -((8 - (tm->tm_year - 1)) / 10);
3583 3584 3585
				break;

			case DTK_CENTURY:
3586 3587
				/* see comments in timestamp_part */
				if (tm->tm_year > 0)
3588
					result = (tm->tm_year + 99) / 100;
3589
				else
B
Bruce Momjian 已提交
3590
					result = -((99 - (tm->tm_year - 1)) / 100);
3591 3592 3593
				break;

			case DTK_MILLENNIUM:
3594 3595
				/* see comments in timestamp_part */
				if (tm->tm_year > 0)
3596
					result = (tm->tm_year + 999) / 1000;
3597
				else
B
Bruce Momjian 已提交
3598
					result = -((999 - (tm->tm_year - 1)) / 1000);
3599 3600
				break;

3601 3602
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3603
#ifdef HAVE_INT64_TIMESTAMP
3604
				result += ((((tm->tm_hour * 60) + tm->tm_min) * 60) +
3605
							tm->tm_sec + (fsec / 1000000e0)) / (double)SECS_PER_DAY;
3606
#else
3607
				result += ((((tm->tm_hour * 60) + tm->tm_min) * 60) +
3608
							tm->tm_sec + fsec) / (double)SECS_PER_DAY;
3609
#endif
3610 3611
				break;

3612
			default:
3613 3614 3615
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp with time zone units \"%s\" not supported",
B
Bruce Momjian 已提交
3616
								lowunits)));
3617 3618
				result = 0;
		}
3619

3620 3621 3622 3623 3624 3625
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
3626
#ifdef HAVE_INT64_TIMESTAMP
3627
				result = (timestamp - SetEpochTimestamp()) /1000000e0;
3628
#else
3629
				result = timestamp - SetEpochTimestamp();
3630
#endif
3631
				break;
3632

3633
			case DTK_DOW:
3634
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
3635 3636 3637
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3638 3639
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3640

3641
			case DTK_DOY:
3642
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
3643 3644 3645
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3646 3647 3648
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3649

3650
			default:
3651 3652 3653
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp with time zone units \"%s\" not supported",
B
Bruce Momjian 已提交
3654
								lowunits)));
3655
				result = 0;
3656 3657
		}
	}
3658 3659
	else
	{
3660 3661
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3662 3663
		   errmsg("timestamp with time zone units \"%s\" not recognized",
				  lowunits)));
3664

3665 3666
		result = 0;
	}
3667

3668 3669
	PG_RETURN_FLOAT8(result);
}
3670 3671 3672 3673 3674


/* interval_part()
 * Extract specified field from interval.
 */
3675 3676
Datum
interval_part(PG_FUNCTION_ARGS)
3677
{
3678 3679 3680
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
	float8		result;
3681 3682
	int			type,
				val;
3683
	char	   *lowunits;
3684
	fsec_t		fsec;
B
Bruce Momjian 已提交
3685
	struct pg_tm tt,
3686 3687
			   *tm = &tt;

3688 3689 3690
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
3691 3692

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

3696
	if (type == UNITS)
3697 3698 3699 3700 3701
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
B
Bruce Momjian 已提交
3702
				case DTK_MICROSEC:
3703
#ifdef HAVE_INT64_TIMESTAMP
3704
					result = tm->tm_sec * 1000000e0 + fsec;
3705
#else
B
Bruce Momjian 已提交
3706
					result = (tm->tm_sec + fsec) * 1000000;
3707
#endif
B
Bruce Momjian 已提交
3708
					break;
3709

B
Bruce Momjian 已提交
3710
				case DTK_MILLISEC:
3711
#ifdef HAVE_INT64_TIMESTAMP
3712
					result = tm->tm_sec * 1000e0 + fsec / 1000e0;
3713
#else
B
Bruce Momjian 已提交
3714
					result = (tm->tm_sec + fsec) * 1000;
3715
#endif
B
Bruce Momjian 已提交
3716
					break;
3717

B
Bruce Momjian 已提交
3718
				case DTK_SECOND:
3719
#ifdef HAVE_INT64_TIMESTAMP
3720
					result = tm->tm_sec + fsec / 1000000e0;
3721
#else
3722
					result = tm->tm_sec + fsec;
3723
#endif
B
Bruce Momjian 已提交
3724
					break;
3725 3726

				case DTK_MINUTE:
3727
					result = tm->tm_min;
3728 3729 3730
					break;

				case DTK_HOUR:
3731
					result = tm->tm_hour;
3732 3733 3734
					break;

				case DTK_DAY:
3735
					result = tm->tm_mday;
3736 3737 3738
					break;

				case DTK_MONTH:
3739
					result = tm->tm_mon;
3740 3741 3742
					break;

				case DTK_QUARTER:
3743
					result = (tm->tm_mon / 3) + 1;
3744 3745 3746
					break;

				case DTK_YEAR:
3747
					result = tm->tm_year;
3748 3749 3750
					break;

				case DTK_DECADE:
3751
					/* caution: C division may have negative remainder */
3752
					result = (tm->tm_year / 10);
3753 3754 3755
					break;

				case DTK_CENTURY:
3756
					/* caution: C division may have negative remainder */
3757
					result = (tm->tm_year / 100);
3758 3759
					break;

3760
				case DTK_MILLENNIUM:
3761
					/* caution: C division may have negative remainder */
3762
					result = (tm->tm_year / 1000);
3763 3764 3765
					break;

				default:
3766 3767 3768 3769
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("interval units \"%s\" not supported",
							 DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
3770
											 PointerGetDatum(units))))));
3771
					result = 0;
3772 3773 3774 3775 3776
			}

		}
		else
		{
3777
			elog(ERROR, "could not convert interval to tm");
3778
			result = 0;
3779 3780
		}
	}
3781
	else if (type == RESERV && val == DTK_EPOCH)
3782
	{
3783
#ifdef HAVE_INT64_TIMESTAMP
3784
		result = interval->time / 1000000e0;
3785
#else
3786
		result = interval->time;
3787
#endif
3788 3789
		if (interval->month != 0)
		{
3790 3791
			result += (365.25 * SECS_PER_DAY) * (interval->month / 12);
			result += (30.0 * SECS_PER_DAY) * (interval->month % 12);
3792 3793 3794 3795
		}
	}
	else
	{
3796 3797 3798
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval units \"%s\" not recognized",
B
Bruce Momjian 已提交
3799 3800
						DatumGetCString(DirectFunctionCall1(textout,
											 PointerGetDatum(units))))));
3801
		result = 0;
3802 3803
	}

3804 3805
	PG_RETURN_FLOAT8(result);
}
3806 3807 3808 3809


/* timestamp_zone()
 * Encode timestamp type with specified time zone.
3810
 * Returns timestamp with time zone, with the input
B
Bruce Momjian 已提交
3811
 *	rotated from local time to the specified zone.
3812
 */
3813 3814
Datum
timestamp_zone(PG_FUNCTION_ARGS)
3815
{
3816
	text	   *zone = PG_GETARG_TEXT_P(0);
3817
	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3818
	TimestampTz result;
3819 3820 3821
	int			tz;
	int			type,
				val;
3822
	char	   *lowzone;
3823 3824 3825 3826

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

3827 3828 3829
	lowzone = downcase_truncate_identifier(VARDATA(zone),
										   VARSIZE(zone) - VARHDRSZ,
										   false);
3830 3831 3832

	type = DecodeSpecial(0, lowzone, &val);

3833
	if (type == TZ || type == DTZ)
3834
	{
3835 3836 3837
		tz = -(val * 60);

		result = dt2local(timestamp, tz);
3838 3839 3840
	}
	else
	{
3841 3842 3843
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("time zone \"%s\" not recognized",
B
Bruce Momjian 已提交
3844
						lowzone)));
3845

3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858
		PG_RETURN_NULL();
	}

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

/* 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);
3859
	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3860
	TimestampTz result;
3861 3862 3863 3864 3865 3866
	int			tz;

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

	if (zone->month != 0)
3867 3868
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3869 3870 3871
			   errmsg("interval time zone \"%s\" must not specify month",
					  DatumGetCString(DirectFunctionCall1(interval_out,
											  PointerGetDatum(zone))))));
3872

3873
#ifdef HAVE_INT64_TIMESTAMP
3874
	tz = zone->time / USECS_PER_SEC;
3875
#else
3876
	tz = zone->time;
3877 3878 3879
#endif

	result = dt2local(timestamp, tz);
3880 3881 3882 3883 3884 3885 3886 3887 3888 3889

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

/* timestamp_timestamptz()
 * Convert local timestamp to timestamp at GMT
 */
Datum
timestamp_timestamptz(PG_FUNCTION_ARGS)
{
3890
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
3891 3892 3893 3894 3895 3896 3897

	PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
}

static TimestampTz
timestamp2timestamptz(Timestamp timestamp)
{
3898
	TimestampTz result;
B
Bruce Momjian 已提交
3899
	struct pg_tm tt,
3900
			   *tm = &tt;
3901
	fsec_t		fsec;
3902 3903 3904 3905
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
3906

3907 3908
	else
	{
3909
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) !=0)
3910 3911 3912
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3913 3914 3915 3916

		tz = DetermineLocalTimeZone(tm);

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
3917 3918 3919
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3920 3921
	}

3922
	return result;
3923 3924 3925 3926 3927 3928 3929 3930
}

/* timestamptz_timestamp()
 * Convert timestamp at GMT to local timestamp
 */
Datum
timestamptz_timestamp(PG_FUNCTION_ARGS)
{
3931
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
3932
	Timestamp	result;
B
Bruce Momjian 已提交
3933
	struct pg_tm tt,
3934
			   *tm = &tt;
3935
	fsec_t		fsec;
3936 3937 3938 3939 3940
	char	   *tzn;
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
3941

3942 3943
	else
	{
3944
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) !=0)
3945 3946 3947
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3948
		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
3949 3950 3951
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3952 3953 3954 3955 3956 3957
	}

	PG_RETURN_TIMESTAMP(result);
}

/* timestamptz_zone()
3958 3959
 * Evaluate timestamp with time zone type at the specified time zone.
 * Returns a timestamp without time zone.
3960 3961 3962 3963 3964
 */
Datum
timestamptz_zone(PG_FUNCTION_ARGS)
{
	text	   *zone = PG_GETARG_TEXT_P(0);
3965
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3966 3967
	Timestamp	result;

3968 3969 3970
	int			tz;
	int			type,
				val;
3971
	char	   *lowzone;
3972

3973
	if (TIMESTAMP_NOT_FINITE(timestamp))
3974
		PG_RETURN_NULL();
3975

3976 3977 3978 3979 3980 3981
	lowzone = downcase_truncate_identifier(VARDATA(zone),
										   VARSIZE(zone) - VARHDRSZ,
										   false);

	type = DecodeSpecial(0, lowzone, &val);

3982
	if (type == TZ || type == DTZ)
3983 3984 3985
	{
		tz = val * 60;

3986
		result = dt2local(timestamp, tz);
3987 3988 3989
	}
	else
	{
3990 3991 3992 3993
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("time zone \"%s\" not recognized", lowzone)));

3994
		PG_RETURN_NULL();
3995 3996
	}

3997
	PG_RETURN_TIMESTAMP(result);
3998
}	/* timestamptz_zone() */
3999

4000 4001
/* timestamptz_izone()
 * Encode timestamp with time zone type with specified time interval as time zone.
4002
 * Returns a timestamp without time zone.
4003 4004
 */
Datum
4005
timestamptz_izone(PG_FUNCTION_ARGS)
4006 4007
{
	Interval   *zone = PG_GETARG_INTERVAL_P(0);
4008
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4009
	Timestamp	result;
4010 4011 4012
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
4013
		PG_RETURN_NULL();
4014 4015

	if (zone->month != 0)
4016 4017
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
4018 4019 4020
			   errmsg("interval time zone \"%s\" must not specify month",
					  DatumGetCString(DirectFunctionCall1(interval_out,
											  PointerGetDatum(zone))))));
4021

4022
#ifdef HAVE_INT64_TIMESTAMP
4023
	tz = -(zone->time / USECS_PER_SEC);
4024
#else
4025
	tz = -(zone->time);
4026
#endif
4027

4028
	result = dt2local(timestamp, tz);
4029

4030
	PG_RETURN_TIMESTAMP(result);
4031
}	/* timestamptz_izone() */