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

#include "postgres.h"

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

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

M
Marc G. Fournier 已提交
34

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

43

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


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

59 60 61 62 63 64 65 66

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

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

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

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

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

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

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

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

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

121
			TIMESTAMP_NOEND(result);
122
			break;
123

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

130 131
	AdjustTimestampForTypmod(&result, typmod);

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

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

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

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

162 163 164 165 166 167 168 169 170 171
/*
 *		timestamp_recv			- converts external binary format to timestamp
 *
 * We make no attempt to provide compatibility between int and float
 * timestamp representations ...
 */
Datum
timestamp_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
172 173 174 175
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
176
	Timestamp timestamp;
B
Bruce Momjian 已提交
177
	struct pg_tm tt,
178 179
			   *tm = &tt;
	fsec_t		fsec;
180 181

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

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

195 196
	AdjustTimestampForTypmod(&timestamp, typmod);

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

/*
 *		timestamp_send			- converts timestamp to binary format
 */
Datum
timestamp_send(PG_FUNCTION_ARGS)
{
206
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
207 208 209 210 211 212 213 214 215 216 217 218
	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));
}


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

	result = timestamp;

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

	PG_RETURN_TIMESTAMP(result);
}

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

B
Bruce Momjian 已提交
251
	static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
252 253 254 255 256 257
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
258 259
		INT64CONST(0)
	};
B
Bruce Momjian 已提交
260

261
#else
B
Bruce Momjian 已提交
262
	static const double TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
263 264 265 266 267 268 269 270 271 272 273 274
		1,
		10,
		100,
		1000,
		10000,
		100000,
		1000000
	};
#endif

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

282 283 284
		/*
		 * Note: this round-to-nearest code is not completely consistent
		 * about rounding values that are exactly halfway between integral
B
Bruce Momjian 已提交
285 286 287
		 * values.	On most platforms, rint() will implement
		 * round-to-nearest-even, but the integer code always rounds up
		 * (away from zero).  Is it worth trying to be consistent?
288
		 */
289 290 291
#ifdef HAVE_INT64_TIMESTAMP
		if (*time >= INT64CONST(0))
		{
292 293
			*time = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) *
					TimestampScales[typmod];
294 295 296
		}
		else
		{
B
Bruce Momjian 已提交
297 298
			*time = -((((-*time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
					  * TimestampScales[typmod]);
299
		}
300
#else
301
		*time = rint((double)*time * TimestampScales[typmod]) / TimestampScales[typmod];
302
#endif
303 304 305
	}
}

306 307 308 309 310 311 312 313

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

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

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

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

		case DTK_EPOCH:
			result = SetEpochTimestamp();
			break;

		case DTK_LATE:
			TIMESTAMP_NOEND(result);
			break;

		case DTK_EARLY:
			TIMESTAMP_NOBEGIN(result);
			break;

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

364 365 366 367
			TIMESTAMP_NOEND(result);
			break;

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

373 374
	AdjustTimestampForTypmod(&result, typmod);

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

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

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

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

406 407 408 409 410 411 412 413 414 415
/*
 *		timestamptz_recv			- converts external binary format to timestamptz
 *
 * We make no attempt to provide compatibility between int and float
 * timestamp representations ...
 */
Datum
timestamptz_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
416 417 418 419
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
B
Bruce Momjian 已提交
420
	TimestampTz timestamp;
421
	int			tz;
B
Bruce Momjian 已提交
422
	struct pg_tm tt,
423 424 425
			   *tm = &tt;
	fsec_t		fsec;
	char	   *tzn;
426 427

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

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

441 442
	AdjustTimestampForTypmod(&timestamp, typmod);

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

/*
 *		timestamptz_send			- converts timestamptz to binary format
 */
Datum
timestamptz_send(PG_FUNCTION_ARGS)
{
B
Bruce Momjian 已提交
452
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
453 454 455 456 457 458 459 460 461 462 463 464
	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));
}


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

	result = timestamp;

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

	PG_RETURN_TIMESTAMPTZ(result);
}

483 484 485 486 487 488 489

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

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

	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;

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

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

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

		case DTK_INVALID:
541 542
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
543
					 errmsg("date/time value \"%s\" is no longer supported", str)));
544
			break;
545

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

551 552
	AdjustIntervalForTypmod(result, typmod);

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

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

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

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

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

579 580 581 582 583 584 585
/*
 *		interval_recv			- converts external binary format to interval
 */
Datum
interval_recv(PG_FUNCTION_ARGS)
{
	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
586 587 588 589
#ifdef NOT_USED
	Oid			typelem = PG_GETARG_OID(1);
#endif
	int32		typmod = PG_GETARG_INT32(2);
590 591 592 593 594
	Interval   *interval;

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

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

602 603
	AdjustIntervalForTypmod(interval, typmod);

604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
	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
622
	pq_sendint(&buf, interval->day, sizeof(interval->day));
623 624 625 626 627
	pq_sendint(&buf, interval->month, sizeof(interval->month));
	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}


628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
/* 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);
}
646 647 648 649
/*
 *	Adjust interval for specified precision, in both YEAR to SECOND
 *	range and sub-second precision.
 */
650 651 652
static void
AdjustIntervalForTypmod(Interval *interval, int32 typmod)
{
653
#ifdef HAVE_INT64_TIMESTAMP
B
Bruce Momjian 已提交
654
	static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
655 656 657 658 659 660 661 662 663
		INT64CONST(1000000),
		INT64CONST(100000),
		INT64CONST(10000),
		INT64CONST(1000),
		INT64CONST(100),
		INT64CONST(10),
		INT64CONST(1)
	};

B
Bruce Momjian 已提交
664
	static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
665 666 667 668 669 670
		INT64CONST(500000),
		INT64CONST(50000),
		INT64CONST(5000),
		INT64CONST(500),
		INT64CONST(50),
		INT64CONST(5),
671 672
		INT64CONST(0)
	};
B
Bruce Momjian 已提交
673

674
#else
B
Bruce Momjian 已提交
675
	static const double IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
676
		1,
677
		10,
678 679 680 681 682
		100,
		1000,
		10000,
		100000,
		1000000
683 684 685
	};
#endif

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

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

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

743
			interval->month = 0;
744
			interval->day = 0;
745

746
#ifdef HAVE_INT64_TIMESTAMP
747 748 749 750
			hour = interval->time / USECS_PER_HOUR;
			interval->time -= hour * USECS_PER_HOUR;
			interval->time = (interval->time / USECS_PER_MINUTE) *
								USECS_PER_MINUTE;
751

752
#else
753 754
			TMODULO(interval->time, hour, (double)SECS_PER_HOUR);
			interval->time = ((int)(interval->time / SECS_PER_MINUTE)) * (double)SECS_PER_MINUTE;
755
#endif
756
		}
757
		else if (range == INTERVAL_MASK(SECOND))
758
		{
759 760 761 762 763
#ifdef HAVE_INT64_TIMESTAMP
			int64		minute;
#else
			double		minute;
#endif
764

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

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

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

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

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

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

840
#else
841
			double		hour;
842
#endif
843

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

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

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

866
			/*
B
Bruce Momjian 已提交
867 868 869 870 871 872
			 * Note: this round-to-nearest code is not completely
			 * consistent about rounding values that are exactly halfway
			 * between integral values.  On most platforms, rint() will
			 * implement round-to-nearest-even, but the integer code
			 * always rounds up (away from zero).  Is it worth trying to
			 * be consistent?
873
			 */
874 875 876
#ifdef HAVE_INT64_TIMESTAMP
			if (interval->time >= INT64CONST(0))
			{
877
				interval->time = ((interval->time +
878 879 880
									IntervalOffsets[precision]) /
									IntervalScales[precision]) *
									IntervalScales[precision];
881 882 883
			}
			else
			{
884 885 886 887
				interval->time = -(((-interval->time +
									IntervalOffsets[precision]) /
									IntervalScales[precision]) *
									IntervalScales[precision]);
888
			}
889
#else
890 891 892
			interval->time = rint(((double) interval->time) *
									IntervalScales[precision]) /
									IntervalScales[precision];
893
#endif
894 895 896 897 898 899
		}
	}

	return;
}

900 901 902 903

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

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

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

923 924 925
Datum
pgsql_postmaster_start_time(PG_FUNCTION_ARGS)
{
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
	PG_RETURN_TIMESTAMPTZ(PgStartTime);
}

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

	gettimeofday(&tp, NULL);

	result = tp.tv_sec -
		((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);

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

	return result;
953 954
}

955
void
956
dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
957
{
958 959 960
#ifdef HAVE_INT64_TIMESTAMP
	int64		time;
#else
961
	double		time;
962
#endif
963 964 965

	time = jd;

966
#ifdef HAVE_INT64_TIMESTAMP
967 968 969 970 971 972
	*hour = time / USECS_PER_HOUR;
	time -= (*hour) * USECS_PER_HOUR;
	*min = time / USECS_PER_MINUTE;
	time -= (*min) * USECS_PER_MINUTE;
	*sec = time / USECS_PER_SEC;
	*fsec = time - (*sec * USECS_PER_SEC);
973
#else
974 975 976 977
	*hour = time / SECS_PER_HOUR;
	time -= (*hour) * SECS_PER_HOUR;
	*min = time / SECS_PER_MINUTE;
	time -= (*min) * SECS_PER_MINUTE;
978 979 980
	*sec = time;
	*fsec = JROUND(time - *sec);
#endif
981 982 983 984 985

	return;
}	/* dt2time() */


986 987 988
/*
 * timestamp2tm() - Convert timestamp data type to POSIX time structure.
 *
989 990 991 992 993
 * 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
994 995 996
 *
 * If attimezone is NULL, the global timezone (including possblly brute forced
 * timezone) will be used.
997 998
 */
