timestamp.c 79.0 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * timestamp.c
4
 *	  Functions for the built-in SQL92 types "timestamp" and "interval".
5
 *
B
Bruce Momjian 已提交
6
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 8 9 10
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.87 2003/07/26 15:17:36 momjian Exp $
12 13 14
 *
 *-------------------------------------------------------------------------
 */
15 16 17

#include "postgres.h"

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

24
#include "access/hash.h"
25
#include "access/xact.h"
26
#include "catalog/pg_type.h"
27
#include "libpq/pqformat.h"
28
#include "miscadmin.h"
29
#include "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

52 53 54 55 56 57 58 59

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

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

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

	if (strlen(str) >= sizeof(lowstr))
		elog(ERROR, "Bad timestamp external representation (too long) '%s'", str);
82 83 84 85 86 87 88 89

	if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
	  || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
		elog(ERROR, "Bad timestamp external representation '%s'", str);

	switch (dtype)
	{
		case DTK_DATE:
90
			if (tm2timestamp(tm, fsec, NULL, &result) != 0)
91
				elog(ERROR, "TIMESTAMP out of range '%s'", str);
92 93 94
			break;

		case DTK_EPOCH:
95
			result = SetEpochTimestamp();
96 97 98
			break;

		case DTK_LATE:
99
			TIMESTAMP_NOEND(result);
100 101 102
			break;

		case DTK_EARLY:
103
			TIMESTAMP_NOBEGIN(result);
104 105 106
			break;

		case DTK_INVALID:
107
			elog(ERROR, "TIMESTAMP '%s' no longer supported", str);
108
			TIMESTAMP_NOEND(result);
109
			break;
110

111
		default:
112
			elog(ERROR, "TIMESTAMP '%s' not parsed; internal coding error", str);
113
			TIMESTAMP_NOEND(result);
114
	}
M
Marc G. Fournier 已提交
115

116 117
	AdjustTimestampForTypmod(&result, typmod);

118 119
	PG_RETURN_TIMESTAMP(result);
}
M
Marc G. Fournier 已提交
120

121 122 123
/* timestamp_out()
 * Convert a timestamp to external form.
 */
124 125
Datum
timestamp_out(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
126
{
127
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
128
	char	   *result;
129 130
	struct tm	tt,
			   *tm = &tt;
131
	fsec_t		fsec;
132 133 134
	char	   *tzn = NULL;
	char		buf[MAXDATELEN + 1];

135 136 137
	if (TIMESTAMP_NOT_FINITE(timestamp))
		EncodeSpecialTimestamp(timestamp, buf);
	else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0)
138 139 140 141 142 143 144 145
		EncodeDateTime(tm, fsec, NULL, &tzn, DateStyle, buf);
	else
		elog(ERROR, "Unable to format timestamp; internal coding error");

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

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
/*
 *		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);

#ifdef HAVE_INT64_TIMESTAMP
	PG_RETURN_TIMESTAMP((Timestamp) pq_getmsgint64(buf));
#else
	PG_RETURN_TIMESTAMP((Timestamp) pq_getmsgfloat8(buf));
#endif
}

/*
 *		timestamp_send			- converts timestamp to binary format
 */
Datum
timestamp_send(PG_FUNCTION_ARGS)
{
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
	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));
}


183 184 185 186 187 188 189 190 191 192 193 194 195
/* timestamp_scale()
 * Adjust time type for specified scale factor.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
timestamp_scale(PG_FUNCTION_ARGS)
{
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
	int32		typmod = PG_GETARG_INT32(1);
	Timestamp	result;

	result = timestamp;

196
	AdjustTimestampForTypmod(&result, typmod);
197 198 199 200 201 202 203

	PG_RETURN_TIMESTAMP(result);
}

static void
AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
{
204
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
205
	static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
206 207 208 209 210 211 212 213 214
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

B
Bruce Momjian 已提交
215
	static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
216 217 218 219 220 221
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
222 223
		INT64CONST(0)
	};
B
Bruce Momjian 已提交
224

225
#else
B
Bruce Momjian 已提交
226
	static const double TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
227 228 229 230 231 232 233 234 235 236 237 238
		1,
		10,
		100,
		1000,
		10000,
		100000,
		1000000
	};
#endif

	if (!TIMESTAMP_NOT_FINITE(*time)
		&& (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
239
	{
240 241 242
		if ((typmod < 0) || (typmod > MAX_TIMESTAMP_PRECISION))
			elog(ERROR, "TIMESTAMP(%d) precision must be between %d and %d",
				 typmod, 0, MAX_TIMESTAMP_PRECISION);
243

244 245 246
		/*
		 * Note: this round-to-nearest code is not completely consistent
		 * about rounding values that are exactly halfway between integral
T
Tom Lane 已提交
247
		 * values.  On most platforms, rint() will implement round-to-nearest-even,
248 249 250
		 * but the integer code always rounds up (away from zero).  Is it
		 * worth trying to be consistent?
		 */
251 252 253 254 255 256 257 258
#ifdef HAVE_INT64_TIMESTAMP
		if (*time >= INT64CONST(0))
		{
			*time = (((*time + TimestampOffsets[typmod]) / TimestampScales[typmod])
					 * TimestampScales[typmod]);
		}
		else
		{
259 260
			*time = - ((((- *time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
					   * TimestampScales[typmod]);
261
		}
262 263 264
#else
		*time = (rint(((double) *time) * TimestampScales[typmod])
				 / TimestampScales[typmod]);
265
#endif
266 267 268
	}
}

269 270 271 272 273 274 275 276

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

278 279 280 281
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
282
	TimestampTz result;
283
	fsec_t		fsec;
284 285 286 287 288 289 290
	struct tm	tt,
			   *tm = &tt;
	int			tz;
	int			dtype;
	int			nf;
	char	   *field[MAXDATEFIELDS];
	int			ftype[MAXDATEFIELDS];
291 292 293 294 295
	char		lowstr[MAXDATELEN + MAXDATEFIELDS];

	if (strlen(str) >= sizeof(lowstr))
		elog(ERROR, "Bad timestamp with time zone"
			 " external representation (too long) '%s'", str);
296 297 298 299 300 301 302 303 304

	if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
	  || (DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
		elog(ERROR, "Bad timestamp external representation '%s'", str);

	switch (dtype)
	{
		case DTK_DATE:
			if (tm2timestamp(tm, fsec, &tz, &result) != 0)
305
				elog(ERROR, "TIMESTAMP WITH TIME ZONE out of range '%s'", str);
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
			break;

		case DTK_EPOCH:
			result = SetEpochTimestamp();
			break;

		case DTK_LATE:
			TIMESTAMP_NOEND(result);
			break;

		case DTK_EARLY:
			TIMESTAMP_NOBEGIN(result);
			break;

		case DTK_INVALID:
321
			elog(ERROR, "TIMESTAMP WITH TIME ZONE '%s' no longer supported", str);
322 323 324 325
			TIMESTAMP_NOEND(result);
			break;

		default:
326
			elog(ERROR, "TIMESTAMP WITH TIME ZONE '%s' not parsed; internal coding error", str);
327 328 329
			TIMESTAMP_NOEND(result);
	}

330 331
	AdjustTimestampForTypmod(&result, typmod);

332 333 334 335 336 337 338 339 340
	PG_RETURN_TIMESTAMPTZ(result);
}

/* timestamptz_out()
 * Convert a timestamp to external form.
 */
Datum
timestamptz_out(PG_FUNCTION_ARGS)
{
341
	TimestampTz dt = PG_GETARG_TIMESTAMPTZ(0);
342
	char	   *result;
343 344 345
	int			tz;
	struct tm	tt,
			   *tm = &tt;
346
	fsec_t		fsec;
347
	char	   *tzn;
348
	char		buf[MAXDATELEN + 1];
M
Marc G. Fournier 已提交
349

350
	if (TIMESTAMP_NOT_FINITE(dt))
351 352
		EncodeSpecialTimestamp(dt, buf);
	else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn) == 0)
353 354
		EncodeDateTime(tm, fsec, &tz, &tzn, DateStyle, buf);
	else
355
		elog(ERROR, "Unable to format timestamp with time zone; internal coding error");
356

357 358 359
	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}
360

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
/*
 *		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);

#ifdef HAVE_INT64_TIMESTAMP
	PG_RETURN_TIMESTAMPTZ((TimestampTz) pq_getmsgint64(buf));
#else
	PG_RETURN_TIMESTAMPTZ((TimestampTz) pq_getmsgfloat8(buf));
#endif
}

/*
 *		timestamptz_send			- converts timestamptz to binary format
 */
Datum
timestamptz_send(PG_FUNCTION_ARGS)
{
	TimestampTz	timestamp = PG_GETARG_TIMESTAMPTZ(0);
	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));
}


398 399 400 401 402 403 404
/* timestamptz_scale()
 * Adjust time type for specified scale factor.
 * Used by PostgreSQL type system to stuff columns.
 */
Datum
timestamptz_scale(PG_FUNCTION_ARGS)
{
405
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
406
	int32		typmod = PG_GETARG_INT32(1);
407
	TimestampTz result;
408 409 410

	result = timestamp;

411
	AdjustTimestampForTypmod(&result, typmod);
412 413 414 415

	PG_RETURN_TIMESTAMPTZ(result);
}

416 417 418 419 420 421 422

/* interval_in()
 * Convert a string to internal form.
 *
 * External format(s):
 *	Uses the generic date/time parsing and decoding routines.
 */
423 424
Datum
interval_in(PG_FUNCTION_ARGS)
425
{
426
	char	   *str = PG_GETARG_CSTRING(0);
427

428 429 430 431
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
432
	Interval   *result;
433
	fsec_t		fsec;
434 435 436 437 438 439
	struct tm	tt,
			   *tm = &tt;
	int			dtype;
	int			nf;
	char	   *field[MAXDATEFIELDS];
	int			ftype[MAXDATEFIELDS];
440
	char		lowstr[MAXDATELEN + MAXDATEFIELDS];
441 442 443 444 445 446 447 448 449

	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;

450 451 452
	if (strlen(str) >= sizeof(lowstr))
		elog(ERROR, "Bad interval external representation (too long) '%s'", str);

453
	if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
454
		|| (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0))
455 456
		elog(ERROR, "Bad interval external representation '%s'", str);

457
	result = (Interval *) palloc(sizeof(Interval));
458 459 460 461

	switch (dtype)
	{
		case DTK_DELTA:
462
			if (tm2interval(tm, fsec, result) != 0)
463
				elog(ERROR, "Bad interval external representation '%s'", str);
464
			AdjustIntervalForTypmod(result, typmod);
465 466 467 468
			break;

		case DTK_INVALID:
			elog(ERROR, "Interval '%s' no longer supported", str);
469
			break;
470

471
		default:
472
			elog(ERROR, "Interval '%s' not parsed; internal coding error", str);
473
	}
474

475
	PG_RETURN_INTERVAL_P(result);
476
}
477 478 479 480

/* interval_out()
 * Convert a time span to external form.
 */