int
999
timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char **tzn, pg_tz *attimezone)
1000
{
1001
	Timestamp date;
1002
	Timestamp	time;
1003
	pg_time_t	utime;
1004

1005 1006 1007 1008
	/*
	 * 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.
1009
	 */
1010
	if ((attimezone==NULL) && HasCTZSet && (tzp != NULL))
1011
	{
1012
#ifdef HAVE_INT64_TIMESTAMP
1013
		dt -= CTimeZone * USECS_PER_SEC;
1014
#else
1015
		dt -= CTimeZone;
1016
#endif
1017
	}
1018

1019
	time = dt;
1020
#ifdef HAVE_INT64_TIMESTAMP
1021
	TMODULO(time, date, USECS_PER_DAY);
1022 1023 1024

	if (time < INT64CONST(0))
	{
1025
		time += USECS_PER_DAY;
1026
		date -= 1;
1027 1028
	}
#else
1029
	TMODULO(time, date, (double)SECS_PER_DAY);
1030 1031 1032

	if (time < 0)
	{
1033
		time += SECS_PER_DAY;
1034
		date -=1;
1035
	}
1036
#endif
1037 1038

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

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

1045
	j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1046
	dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1047

1048 1049
	/* Done if no TZ conversion wanted */
	if (tzp == NULL)
1050
	{
1051 1052 1053 1054 1055 1056 1057
		tm->tm_isdst = -1;
		tm->tm_gmtoff = 0;
		tm->tm_zone = NULL;
		if (tzn != NULL)
			*tzn = NULL;
		return 0;
	}
1058

1059 1060 1061 1062
	/*
	 * We have a brute force time zone per SQL99? Then use it without
	 * change since we have already rotated to the time zone.
	 */
1063
	if ((attimezone==NULL) && HasCTZSet)
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
	{
		*tzp = CTimeZone;
		tm->tm_isdst = 0;
		tm->tm_gmtoff = CTimeZone;
		tm->tm_zone = NULL;
		if (tzn != NULL)
			*tzn = NULL;
		return 0;
	}

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

		tm->tm_year = tx->tm_year + 1900;
		tm->tm_mon = tx->tm_mon + 1;
		tm->tm_mday = tx->tm_mday;
		tm->tm_hour = tx->tm_hour;
		tm->tm_min = tx->tm_min;
		tm->tm_sec = tx->tm_sec;
		tm->tm_isdst = tx->tm_isdst;
		tm->tm_gmtoff = tx->tm_gmtoff;
		tm->tm_zone = tx->tm_zone;
		*tzp = -(tm->tm_gmtoff);
		if (tzn != NULL)
			*tzn = (char *) tm->tm_zone;
1108 1109 1110
	}
	else
	{
1111 1112 1113 1114 1115
		/*
		 * When out of range of pg_time_t, treat as GMT
		 */
		*tzp = 0;
		/* Mark this as *no* time zone available */
1116
		tm->tm_isdst = -1;
1117 1118
		tm->tm_gmtoff = 0;
		tm->tm_zone = NULL;
1119 1120 1121 1122 1123
		if (tzn != NULL)
			*tzn = NULL;
	}

	return 0;
1124
}
1125 1126 1127 1128 1129 1130


/* 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.
1131
 *
1132
 * Returns -1 on failure (value out of range).
1133 1134
 */
int
1135
tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp *result)
1136
{
1137
#ifdef HAVE_INT64_TIMESTAMP
1138
	int date;
B
Bruce Momjian 已提交
1139
	int64		time;
1140
#else
1141
	double date,
1142
				time;
1143
#endif
1144 1145 1146 1147 1148

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

1149
	date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
1150

1151 1152
	time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
#ifdef HAVE_INT64_TIMESTAMP
1153
	*result = date * USECS_PER_DAY + time;
1154
	/* check for major overflow */
1155
	if ((*result - time) / USECS_PER_DAY != date)
1156 1157
		return -1;
	/* check for just-barely overflow (okay except time-of-day wraps) */
1158 1159
	if ((*result < 0 && date >= 0) ||
		(*result >= 0 && date < 0))
1160
		return -1;
1161
#else
1162
	*result = date * SECS_PER_DAY + time;
1163
#endif
1164 1165 1166 1167
	if (tzp != NULL)
		*result = dt2local(*result, -(*tzp));

	return 0;
1168
}
1169 1170 1171 1172 1173


/* interval2tm()
 * Convert a interval data type to a tm structure.
 */
B
Bruce Momjian 已提交
1174
int
1175
interval2tm(Interval span, struct pg_tm *tm, fsec_t *fsec)
1176
{
1177 1178 1179
#ifdef HAVE_INT64_TIMESTAMP
	int64		time;
#else
1180
	double		time;
1181
#endif
1182

1183 1184
	tm->tm_year = span.month / MONTHS_PER_YEAR;
	tm->tm_mon = span.month % MONTHS_PER_YEAR;
1185
	tm->tm_mday = span.day;
1186 1187
	time = span.time;

1188
#ifdef HAVE_INT64_TIMESTAMP
1189 1190 1191 1192 1193 1194
	tm->tm_hour = time / USECS_PER_HOUR;
	time -= tm->tm_hour * USECS_PER_HOUR;
	tm->tm_min = time / USECS_PER_MINUTE;
	time -= tm->tm_min * USECS_PER_MINUTE;
	tm->tm_sec = time / USECS_PER_SEC;
	*fsec = time - (tm->tm_sec * USECS_PER_SEC);
1195
#else
1196 1197
	TMODULO(time, tm->tm_hour, (double)SECS_PER_HOUR);
	TMODULO(time, tm->tm_min, (double)SECS_PER_MINUTE);
1198
	TMODULO(time, tm->tm_sec, 1.0);
1199
	*fsec = time;
1200
#endif
1201 1202

	return 0;
1203
}
1204

B
Bruce Momjian 已提交
1205
int
1206
tm2interval(struct pg_tm *tm, fsec_t fsec, Interval *span)
1207
{
1208
	span->month = tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon;
1209
	span->day   = tm->tm_mday;
1210
#ifdef HAVE_INT64_TIMESTAMP
1211 1212
	span->time = (((((tm->tm_hour * INT64CONST(60)) +
						tm->tm_min) * INT64CONST(60)) +
1213
						tm->tm_sec) * USECS_PER_SEC) + fsec;
1214
#else
1215 1216
	span->time = (((tm->tm_hour * (double)SECS_PER_MINUTE) +
						tm->tm_min) * (double)SECS_PER_MINUTE) +
1217
						tm->tm_sec;
1218
	span->time = JROUND(span->time + fsec);
1219
#endif
1220 1221

	return 0;
1222
}
1223

1224 1225 1226 1227
#ifdef HAVE_INT64_TIMESTAMP
static int64
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
{
1228
	return (((((hour * SECS_PER_MINUTE) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
1229
}	/* time2t() */
B
Bruce Momjian 已提交
1230

1231
#else
1232
static double
1233
time2t(const int hour, const int min, const int sec, const fsec_t fsec)
1234
{
1235
	return (((hour * SECS_PER_MINUTE) + min) * SECS_PER_MINUTE) + sec + fsec;
1236
}	/* time2t() */
1237
#endif
1238

1239
static Timestamp
1240 1241
dt2local(Timestamp dt, int tz)
{
1242
#ifdef HAVE_INT64_TIMESTAMP
1243
	dt -= (tz * USECS_PER_SEC);
1244
#else
1245 1246
	dt -= tz;
	dt = JROUND(dt);
1247
#endif
1248 1249 1250 1251 1252 1253 1254 1255 1256
	return dt;
}	/* dt2local() */


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


1257 1258
Datum
timestamp_finite(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1259
{
1260
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1261

B
Bruce Momjian 已提交
1262
	PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp));
1263
}
M
Marc G. Fournier 已提交
1264

1265 1266
Datum
interval_finite(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1267
{
1268
	PG_RETURN_BOOL(true);
1269
}
1270 1271 1272 1273 1274 1275


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

1276
void
1277
GetEpochTime(struct pg_tm *tm)
1278
{
B
Bruce Momjian 已提交
1279 1280
	struct pg_tm *t0;
	pg_time_t	epoch = 0;
1281

1282
	t0 = pg_gmtime(&epoch);
1283 1284 1285 1286 1287 1288 1289 1290

	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;

1291
	tm->tm_year += 1900;
1292
	tm->tm_mon++;
1293
}
1294 1295

Timestamp
1296
SetEpochTimestamp(void)
1297
{
1298
	Timestamp	dt;
B
Bruce Momjian 已提交
1299
	struct pg_tm tt,
1300
			   *tm = &tt;
1301

1302
	GetEpochTime(tm);
1303
	/* we don't bother to test for failure ... */
1304
	tm2timestamp(tm, 0, NULL, &dt);
M
Marc G. Fournier 已提交
1305

1306
	return dt;
1307
}	/* SetEpochTimestamp() */
1308

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

1324 1325 1326
	/*
	 * When using float representation, we have to be wary of NaNs.
	 *
1327 1328
	 * We consider all NANs to be equal and larger than any non-NAN. This is
	 * somewhat arbitrary; the important thing is to have a consistent
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351
	 * sort order.
	 */
	if (isnan(dt1))
	{
		if (isnan(dt2))
			return 0;			/* NAN = NAN */
		else
			return 1;			/* NAN > non-NAN */
	}
	else if (isnan(dt2))
	{
		return -1;				/* non-NAN < NAN */
	}
	else
	{
		if (dt1 > dt2)
			return 1;
		else if (dt1 < dt2)
			return -1;
		else
			return 0;
	}
#endif
1352 1353
}

1354 1355
Datum
timestamp_eq(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1356
{
1357 1358
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1359

1360
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
1361
}
M
Marc G. Fournier 已提交
1362

1363 1364
Datum
timestamp_ne(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1365
{
1366 1367
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1368

1369
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
1370
}
M
Marc G. Fournier 已提交
1371

1372 1373
Datum
timestamp_lt(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1374
{
1375 1376
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1377

1378
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
1379
}
M
Marc G. Fournier 已提交
1380

1381 1382
Datum
timestamp_gt(PG_FUNCTION_ARGS)
M
Marc G. Fournier 已提交
1383
{
1384 1385
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1386

1387
	PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
1388
}
1389

1390 1391
Datum
timestamp_le(PG_FUNCTION_ARGS)
1392
{
1393 1394
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1395

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

1399 1400
Datum
timestamp_ge(PG_FUNCTION_ARGS)
1401
{
1402 1403
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1404

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

1408 1409
Datum
timestamp_cmp(PG_FUNCTION_ARGS)
1410
{
1411 1412
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1413

1414
	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
1415
}
1416 1417


1418 1419 1420 1421 1422 1423 1424 1425
/*
 * Crosstype comparison functions for timestamp vs timestamptz
 */

Datum
timestamp_eq_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1426 1427
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1428 1429 1430 1431 1432 1433 1434 1435 1436 1437

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_ne_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1438 1439
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1440 1441 1442 1443 1444 1445 1446 1447 1448 1449

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_lt_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1450 1451
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1452 1453 1454 1455 1456 1457 1458 1459 1460 1461

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_gt_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1462 1463
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1464 1465 1466 1467 1468 1469 1470 1471 1472 1473

	dt1 = timestamp2timestamptz(timestampVal);

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

Datum
timestamp_le_timestamptz(PG_FUNCTION_ARGS)
{
	Timestamp	timestampVal = PG_GETARG_TIMESTAMP(0);
B
Bruce Momjian 已提交
1474 1475
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
	TimestampTz dt1;
1476 1477 1478 1479 1480 1481 1482 1483 1484 1485

	dt1 = timestamp2timestamptz(timestampVal);

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

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

	dt1 = timestamp2timestamptz(timestampVal);

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

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

	dt1 = timestamp2timestamptz(timestampVal);

	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}

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

	dt2 = timestamp2timestamptz(timestampVal);

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

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

	dt2 = timestamp2timestamptz(timestampVal);

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

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

	dt2 = timestamp2timestamptz(timestampVal);

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

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

	dt2 = timestamp2timestamptz(timestampVal);

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

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

	dt2 = timestamp2timestamptz(timestampVal);

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

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

	dt2 = timestamp2timestamptz(timestampVal);

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

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

	dt2 = timestamp2timestamptz(timestampVal);

	PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
}


1591 1592
/*
 *		interval_relop	- is interval1 relop interval2
1593 1594
 *
 *		collate invalid interval at the end
1595
 */
1596 1597 1598
static int
interval_cmp_internal(Interval *interval1, Interval *interval2)
{
1599 1600 1601 1602
#ifdef HAVE_INT64_TIMESTAMP
	int64		span1,
				span2;
#else
1603 1604
	double		span1,
				span2;
1605
#endif
1606

1607
	span1 = interval1->time;
1608 1609 1610
	span2 = interval2->time;

#ifdef HAVE_INT64_TIMESTAMP
1611 1612 1613 1614
	span1 += interval1->month * INT64CONST(30) * USECS_PER_DAY;
	span1 += interval1->day * INT64CONST(24) * USECS_PER_HOUR;
	span2 += interval2->month * INT64CONST(30) * USECS_PER_DAY;
	span2 += interval2->day * INT64CONST(24) * USECS_PER_HOUR;
1615
#else
1616 1617 1618 1619
	span1 += interval1->month * ((double)DAYS_PER_MONTH * SECS_PER_DAY);
	span1 += interval1->day * ((double)HOURS_PER_DAY * SECS_PER_HOUR);
	span2 += interval2->month * ((double)DAYS_PER_MONTH * SECS_PER_DAY);
	span2 += interval2->day * ((double)HOURS_PER_DAY * SECS_PER_HOUR);
1620
#endif
1621

1622
	return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
1623 1624
}

1625 1626
Datum
interval_eq(PG_FUNCTION_ARGS)
1627
{
1628 1629
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1630

1631
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0);
1632
}
1633

1634 1635
Datum
interval_ne(PG_FUNCTION_ARGS)
1636
{
1637 1638
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1639

1640
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0);
1641
}
1642

1643 1644
Datum
interval_lt(PG_FUNCTION_ARGS)
1645
{
1646 1647
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1648

1649
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0);
1650
}
1651

1652 1653
Datum
interval_gt(PG_FUNCTION_ARGS)
1654
{
1655 1656
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1657

1658
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0);
1659
}
1660

1661 1662
Datum
interval_le(PG_FUNCTION_ARGS)
1663
{
1664 1665
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1666

1667
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0);
1668
}
1669

1670 1671
Datum
interval_ge(PG_FUNCTION_ARGS)
1672
{
1673 1674
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1675

1676
	PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0);
1677
}
1678

1679 1680
Datum
interval_cmp(PG_FUNCTION_ARGS)
1681
{
1682 1683
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
1684

1685
	PG_RETURN_INT32(interval_cmp_internal(interval1, interval2));
1686
}
1687

1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700
/*
 * 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!
	 */
1701 1702
	return hash_any((unsigned char *) key, 
			sizeof(key->time) + sizeof(key->day) + sizeof(key->month));
1703 1704
}

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

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

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

1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762
			ts1 = te1;
			te1 = tt;
		}
	}

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

1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782
			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 已提交
1783 1784
		/*
		 * This case is ts1 < te2 OR te1 < te2, which may look redundant
1785 1786 1787 1788 1789 1790 1791 1792
		 * 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 已提交
1793 1794 1795

		/*
		 * If te1 is not null then we had ts1 <= te1 above, and we just
1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808
		 * 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 已提交
1809 1810 1811

		/*
		 * If te2 is not null then we had ts2 <= te2 above, and we just
1812 1813 1814 1815 1816 1817
		 * found ts2 >= te1, hence te2 >= te1.
		 */
		PG_RETURN_BOOL(false);
	}
	else
	{
B
Bruce Momjian 已提交
1818 1819 1820 1821
		/*
		 * 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".
1822 1823 1824 1825 1826
		 */
		if (te1IsNull || te2IsNull)
			PG_RETURN_NULL();
		PG_RETURN_BOOL(true);
	}
1827 1828 1829 1830

#undef TIMESTAMP_GT
#undef TIMESTAMP_LT
}
1831

1832 1833 1834 1835 1836

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

1837 1838
Datum
timestamp_smaller(PG_FUNCTION_ARGS)
1839
{
1840 1841 1842
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
1843

1844 1845 1846 1847 1848
	/* use timestamp_cmp_internal to be sure this agrees with comparisons */
	if (timestamp_cmp_internal(dt1, dt2) < 0)
		result = dt1;
	else
		result = dt2;
1849 1850
	PG_RETURN_TIMESTAMP(result);
}
1851

1852 1853
Datum
timestamp_larger(PG_FUNCTION_ARGS)
1854
{
1855 1856 1857
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
	Timestamp	result;
1858

1859 1860 1861 1862
	if (timestamp_cmp_internal(dt1, dt2) > 0)
		result = dt1;
	else
		result = dt2;
1863 1864
	PG_RETURN_TIMESTAMP(result);
}
1865 1866


1867 1868
Datum
timestamp_mi(PG_FUNCTION_ARGS)
1869
{
1870 1871
	Timestamp	dt1 = PG_GETARG_TIMESTAMP(0);
	Timestamp	dt2 = PG_GETARG_TIMESTAMP(1);
1872 1873
	Interval   *result;

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

1876 1877
	if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
	{
1878 1879
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1880
				 errmsg("cannot subtract infinite timestamps")));
1881

1882 1883
		result->time = 0;
	}
1884
	else
1885
#ifdef HAVE_INT64_TIMESTAMP
1886
		result->time = dt1 - dt2;
1887
#else
1888
		result->time = JROUND(dt1 - dt2);
1889
#endif
1890

1891
	result->month = 0;
1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923
	result->day = 0;

	result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
												IntervalPGetDatum(result)));
	PG_RETURN_INTERVAL_P(result);
}

/*	interval_justify_hours()
 *	Adjust interval so 'time' contains less than a whole day, and
 *	'day' contains an integral number of days.  This is useful for
 *	situations (such as non-TZ) where '1 day' = '24 hours' is valid,
 *	e.g. interval subtraction and division.  The SQL standard requires
 *	such conversion in these cases, but not the conversion of days to months.
 */
Datum
interval_justify_hours(PG_FUNCTION_ARGS)
{
	Interval  *span = PG_GETARG_INTERVAL_P(0);
	Interval  *result;

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

#ifdef HAVE_INT64_TIMESTAMP
	result->time += span->day * USECS_PER_DAY;
	result->day = result->time / USECS_PER_DAY;
	result->time -= result->day * USECS_PER_DAY;
#else
	result->time += span->day * (double)SECS_PER_DAY;
	TMODULO(result->time, result->day, (double)SECS_PER_DAY);
#endif
1924

1925 1926
	PG_RETURN_INTERVAL_P(result);
}
1927

1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942
/*	interval_justify_days()
 *	Adjust interval so 'time' contains less than 30 days, and
 *	adds as months.
 */
Datum
interval_justify_days(PG_FUNCTION_ARGS)
{
	Interval  *span = PG_GETARG_INTERVAL_P(0);
	Interval  *result;

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

#ifdef HAVE_INT64_TIMESTAMP
1943 1944 1945
	result->day += span->month * (double)DAYS_PER_MONTH;
	result->month = span->day / DAYS_PER_MONTH;
	result->day -= result->month * DAYS_PER_MONTH;
1946
#else
1947 1948
	result->day += span->month * (double)DAYS_PER_MONTH;
	TMODULO(result->day, result->month, (double)DAYS_PER_MONTH);
1949 1950 1951 1952
#endif

	PG_RETURN_INTERVAL_P(result);
}
1953

1954
/* timestamp_pl_interval()
1955
 * Add a interval to a timestamp data type.
1956
 * Note that interval has provisions for qualitative year/month and day
1957 1958 1959 1960
 *	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.
1961
 * To add a day, increment the mday, and use the same time of day.
1962 1963
 * Lastly, add in the "quantitative time".
 */
1964
Datum
1965
timestamp_pl_interval(PG_FUNCTION_ARGS)
1966
{
1967
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
1968 1969
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Timestamp	result;
1970 1971 1972 1973 1974 1975 1976

	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
	else
	{
		if (span->month != 0)
		{
B
Bruce Momjian 已提交
1977
			struct pg_tm tt,
1978
					   *tm = &tt;
1979
			fsec_t		fsec;
1980

1981
			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
1982 1983 1984 1985 1986
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));

			tm->tm_mon += span->month;
1987
			if (tm->tm_mon > MONTHS_PER_YEAR)
1988
			{
1989 1990
				tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
				tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
1991
			}
1992
			else if (tm->tm_mon < 1)
1993
			{
1994 1995
				tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
				tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
1996
			}
1997 1998 1999 2000 2001

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

2002
			if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2003 2004 2005
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2006 2007
		}

2008 2009 2010 2011 2012 2013 2014
		if (span->day != 0)
		{
			struct pg_tm tt,
					   *tm = &tt;
			fsec_t		fsec;
			int			julian;
			
2015
			if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2016 2017 2018 2019 2020 2021 2022 2023
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));

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

2024
			if (tm2timestamp(tm, fsec, NULL, &timestamp) != 0)
2025 2026 2027 2028 2029 2030
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
		}

		timestamp += span->time;
2031 2032 2033 2034 2035 2036 2037
		result = timestamp;
	}

	PG_RETURN_TIMESTAMP(result);
}