481 482
Datum
interval_out(PG_FUNCTION_ARGS)
483
{
484
	Interval   *span = PG_GETARG_INTERVAL_P(0);
485 486 487
	char	   *result;
	struct tm	tt,
			   *tm = &tt;
488
	fsec_t		fsec;
489 490 491
	char		buf[MAXDATELEN + 1];

	if (interval2tm(*span, tm, &fsec) != 0)
492
		elog(ERROR, "Unable to encode interval; internal coding error");
493

494
	if (EncodeInterval(tm, fsec, DateStyle, buf) != 0)
495
		elog(ERROR, "Unable to format interval; internal coding error");
496

497 498 499
	result = pstrdup(buf);
	PG_RETURN_CSTRING(result);
}
500

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
/*
 *		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
	interval->time = pq_getmsgint64(buf);
#else
	interval->time = pq_getmsgfloat8(buf);
#endif
	interval->month = pq_getmsgint(buf, sizeof(interval->month));

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


542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
/* 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)
{
564
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
565
	static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
566 567 568 569 570 571 572 573 574
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

B
Bruce Momjian 已提交
575
	static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
576 577 578 579 580 581
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
582 583
		INT64CONST(0)
	};
B
Bruce Momjian 已提交
584

585
#else
B
Bruce Momjian 已提交
586
	static const double IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
587
		1,
588
		10,
589 590 591 592 593
		100,
		1000,
		10000,
		100000,
		1000000
594 595 596
	};
#endif

B
Bruce Momjian 已提交
597 598
	/*
	 * Unspecified range and precision? Then not necessary to adjust.
599 600
	 * Setting typmod to -1 is the convention for all types.
	 */
601 602
	if (typmod != -1)
	{
603 604
		int			range = INTERVAL_RANGE(typmod);
		int			precision = INTERVAL_PRECISION(typmod);
605

606
		if (range == INTERVAL_FULL_RANGE)
607 608 609
		{
			/* Do nothing... */
		}
610
		else if (range == INTERVAL_MASK(YEAR))
611 612 613 614
		{
			interval->month = ((interval->month / 12) * 12);
			interval->time = 0;
		}
615
		else if (range == INTERVAL_MASK(MONTH))
616 617 618 619 620
		{
			interval->month %= 12;
			interval->time = 0;
		}
		/* YEAR TO MONTH */
621
		else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
622
			interval->time = 0;
623
		else if (range == INTERVAL_MASK(DAY))
624 625
		{
			interval->month = 0;
626 627 628 629
#ifdef HAVE_INT64_TIMESTAMP
			interval->time = (((int) (interval->time / INT64CONST(86400000000)))
							  * INT64CONST(86400000000));
#else
630
			interval->time = (((int) (interval->time / 86400)) * 86400);
631
#endif
632
		}
633
		else if (range == INTERVAL_MASK(HOUR))
634
		{
635 636
#ifdef HAVE_INT64_TIMESTAMP
			int64		day;
B
Bruce Momjian 已提交
637

638
#else
639
			double		day;
640
#endif
641 642

			interval->month = 0;
643 644 645 646 647 648
#ifdef HAVE_INT64_TIMESTAMP
			day = (interval->time / INT64CONST(86400000000));
			interval->time -= (day * INT64CONST(86400000000));
			interval->time = ((interval->time / INT64CONST(3600000000))
							  * INT64CONST(3600000000));
#else
649
			TMODULO(interval->time, day, 86400.0);
650
			interval->time = (((int) (interval->time / 3600)) * 3600.0);
651
#endif
652
		}
653
		else if (range == INTERVAL_MASK(MINUTE))
654
		{
655 656
#ifdef HAVE_INT64_TIMESTAMP
			int64		hour;
B
Bruce Momjian 已提交
657

658
#else
659
			double		hour;
660
#endif
661 662

			interval->month = 0;
663 664 665 666 667 668
#ifdef HAVE_INT64_TIMESTAMP
			hour = (interval->time / INT64CONST(3600000000));
			interval->time -= (hour * INT64CONST(3600000000));
			interval->time = ((interval->time / INT64CONST(60000000))
							  * INT64CONST(60000000));
#else
669
			TMODULO(interval->time, hour, 3600.0);
670
			interval->time = (((int) (interval->time / 60)) * 60);
671
#endif
672
		}
673
		else if (range == INTERVAL_MASK(SECOND))
674
		{
675 676
#ifdef HAVE_INT64_TIMESTAMP
			int64		minute;
B
Bruce Momjian 已提交
677

678 679 680
#else
			double		minute;
#endif
681 682

			interval->month = 0;
683 684 685 686 687
#ifdef HAVE_INT64_TIMESTAMP
			minute = (interval->time / INT64CONST(60000000));
			interval->time -= (minute * INT64CONST(60000000));
#else
			TMODULO(interval->time, minute, 60.0);
688
/*			interval->time = (int)(interval->time); */
689
#endif
690 691
		}
		/* DAY TO HOUR */
692 693
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR)))
694 695
		{
			interval->month = 0;
696 697 698 699
#ifdef HAVE_INT64_TIMESTAMP
			interval->time = ((interval->time / INT64CONST(3600000000))
							  * INT64CONST(3600000000));
#else
700
			interval->time = (((int) (interval->time / 3600)) * 3600);
701
#endif
702 703
		}
		/* DAY TO MINUTE */
704 705 706
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE)))
707 708
		{
			interval->month = 0;
709 710 711 712
#ifdef HAVE_INT64_TIMESTAMP
			interval->time = ((interval->time / INT64CONST(60000000))
							  * INT64CONST(60000000));
#else
713
			interval->time = (((int) (interval->time / 60)) * 60);
714
#endif
715 716
		}
		/* DAY TO SECOND */
717 718 719 720
		else if (range == (INTERVAL_MASK(DAY) |
						   INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
721 722
			interval->month = 0;
		/* HOUR TO MINUTE */
723 724
		else if (range == (INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE)))
725
		{
726 727
#ifdef HAVE_INT64_TIMESTAMP
			int64		day;
B
Bruce Momjian 已提交
728

729
#else
730
			double		day;
731
#endif
732 733

			interval->month = 0;
734 735 736 737 738 739
#ifdef HAVE_INT64_TIMESTAMP
			day = (interval->time / INT64CONST(86400000000));
			interval->time -= (day * INT64CONST(86400000000));
			interval->time = ((interval->time / INT64CONST(60000000))
							  * INT64CONST(60000000));
#else
740
			TMODULO(interval->time, day, 86400.0);
741
			interval->time = (((int) (interval->time / 60)) * 60);
742
#endif
743 744
		}
		/* HOUR TO SECOND */
745 746 747
		else if (range == (INTERVAL_MASK(HOUR) |
						   INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
748
		{
749 750
#ifdef HAVE_INT64_TIMESTAMP
			int64		day;
B
Bruce Momjian 已提交
751

752
#else
753
			double		day;
754
#endif
755 756

			interval->month = 0;
757 758 759 760
#ifdef HAVE_INT64_TIMESTAMP
			day = (interval->time / INT64CONST(86400000000));
			interval->time -= (day * INT64CONST(86400000000));
#else
761
			TMODULO(interval->time, day, 86400.0);
762
#endif
763 764
		}
		/* MINUTE TO SECOND */
765 766
		else if (range == (INTERVAL_MASK(MINUTE) |
						   INTERVAL_MASK(SECOND)))
767
		{
768 769
#ifdef HAVE_INT64_TIMESTAMP
			int64		hour;
B
Bruce Momjian 已提交
770

771
#else
772
			double		hour;
773
#endif
774 775

			interval->month = 0;
776 777 778 779
#ifdef HAVE_INT64_TIMESTAMP
			hour = (interval->time / INT64CONST(3600000000));
			interval->time -= (hour * INT64CONST(3600000000));
#else
780
			TMODULO(interval->time, hour, 3600.0);
781
#endif
782 783 784 785
		}
		else
			elog(ERROR, "AdjustIntervalForTypmod(): internal coding error");

786
		/* Need to adjust precision? If not, don't even try! */
787
		if (precision != INTERVAL_FULL_PRECISION)
788
		{
789 790 791
			if ((precision < 0) || (precision > MAX_INTERVAL_PRECISION))
				elog(ERROR, "INTERVAL(%d) precision must be between %d and %d",
					 precision, 0, MAX_INTERVAL_PRECISION);
792

793 794 795
			/*
			 * Note: this round-to-nearest code is not completely consistent
			 * about rounding values that are exactly halfway between integral
T
Tom Lane 已提交
796
			 * values.  On most platforms, rint() will implement round-to-nearest-even,
797 798 799
			 * but the integer code always rounds up (away from zero).  Is it
			 * worth trying to be consistent?
			 */
800 801 802 803 804 805 806 807
#ifdef HAVE_INT64_TIMESTAMP
			if (interval->time >= INT64CONST(0))
			{
				interval->time = (((interval->time + IntervalOffsets[precision]) / IntervalScales[precision])
								  * IntervalScales[precision]);
			}
			else
			{
808 809
				interval->time = - (((-interval->time + IntervalOffsets[precision]) / IntervalScales[precision])
									* IntervalScales[precision]);
810
			}
811 812 813
#else
			interval->time = (rint(((double) interval->time) * IntervalScales[precision])
							  / IntervalScales[precision]);
814
#endif
815 816 817 818 819 820
		}
	}

	return;
}

821 822 823 824

/* EncodeSpecialTimestamp()
 * Convert reserved timestamp data type to string.
 */
825
static int
826 827
EncodeSpecialTimestamp(Timestamp dt, char *str)
{
828 829 830 831 832 833
	if (TIMESTAMP_IS_NOBEGIN(dt))
		strcpy(str, EARLY);
	else if (TIMESTAMP_IS_NOEND(dt))
		strcpy(str, LATE);
	else
		return FALSE;
M
Marc G. Fournier 已提交
834

835
	return TRUE;
836 837
}	/* EncodeSpecialTimestamp() */

838 839
Datum
now(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
840
{
841
	TimestampTz result;
842
	AbsoluteTime sec;
843
	int			usec;
844

845
	sec = GetCurrentTransactionStartTimeUsec(&usec);
846

847
	result = AbsoluteTimeUsecToTimestampTz(sec, usec);
848

849
	PG_RETURN_TIMESTAMPTZ(result);
M
Marc G. Fournier 已提交
850 851
}

852
void
853
dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
854
{
855 856
#ifdef HAVE_INT64_TIMESTAMP
	int64		time;
B
Bruce Momjian 已提交
857

858
#else
859
	double		time;
860
#endif
861 862 863

	time = jd;

864 865 866 867 868 869 870 871
#ifdef HAVE_INT64_TIMESTAMP
	*hour = (time / INT64CONST(3600000000));
	time -= ((*hour) * INT64CONST(3600000000));
	*min = (time / INT64CONST(60000000));
	time -= ((*min) * INT64CONST(60000000));
	*sec = (time / INT64CONST(1000000));
	*fsec = (time - (*sec * INT64CONST(1000000)));
#else
872 873 874 875
	*hour = (time / 3600);
	time -= ((*hour) * 3600);
	*min = (time / 60);
	time -= ((*min) * 60);
876 877 878
	*sec = time;
	*fsec = JROUND(time - *sec);
#endif
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895

	return;
}	/* dt2time() */


/* timestamp2tm()
 * Convert timestamp data type to POSIX time structure.
 * 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
 *
 * For dates within the system-supported time_t range, convert to the
 *	local time zone. If out of this range, leave as GMT. - tgl 97/05/27
 */
int
B
Bruce Momjian 已提交
896
timestamp2tm(Timestamp dt, int *tzp, struct tm * tm, fsec_t *fsec, char **tzn)
897
{
898
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
899 900 901 902
	int			date,
				date0;
	int64		time;

903
#else
B
Bruce Momjian 已提交
904 905 906
	double		date,
				date0;
	double		time;
907
#endif
B
Bruce Momjian 已提交
908
	time_t		utime;
909

910
#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
911 912 913
	struct tm  *tx;
#endif

914
	date0 = POSTGRES_EPOCH_JDATE;
915

916 917 918 919
	/*
	 * 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.
920 921
	 */
	if (HasCTZSet && (tzp != NULL))
922
	{
923 924 925
#ifdef HAVE_INT64_TIMESTAMP
		dt -= (CTimeZone * INT64CONST(1000000));
#else
926
		dt -= CTimeZone;
927
#endif
928
	}
929

930
	time = dt;
931 932 933 934 935 936 937 938 939
#ifdef HAVE_INT64_TIMESTAMP
	TMODULO(time, date, INT64CONST(86400000000));

	if (time < INT64CONST(0))
	{
		time += INT64CONST(86400000000);
		date -= 1;
	}
#else
940 941 942 943 944 945 946
	TMODULO(time, date, 86400e0);

	if (time < 0)
	{
		time += 86400;
		date -= 1;
	}
947
#endif
948 949 950 951 952 953 954 955 956

	/* Julian day routine does not work for negative Julian days */
	if (date < -date0)
		return -1;

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

	j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
957
	dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
958 959 960

	if (tzp != NULL)
	{
961 962 963
		/*
		 * We have a brute force time zone per SQL99? Then use it without
		 * change since we have already rotated to the time zone.
964 965 966 967 968
		 */
		if (HasCTZSet)
		{
			*tzp = CTimeZone;
			tm->tm_isdst = 0;
969 970
#if defined(HAVE_TM_ZONE)
			tm->tm_gmtoff = CTimeZone;
971
			tm->tm_zone = NULL;
972
#endif
973 974 975 976
			if (tzn != NULL)
				*tzn = NULL;
		}

977 978 979
		/*
		 * Does this fall within the capabilities of the localtime()
		 * interface? Then use this to rotate to the local time zone.
980 981
		 */
		else if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
982
		{
983 984
#ifdef HAVE_INT64_TIMESTAMP
			utime = ((dt / INT64CONST(1000000))
985
				   + ((date0 - UNIX_EPOCH_JDATE) * INT64CONST(86400)));
986
#else
987
			utime = (dt + ((date0 - UNIX_EPOCH_JDATE) * 86400));
988
#endif
989

990
#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
991 992 993 994 995 996 997 998 999 1000
			tx = localtime(&utime);
			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;
#if NOT_USED
/* XXX HACK
 * Argh! My Linux box puts in a 1 second offset for dates less than 1970
 *	but only if the seconds field was non-zero. So, don't copy the seconds
1001
 *	field and instead carry forward from the original - thomas 97/06/18
1002 1003
 * Note that GNU/Linux uses the standard freeware zic package as do
 *	many other platforms so this may not be GNU/Linux/ix86-specific.
1004
 * Still shows a problem on my up to date Linux box - thomas 2001-01-17
1005 1006 1007 1008 1009
 */
			tm->tm_sec = tx->tm_sec;
#endif
			tm->tm_isdst = tx->tm_isdst;

B
Bruce Momjian 已提交
1010
#if defined(HAVE_TM_ZONE)
1011 1012 1013 1014 1015 1016
			tm->tm_gmtoff = tx->tm_gmtoff;
			tm->tm_zone = tx->tm_zone;

			*tzp = -(tm->tm_gmtoff);	/* tm_gmtoff is Sun/DEC-ism */
			if (tzn != NULL)
				*tzn = (char *) tm->tm_zone;
B
Bruce Momjian 已提交
1017
#elif defined(HAVE_INT_TIMEZONE)
1018
			*tzp = ((tm->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
1019 1020
			if (tzn != NULL)
				*tzn = tzname[(tm->tm_isdst > 0)];
B
Bruce Momjian 已提交
1021
#endif
1022

B
Bruce Momjian 已提交
1023
#else							/* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
1024 1025 1026
			*tzp = 0;
			/* Mark this as *no* time zone available */
			tm->tm_isdst = -1;
1027
			if (tzn != NULL)
1028
				*tzn = NULL;
1029 1030 1031 1032 1033
#endif
		}
		else
		{
			*tzp = 0;
1034 1035
			/* Mark this as *no* time zone available */
			tm->tm_isdst = -1;
1036 1037 1038 1039 1040 1041
			if (tzn != NULL)
				*tzn = NULL;
		}
	}
	else
	{
1042
		tm->tm_isdst = -1;
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
		if (tzn != NULL)
			*tzn = NULL;
	}

	return 0;
}	/* timestamp2tm() */


/* 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.
1055 1056
 *
 * Returns -1 on failure (overflow).
1057 1058
 */
int
1059
tm2timestamp(struct tm * tm, fsec_t fsec, int *tzp, Timestamp *result)
1060
{
1061
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
1062 1063 1064
	int			date;
	int64		time;

1065
#else
1066 1067
	double		date,
				time;
1068
#endif
1069 1070 1071 1072 1073

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

1074
	date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1075 1076
	time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
#ifdef HAVE_INT64_TIMESTAMP
1077 1078 1079 1080 1081 1082 1083
	*result = (date * INT64CONST(86400000000)) + time;
	/* check for major overflow */
	if ((*result - time) / INT64CONST(86400000000) != date)
		return -1;
	/* check for just-barely overflow (okay except time-of-day wraps) */
	if ((*result < 0) ? (date >= 0) : (date < 0))
		return -1;
1084 1085 1086
#else
	*result = ((date * 86400) + time);
#endif
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
	if (tzp != NULL)
		*result = dt2local(*result, -(*tzp));

	return 0;
}	/* tm2timestamp() */


/* interval2tm()
 * Convert a interval data type to a tm structure.
 */
B
Bruce Momjian 已提交
1097
int
1098
interval2tm(Interval span, struct tm * tm, fsec_t *fsec)
1099
{
1100 1101
#ifdef HAVE_INT64_TIMESTAMP
	int64		time;
B
Bruce Momjian 已提交
1102

1103
#else
1104
	double		time;
1105
#endif
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120

	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;

1121 1122 1123 1124 1125 1126 1127 1128 1129 1130
#ifdef HAVE_INT64_TIMESTAMP
	tm->tm_mday = (time / INT64CONST(86400000000));
	time -= (tm->tm_mday * INT64CONST(86400000000));
	tm->tm_hour = (time / INT64CONST(3600000000));
	time -= (tm->tm_hour * INT64CONST(3600000000));
	tm->tm_min = (time / INT64CONST(60000000));
	time -= (tm->tm_min * INT64CONST(60000000));
	tm->tm_sec = (time / INT64CONST(1000000));
	*fsec = (time - (tm->tm_sec * INT64CONST(1000000)));
#else
1131 1132 1133 1134 1135
	TMODULO(time, tm->tm_mday, 86400e0);
	TMODULO(time, tm->tm_hour, 3600e0);
	TMODULO(time, tm->tm_min, 60e0);
	TMODULO(time, tm->tm_sec, 1e0);
	*fsec = time;
1136
#endif
1137 1138 1139 1140

	return 0;
}	/* interval2tm() */

B
Bruce Momjian 已提交
1141
int
1142
tm2interval(struct tm * tm, fsec_t fsec, Interval *span)
1143 1144
{
	span->month = ((tm->tm_year * 12) + tm->tm_mon);
1145 1146
#ifdef HAVE_INT64_TIMESTAMP
	span->time = ((((((((tm->tm_mday * INT64CONST(24))
B
Bruce Momjian 已提交
1147 1148 1149
						+ tm->tm_hour) * INT64CONST(60))
					  + tm->tm_min) * INT64CONST(60))
					+ tm->tm_sec) * INT64CONST(1000000)) + fsec);
1150
#else
1151
	span->time = ((((((tm->tm_mday * 24.0)
1152 1153 1154
					  + tm->tm_hour) * 60.0)
					+ tm->tm_min) * 60.0)
				  + tm->tm_sec);
1155
	span->time = JROUND(span->time + fsec);
1156
#endif
1157 1158 1159 1160

	return 0;
}	/* tm2interval() */

1161 1162 1163 1164 1165 1166
#ifdef HAVE_INT64_TIMESTAMP
static int64
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
{
	return ((((((hour * 60) + min) * 60) + sec) * INT64CONST(1000000)) + fsec);
}	/* time2t() */
B
Bruce Momjian 已提交
1167

1168
#else
1169
static double
1170
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
1171
{
1172
	return ((((hour * 60) + min) * 60) + sec + fsec);
1173
}	/* time2t() */
1174
#endif
1175

1176
static Timestamp
1177 1178
dt2local(Timestamp dt, int tz)
{
1179 1180 1181
#ifdef HAVE_INT64_TIMESTAMP
	dt -= (tz * INT64CONST(1000000));
#else
1182 1183
	dt -= tz;
	dt = JROUND(dt);
1184
#endif
1185 1186 1187 1188 1189 1190 1191 1192 1193
	return dt;
}	/* dt2local() */


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


1194 1195
Datum
timestamp_finite(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1196
{
1197
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
1198

B
Bruce Momjian 已提交
1199
	PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp));
1200
}
M
Marc G. Fournier 已提交
1201

1202 1203
Datum
interval_finite(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1204
{
1205
	PG_RETURN_BOOL(true);
1206
}
1207 1208 1209 1210 1211 1212


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

1213
void
1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
GetEpochTime(struct tm * tm)
{
	struct tm  *t0;
	time_t		epoch = 0;

	t0 = gmtime(&epoch);

	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;

	if (tm->tm_year < 1900)
		tm->tm_year += 1900;
	tm->tm_mon++;

	return;
}	/* GetEpochTime() */

Timestamp
1236
SetEpochTimestamp(void)
1237
{
1238 1239 1240
	Timestamp	dt;
	struct tm	tt,
			   *tm = &tt;
1241

1242 1243
	GetEpochTime(tm);
	tm2timestamp(tm, 0, NULL, &dt);
M
Marc G. Fournier 已提交
1244

1245
	return dt;
1246
}	/* SetEpochTimestamp() */
1247

1248 1249
/*
 *		timestamp_relop - is timestamp1 relop timestamp2
1250 1251
 *
 *		collate invalid timestamp at the end
1252
 */
1253 1254 1255
static int
timestamp_cmp_internal(Timestamp dt1, Timestamp dt2)
{
1256
	return ((dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0));
1257 1258
}

1259 1260
Datum
timestamp_eq(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1261
{
1262 1263
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1264

1265
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
1266
}
M
Marc G. Fournier 已提交
1267

1268 1269
Datum
timestamp_ne(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1270
{
1271 1272
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1273

1274
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
1275
}
M
Marc G. Fournier 已提交
1276

1277 1278
Datum
timestamp_lt(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1279
{
1280 1281
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1282

1283
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
1284
}
M
Marc G. Fournier 已提交
1285

1286 1287
Datum
timestamp_gt(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1288
{
1289 1290
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1291

1292
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
1293
}
1294

1295 1296
Datum
timestamp_le(PG_FUNCTION_ARGS)
1297
{
1298 1299
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1300

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

1304 1305
Datum
timestamp_ge(PG_FUNCTION_ARGS)
1306
{
1307 1308
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1309

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

1313 1314
Datum
timestamp_cmp(PG_FUNCTION_ARGS)
1315
{
1316 1317
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1318

1319
	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
1320
}
1321 1322


1323 1324
/*
 *		interval_relop	- is interval1 relop interval2
1325 1326
 *
 *		collate invalid interval at the end
1327
 */
1328 1329 1330
static int
interval_cmp_internal(Interval *interval1, Interval *interval2)
{
1331 1332 1333
#ifdef HAVE_INT64_TIMESTAMP
	int64		span1,
				span2;
B
Bruce Momjian 已提交
1334

1335
#else
1336 1337
	double		span1,
				span2;
1338
#endif
1339

1340
	span1 = interval1->time;
1341 1342 1343 1344 1345 1346 1347 1348
	span2 = interval2->time;

#ifdef HAVE_INT64_TIMESTAMP
	if (interval1->month != 0)
		span1 += ((interval1->month * INT64CONST(30) * INT64CONST(86400000000)));
	if (interval2->month != 0)
		span2 += ((interval2->month * INT64CONST(30) * INT64CONST(86400000000)));
#else
1349 1350 1351 1352
	if (interval1->month != 0)
		span1 += (interval1->month * (30.0 * 86400));
	if (interval2->month != 0)
		span2 += (interval2->month * (30.0 * 86400));
1353
#endif
1354

1355
	return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
1356 1357
}

1358 1359
Datum
interval_eq(PG_FUNCTION_ARGS)
1360
{
1361 1362
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1363

1364
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0);
1365
}
1366

1367 1368
Datum
interval_ne(PG_FUNCTION_ARGS)
1369
{
1370 1371
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1372

1373
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0);
1374
}
1375

1376 1377
Datum
interval_lt(PG_FUNCTION_ARGS)
1378
{
1379 1380
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1381

1382
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0);
1383
}
1384

1385 1386
Datum
interval_gt(PG_FUNCTION_ARGS)
1387
{
1388 1389
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1390

1391
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0);
1392
}
1393

1394 1395
Datum
interval_le(PG_FUNCTION_ARGS)
1396
{
1397 1398
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1399

1400
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0);
1401
}
1402

1403 1404
Datum
interval_ge(PG_FUNCTION_ARGS)
1405
{
1406 1407
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1408

1409
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0);
1410
}
1411

1412 1413
Datum
interval_cmp(PG_FUNCTION_ARGS)
1414
{
1415 1416
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1417

1418
	PG_RETURN_INT32(interval_cmp_internal(interval1, interval2));
1419
}
1420

1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433
/*
 * 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!
	 */
1434
	return hash_any((unsigned char *) key, sizeof(key->time) + sizeof(key->month));
1435 1436
}

1437 1438 1439 1440 1441
/* 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.
1442
 */
1443 1444
Datum
overlaps_timestamp(PG_FUNCTION_ARGS)
1445
{
B
Bruce Momjian 已提交
1446 1447
	/*
	 * The arguments are Timestamps, but we leave them as generic Datums
1448 1449
	 * to avoid unnecessary conversions between value and reference forms
	 * --- not to mention possible dereferences of null pointers.
1450 1451 1452 1453 1454
	 */
	Datum		ts1 = PG_GETARG_DATUM(0);
	Datum		te1 = PG_GETARG_DATUM(1);
	Datum		ts2 = PG_GETARG_DATUM(2);
	Datum		te2 = PG_GETARG_DATUM(3);
1455 1456 1457 1458
	bool		ts1IsNull = PG_ARGISNULL(0);
	bool		te1IsNull = PG_ARGISNULL(1);
	bool		ts2IsNull = PG_ARGISNULL(2);
	bool		te2IsNull = PG_ARGISNULL(3);
1459 1460 1461 1462 1463 1464

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

1465
	/*
B
Bruce Momjian 已提交
1466 1467 1468
	 * 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.
1469 1470
	 */
	if (ts1IsNull)
1471
	{
1472 1473 1474
		if (te1IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
1475
		ts1 = te1;
1476
		te1IsNull = true;
1477
	}
1478
	else if (!te1IsNull)
1479
	{
1480 1481
		if (TIMESTAMP_GT(ts1, te1))
		{
B
Bruce Momjian 已提交
1482
			Datum		tt = ts1;
1483

1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
			ts1 = te1;
			te1 = tt;
		}
	}

	/* Likewise for interval 2. */
	if (ts2IsNull)
	{
		if (te2IsNull)
			PG_RETURN_NULL();
		/* swap null for non-null */
1495
		ts2 = te2;
1496
		te2IsNull = true;
1497
	}
1498 1499 1500 1501
	else if (!te2IsNull)
	{
		if (TIMESTAMP_GT(ts2, te2))
		{
B
Bruce Momjian 已提交
1502
			Datum		tt = ts2;
1503

1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514
			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 已提交
1515 1516
		/*
		 * This case is ts1 < te2 OR te1 < te2, which may look redundant
1517 1518 1519 1520 1521 1522 1523 1524
		 * 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 已提交
1525 1526 1527

		/*
		 * If te1 is not null then we had ts1 <= te1 above, and we just
1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540
		 * 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 已提交
1541 1542 1543

		/*
		 * If te2 is not null then we had ts2 <= te2 above, and we just
1544 1545 1546 1547 1548 1549
		 * found ts2 >= te1, hence te2 >= te1.
		 */
		PG_RETURN_BOOL(false);
	}
	else
	{
B
Bruce Momjian 已提交
1550 1551 1552 1553
		/*
		 * 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".
1554 1555 1556 1557 1558
		 */
		if (te1IsNull || te2IsNull)
			PG_RETURN_NULL();
		PG_RETURN_BOOL(true);
	}
1559 1560 1561 1562

#undef TIMESTAMP_GT
#undef TIMESTAMP_LT
}
1563

1564 1565 1566 1567 1568

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

1569 1570 1571
/* We are currently sharing some code between timestamp and timestamptz.
 * The comparison functions are among them. - thomas 2001-09-25
 */
1572 1573
Datum
timestamp_smaller(PG_FUNCTION_ARGS)
1574
{
1575 1576 1577
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
1578

1579
	result = ((dt2 < dt1) ? dt2 : dt1);
1580

1581 1582
	PG_RETURN_TIMESTAMP(result);
}
1583

1584 1585
Datum
timestamp_larger(PG_FUNCTION_ARGS)
1586
{
1587 1588 1589
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
1590

1591
	result = ((dt2 > dt1) ? dt2 : dt1);
1592

1593 1594
	PG_RETURN_TIMESTAMP(result);
}
1595 1596


1597 1598
Datum
timestamp_mi(PG_FUNCTION_ARGS)
1599
{
1600 1601
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1602 1603
	Interval   *result;

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

1606 1607 1608 1609 1610
	if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
	{
		elog(ERROR, "Unable to subtract non-finite timestamps");
		result->time = 0;
	}
1611
	else
1612 1613 1614
#ifdef HAVE_INT64_TIMESTAMP
		result->time = (dt1 - dt2);
#else
1615
		result->time = JROUND(dt1 - dt2);
1616
#endif
1617

1618 1619
	result->month = 0;

1620 1621
	PG_RETURN_INTERVAL_P(result);
}
1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632


/* timestamp_pl_span()
 * 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".
 */
1633 1634
Datum
timestamp_pl_span(PG_FUNCTION_ARGS)
1635
{
1636 1637 1638
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Timestamp	result;
1639 1640 1641 1642 1643 1644 1645 1646 1647

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
		if (span->month != 0)
		{
			struct tm	tt,
					   *tm = &tt;
1648
			fsec_t		fsec;
1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669

			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0)
			{
				tm->tm_mon += span->month;
				if (tm->tm_mon > 12)
				{
					tm->tm_year += ((tm->tm_mon - 1) / 12);
					tm->tm_mon = (((tm->tm_mon - 1) % 12) + 1);
				}
				else if (tm->tm_mon < 1)
				{
					tm->tm_year += ((tm->tm_mon / 12) - 1);
					tm->tm_mon = ((tm->tm_mon % 12) + 12);
				}

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

				if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
				{
1670
					elog(ERROR, "Unable to add TIMESTAMP and INTERVAL"
1671 1672 1673 1674 1675 1676
						 "\n\ttimestamp_pl_span() internal error encoding timestamp");
					PG_RETURN_NULL();
				}
			}
			else
			{
1677
				elog(ERROR, "Unable to add TIMESTAMP and INTERVAL"
1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705
					 "\n\ttimestamp_pl_span() internal error decoding timestamp");
				PG_RETURN_NULL();
			}
		}

		timestamp += span->time;
		result = timestamp;
	}

	PG_RETURN_TIMESTAMP(result);
}

Datum
timestamp_mi_span(PG_FUNCTION_ARGS)
{
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Interval	tspan;

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

	return DirectFunctionCall2(timestamp_pl_span,
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}


1706
/* timestamptz_pl_span()
1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717
 * 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
timestamptz_pl_span(PG_FUNCTION_ARGS)
{
1718
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
1719
	Interval   *span = PG_GETARG_INTERVAL_P(1);
1720
	TimestampTz result;
1721 1722 1723
	int			tz;
	char	   *tzn;

1724 1725
	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
1726 1727 1728 1729 1730 1731
	else
	{
		if (span->month != 0)
		{
			struct tm	tt,
					   *tm = &tt;
1732
			fsec_t		fsec;
1733

1734
			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0)
1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751
			{
				tm->tm_mon += span->month;
				if (tm->tm_mon > 12)
				{
					tm->tm_year += ((tm->tm_mon - 1) / 12);
					tm->tm_mon = (((tm->tm_mon - 1) % 12) + 1);
				}
				else if (tm->tm_mon < 1)
				{
					tm->tm_year += ((tm->tm_mon / 12) - 1);
					tm->tm_mon = ((tm->tm_mon % 12) + 12);
				}

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

1752
				tz = DetermineLocalTimeZone(tm);
1753

1754
				if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
1755
					elog(ERROR, "Unable to add TIMESTAMP and INTERVAL"
1756
						 "\n\ttimestamptz_pl_span() internal error encoding timestamp");
1757 1758
			}
			else
1759
			{
1760
				elog(ERROR, "Unable to add TIMESTAMP and INTERVAL"
1761 1762
					 "\n\ttimestamptz_pl_span() internal error decoding timestamp");
			}
1763 1764
		}

1765 1766
		timestamp += span->time;
		result = timestamp;
1767 1768
	}

1769 1770
	PG_RETURN_TIMESTAMP(result);
}
1771

1772
Datum
1773
timestamptz_mi_span(PG_FUNCTION_ARGS)
1774
{
1775
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
1776
	Interval   *span = PG_GETARG_INTERVAL_P(1);
1777 1778
	Interval	tspan;

B
Bruce Momjian 已提交
1779 1780
	tspan.month = -span->month;
	tspan.time = -span->time;
1781

1782
	return DirectFunctionCall2(timestamptz_pl_span,
1783 1784 1785
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}
1786 1787


1788 1789
Datum
interval_um(PG_FUNCTION_ARGS)
1790
{
1791
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
1792 1793
	Interval   *result;

1794
	result = (Interval *) palloc(sizeof(Interval));
1795 1796 1797 1798

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

1799 1800
	PG_RETURN_INTERVAL_P(result);
}
1801 1802


1803 1804
Datum
interval_smaller(PG_FUNCTION_ARGS)
1805
{
1806 1807
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1808
	Interval   *result;
B
Bruce Momjian 已提交
1809

1810 1811 1812
#ifdef HAVE_INT64_TIMESTAMP
	int64		span1,
				span2;
B
Bruce Momjian 已提交
1813

1814
#else
1815 1816
	double		span1,
				span2;
1817
#endif
1818

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

1821
	span1 = interval1->time;
1822 1823 1824 1825 1826 1827 1828
	span2 = interval2->time;
#ifdef HAVE_INT64_TIMESTAMP
	if (interval1->month != 0)
		span1 += ((interval1->month * INT64CONST(30) * INT64CONST(86400000000)));
	if (interval2->month != 0)
		span2 += ((interval2->month * INT64CONST(30) * INT64CONST(86400000000)));
#else
1829 1830 1831 1832
	if (interval1->month != 0)
		span1 += (interval1->month * (30.0 * 86400));
	if (interval2->month != 0)
		span2 += (interval2->month * (30.0 * 86400));
1833
#endif
1834 1835

	if (span2 < span1)
1836 1837 1838 1839
	{
		result->time = interval2->time;
		result->month = interval2->month;
	}
1840
	else
1841 1842 1843 1844 1845
	{
		result->time = interval1->time;
		result->month = interval1->month;
	}

1846 1847
	PG_RETURN_INTERVAL_P(result);
}
1848

1849 1850
Datum
interval_larger(PG_FUNCTION_ARGS)
1851
{
1852 1853
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1854
	Interval   *result;
B
Bruce Momjian 已提交
1855

1856 1857 1858
#ifdef HAVE_INT64_TIMESTAMP
	int64		span1,
				span2;
B
Bruce Momjian 已提交
1859

1860
#else
1861 1862
	double		span1,
				span2;
1863
#endif
1864

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

1867
	span1 = interval1->time;
1868 1869 1870 1871 1872 1873 1874
	span2 = interval2->time;
#ifdef HAVE_INT64_TIMESTAMP
	if (interval1->month != 0)
		span1 += ((interval1->month * INT64CONST(30) * INT64CONST(86400000000)));
	if (interval2->month != 0)
		span2 += ((interval2->month * INT64CONST(30) * INT64CONST(86400000000)));
#else
1875 1876 1877 1878
	if (interval1->month != 0)
		span1 += (interval1->month * (30.0 * 86400));
	if (interval2->month != 0)
		span2 += (interval2->month * (30.0 * 86400));
1879
#endif
1880 1881

	if (span2 > span1)
1882 1883 1884 1885
	{
		result->time = interval2->time;
		result->month = interval2->month;
	}
1886
	else
1887 1888 1889 1890 1891
	{
		result->time = interval1->time;
		result->month = interval1->month;
	}

1892 1893
	PG_RETURN_INTERVAL_P(result);
}
1894

1895 1896
Datum
interval_pl(PG_FUNCTION_ARGS)
1897
{
1898 1899
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
1900 1901
	Interval   *result;

1902
	result = (Interval *) palloc(sizeof(Interval));
1903 1904

	result->month = (span1->month + span2->month);
1905 1906 1907
#ifdef HAVE_INT64_TIMESTAMP
	result->time = (span1->time + span2->time);
#else
1908
	result->time = JROUND(span1->time + span2->time);
1909
#endif
1910

1911 1912
	PG_RETURN_INTERVAL_P(result);
}
1913

1914 1915
Datum
interval_mi(PG_FUNCTION_ARGS)
1916
{
1917 1918
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
1919 1920
	Interval   *result;

1921
	result = (Interval *) palloc(sizeof(Interval));
1922 1923

	result->month = (span1->month - span2->month);
1924 1925 1926
#ifdef HAVE_INT64_TIMESTAMP
	result->time = (span1->time - span2->time);
#else
1927
	result->time = JROUND(span1->time - span2->time);
1928
#endif
1929

1930 1931
	PG_RETURN_INTERVAL_P(result);
}
1932

1933 1934
Datum
interval_mul(PG_FUNCTION_ARGS)
1935
{
1936 1937
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	float8		factor = PG_GETARG_FLOAT8(1);
1938
	Interval   *result;
B
Bruce Momjian 已提交
1939

1940 1941
#ifdef HAVE_INT64_TIMESTAMP
	int64		months;
B
Bruce Momjian 已提交
1942

1943
#else
1944
	double		months;
1945
#endif
1946

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

1949
	months = (span1->month * factor);
1950 1951 1952 1953 1954 1955
#ifdef HAVE_INT64_TIMESTAMP
	result->month = months;
	result->time = (span1->time * factor);
	result->time += ((months - result->month) * INT64CONST(30)
					 * INT64CONST(86400000000));
#else
1956
	result->month = rint(months);
1957
	result->time = JROUND(span1->time * factor);
1958 1959
	/* evaluate fractional months as 30 days */
	result->time += JROUND((months - result->month) * 30 * 86400);
1960
#endif
1961

1962 1963
	PG_RETURN_INTERVAL_P(result);
}
1964

1965 1966
Datum
mul_d_interval(PG_FUNCTION_ARGS)
1967
{
1968 1969 1970
	/* Args are float8 and Interval *, but leave them as generic Datum */
	Datum		factor = PG_GETARG_DATUM(0);
	Datum		span1 = PG_GETARG_DATUM(1);
1971

1972 1973 1974 1975 1976
	return DirectFunctionCall2(interval_mul, span1, factor);
}

Datum
interval_div(PG_FUNCTION_ARGS)
1977
{
1978
	Interval   *span = PG_GETARG_INTERVAL_P(0);
1979
	float8		factor = PG_GETARG_FLOAT8(1);
1980
	Interval   *result;
B
Bruce Momjian 已提交
1981

1982
#ifndef HAVE_INT64_TIMESTAMP
1983
	double		months;
1984
#endif
1985

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

1988
	if (factor == 0.0)
1989
		elog(ERROR, "division by zero");
1990

1991 1992 1993 1994 1995
#ifdef HAVE_INT64_TIMESTAMP
	result->month = (span->month / factor);
	result->time = (span->time / factor);
	/* evaluate fractional months as 30 days */
	result->time += (((span->month - (result->month * factor))
B
Bruce Momjian 已提交
1996
				   * INT64CONST(30) * INT64CONST(86400000000)) / factor);
1997 1998
#else
	months = (span->month / factor);
1999
	result->month = rint(months);
2000
	result->time = JROUND(span->time / factor);
2001 2002
	/* evaluate fractional months as 30 days */
	result->time += JROUND((months - result->month) * 30 * 86400);
2003
#endif
2004

2005 2006
	PG_RETURN_INTERVAL_P(result);
}
2007

2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030
/*
 * 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,
2031
					  INTERVALOID, 12, false, 'd',
2032 2033 2034
					  &transdatums, &ndatums);
	if (ndatums != 2)
		elog(ERROR, "interval_accum: expected 2-element interval array");
B
Bruce Momjian 已提交
2035

2036 2037 2038
	/*
	 * 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 已提交
2039 2040
	 * objects on machines where double requires 8-byte alignment. That
	 * should be fixed, but in the meantime...
2041
	 *
B
Bruce Momjian 已提交
2042 2043
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2044
	 */
2045 2046
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2047 2048

	newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
2049 2050
												IntervalPGetDatum(&sumX),
											 IntervalPGetDatum(newval)));
2051 2052 2053 2054 2055 2056
	N.time += 1;

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

	result = construct_array(transdatums, 2,
2057
							 INTERVALOID, 12, false, 'd');
2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072

	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,
2073
					  INTERVALOID, 12, false, 'd',
2074 2075 2076
					  &transdatums, &ndatums);
	if (ndatums != 2)
		elog(ERROR, "interval_avg: expected 2-element interval array");