Datum
2038
timestamp_mi_interval(PG_FUNCTION_ARGS)
2039
{
2040
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
2041 2042 2043 2044
	Interval   *span = PG_GETARG_INTERVAL_P(1);
	Interval	tspan;

	tspan.month = -span->month;
2045
	tspan.day = -span->day;
2046 2047
	tspan.time = -span->time;

2048
	return DirectFunctionCall2(timestamp_pl_interval,
2049 2050 2051 2052 2053
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}


2054
/* timestamptz_pl_interval()
2055 2056 2057 2058 2059 2060 2061 2062 2063
 * 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
2064
timestamptz_pl_interval(PG_FUNCTION_ARGS)
2065
{
2066
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2067
	Interval   *span = PG_GETARG_INTERVAL_P(1);
2068
	TimestampTz result;
2069 2070 2071
	int			tz;
	char	   *tzn;

2072 2073
	if (TIMESTAMP_NOT_FINITE(timestamp))
		result = timestamp;
2074 2075 2076 2077
	else
	{
		if (span->month != 0)
		{
B
Bruce Momjian 已提交
2078
			struct pg_tm tt,
2079
					   *tm = &tt;
2080
			fsec_t		fsec;
2081

2082
			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2083 2084 2085
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
2086

2087
			tm->tm_mon += span->month;
2088
			if (tm->tm_mon > MONTHS_PER_YEAR)
2089
			{
2090 2091
				tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
				tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
2092
			}
2093
			else if (tm->tm_mon < 1)
2094
			{
2095 2096
				tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
				tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
2097
			}
2098 2099 2100 2101 2102 2103 2104

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

			tz = DetermineLocalTimeZone(tm);

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

2111 2112 2113 2114 2115 2116 2117
		if (span->day != 0)
		{
			struct pg_tm tt,
					   *tm = &tt;
			fsec_t		fsec;
			int			julian;

2118
			if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
2119 2120 2121 2122 2123 2124 2125 2126 2127 2128
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));

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

			tz = DetermineLocalTimeZone(tm);

2129
			if (tm2timestamp(tm, fsec, &tz, &timestamp) != 0)
2130 2131 2132 2133 2134 2135
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("timestamp out of range")));
		}

		timestamp += span->time;
2136
		result = timestamp;
2137 2138
	}

2139 2140
	PG_RETURN_TIMESTAMP(result);
}
2141

2142
Datum
2143
timestamptz_mi_interval(PG_FUNCTION_ARGS)
2144
{
2145
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
2146
	Interval   *span = PG_GETARG_INTERVAL_P(1);
2147 2148
	Interval	tspan;

B
Bruce Momjian 已提交
2149
	tspan.month = -span->month;
2150
	tspan.day = -span->day;
B
Bruce Momjian 已提交
2151
	tspan.time = -span->time;
2152

2153
	return DirectFunctionCall2(timestamptz_pl_interval,
2154 2155 2156
							   TimestampGetDatum(timestamp),
							   PointerGetDatum(&tspan));
}
2157 2158


2159 2160
Datum
interval_um(PG_FUNCTION_ARGS)
2161
{
2162
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2163 2164
	Interval   *result;

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

2167 2168 2169
	result->time = -interval->time;
	result->day = -interval->day;
	result->month = -interval->month;
2170

2171 2172
	PG_RETURN_INTERVAL_P(result);
}
2173 2174


2175 2176
Datum
interval_smaller(PG_FUNCTION_ARGS)
2177
{
2178 2179
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
2180
	Interval   *result;
B
Bruce Momjian 已提交
2181

2182 2183 2184
	/* use interval_cmp_internal to be sure this agrees with comparisons */
	if (interval_cmp_internal(interval1, interval2) < 0)
		result = interval1;
2185
	else
2186
		result = interval2;
2187 2188
	PG_RETURN_INTERVAL_P(result);
}
2189

2190 2191
Datum
interval_larger(PG_FUNCTION_ARGS)
2192
{
2193 2194
	Interval   *interval1 = PG_GETARG_INTERVAL_P(0);
	Interval   *interval2 = PG_GETARG_INTERVAL_P(1);
2195
	Interval   *result;
B
Bruce Momjian 已提交
2196

2197 2198
	if (interval_cmp_internal(interval1, interval2) > 0)
		result = interval1;
2199
	else
2200
		result = interval2;
2201 2202
	PG_RETURN_INTERVAL_P(result);
}
2203

2204 2205
Datum
interval_pl(PG_FUNCTION_ARGS)
2206
{
2207 2208
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
2209 2210
	Interval   *result;

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

2213 2214
	result->month = span1->month + span2->month;
	result->day = span1->day + span2->day;
2215
#ifdef HAVE_INT64_TIMESTAMP
2216
	result->time = span1->time + span2->time;
2217
#else
2218
	result->time = JROUND(span1->time + span2->time);
2219
#endif
2220

2221 2222
	PG_RETURN_INTERVAL_P(result);
}
2223

2224 2225
Datum
interval_mi(PG_FUNCTION_ARGS)
2226
{
2227 2228
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	Interval   *span2 = PG_GETARG_INTERVAL_P(1);
2229 2230
	Interval   *result;

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

2233 2234
	result->month = span1->month - span2->month;
	result->day = span1->day - span2->day;
2235
#ifdef HAVE_INT64_TIMESTAMP
2236
	result->time = span1->time - span2->time;
2237
#else
2238
	result->time = JROUND(span1->time - span2->time);
2239
#endif
2240

2241 2242
	PG_RETURN_INTERVAL_P(result);
}
2243

2244 2245
Datum
interval_mul(PG_FUNCTION_ARGS)
2246
{
2247 2248
	Interval   *span1 = PG_GETARG_INTERVAL_P(0);
	float8		factor = PG_GETARG_FLOAT8(1);
2249
	Interval   *result;
B
Bruce Momjian 已提交
2250

2251 2252
#ifdef HAVE_INT64_TIMESTAMP
	int64		months;
2253
	int64       days;
2254
#else
2255
	double		months;
2256
	double      days;
2257
#endif
2258

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

2261 2262
	months = span1->month * factor;
	days = span1->day * factor;
2263 2264
#ifdef HAVE_INT64_TIMESTAMP
	result->month = months;
2265
	result->day = days;
2266
	result->time = span1->time * factor;
2267 2268
	result->time += (months - result->month) * INT64CONST(30) * USECS_PER_DAY;
	result->time += (days - result->day) * INT64CONST(24) * USECS_PER_HOUR;
2269
#else
2270
	result->month = (int)months;
2271
	result->day = (int)days;
2272
	result->time = JROUND(span1->time * factor);
2273
	/* evaluate fractional months as 30 days */
2274
	result->time += JROUND((months - result->month) * DAYS_PER_MONTH * SECS_PER_DAY);
2275
	/* evaluate fractional days as 24 hours */
2276
	result->time += JROUND((days - result->day) * HOURS_PER_DAY * SECS_PER_HOUR);
2277
#endif
2278

2279 2280
	PG_RETURN_INTERVAL_P(result);
}
2281

2282 2283
Datum
mul_d_interval(PG_FUNCTION_ARGS)
2284
{
2285 2286 2287
	/* Args are float8 and Interval *, but leave them as generic Datum */
	Datum		factor = PG_GETARG_DATUM(0);
	Datum		span1 = PG_GETARG_DATUM(1);
2288

2289 2290 2291 2292 2293
	return DirectFunctionCall2(interval_mul, span1, factor);
}

Datum
interval_div(PG_FUNCTION_ARGS)
2294
{
2295
	Interval   *span = PG_GETARG_INTERVAL_P(0);
2296
	float8		factor = PG_GETARG_FLOAT8(1);
2297
	double		month_remainder, day_remainder;
2298
	Interval   *result;
B
Bruce Momjian 已提交
2299

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

2302
	if (factor == 0.0)
2303 2304 2305
		ereport(ERROR,
				(errcode(ERRCODE_DIVISION_BY_ZERO),
				 errmsg("division by zero")));
2306

2307 2308 2309 2310 2311 2312 2313 2314 2315 2316
	result->month = span->month / factor;
	result->day = span->day / factor;
	result->time = span->time / factor;

	/* Computer remainders */
	month_remainder = (span->month - result->month * factor) / factor;
	day_remainder = (span->day - result->day * factor) / factor;

	/* Cascade fractions to lower units */
	/* fractional months full days into days */
2317
	result->day += month_remainder * DAYS_PER_MONTH;
2318
	/* fractional months partial days into time */
2319
	day_remainder += (month_remainder * DAYS_PER_MONTH) - (int)(month_remainder * DAYS_PER_MONTH);
2320

2321
#ifdef HAVE_INT64_TIMESTAMP
2322
	result->time += day_remainder * USECS_PER_DAY;
2323
#else
2324 2325
	result->time += day_remainder * SECS_PER_DAY;
	result->time = JROUND(result->time);
2326
#endif
2327

2328 2329
	result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
												IntervalPGetDatum(result)));
2330 2331
	PG_RETURN_INTERVAL_P(result);
}
2332

2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354
/*
 * interval_accum and interval_avg implement the AVG(interval) aggregate.
 *
 * The transition datatype for this aggregate is a 2-element array of
 * intervals, where the first is the running sum and the second contains
 * the number of values so far in its 'time' field.  This is a bit ugly
 * but it beats inventing a specialized datatype for the purpose.
 */

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

	deconstruct_array(transarray,
2355
					  INTERVALOID, sizeof(Interval), false, 'd',
2356 2357
					  &transdatums, &ndatums);
	if (ndatums != 2)
2358
		elog(ERROR, "expected 2-element interval array");
B
Bruce Momjian 已提交
2359

2360 2361 2362
	/*
	 * 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 已提交
2363 2364
	 * objects on machines where double requires 8-byte alignment. That
	 * should be fixed, but in the meantime...
2365
	 *
B
Bruce Momjian 已提交
2366 2367
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2368
	 */
2369 2370
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2371 2372

	newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
2373 2374
												IntervalPGetDatum(&sumX),
											 IntervalPGetDatum(newval)));
2375 2376 2377 2378 2379 2380
	N.time += 1;

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

	result = construct_array(transdatums, 2,
2381
							 INTERVALOID, sizeof(Interval), false, 'd');
2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395

	PG_RETURN_ARRAYTYPE_P(result);
}

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

	deconstruct_array(transarray,
2396
					  INTERVALOID, sizeof(Interval), false, 'd',
2397 2398
					  &transdatums, &ndatums);
	if (ndatums != 2)
2399
		elog(ERROR, "expected 2-element interval array");
B
Bruce Momjian 已提交
2400

2401 2402 2403
	/*
	 * 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 已提交
2404 2405
	 * objects on machines where double requires 8-byte alignment. That
	 * should be fixed, but in the meantime...
2406
	 *
B
Bruce Momjian 已提交
2407 2408
	 * Note: must use DatumGetPointer here, not DatumGetIntervalP, else some
	 * compilers optimize into double-aligned load/store anyway.
2409
	 */
2410 2411
	memcpy((void *) &sumX, DatumGetPointer(transdatums[0]), sizeof(Interval));
	memcpy((void *) &N, DatumGetPointer(transdatums[1]), sizeof(Interval));
2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422

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


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

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

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

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

2470
		while (tm->tm_sec < 0)
2471
		{
2472
			tm->tm_sec += SECS_PER_MINUTE;
2473 2474 2475
			tm->tm_min--;
		}

2476
		while (tm->tm_min < 0)
2477
		{
2478
			tm->tm_min += SECS_PER_MINUTE;
2479 2480 2481
			tm->tm_hour--;
		}

2482
		while (tm->tm_hour < 0)
2483
		{
2484
			tm->tm_hour += HOURS_PER_DAY;
2485 2486 2487
			tm->tm_mday--;
		}

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

2502
		while (tm->tm_mon < 0)
2503
		{
2504
			tm->tm_mon += MONTHS_PER_YEAR;
2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520
			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)
2521 2522 2523
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("interval out of range")));
2524 2525
	}
	else
2526 2527 2528
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2529

2530 2531
	PG_RETURN_INTERVAL_P(result);
}
2532 2533


2534 2535 2536 2537 2538
/* 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.
2539
 */
2540
Datum
2541
timestamptz_age(PG_FUNCTION_ARGS)
2542
{
2543 2544
	TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
	TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2545
	Interval   *result;
2546
	fsec_t		fsec,
2547 2548
				fsec1,
				fsec2;
B
Bruce Momjian 已提交
2549
	struct pg_tm tt,
2550
			   *tm = &tt;
B
Bruce Momjian 已提交
2551
	struct pg_tm tt1,
2552
			   *tm1 = &tt1;
B
Bruce Momjian 已提交
2553
	struct pg_tm tt2,
2554
			   *tm2 = &tt2;
2555 2556 2557
	int			tz1;
	int			tz2;
	char	   *tzn;
2558 2559 2560

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

2561 2562
	if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn, NULL) == 0 &&
		timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn, NULL) == 0)
2563
	{
2564 2565 2566 2567 2568 2569 2570
		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;
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583

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

2584
		while (tm->tm_sec < 0)
2585
		{
2586
			tm->tm_sec += SECS_PER_MINUTE;
2587 2588 2589
			tm->tm_min--;
		}

2590
		while (tm->tm_min < 0)
2591
		{
2592
			tm->tm_min += SECS_PER_MINUTE;
2593 2594 2595
			tm->tm_hour--;
		}

2596
		while (tm->tm_hour < 0)
2597
		{
2598
			tm->tm_hour += HOURS_PER_DAY;
2599 2600 2601
			tm->tm_mday--;
		}

2602
		while (tm->tm_mday < 0)
2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615
		{
			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--;
			}
		}

2616
		while (tm->tm_mon < 0)
2617
		{
2618
			tm->tm_mon += MONTHS_PER_YEAR;
2619 2620 2621
			tm->tm_year--;
		}

2622 2623 2624 2625
		/*
		 * Note: we deliberately ignore any difference between tz1 and tz2.
		 */

2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638
		/* 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)
2639 2640 2641
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("interval out of range")));
2642 2643
	}
	else
2644 2645 2646
		ereport(ERROR,
				(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
				 errmsg("timestamp out of range")));
2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663

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

2669
	str = DatumGetCString(DirectFunctionCall1(timestamp_out, timestamp));
2670 2671 2672 2673 2674

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2675
	VARATT_SIZEP(result) = len;
2676 2677 2678 2679
	memmove(VARDATA(result), str, (len - VARHDRSZ));

	pfree(str);

2680 2681
	PG_RETURN_TEXT_P(result);
}
2682 2683 2684 2685 2686 2687 2688


/* text_timestamp()
 * Convert text string to timestamp.
 * Text type is not null terminated, so use temporary string
 *	then call the standard input routine.
 */
2689 2690
Datum
text_timestamp(PG_FUNCTION_ARGS)
2691
{
2692
	text	   *str = PG_GETARG_TEXT_P(0);
2693 2694 2695 2696 2697
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2698
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2699 2700
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2701
				 errmsg("invalid input syntax for type timestamp: \"%s\"",
2702
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2703
											   PointerGetDatum(str))))));
2704 2705 2706 2707 2708 2709 2710

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

2711 2712 2713 2714
	return DirectFunctionCall3(timestamp_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2715
}
2716 2717


2718 2719 2720 2721 2722 2723 2724
/* 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 */
2725
	Datum timestamp = PG_GETARG_DATUM(0);
2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758
	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)
2759 2760
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2761
				 errmsg("invalid input syntax for type timestamp with time zone: \"%s\"",
2762
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2763
											   PointerGetDatum(str))))));
2764 2765 2766 2767 2768 2769 2770

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

2771 2772 2773 2774
	return DirectFunctionCall3(timestamptz_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2775 2776 2777
}


2778 2779 2780
/* interval_text()
 * Convert interval to text data type.
 */
2781 2782
Datum
interval_text(PG_FUNCTION_ARGS)
2783
{
2784
	Interval   *interval = PG_GETARG_INTERVAL_P(0);
2785 2786 2787 2788
	text	   *result;
	char	   *str;
	int			len;

2789
	str = DatumGetCString(DirectFunctionCall1(interval_out,
2790
										   IntervalPGetDatum(interval)));
2791 2792 2793 2794 2795

	len = (strlen(str) + VARHDRSZ);

	result = palloc(len);

J
TOAST  
Jan Wieck 已提交
2796
	VARATT_SIZEP(result) = len;
2797
	memmove(VARDATA(result), str, len - VARHDRSZ);
2798 2799 2800

	pfree(str);

2801 2802
	PG_RETURN_TEXT_P(result);
}
2803 2804 2805 2806 2807 2808 2809


/* 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.
 */
2810 2811
Datum
text_interval(PG_FUNCTION_ARGS)
2812
{
2813
	text	   *str = PG_GETARG_TEXT_P(0);
2814 2815 2816 2817 2818
	int			i;
	char	   *sp,
			   *dp,
				dstr[MAXDATELEN + 1];

2819
	if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
2820 2821
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
2822
				 errmsg("invalid input syntax for type interval: \"%s\"",
2823
						DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
2824
											   PointerGetDatum(str))))));
2825

2826 2827 2828 2829 2830 2831
	sp = VARDATA(str);
	dp = dstr;
	for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
		*dp++ = *sp++;
	*dp = '\0';

2832 2833 2834 2835
	return DirectFunctionCall3(interval_in,
							   CStringGetDatum(dstr),
							   ObjectIdGetDatum(InvalidOid),
							   Int32GetDatum(-1));
2836
}
2837 2838

/* timestamp_trunc()
2839
 * Truncate timestamp to specified units.
2840
 */
2841 2842
Datum
timestamp_trunc(PG_FUNCTION_ARGS)
2843
{
2844
	text	   *units = PG_GETARG_TEXT_P(0);
2845
	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
2846
	Timestamp	result;
2847 2848
	int			type,
				val;
2849
	char	   *lowunits;
2850
	fsec_t		fsec;
B
Bruce Momjian 已提交
2851
	struct pg_tm tt,
2852 2853
			   *tm = &tt;

2854 2855
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMP(timestamp);
2856

2857 2858 2859
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
2860 2861 2862

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

2863
	if (type == UNITS)
2864
	{
2865
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
2866 2867 2868 2869
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

2870 2871
		switch (val)
		{
2872
			case DTK_WEEK:
2873 2874 2875 2876 2877 2878 2879
			{
				int woy;
				
				woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
				/*
				 *	If it is week 52/53 and the month is January,
				 *	then the week must belong to the previous year.
2880
				 *	Also, some December dates belong to the next year.
2881 2882 2883
				 */
				if (woy >= 52 && tm->tm_mon == 1)
					--tm->tm_year;
2884
				if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
2885
					++tm->tm_year;
2886
				isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
2887 2888 2889 2890 2891
				tm->tm_hour = 0;
				tm->tm_min = 0;
				tm->tm_sec = 0;
				fsec = 0;
				break;
2892
			}
2893
			case DTK_MILLENNIUM:
2894 2895
				/* see comments in timestamptz_trunc */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
2896
					tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
2897
				else
B
Bruce Momjian 已提交
2898
					tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
2899
			case DTK_CENTURY:
2900 2901
				/* see comments in timestamptz_trunc */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
2902
					tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
2903
				else
B
Bruce Momjian 已提交
2904
					tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
2905
			case DTK_DECADE:
2906 2907 2908 2909 2910 2911
				/* see comments in timestamptz_trunc */
				if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
				{
					if (tm->tm_year > 0)
						tm->tm_year = (tm->tm_year / 10) * 10;
					else
B
Bruce Momjian 已提交
2912
						tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
2913
				}
2914 2915 2916
			case DTK_YEAR:
				tm->tm_mon = 1;
			case DTK_QUARTER:
2917
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930
			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:
2931
#ifdef HAVE_INT64_TIMESTAMP
2932
				fsec = (fsec / 1000) * 1000;
2933
#else
2934
				fsec = rint(fsec * 1000) / 1000;
2935
#endif
2936 2937 2938
				break;

			case DTK_MICROSEC:
2939
#ifndef HAVE_INT64_TIMESTAMP
2940
				fsec = rint(fsec * 1000000) / 1000000;
2941
#endif
2942 2943 2944
				break;

			default:
2945 2946 2947 2948
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
								lowunits)));
2949 2950 2951 2952
				result = 0;
		}

		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
2953 2954 2955
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
2956
	}
2957 2958
	else
	{
2959 2960 2961 2962
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("timestamp units \"%s\" not recognized",
						lowunits)));
2963 2964
		result = 0;
	}
2965

2966 2967
	PG_RETURN_TIMESTAMP(result);
}
2968

2969 2970 2971 2972 2973 2974 2975
/* timestamptz_trunc()
 * Truncate timestamp to specified units.
 */