B
Bruce Momjian 已提交
2077

2078 2079 2080
	/*
	 * 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 已提交
2081 2082
	 * objects on machines where double requires 8-byte alignment. That
	 * should be fixed, but in the meantime...
2083
	 *
B
Bruce Momjian 已提交
2084 2085
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2086
	 */
2087 2088
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099

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


2100 2101 2102 2103 2104 2105
/* 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.
 */
2106 2107
Datum
timestamp_age(PG_FUNCTION_ARGS)
2108
{
2109 2110
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
2111
	Interval   *result;
2112
	fsec_t		fsec,
2113 2114 2115 2116 2117 2118 2119 2120 2121
				fsec1,
				fsec2;
	struct tm	tt,
			   *tm = &tt;
	struct tm	tt1,
			   *tm1 = &tt1;
	struct tm	tt2,
			   *tm2 = &tt2;

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

2124 2125
	if ((timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0)
		&& (timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0))
2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197
	{
		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);

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

		if (tm->tm_sec < 0)
		{
			tm->tm_sec += 60;
			tm->tm_min--;
		}

		if (tm->tm_min < 0)
		{
			tm->tm_min += 60;
			tm->tm_hour--;
		}

		if (tm->tm_hour < 0)
		{
			tm->tm_hour += 24;
			tm->tm_mday--;
		}

		if (tm->tm_mday < 0)
		{
			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--;
			}
		}

		if (tm->tm_mon < 0)
		{
			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)