Datum
timestamptz_trunc(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
2976
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
2977
	TimestampTz result;
2978 2979 2980
	int			tz;
	int			type,
				val;
2981
	bool		redotz = false;
2982
	char	   *lowunits;
2983
	fsec_t		fsec;
2984
	char	   *tzn;
B
Bruce Momjian 已提交
2985
	struct pg_tm tt,
2986
			   *tm = &tt;
2987

2988 2989
	if (TIMESTAMP_NOT_FINITE(timestamp))
		PG_RETURN_TIMESTAMPTZ(timestamp);
2990

2991 2992 2993 2994 2995 2996
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

2997
	if (type == UNITS)
2998
	{
2999
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3000 3001 3002 3003
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3004
		switch (val)
3005
		{
3006
			case DTK_WEEK:
3007 3008 3009 3010 3011 3012 3013
			{
				int woy;
				
				woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
				/*
				 *	If it is week 52/53 and the month is January,
				 *	then the week must belong to the previous year.
3014
				 *	Also, some December dates belong to the next year.
3015 3016 3017
				 */
				if (woy >= 52 && tm->tm_mon == 1)
					--tm->tm_year;
3018
				if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
3019
					++tm->tm_year;
3020
				isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
3021 3022 3023 3024
				tm->tm_hour = 0;
				tm->tm_min = 0;
				tm->tm_sec = 0;
				fsec = 0;
3025
				redotz = true;
3026
				break;
3027
			}
3028
				/* one may consider DTK_THOUSAND and DTK_HUNDRED... */
3029
			case DTK_MILLENNIUM:
B
Bruce Momjian 已提交
3030 3031 3032 3033

				/*
				 * truncating to the millennium? what is this supposed to
				 * mean? let us put the first year of the millennium...
3034 3035 3036
				 * i.e. -1000, 1, 1001, 2001...
				 */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3037
					tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
3038
				else
B
Bruce Momjian 已提交
3039
					tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
3040
				/* FALL THRU */
3041
			case DTK_CENTURY:
3042 3043
				/* truncating to the century? as above: -100, 1, 101... */
				if (tm->tm_year > 0)
B
Bruce Momjian 已提交
3044
					tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
3045
				else
B
Bruce Momjian 已提交
3046
					tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
3047
				/* FALL THRU */
3048
			case DTK_DECADE:
B
Bruce Momjian 已提交
3049 3050 3051

				/*
				 * truncating to the decade? first year of the decade.
3052 3053 3054 3055 3056 3057 3058
				 * must not be applied if year was truncated before!
				 */
				if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
				{
					if (tm->tm_year > 0)
						tm->tm_year = (tm->tm_year / 10) * 10;
					else
B
Bruce Momjian 已提交
3059
						tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
3060
				}
3061
				/* FALL THRU */
3062 3063
			case DTK_YEAR:
				tm->tm_mon = 1;
3064
				/* FALL THRU */
3065
			case DTK_QUARTER:
3066
				tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
3067
				/* FALL THRU */
3068 3069
			case DTK_MONTH:
				tm->tm_mday = 1;
3070
				/* FALL THRU */
3071 3072
			case DTK_DAY:
				tm->tm_hour = 0;
3073 3074
				redotz = true;	/* for all cases >= DAY */
				/* FALL THRU */
3075 3076
			case DTK_HOUR:
				tm->tm_min = 0;
3077
				/* FALL THRU */
3078 3079
			case DTK_MINUTE:
				tm->tm_sec = 0;
3080
				/* FALL THRU */
3081 3082 3083 3084 3085
			case DTK_SECOND:
				fsec = 0;
				break;

			case DTK_MILLISEC:
3086 3087 3088
#ifdef HAVE_INT64_TIMESTAMP
				fsec = ((fsec / 1000) * 1000);
#else
3089
				fsec = rint(fsec * 1000) / 1000;
3090
#endif
3091 3092
				break;
			case DTK_MICROSEC:
3093
#ifndef HAVE_INT64_TIMESTAMP
3094
				fsec = rint(fsec * 1000000) / 1000000;
3095
#endif
3096 3097 3098
				break;

			default:
3099 3100
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
B
Bruce Momjian 已提交
3101 3102
					  errmsg("timestamp with time zone units \"%s\" not "
							 "supported", lowunits)));
3103
				result = 0;
3104
		}
3105

3106 3107
		if (redotz)
			tz = DetermineLocalTimeZone(tm);
3108 3109

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
3110 3111 3112
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
3113 3114 3115
	}
	else
	{
3116 3117
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3118 3119
		   errmsg("timestamp with time zone units \"%s\" not recognized",
				  lowunits)));
3120
		result = 0;
3121 3122
	}

3123
	PG_RETURN_TIMESTAMPTZ(result);
3124
}
3125 3126 3127 3128

/* interval_trunc()
 * Extract specified field from interval.
 */
3129 3130
Datum
interval_trunc(PG_FUNCTION_ARGS)
3131
{
3132 3133
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
3134 3135 3136
	Interval   *result;
	int			type,
				val;
3137
	char	   *lowunits;
3138
	fsec_t		fsec;
B
Bruce Momjian 已提交
3139
	struct pg_tm tt,
3140 3141
			   *tm = &tt;

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

3144 3145 3146
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
3147 3148 3149

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

3150
	if (type == UNITS)
3151 3152 3153 3154 3155
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
3156
				/* fall through */
3157
				case DTK_MILLENNIUM:
3158
					/* caution: C division may have negative remainder */
3159 3160
					tm->tm_year = (tm->tm_year / 1000) * 1000;
				case DTK_CENTURY:
3161
					/* caution: C division may have negative remainder */
3162 3163
					tm->tm_year = (tm->tm_year / 100) * 100;
				case DTK_DECADE:
3164
					/* caution: C division may have negative remainder */
3165 3166 3167 3168
					tm->tm_year = (tm->tm_year / 10) * 10;
				case DTK_YEAR:
					tm->tm_mon = 0;
				case DTK_QUARTER:
3169
					tm->tm_mon = 3 * (tm->tm_mon / 3);
3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182
				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:
3183 3184 3185
#ifdef HAVE_INT64_TIMESTAMP
					fsec = ((fsec / 1000) * 1000);
#else
3186
					fsec = rint(fsec * 1000) / 1000;
3187
#endif
3188 3189
					break;
				case DTK_MICROSEC:
3190
#ifndef HAVE_INT64_TIMESTAMP
3191
					fsec = rint(fsec * 1000000) / 1000000;
3192
#endif
3193 3194 3195
					break;

				default:
3196 3197 3198
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("interval units \"%s\" not supported",
B
Bruce Momjian 已提交
3199
									lowunits)));
3200 3201 3202
			}

			if (tm2interval(tm, fsec, result) != 0)
3203 3204 3205
				ereport(ERROR,
						(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
						 errmsg("interval out of range")));
3206 3207
		}
		else
3208
			elog(ERROR, "could not convert interval to tm");
3209 3210 3211
	}
	else
	{
3212 3213 3214
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval units \"%s\" not recognized",
B
Bruce Momjian 已提交
3215 3216
						DatumGetCString(DirectFunctionCall1(textout,
											 PointerGetDatum(units))))));
3217
		*result = *interval;
3218 3219
	}

3220 3221
	PG_RETURN_INTERVAL_P(result);
}
3222

B
Bruce Momjian 已提交
3223
/* isoweek2date()
3224
 * Convert ISO week of year number to date.
3225
 * The year field must be specified with the ISO year!
3226
 * karel 2000/08/07
B
Bruce Momjian 已提交
3227 3228
 */
void
B
Bruce Momjian 已提交
3229
isoweek2date(int woy, int *year, int *mon, int *mday)
B
Bruce Momjian 已提交
3230
{
B
Bruce Momjian 已提交
3231 3232 3233 3234
	int			day0,
				day4,
				dayn;

B
Bruce Momjian 已提交
3235
	if (!*year)
3236 3237
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3238
		errmsg("cannot calculate week number without year information")));
B
Bruce Momjian 已提交
3239 3240 3241

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

B
Bruce Momjian 已提交
3243
	/* day0 == offset to first day of week (Monday) */
3244
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3245 3246

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

B
Bruce Momjian 已提交
3248 3249 3250 3251
	j2date(dayn, year, mon, mday);
}

/* date2isoweek()
B
Bruce Momjian 已提交
3252
 *
B
Bruce Momjian 已提交
3253 3254 3255
 *	Returns ISO week number of year.
 */
int
B
Bruce Momjian 已提交
3256
date2isoweek(int year, int mon, int mday)
B
Bruce Momjian 已提交
3257
{
B
Bruce Momjian 已提交
3258 3259 3260 3261 3262 3263
	float8		result;
	int			day0,
				day4,
				dayn;

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

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

B
Bruce Momjian 已提交
3269
	/* day0 == offset to first day of week (Monday) */
3270
	day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3271 3272 3273 3274

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

B
Bruce Momjian 已提交
3280
		/* day0 == offset to first day of week (Monday) */
3281
		day0 = j2day(day4 - 1);
B
Bruce Momjian 已提交
3282
	}
B
Bruce Momjian 已提交
3283

3284
	result = (dayn - (day4 - day0)) / 7 + 1;
B
Bruce Momjian 已提交
3285 3286 3287 3288

	/*
	 * 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 已提交
3289
	 */
3290
	if (result >= 52)
B
Bruce Momjian 已提交
3291
	{
3292
		day4 = date2j(year + 1, 1, 4);
B
Bruce Momjian 已提交
3293

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

3297 3298
		if (dayn >= day4 - day0)
			result = (dayn - (day4 - day0)) / 7 + 1;
B
Bruce Momjian 已提交
3299
	}
3300

B
Bruce Momjian 已提交
3301
	return (int) result;
B
Bruce Momjian 已提交
3302 3303 3304
}


3305 3306 3307 3308 3309 3310 3311
/* date2isoyear()
 *
 *	Returns ISO 8601 year number.
 */
int
date2isoyear(int year, int mon, int mday)
{
B
Bruce Momjian 已提交
3312 3313 3314 3315
	float8		result;
	int			day0,
				day4,
				dayn;
3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329

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

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

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

	/*
	 * We need the first week containing a Thursday, otherwise this day
	 * falls into the previous year for purposes of counting weeks
	 */
3330
	if (dayn < day4 - day0)
3331 3332 3333 3334 3335 3336 3337 3338 3339
	{
		day4 = date2j(year - 1, 1, 4);

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

		year--;
	}

3340
	result = (dayn - (day4 - day0)) / 7 + 1;
3341 3342 3343 3344 3345

	/*
	 * Sometimes the last few days in a year will fall into the first week
	 * of the next year, so check for this.
	 */
3346
	if (result >= 52)
3347 3348 3349 3350 3351 3352
	{
		day4 = date2j(year + 1, 1, 4);

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

3353
		if (dayn >= day4 - day0)
3354 3355 3356 3357 3358 3359 3360
			year++;
	}

	return year;
}


3361 3362 3363
/* timestamp_part()
 * Extract specified field from timestamp.
 */
3364 3365
Datum
timestamp_part(PG_FUNCTION_ARGS)
3366
{
3367
	text	   *units = PG_GETARG_TEXT_P(0);
3368
	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3369
	float8		result;
3370 3371
	int			type,
				val;
3372
	char	   *lowunits;
3373
	fsec_t		fsec;
B
Bruce Momjian 已提交
3374
	struct pg_tm tt,
3375 3376
			   *tm = &tt;

3377
	if (TIMESTAMP_NOT_FINITE(timestamp))
3378
	{
3379 3380 3381
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3382

3383 3384 3385 3386 3387 3388 3389 3390
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3391
	if (type == UNITS)
3392
	{
3393
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3394 3395 3396 3397
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3398
		switch (val)
3399
		{
3400
			case DTK_MICROSEC:
3401
#ifdef HAVE_INT64_TIMESTAMP
3402
				result = tm->tm_sec * 1000000.0 + fsec;
3403
#else
3404
				result = (tm->tm_sec + fsec) * 1000000;
3405
#endif
3406 3407 3408
				break;

			case DTK_MILLISEC:
3409
#ifdef HAVE_INT64_TIMESTAMP
3410
				result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3411
#else
3412
				result = (tm->tm_sec + fsec) * 1000;
3413
#endif
3414 3415 3416
				break;

			case DTK_SECOND:
3417
#ifdef HAVE_INT64_TIMESTAMP
3418
				result = tm->tm_sec + fsec / 1000000.0;
3419
#else
3420
				result = tm->tm_sec + fsec;
3421
#endif
3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440
				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:
3441
				result = (tm->tm_mon - 1) / 3 + 1;
3442 3443 3444 3445 3446 3447 3448
				break;

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

			case DTK_YEAR:
3449 3450 3451
				if (tm->tm_year > 0)
					result = tm->tm_year;
				else
B
Bruce Momjian 已提交
3452 3453
					/* there is no year 0, just 1 BC and 1 AD */
					result = tm->tm_year - 1;
3454 3455 3456
				break;

			case DTK_DECADE:
B
Bruce Momjian 已提交
3457 3458 3459 3460 3461

				/*
				 * what is a decade wrt dates? let us assume that decade
				 * 199 is 1990 thru 1999... decade 0 starts on year 1 BC,
				 * and -1 is 11 BC thru 2 BC...
3462
				 */
B
Bruce Momjian 已提交
3463
				if (tm->tm_year >= 0)
3464
					result = tm->tm_year / 10;
3465
				else
B
Bruce Momjian 已提交
3466
					result = -((8 - (tm->tm_year - 1)) / 10);
3467 3468 3469
				break;

			case DTK_CENTURY:
B
Bruce Momjian 已提交
3470

3471 3472 3473 3474 3475
				/* ----
				 * centuries AD, c>0: year in [ (c-1)* 100 + 1 : c*100 ]
				 * centuries BC, c<0: year in [ c*100 : (c+1) * 100 - 1]
				 * there is no number 0 century.
				 * ----
3476 3477
				 */
				if (tm->tm_year > 0)
3478
					result = (tm->tm_year + 99) / 100;
3479
				else
3480
					/* caution: C division may have negative remainder */
B
Bruce Momjian 已提交
3481
					result = -((99 - (tm->tm_year - 1)) / 100);
3482 3483 3484
				break;

			case DTK_MILLENNIUM:
3485 3486
				/* see comments above. */
				if (tm->tm_year > 0)
3487
					result = (tm->tm_year + 999) / 1000;
3488
				else
B
Bruce Momjian 已提交
3489
					result = -((999 - (tm->tm_year - 1)) / 1000);
3490 3491
				break;

3492 3493
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3494
#ifdef HAVE_INT64_TIMESTAMP
3495
				result += ((((tm->tm_hour * SECS_PER_MINUTE) + tm->tm_min) * SECS_PER_MINUTE) +
3496
							tm->tm_sec + (fsec / 1000000.0)) / (double)SECS_PER_DAY;
3497
#else
3498
				result += ((((tm->tm_hour * SECS_PER_MINUTE) + tm->tm_min) * SECS_PER_MINUTE) +
3499
							tm->tm_sec + fsec) / (double)SECS_PER_DAY;
3500
#endif
3501 3502
				break;

3503 3504 3505
			case DTK_TZ:
			case DTK_TZ_MINUTE:
			case DTK_TZ_HOUR:
3506
			default:
3507 3508 3509
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
B
Bruce Momjian 已提交
3510
								lowunits)));
3511 3512 3513 3514 3515 3516 3517 3518
				result = 0;
		}
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
B
Bruce Momjian 已提交
3519 3520 3521
				{
					int			tz;
					TimestampTz timestamptz;
3522

B
Bruce Momjian 已提交
3523 3524 3525 3526
					/*
					 * convert to timestamptz to produce consistent
					 * results
					 */
3527
					if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
B
Bruce Momjian 已提交
3528 3529 3530
						ereport(ERROR,
						   (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							errmsg("timestamp out of range")));
3531

B
Bruce Momjian 已提交
3532
					tz = DetermineLocalTimeZone(tm);
3533

B
Bruce Momjian 已提交
3534 3535 3536 3537
					if (tm2timestamp(tm, fsec, &tz, &timestamptz) != 0)
						ereport(ERROR,
						   (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							errmsg("timestamp out of range")));
3538

3539
#ifdef HAVE_INT64_TIMESTAMP
3540
					result = (timestamptz - SetEpochTimestamp()) / 1000000.0;
3541
#else
B
Bruce Momjian 已提交
3542
					result = timestamptz - SetEpochTimestamp();
3543
#endif
B
Bruce Momjian 已提交
3544 3545
					break;
				}
3546
			case DTK_DOW:
3547
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3548 3549 3550
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3551 3552
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3553

3554
			case DTK_DOY:
3555
				if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
3556 3557 3558
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3559 3560 3561
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3562

3563
			default:
3564 3565 3566
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp units \"%s\" not supported",
B
Bruce Momjian 已提交
3567
								lowunits)));
3568 3569
				result = 0;
		}
3570

3571 3572 3573
	}
	else
	{
3574 3575
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3576
			 errmsg("timestamp units \"%s\" not recognized", lowunits)));
3577 3578
		result = 0;
	}
3579

3580 3581
	PG_RETURN_FLOAT8(result);
}
3582

3583 3584 3585 3586 3587 3588 3589
/* timestamptz_part()
 * Extract specified field from timestamp with time zone.
 */
Datum
timestamptz_part(PG_FUNCTION_ARGS)
{
	text	   *units = PG_GETARG_TEXT_P(0);
3590
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
3591 3592 3593 3594
	float8		result;
	int			tz;
	int			type,
				val;
3595
	char	   *lowunits;
3596
	double		dummy;
3597
	fsec_t		fsec;
3598
	char	   *tzn;
B
Bruce Momjian 已提交
3599
	struct pg_tm tt,
3600
			   *tm = &tt;
3601

3602 3603 3604 3605 3606
	if (TIMESTAMP_NOT_FINITE(timestamp))
	{
		result = 0;
		PG_RETURN_FLOAT8(result);
	}
3607

3608 3609 3610 3611 3612 3613 3614 3615
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);

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

3616
	if (type == UNITS)
3617
	{
3618
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3619 3620 3621 3622
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

3623
		switch (val)
3624
		{
3625
			case DTK_TZ:
3626
				result = -tz;
3627 3628 3629
				break;

			case DTK_TZ_MINUTE:
3630
				result = -tz;
3631 3632
				result /= SECS_PER_MINUTE;
				FMODULO(result, dummy, (double)SECS_PER_MINUTE);
3633 3634 3635
				break;

			case DTK_TZ_HOUR:
3636
				dummy = -tz;
3637
				FMODULO(dummy, result, (double)SECS_PER_HOUR);
3638 3639 3640
				break;

			case DTK_MICROSEC:
3641
#ifdef HAVE_INT64_TIMESTAMP
3642
				result = tm->tm_sec * 1000000.0 + fsec;
3643
#else
3644
				result = (tm->tm_sec + fsec) * 1000000;
3645
#endif
3646 3647 3648
				break;

			case DTK_MILLISEC:
3649
#ifdef HAVE_INT64_TIMESTAMP
3650
				result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3651
#else
3652
				result = (tm->tm_sec + fsec) * 1000;
3653
#endif
3654 3655 3656
				break;

			case DTK_SECOND:
3657
#ifdef HAVE_INT64_TIMESTAMP
3658
				result = tm->tm_sec + fsec / 1000000.0;
3659
#else
3660
				result = tm->tm_sec + fsec;
3661
#endif
3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680
				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:
3681
				result = (tm->tm_mon - 1) / 3 + 1;
3682 3683 3684 3685 3686 3687 3688
				break;

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

			case DTK_YEAR:
3689 3690 3691 3692 3693
				if (tm->tm_year > 0)
					result = tm->tm_year;
				else
					/* there is no year 0, just 1 BC and 1 AD */
					result = tm->tm_year - 1;
3694 3695 3696
				break;

			case DTK_DECADE:
3697
				/* see comments in timestamp_part */
B
Bruce Momjian 已提交
3698
				if (tm->tm_year > 0)
3699
					result = tm->tm_year / 10;
3700
				else
B
Bruce Momjian 已提交
3701
					result = -((8 - (tm->tm_year - 1)) / 10);
3702 3703 3704
				break;

			case DTK_CENTURY:
3705 3706
				/* see comments in timestamp_part */
				if (tm->tm_year > 0)
3707
					result = (tm->tm_year + 99) / 100;
3708
				else
B
Bruce Momjian 已提交
3709
					result = -((99 - (tm->tm_year - 1)) / 100);
3710 3711 3712
				break;

			case DTK_MILLENNIUM:
3713 3714
				/* see comments in timestamp_part */
				if (tm->tm_year > 0)
3715
					result = (tm->tm_year + 999) / 1000;
3716
				else
B
Bruce Momjian 已提交
3717
					result = -((999 - (tm->tm_year - 1)) / 1000);
3718 3719
				break;

3720 3721
			case DTK_JULIAN:
				result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3722
#ifdef HAVE_INT64_TIMESTAMP
3723
				result += ((((tm->tm_hour * SECS_PER_MINUTE) + tm->tm_min) * SECS_PER_MINUTE) +
3724
							tm->tm_sec + (fsec / 1000000.0)) / (double)SECS_PER_DAY;
3725
#else
3726
				result += ((((tm->tm_hour * SECS_PER_MINUTE) + tm->tm_min) * SECS_PER_MINUTE) +
3727
							tm->tm_sec + fsec) / (double)SECS_PER_DAY;
3728
#endif
3729 3730
				break;

3731
			default:
3732 3733 3734
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp with time zone units \"%s\" not supported",
B
Bruce Momjian 已提交
3735
								lowunits)));
3736 3737
				result = 0;
		}
3738

3739 3740 3741 3742 3743 3744
	}
	else if (type == RESERV)
	{
		switch (val)
		{
			case DTK_EPOCH:
3745
#ifdef HAVE_INT64_TIMESTAMP
3746
				result = (timestamp - SetEpochTimestamp()) /1000000.0;
3747
#else
3748
				result = timestamp - SetEpochTimestamp();
3749
#endif
3750
				break;
3751

3752
			case DTK_DOW:
3753
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3754 3755 3756
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3757 3758
				result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
				break;
3759

3760
			case DTK_DOY:
3761
				if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
3762 3763 3764
					ereport(ERROR,
							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
							 errmsg("timestamp out of range")));
3765 3766 3767
				result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
						  - date2j(tm->tm_year, 1, 1) + 1);
				break;
3768

3769
			default:
3770 3771 3772
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("timestamp with time zone units \"%s\" not supported",
B
Bruce Momjian 已提交
3773
								lowunits)));
3774
				result = 0;
3775 3776
		}
	}
3777 3778
	else
	{
3779 3780
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3781 3782
		   errmsg("timestamp with time zone units \"%s\" not recognized",
				  lowunits)));
3783

3784 3785
		result = 0;
	}
3786

3787 3788
	PG_RETURN_FLOAT8(result);
}
3789 3790 3791 3792 3793


/* interval_part()
 * Extract specified field from interval.
 */