2198
			elog(ERROR, "Unable to encode INTERVAL"
2199
				 "\n\ttimestamp_age() internal coding error");
2200 2201
	}
	else
2202
		elog(ERROR, "Unable to decode TIMESTAMP"
2203
			 "\n\ttimestamp_age() internal coding error");
2204

2205 2206
	PG_RETURN_INTERVAL_P(result);
}
2207 2208


2209 2210 2211 2212 2213
/* 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.
2214
 */
2215
Datum
2216
timestamptz_age(PG_FUNCTION_ARGS)
2217
{
2218 2219
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2220
	Interval   *result;
2221
	fsec_t		fsec,
2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306
				fsec1,
				fsec2;
	struct tm	tt,
			   *tm = &tt;
	struct tm	tt1,
			   *tm1 = &tt1;
	struct tm	tt2,
			   *tm2 = &tt2;

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

	if ((timestamp2tm(dt1, NULL, tm1, &fsec1, NULL) == 0)
		&& (timestamp2tm(dt2, NULL, tm2, &fsec2, NULL) == 0))
	{
		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);

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

		if (tm->tm_sec < 0)
		{
			tm->tm_sec += 60;
			tm->tm_min--;
		}

		if (tm->tm_min < 0)
		{
			tm->tm_min += 60;
			tm->tm_hour--;
		}

		if (tm->tm_hour < 0)
		{
			tm->tm_hour += 24;
			tm->tm_mday--;
		}

		if (tm->tm_mday < 0)
		{
			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--;
			}
		}

		if (tm->tm_mon < 0)
		{
			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)