3794 3795
Datum
interval_part(PG_FUNCTION_ARGS)
3796
{
3797 3798 3799
	text	   *units = PG_GETARG_TEXT_P(0);
	Interval   *interval = PG_GETARG_INTERVAL_P(1);
	float8		result;
3800 3801
	int			type,
				val;
3802
	char	   *lowunits;
3803
	fsec_t		fsec;
B
Bruce Momjian 已提交
3804
	struct pg_tm tt,
3805 3806
			   *tm = &tt;

3807 3808 3809
	lowunits = downcase_truncate_identifier(VARDATA(units),
											VARSIZE(units) - VARHDRSZ,
											false);
3810 3811

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

3815
	if (type == UNITS)
3816 3817 3818 3819 3820
	{
		if (interval2tm(*interval, tm, &fsec) == 0)
		{
			switch (val)
			{
B
Bruce Momjian 已提交
3821
				case DTK_MICROSEC:
3822
#ifdef HAVE_INT64_TIMESTAMP
3823
					result = tm->tm_sec * 1000000.0 + fsec;
3824
#else
B
Bruce Momjian 已提交
3825
					result = (tm->tm_sec + fsec) * 1000000;
3826
#endif
B
Bruce Momjian 已提交
3827
					break;
3828

B
Bruce Momjian 已提交
3829
				case DTK_MILLISEC:
3830
#ifdef HAVE_INT64_TIMESTAMP
3831
					result = tm->tm_sec * 1000.0 + fsec / 1000.0;
3832
#else
B
Bruce Momjian 已提交
3833
					result = (tm->tm_sec + fsec) * 1000;
3834
#endif
B
Bruce Momjian 已提交
3835
					break;
3836

B
Bruce Momjian 已提交
3837
				case DTK_SECOND:
3838
#ifdef HAVE_INT64_TIMESTAMP
3839
					result = tm->tm_sec + fsec / 1000000.0;
3840
#else
3841
					result = tm->tm_sec + fsec;
3842
#endif
B
Bruce Momjian 已提交
3843
					break;
3844 3845

				case DTK_MINUTE:
3846
					result = tm->tm_min;
3847 3848 3849
					break;

				case DTK_HOUR:
3850
					result = tm->tm_hour;
3851 3852 3853
					break;

				case DTK_DAY:
3854
					result = tm->tm_mday;
3855 3856 3857
					break;

				case DTK_MONTH:
3858
					result = tm->tm_mon;
3859 3860 3861
					break;

				case DTK_QUARTER:
3862
					result = (tm->tm_mon / 3) + 1;
3863 3864 3865
					break;

				case DTK_YEAR:
3866
					result = tm->tm_year;
3867 3868 3869
					break;

				case DTK_DECADE:
3870
					/* caution: C division may have negative remainder */
3871
					result = tm->tm_year / 10;
3872 3873 3874
					break;

				case DTK_CENTURY:
3875
					/* caution: C division may have negative remainder */
3876
					result = tm->tm_year / 100;
3877 3878
					break;

3879
				case DTK_MILLENNIUM:
3880
					/* caution: C division may have negative remainder */
3881
					result = tm->tm_year / 1000;
3882 3883 3884
					break;

				default:
3885 3886 3887 3888
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("interval units \"%s\" not supported",
							 DatumGetCString(DirectFunctionCall1(textout,
B
Bruce Momjian 已提交
3889
											 PointerGetDatum(units))))));
3890
					result = 0;
3891 3892 3893 3894 3895
			}

		}
		else
		{
3896
			elog(ERROR, "could not convert interval to tm");
3897
			result = 0;
3898 3899
		}
	}
3900
	else if (type == RESERV && val == DTK_EPOCH)
3901
	{
3902
#ifdef HAVE_INT64_TIMESTAMP
3903
		result = interval->time / 1000000.0;
3904
#else
3905
		result = interval->time;
3906
#endif
3907 3908
		result += (DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR);
		result += ((double)DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR);
3909
		result += interval->day * SECS_PER_DAY;
3910 3911 3912
	}
	else
	{
3913 3914 3915
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("interval units \"%s\" not recognized",
B
Bruce Momjian 已提交
3916 3917
						DatumGetCString(DirectFunctionCall1(textout,
											 PointerGetDatum(units))))));
3918
		result = 0;
3919 3920
	}

3921 3922
	PG_RETURN_FLOAT8(result);
}
3923 3924 3925 3926


/* timestamp_zone()
 * Encode timestamp type with specified time zone.
3927
 * Returns timestamp with time zone, with the input
B
Bruce Momjian 已提交
3928
 *	rotated from local time to the specified zone.
3929
 */
3930 3931
Datum
timestamp_zone(PG_FUNCTION_ARGS)
3932
{
3933
	text	   *zone = PG_GETARG_TEXT_P(0);
3934
	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3935
	Timestamp result;
3936
	int			tz;
3937 3938 3939 3940 3941
	pg_tz      *tzp;
	char        tzname[TZ_STRLEN_MAX+1];
	int         len;
	struct pg_tm tm;
	fsec_t      fsec;
3942 3943 3944 3945

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

3946 3947 3948 3949 3950 3951
	/* Find the specified timezone? */
	len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX)?TZ_STRLEN_MAX:(VARSIZE(zone)-VARHDRSZ);
	memcpy(tzname,VARDATA(zone),len);
	tzname[len] = 0;
	tzp = pg_tzset(tzname);
	if (!tzp) {
3952 3953
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3954 3955
				 errmsg("time zone \"%s\" not recognised",
				        tzname)));
3956 3957 3958
		PG_RETURN_NULL();
	}

3959 3960 3961 3962 3963 3964 3965 3966 3967 3968
	/* Apply the timezone change */
	if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 ||
	    tm2timestamp(&tm, fsec, NULL, &result) != 0) {
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("could not convert to time zone \"%s\"",
				        tzname)));
		PG_RETURN_NULL();
	}
	PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result));
3969 3970 3971 3972 3973 3974 3975 3976 3977
}	/* 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);
3978
	Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
3979
	TimestampTz result;
3980 3981 3982 3983 3984 3985
	int			tz;

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

	if (zone->month != 0)
3986 3987
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
3988 3989 3990
			   errmsg("interval time zone \"%s\" must not specify month",
					  DatumGetCString(DirectFunctionCall1(interval_out,
											  PointerGetDatum(zone))))));
3991

3992
#ifdef HAVE_INT64_TIMESTAMP
3993
	tz = zone->time / USECS_PER_SEC;
3994
#else
3995
	tz = zone->time;
3996 3997 3998
#endif

	result = dt2local(timestamp, tz);
3999 4000 4001 4002 4003 4004 4005 4006 4007 4008

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

/* timestamp_timestamptz()
 * Convert local timestamp to timestamp at GMT
 */
Datum
timestamp_timestamptz(PG_FUNCTION_ARGS)
{
4009
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
4010 4011 4012 4013 4014 4015 4016

	PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
}

static TimestampTz
timestamp2timestamptz(Timestamp timestamp)
{
4017
	TimestampTz result;
B
Bruce Momjian 已提交
4018
	struct pg_tm tt,
4019
			   *tm = &tt;
4020
	fsec_t		fsec;
4021 4022 4023 4024
	int			tz;

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

4026 4027
	else
	{
4028
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
4029 4030 4031
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4032 4033 4034 4035

		tz = DetermineLocalTimeZone(tm);

		if (tm2timestamp(tm, fsec, &tz, &result) != 0)
4036 4037 4038
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4039 4040
	}

4041
	return result;
4042 4043 4044 4045 4046 4047 4048 4049
}

/* timestamptz_timestamp()
 * Convert timestamp at GMT to local timestamp
 */
Datum
timestamptz_timestamp(PG_FUNCTION_ARGS)
{
4050
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
4051
	Timestamp	result;
B
Bruce Momjian 已提交
4052
	struct pg_tm tt,
4053
			   *tm = &tt;
4054
	fsec_t		fsec;
4055 4056 4057 4058 4059
	char	   *tzn;
	int			tz;

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

4061 4062
	else
	{
4063
		if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
4064 4065 4066
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4067
		if (tm2timestamp(tm, fsec, NULL, &result) != 0)
4068 4069 4070
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));
4071 4072 4073 4074 4075
	}
	PG_RETURN_TIMESTAMP(result);
}

/* timestamptz_zone()
4076 4077
 * Evaluate timestamp with time zone type at the specified time zone.
 * Returns a timestamp without time zone.
4078 4079 4080 4081 4082
 */
Datum
timestamptz_zone(PG_FUNCTION_ARGS)
{
	text	   *zone = PG_GETARG_TEXT_P(0);
4083
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4084 4085
	Timestamp	result;

4086
	int			tz;
4087 4088 4089 4090 4091
	pg_tz	   *tzp;
	char        tzname[TZ_STRLEN_MAX];
	int         len;
	struct pg_tm tm;
	fsec_t      fsec = 0;
4092

4093
	if (TIMESTAMP_NOT_FINITE(timestamp))
4094
		PG_RETURN_NULL();
4095

4096 4097 4098 4099 4100
	/* Find the specified zone */
	len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX)?TZ_STRLEN_MAX:(VARSIZE(zone)-VARHDRSZ);
	memcpy(tzname,VARDATA(zone),len);
	tzname[len] = 0;
	tzp = pg_tzset(tzname);
4101

4102 4103 4104 4105
	if (!tzp) {
		ereport(ERROR,
			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
			 errmsg("time zone \"%s\" not recognized", tzname)));
4106

4107
		PG_RETURN_NULL();
4108
	}
4109

4110 4111 4112 4113 4114
	if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0 ||
	    tm2timestamp(&tm, fsec, NULL, &result)) { 
		ereport(ERROR,
			(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
			errmsg("could not to convert to time zone \"%s\"", tzname)));
4115
		PG_RETURN_NULL();
4116 4117
	}

4118
	PG_RETURN_TIMESTAMP(result);
4119
}	/* timestamptz_zone() */
4120

4121 4122
/* timestamptz_izone()
 * Encode timestamp with time zone type with specified time interval as time zone.
4123
 * Returns a timestamp without time zone.
4124 4125
 */
Datum
4126
timestamptz_izone(PG_FUNCTION_ARGS)
4127 4128
{
	Interval   *zone = PG_GETARG_INTERVAL_P(0);
4129
	TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4130
	Timestamp	result;
4131 4132 4133
	int			tz;

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

	if (zone->month != 0)
4137 4138
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
4139 4140 4141
			   errmsg("interval time zone \"%s\" must not specify month",
					  DatumGetCString(DirectFunctionCall1(interval_out,
											  PointerGetDatum(zone))))));
4142

4143
#ifdef HAVE_INT64_TIMESTAMP
4144
	tz = -(zone->time / USECS_PER_SEC);
4145
#else
4146
	tz = -(zone->time);
4147
#endif
4148

4149
	result = dt2local(timestamp, tz);
4150

4151
	PG_RETURN_TIMESTAMP(result);
4152
}	/* timestamptz_izone() */