2307
			elog(ERROR, "Unable to decode TIMESTAMP");
2308 2309
	}
	else
2310
		elog(ERROR, "Unable to decode TIMESTAMP");
2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331

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

2333
	str = DatumGetCString(DirectFunctionCall1(timestamp_out, timestamp));
2334 2335 2336 2337 2338

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2339
	VARATT_SIZEP(result) = len;
2340 2341 2342 2343
	memmove(VARDATA(result), str, (len - VARHDRSZ));

	pfree(str);

2344 2345
	PG_RETURN_TEXT_P(result);
}
2346 2347 2348 2349 2350 2351 2352


/* text_timestamp()
 * Convert text string to timestamp.
 * Text type is not null terminated, so use temporary string
 *	then call the standard input routine.
 */
2353 2354
Datum
text_timestamp(PG_FUNCTION_ARGS)
2355
{
2356
	text	   *str = PG_GETARG_TEXT_P(0);
2357 2358 2359 2360 2361
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2362
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2363
		elog(ERROR, "TIMESTAMP bad external representation (too long)");
2364 2365 2366 2367 2368 2369 2370

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

2371 2372 2373 2374
	return DirectFunctionCall3(timestamp_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2375
}
2376 2377


2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418
/* 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 */
	Datum		timestamp = PG_GETARG_DATUM(0);
	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)
2419
		elog(ERROR, "TIMESTAMP WITH TIME ZONE bad external representation (too long)");
2420 2421 2422 2423 2424 2425 2426

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

2427 2428 2429 2430
	return DirectFunctionCall3(timestamptz_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2431 2432 2433
}


2434 2435 2436
/* interval_text()
 * Convert interval to text data type.
 */
2437 2438
Datum
interval_text(PG_FUNCTION_ARGS)
2439
{
2440
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2441 2442 2443 2444
	text	   *result;
	char	   *str;
	int			len;

2445
	str = DatumGetCString(DirectFunctionCall1(interval_out,
2446
										   IntervalPGetDatum(interval)));
2447 2448 2449 2450 2451

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2452
	VARATT_SIZEP(result) = len;
2453 2454 2455 2456
	memmove(VARDATA(result), str, (len - VARHDRSZ));

	pfree(str);

2457 2458
	PG_RETURN_TEXT_P(result);
}
2459 2460 2461 2462 2463 2464 2465


/* 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.
 */
2466 2467
Datum
text_interval(PG_FUNCTION_ARGS)
2468
{
2469
	text	   *str = PG_GETARG_TEXT_P(0);
2470 2471 2472 2473 2474
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2475
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2476
		elog(ERROR, "INTERVAL bad external representation (too long)");
2477 2478 2479 2480 2481 2482
	sp = VARDATA(str);
	dp = dstr;
	for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
		*dp++ = *sp++;
	*dp = '\0';

2483 2484 2485 2486
	return DirectFunctionCall3(interval_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2487
}
2488 2489

/* timestamp_trunc()
2490
 * Truncate timestamp to specified units.
2491
 */
2492 2493
Datum
timestamp_trunc(PG_FUNCTION_ARGS)
2494
{
2495 2496 2497
	text	   *units = PG_GETARG_TEXT_P(0);
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
2498 2499 2500 2501 2502 2503
	int			type,
				val;
	int			i;
	char	   *up,
			   *lp,
				lowunits[MAXDATELEN + 1];
2504
	fsec_t		fsec;
2505 2506 2507
	struct tm	tt,
			   *tm = &tt;

2508
	if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
2509
		elog(ERROR, "TIMESTAMP units '%s' not recognized",
2510
			 DatumGetCString(DirectFunctionCall1(textout,
2511
											   PointerGetDatum(units))));
2512 2513 2514
	up = VARDATA(units);
	lp = lowunits;
	for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
2515
		*lp++ = tolower((unsigned char) *up++);
2516 2517 2518 2519
	*lp = '\0';

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

2520
	if (TIMESTAMP_NOT_FINITE(timestamp))
2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535
		PG_RETURN_TIMESTAMP(timestamp);

	if ((type == UNITS) && (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0))
	{
		switch (val)
		{
			case DTK_MILLENNIUM:
				tm->tm_year = (tm->tm_year / 1000) * 1000;
			case DTK_CENTURY:
				tm->tm_year = (tm->tm_year / 100) * 100;
			case DTK_DECADE:
				tm->tm_year = (tm->tm_year / 10) * 10;
			case DTK_YEAR:
				tm->tm_mon = 1;
			case DTK_QUARTER:
2536
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549
			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:
2550 2551 2552
#ifdef HAVE_INT64_TIMESTAMP
				fsec = ((fsec / 1000) * 1000);
#else
2553
				fsec = rint(fsec * 1000) / 1000;
2554
#endif
2555 2556 2557
				break;

			case DTK_MICROSEC:
2558
#ifndef HAVE_INT64_TIMESTAMP
2559
				fsec = rint(fsec * 1000000) / 1000000;
2560
#endif
2561 2562 2563
				break;

			default:
2564
				elog(ERROR, "TIMESTAMP units '%s' not supported", lowunits);
2565 2566 2567 2568
				result = 0;
		}

		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
2569
			elog(ERROR, "Unable to truncate TIMESTAMP to '%s'", lowunits);
2570
	}
2571 2572
	else
	{
2573
		elog(ERROR, "TIMESTAMP units '%s' not recognized", lowunits);
2574 2575
		result = 0;
	}
2576

2577 2578
	PG_RETURN_TIMESTAMP(result);
}
2579

2580 2581 2582 2583 2584 2585 2586
/* timestamptz_trunc()
 * Truncate timestamp to specified units.
 */
Datum
timestamptz_trunc(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
2587
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
2588
	TimestampTz result;
2589 2590 2591 2592 2593 2594 2595
	int			tz;
	int			type,
				val;
	int			i;
	char	   *up,
			   *lp,
				lowunits[MAXDATELEN + 1];
2596
	fsec_t		fsec;
2597 2598 2599
	char	   *tzn;
	struct tm	tt,
			   *tm = &tt;
2600

2601
	if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
2602
		elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not recognized",
2603
			 DatumGetCString(DirectFunctionCall1(textout,
2604
											   PointerGetDatum(units))));
2605 2606 2607 2608 2609
	up = VARDATA(units);
	lp = lowunits;
	for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
		*lp++ = tolower((unsigned char) *up++);
	*lp = '\0';
2610

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

2613 2614
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMPTZ(timestamp);
2615

2616 2617 2618
	if ((type == UNITS) && (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0))
	{
		switch (val)
2619
		{
2620 2621 2622 2623 2624 2625 2626 2627 2628
			case DTK_MILLENNIUM:
				tm->tm_year = (tm->tm_year / 1000) * 1000;
			case DTK_CENTURY:
				tm->tm_year = (tm->tm_year / 100) * 100;
			case DTK_DECADE:
				tm->tm_year = (tm->tm_year / 10) * 10;
			case DTK_YEAR:
				tm->tm_mon = 1;
			case DTK_QUARTER:
2629
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642
			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:
2643 2644 2645
#ifdef HAVE_INT64_TIMESTAMP
				fsec = ((fsec / 1000) * 1000);
#else
2646
				fsec = rint(fsec * 1000) / 1000;
2647
#endif
2648 2649
				break;
			case DTK_MICROSEC:
2650
#ifndef HAVE_INT64_TIMESTAMP
2651
				fsec = rint(fsec * 1000000) / 1000000;
2652
#endif
2653 2654 2655
				break;

			default:
2656
				elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not supported", lowunits);
2657
				result = 0;
2658
		}
2659 2660 2661 2662

		tz = DetermineLocalTimeZone(tm);

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
2663
			elog(ERROR, "Unable to truncate TIMESTAMP WITH TIME ZONE to '%s'", lowunits);
2664 2665 2666
	}
	else
	{
2667
		elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not recognized", lowunits);
2668
		PG_RETURN_NULL();
2669 2670
	}

2671
	PG_RETURN_TIMESTAMPTZ(result);
2672
}
2673 2674 2675 2676

/* interval_trunc()
 * Extract specified field from interval.
 */
2677 2678
Datum
interval_trunc(PG_FUNCTION_ARGS)
2679
{
2680 2681
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
2682 2683 2684 2685 2686 2687 2688
	Interval   *result;
	int			type,
				val;
	int			i;
	char	   *up,
			   *lp,
				lowunits[MAXDATELEN + 1];
2689
	fsec_t		fsec;
2690 2691 2692
	struct tm	tt,
			   *tm = &tt;

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

2695
	if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
2696
		elog(ERROR, "INTERVAL units '%s' not recognized",
2697
			 DatumGetCString(DirectFunctionCall1(textout,
2698
											   PointerGetDatum(units))));
2699 2700 2701
	up = VARDATA(units);
	lp = lowunits;
	for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
2702
		*lp++ = tolower((unsigned char) *up++);
2703 2704 2705 2706
	*lp = '\0';

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

2707
	if (type == UNITS)
2708 2709 2710 2711 2712
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
2713
				case DTK_MILLENNIUM:
2714 2715 2716 2717 2718 2719 2720 2721
					tm->tm_year = (tm->tm_year / 1000) * 1000;
				case DTK_CENTURY:
					tm->tm_year = (tm->tm_year / 100) * 100;
				case DTK_DECADE:
					tm->tm_year = (tm->tm_year / 10) * 10;
				case DTK_YEAR:
					tm->tm_mon = 0;
				case DTK_QUARTER:
2722
					tm->tm_mon = (3 * (tm->tm_mon / 3));
2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735
				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:
2736 2737 2738
#ifdef HAVE_INT64_TIMESTAMP
					fsec = ((fsec / 1000) * 1000);
#else
2739
					fsec = rint(fsec * 1000) / 1000;
2740
#endif
2741 2742
					break;
				case DTK_MICROSEC:
2743
#ifndef HAVE_INT64_TIMESTAMP
2744
					fsec = rint(fsec * 1000000) / 1000000;
2745
#endif
2746 2747 2748
					break;

				default:
2749
					elog(ERROR, "INTERVAL units '%s' not supported", lowunits);
2750 2751 2752
			}

			if (tm2interval(tm, fsec, result) != 0)
2753
				elog(ERROR, "Unable to truncate INTERVAL to '%s'", lowunits);
2754 2755 2756 2757

		}
		else
		{
B
Bruce Momjian 已提交
2758
			elog(WARNING, "Unable to decode INTERVAL; internal coding error");
2759
			*result = *interval;
2760 2761 2762 2763
		}
	}
	else
	{
2764
		elog(ERROR, "INTERVAL units '%s' not recognized",
2765
			 DatumGetCString(DirectFunctionCall1(textout,
2766
											   PointerGetDatum(units))));
2767
		*result = *interval;
2768 2769
	}

2770 2771
	PG_RETURN_INTERVAL_P(result);
}
2772

B
Bruce Momjian 已提交
2773
/* isoweek2date()
2774 2775 2776
 * Convert ISO week of year number to date.
 * The year field must be specified!
 * karel 2000/08/07
B
Bruce Momjian 已提交
2777 2778
 */
void
B
Bruce Momjian 已提交
2779
isoweek2date(int woy, int *year, int *mon, int *mday)
B
Bruce Momjian 已提交
2780
{
B
Bruce Momjian 已提交
2781 2782 2783 2784
	int			day0,
				day4,
				dayn;

B
Bruce Momjian 已提交
2785 2786 2787 2788 2789
	if (!*year)
		elog(ERROR, "isoweek2date(): can't convert without year information");

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

B
Bruce Momjian 已提交
2791
	/* day0 == offset to first day of week (Monday) */
2792
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
2793 2794

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

B
Bruce Momjian 已提交
2796 2797 2798 2799
	j2date(dayn, year, mon, mday);
}

/* date2isoweek()
B
Bruce Momjian 已提交
2800
 *
B
Bruce Momjian 已提交
2801 2802 2803
 *	Returns ISO week number of year.
 */
int
B
Bruce Momjian 已提交
2804
date2isoweek(int year, int mon, int mday)
B
Bruce Momjian 已提交
2805
{
B
Bruce Momjian 已提交
2806 2807 2808 2809 2810 2811
	float8		result;
	int			day0,
				day4,
				dayn;

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

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

B
Bruce Momjian 已提交
2817
	/* day0 == offset to first day of week (Monday) */
2818
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
2819 2820 2821 2822

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

B
Bruce Momjian 已提交
2828
		/* day0 == offset to first day of week (Monday) */
2829
		day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
2830
	}
B
Bruce Momjian 已提交
2831

B
Bruce Momjian 已提交
2832
	result = (((dayn - (day4 - day0)) / 7) + 1);
B
Bruce Momjian 已提交
2833 2834 2835 2836

	/*
	 * 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 已提交
2837 2838 2839
	 */
	if (result >= 53)
	{
2840
		day4 = date2j(year + 1, 1, 4);
B
Bruce Momjian 已提交
2841

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

B
Bruce Momjian 已提交
2845 2846 2847
		if (dayn >= (day4 - day0))
			result = (((dayn - (day4 - day0)) / 7) + 1);
	}
2848

B
Bruce Momjian 已提交
2849
	return (int) result;
B
Bruce Momjian 已提交
2850 2851 2852
}


2853 2854 2855
/* timestamp_part()
 * Extract specified field from timestamp.
 */
2856 2857
Datum
timestamp_part(PG_FUNCTION_ARGS)
2858
{
2859 2860 2861
	text	   *units = PG_GETARG_TEXT_P(0);
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
	float8		result;
2862 2863 2864 2865 2866 2867
	int			type,
				val;
	int			i;
	char	   *up,
			   *lp,
				lowunits[MAXDATELEN + 1];
2868
	fsec_t		fsec;
2869 2870 2871
	struct tm	tt,
			   *tm = &tt;

2872
	if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
2873
		elog(ERROR, "TIMESTAMP units '%s' not recognized",
2874
			 DatumGetCString(DirectFunctionCall1(textout,
2875
											   PointerGetDatum(units))));
2876 2877 2878
	up = VARDATA(units);
	lp = lowunits;
	for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
2879
		*lp++ = tolower((unsigned char) *up++);
2880 2881 2882
	*lp = '\0';

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

2886
	if (TIMESTAMP_NOT_FINITE(timestamp))
2887
	{
2888 2889 2890
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
2891

2892 2893
	if ((type == UNITS)
		&& (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) == 0))
2894 2895
	{
		switch (val)
2896
		{
2897
			case DTK_MICROSEC:
2898 2899 2900
#ifdef HAVE_INT64_TIMESTAMP
				result = ((tm->tm_sec * 1000000e0) + fsec);
#else
2901
				result = (tm->tm_sec + fsec) * 1000000;
2902
#endif
2903 2904 2905
				break;

			case DTK_MILLISEC:
2906 2907 2908
#ifdef HAVE_INT64_TIMESTAMP
				result = ((tm->tm_sec * 1000e0) + (fsec / 1000e0));
#else
2909
				result = (tm->tm_sec + fsec) * 1000;
2910
#endif
2911 2912 2913
				break;

			case DTK_SECOND:
2914 2915 2916
#ifdef HAVE_INT64_TIMESTAMP
				result = (tm->tm_sec + (fsec / 1000000e0));
#else
2917
				result = (tm->tm_sec + fsec);
2918
#endif
2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960
				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:
				result = ((tm->tm_mon - 1) / 3) + 1;
				break;

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

			case DTK_YEAR:
				result = tm->tm_year;
				break;

			case DTK_DECADE:
				result = (tm->tm_year / 10);
				break;

			case DTK_CENTURY:
				result = (tm->tm_year / 100);
				break;

			case DTK_MILLENNIUM:
				result = (tm->tm_year / 1000);
				break;

2961 2962
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
2963 2964 2965 2966 2967 2968 2969
#ifdef HAVE_INT64_TIMESTAMP
				result += (((((tm->tm_hour * 60) + tm->tm_min) * 60)
							+ tm->tm_sec + (fsec / 1000000e0)) / 86400e0);
#else
				result += (((((tm->tm_hour * 60) + tm->tm_min) * 60)
							+ tm->tm_sec + fsec) / 86400e0);
#endif
2970 2971
				break;

2972 2973 2974
			case DTK_TZ:
			case DTK_TZ_MINUTE:
			case DTK_TZ_HOUR:
2975
			default:
2976
				elog(ERROR, "TIMESTAMP units '%s' not supported", lowunits);
2977 2978 2979 2980 2981 2982 2983 2984
				result = 0;
		}
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997
			{
				int		tz;
				TimestampTz timestamptz;

				/* convert to timestamptz to produce consistent results */
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
					elog(ERROR, "Unable to convert TIMESTAMP to TIMESTAMP WITH TIME ZONE (tm)");

				tz = DetermineLocalTimeZone(tm);

				if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
					elog(ERROR, "Unable to convert TIMESTAMP to TIMESTAMP WITH TIME ZONE");

2998
#ifdef HAVE_INT64_TIMESTAMP
2999
				result = ((timestamptz - SetEpochTimestamp()) / 1000000e0);
3000
#else
3001
				result = timestamptz - SetEpochTimestamp();
3002
#endif
3003
				break;
3004
			}
3005
			case DTK_DOW:
3006
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
3007
					elog(ERROR, "Unable to encode TIMESTAMP");
3008

3009 3010
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3011

3012
			case DTK_DOY:
3013
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
3014
					elog(ERROR, "Unable to encode TIMESTAMP");
3015

3016 3017 3018
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3019

3020
			default:
3021
				elog(ERROR, "TIMESTAMP units '%s' not supported", lowunits);
3022 3023
				result = 0;
		}
3024

3025 3026 3027
	}
	else
	{
3028
		elog(ERROR, "TIMESTAMP units '%s' not recognized", lowunits);
3029 3030
		result = 0;
	}
3031

3032 3033
	PG_RETURN_FLOAT8(result);
}
3034

3035 3036 3037 3038 3039 3040 3041
/* timestamptz_part()
 * Extract specified field from timestamp with time zone.
 */
Datum
timestamptz_part(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
3042
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3043 3044 3045 3046 3047 3048 3049 3050 3051
	float8		result;
	int			tz;
	int			type,
				val;
	int			i;
	char	   *up,
			   *lp,
				lowunits[MAXDATELEN + 1];
	double		dummy;
3052
	fsec_t		fsec;
3053 3054 3055
	char	   *tzn;
	struct tm	tt,
			   *tm = &tt;
3056

3057
	if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
3058
		elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not recognized",
3059 3060 3061 3062 3063 3064 3065
			 DatumGetCString(DirectFunctionCall1(textout,
											   PointerGetDatum(units))));
	up = VARDATA(units);
	lp = lowunits;
	for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
		*lp++ = tolower((unsigned char) *up++);
	*lp = '\0';
3066

3067
	type = DecodeUnits(0, lowunits, &val);
3068
	if (type == UNKNOWN_FIELD)
3069
		type = DecodeSpecial(0, lowunits, &val);
3070

3071 3072 3073 3074 3075
	if (TIMESTAMP_NOT_FINITE(timestamp))
	{
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3076

3077 3078
	if ((type == UNITS)
		&& (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) == 0))
3079 3080
	{
		switch (val)
3081
		{
3082
			case DTK_TZ:
3083
				result = -tz;
3084 3085 3086
				break;

			case DTK_TZ_MINUTE:
3087 3088 3089
				result = -tz;
				result /= 60;
				FMODULO(result, dummy, 60e0);
3090 3091 3092
				break;

			case DTK_TZ_HOUR:
3093 3094
				dummy = -tz;
				FMODULO(dummy, result, 3600e0);
3095 3096 3097
				break;

			case DTK_MICROSEC:
3098 3099 3100
#ifdef HAVE_INT64_TIMESTAMP
				result = ((tm->tm_sec * 1000000e0) + fsec);
#else
3101
				result = (tm->tm_sec + fsec) * 1000000;
3102
#endif
3103 3104 3105
				break;

			case DTK_MILLISEC:
3106 3107 3108
#ifdef HAVE_INT64_TIMESTAMP
				result = ((tm->tm_sec * 1000e0) + (fsec / 1000e0));
#else
3109
				result = (tm->tm_sec + fsec) * 1000;
3110
#endif
3111 3112 3113
				break;

			case DTK_SECOND:
3114 3115 3116
#ifdef HAVE_INT64_TIMESTAMP
				result = (tm->tm_sec + (fsec / 1000000e0));
#else
3117
				result = (tm->tm_sec + fsec);
3118
#endif
3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160
				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:
				result = ((tm->tm_mon - 1) / 3) + 1;
				break;

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

			case DTK_YEAR:
				result = tm->tm_year;
				break;

			case DTK_DECADE:
				result = (tm->tm_year / 10);
				break;

			case DTK_CENTURY:
				result = (tm->tm_year / 100);
				break;

			case DTK_MILLENNIUM:
				result = (tm->tm_year / 1000);
				break;

3161 3162
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3163 3164 3165 3166 3167 3168 3169
#ifdef HAVE_INT64_TIMESTAMP
				result += (((((tm->tm_hour * 60) + tm->tm_min) * 60)
							+ tm->tm_sec + (fsec / 1000000e0)) / 86400e0);
#else
				result += (((((tm->tm_hour * 60) + tm->tm_min) * 60)
							+ tm->tm_sec + fsec) / 86400e0);
#endif
3170 3171
				break;

3172
			default:
3173
				elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not supported", lowunits);
3174 3175
				result = 0;
		}
3176

3177 3178 3179 3180 3181 3182
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
3183
#ifdef HAVE_INT64_TIMESTAMP
3184
				result = ((timestamp - SetEpochTimestamp()) / 1000000e0);
3185
#else
3186
				result = timestamp - SetEpochTimestamp();
3187
#endif
3188
				break;
3189

3190 3191
			case DTK_DOW:
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
3192
					elog(ERROR, "Unable to encode TIMESTAMP WITH TIME ZONE");
3193

3194 3195
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3196

3197 3198
			case DTK_DOY:
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
3199
					elog(ERROR, "Unable to encode TIMESTAMP WITH TIME ZONE");
3200

3201 3202 3203
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3204

3205
			default:
3206
				elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not supported", lowunits);
3207
				result = 0;
3208 3209
		}
	}
3210 3211
	else
	{
3212
		elog(ERROR, "TIMESTAMP WITH TIME ZONE units '%s' not recognized", lowunits);
3213 3214
		result = 0;
	}
3215

3216 3217
	PG_RETURN_FLOAT8(result);
}
3218 3219 3220 3221 3222


/* interval_part()
 * Extract specified field from interval.
 */
3223 3224
Datum
interval_part(PG_FUNCTION_ARGS)
3225
{
3226 3227 3228
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
	float8		result;
3229 3230 3231 3232 3233 3234
	int			type,
				val;
	int			i;
	char	   *up,
			   *lp,
				lowunits[MAXDATELEN + 1];
3235
	fsec_t		fsec;
3236 3237 3238
	struct tm	tt,
			   *tm = &tt;

3239
	if (VARSIZE(units) - VARHDRSZ > MAXDATELEN)
3240
		elog(ERROR, "INTERVAL units '%s' not recognized",
3241
			 DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
3242
											   PointerGetDatum(units))));
3243 3244 3245
	up = VARDATA(units);
	lp = lowunits;
	for (i = 0; i < (VARSIZE(units) - VARHDRSZ); i++)
3246
		*lp++ = tolower((unsigned char) *up++);
3247 3248 3249
	*lp = '\0';

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

3253
	if (type == UNITS)
3254 3255 3256 3257 3258
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
B
Bruce Momjian 已提交
3259
				case DTK_MICROSEC:
3260
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3261
					result = ((tm->tm_sec * 1000000e0) + fsec);
3262
#else
B
Bruce Momjian 已提交
3263
					result = (tm->tm_sec + fsec) * 1000000;
3264
#endif
B
Bruce Momjian 已提交
3265
					break;
3266

B
Bruce Momjian 已提交
3267
				case DTK_MILLISEC:
3268
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3269
					result = ((tm->tm_sec * 1000e0) + (fsec / 1000e0));
3270
#else
B
Bruce Momjian 已提交
3271
					result = (tm->tm_sec + fsec) * 1000;
3272
#endif
B
Bruce Momjian 已提交
3273
					break;
3274

B
Bruce Momjian 已提交
3275
				case DTK_SECOND:
3276
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
3277
					result = (tm->tm_sec + (fsec / 1000000e0));
3278
#else
B
Bruce Momjian 已提交
3279
					result = (tm->tm_sec + fsec);
3280
#endif
B
Bruce Momjian 已提交
3281
					break;
3282 3283

				case DTK_MINUTE:
3284
					result = tm->tm_min;
3285 3286 3287
					break;

				case DTK_HOUR:
3288
					result = tm->tm_hour;
3289 3290 3291
					break;

				case DTK_DAY:
3292
					result = tm->tm_mday;
3293 3294 3295
					break;

				case DTK_MONTH:
3296
					result = tm->tm_mon;
3297 3298 3299
					break;

				case DTK_QUARTER:
3300
					result = (tm->tm_mon / 3) + 1;
3301 3302 3303
					break;

				case DTK_YEAR:
3304
					result = tm->tm_year;
3305 3306 3307
					break;

				case DTK_DECADE:
3308
					result = (tm->tm_year / 10);
3309 3310 3311
					break;

				case DTK_CENTURY:
3312
					result = (tm->tm_year / 100);
3313 3314
					break;

3315
				case DTK_MILLENNIUM:
3316
					result = (tm->tm_year / 1000);
3317 3318 3319
					break;

				default:
3320
					elog(ERROR, "INTERVAL units '%s' not supported",
3321
						 DatumGetCString(DirectFunctionCall1(textout,
3322
											   PointerGetDatum(units))));
3323
					result = 0;
3324 3325 3326 3327 3328
			}

		}
		else
		{
B
Bruce Momjian 已提交
3329
			elog(WARNING, "Unable to decode INTERVAL"
3330
				 "\n\tinterval_part() internal coding error");
3331
			result = 0;
3332 3333 3334 3335
		}
	}
	else if ((type == RESERV) && (val == DTK_EPOCH))
	{
3336 3337 3338
#ifdef HAVE_INT64_TIMESTAMP
		result = (interval->time / 1000000e0);
#else
3339
		result = interval->time;
3340
#endif
3341 3342
		if (interval->month != 0)
		{
3343
			result += ((365.25 * 86400) * (interval->month / 12));
3344
			result += ((30.0 * 86400) * (interval->month % 12));
3345 3346 3347 3348
		}
	}
	else
	{
3349
		elog(ERROR, "INTERVAL units '%s' not recognized",
3350
			 DatumGetCString(DirectFunctionCall1(textout,
3351
											   PointerGetDatum(units))));
3352
		result = 0;
3353 3354
	}

3355 3356
	PG_RETURN_FLOAT8(result);
}
3357 3358 3359 3360


/* timestamp_zone()
 * Encode timestamp type with specified time zone.
3361
 * Returns timestamp with time zone, with the input
B
Bruce Momjian 已提交
3362
 *	rotated from local time to the specified zone.
3363
 */
3364 3365
Datum
timestamp_zone(PG_FUNCTION_ARGS)
3366
{
3367 3368
	text	   *zone = PG_GETARG_TEXT_P(0);
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
3369
	TimestampTz result;
3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395
	int			tz;
	int			type,
				val;
	int			i;
	char	   *up,
			   *lp,
				lowzone[MAXDATELEN + 1];

	if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN)
		elog(ERROR, "Time zone '%s' not recognized",
			 DatumGetCString(DirectFunctionCall1(textout,
												 PointerGetDatum(zone))));

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

	up = VARDATA(zone);
	lp = lowzone;
	for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++)
		*lp++ = tolower((unsigned char) *up++);
	*lp = '\0';

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

	if ((type == TZ) || (type == DTZ))
	{
3396 3397 3398
		tz = -(val * 60);

		result = dt2local(timestamp, tz);
3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416
	}
	else
	{
		elog(ERROR, "Time zone '%s' not recognized", lowzone);
		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);
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(1);
3417
	TimestampTz result;
3418 3419 3420 3421 3422 3423 3424 3425 3426 3427
	int			tz;

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

	if (zone->month != 0)
		elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)",
			 DatumGetCString(DirectFunctionCall1(interval_out,
												 PointerGetDatum(zone))));

3428 3429 3430 3431 3432 3433 3434
#ifdef HAVE_INT64_TIMESTAMP
	tz = (zone->time / INT64CONST(1000000));
#else
	tz = (zone->time);
#endif

	result = dt2local(timestamp, tz);
3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445

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

/* timestamp_timestamptz()
 * Convert local timestamp to timestamp at GMT
 */
Datum
timestamp_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
3446
	TimestampTz result;
3447 3448
	struct tm	tt,
			   *tm = &tt;
3449
	fsec_t		fsec;
3450 3451 3452 3453 3454 3455 3456
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL) != 0)
3457
			elog(ERROR, "Unable to convert TIMESTAMP to TIMESTAMP WITH TIME ZONE (tm)");
3458 3459 3460 3461

		tz = DetermineLocalTimeZone(tm);

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
3462
			elog(ERROR, "Unable to convert TIMESTAMP to TIMESTAMP WITH TIME ZONE");
3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473
	}

	PG_RETURN_TIMESTAMPTZ(result);
}

/* timestamptz_timestamp()
 * Convert timestamp at GMT to local timestamp
 */
Datum
timestamptz_timestamp(PG_FUNCTION_ARGS)
{
3474
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
3475 3476 3477
	Timestamp	result;
	struct tm	tt,
			   *tm = &tt;
3478
	fsec_t		fsec;
3479 3480 3481 3482 3483 3484 3485 3486
	char	   *tzn;
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn) != 0)
3487
			elog(ERROR, "Unable to convert TIMESTAMP WITH TIME ZONE to TIMESTAMP (tm)");
3488 3489

		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
3490
			elog(ERROR, "Unable to convert TIMESTAMP WITH TIME ZONE to TIMESTAMP");
3491 3492 3493 3494 3495 3496
	}

	PG_RETURN_TIMESTAMP(result);
}

/* timestamptz_zone()
3497 3498
 * Evaluate timestamp with time zone type at the specified time zone.
 * Returns a timestamp without time zone.
3499 3500 3501 3502 3503
 */
Datum
timestamptz_zone(PG_FUNCTION_ARGS)
{
	text	   *zone = PG_GETARG_TEXT_P(0);
3504
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3505 3506
	Timestamp	result;

3507 3508 3509 3510 3511 3512 3513 3514
	int			tz;
	int			type,
				val;
	int			i;
	char	   *up,
			   *lp,
				lowzone[MAXDATELEN + 1];

3515
	if (VARSIZE(zone) - VARHDRSZ > MAXDATELEN)
3516 3517 3518
		elog(ERROR, "Time zone '%s' not recognized",
			 DatumGetCString(DirectFunctionCall1(textout,
												 PointerGetDatum(zone))));
3519 3520 3521
	up = VARDATA(zone);
	lp = lowzone;
	for (i = 0; i < (VARSIZE(zone) - VARHDRSZ); i++)
3522
		*lp++ = tolower((unsigned char) *up++);
3523 3524 3525 3526
	*lp = '\0';

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

3527
	if (TIMESTAMP_NOT_FINITE(timestamp))
3528
		PG_RETURN_NULL();
3529 3530

	if ((type == TZ) || (type == DTZ))
3531 3532 3533
	{
		tz = val * 60;

3534
		result = dt2local(timestamp, tz);
3535 3536 3537 3538
	}
	else
	{
		elog(ERROR, "Time zone '%s' not recognized", lowzone);
3539
		PG_RETURN_NULL();
3540 3541
	}

3542
	PG_RETURN_TIMESTAMP(result);
3543
}	/* timestamptz_zone() */
3544

3545 3546
/* timestamptz_izone()
 * Encode timestamp with time zone type with specified time interval as time zone.
3547
 * Returns a timestamp without time zone.
3548 3549
 */
Datum
3550
timestamptz_izone(PG_FUNCTION_ARGS)
3551 3552
{
	Interval   *zone = PG_GETARG_INTERVAL_P(0);
3553
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3554
	Timestamp	result;
3555 3556 3557
	int			tz;

	if (TIMESTAMP_NOT_FINITE(timestamp))
3558
		PG_RETURN_NULL();
3559 3560

	if (zone->month != 0)
3561 3562 3563
		elog(ERROR, "INTERVAL time zone '%s' not legal (month specified)",
			 DatumGetCString(DirectFunctionCall1(interval_out,
												 PointerGetDatum(zone))));
3564

3565 3566 3567
#ifdef HAVE_INT64_TIMESTAMP
	tz = -(zone->time / INT64CONST(1000000));
#else
3568
	tz = -(zone->time);
3569
#endif
3570

3571
	result = dt2local(timestamp, tz);
3572

3573
	PG_RETURN_TIMESTAMP(result);
3574
}	/* timestamptz_izone() */