Skip to content

Commit a303e4d

Browse files
committed
Extend the date type to support infinity and -infinity, analogously to
the timestamp types. Turns out this doesn't even reduce the available range of dates, since the restriction to dates that work for Julian-date arithmetic is much tighter than the int32 range anyway. Per a longstanding TODO item.
1 parent 791359f commit a303e4d

File tree

9 files changed

+206
-65
lines changed

9 files changed

+206
-65
lines changed

doc/src/sgml/datatype.sgml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.229 2008/10/03 15:37:18 petere Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.230 2008/10/14 17:12:32 tgl Exp $ -->
22

33
<chapter id="datatype">
44
<title id="datatype-title">Data Types</title>
@@ -2032,12 +2032,12 @@ January 8 04:05:06 1999 PST
20322032
</row>
20332033
<row>
20342034
<entry><literal>infinity</literal></entry>
2035-
<entry><type>timestamp</type></entry>
2035+
<entry><type>date</type>, <type>timestamp</type></entry>
20362036
<entry>later than all other time stamps</entry>
20372037
</row>
20382038
<row>
20392039
<entry><literal>-infinity</literal></entry>
2040-
<entry><type>timestamp</type></entry>
2040+
<entry><type>date</type>, <type>timestamp</type></entry>
20412041
<entry>earlier than all other time stamps</entry>
20422042
</row>
20432043
<row>

doc/src/sgml/func.sgml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.449 2008/10/13 16:25:19 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.450 2008/10/14 17:12:32 tgl Exp $ -->
22

33
<chapter id="functions">
44
<title>Functions and Operators</title>
@@ -5912,10 +5912,18 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
59125912
<entry><literal>3</literal></entry>
59135913
</row>
59145914

5915+
<row>
5916+
<entry><literal><function>isfinite</function>(<type>date</type>)</literal></entry>
5917+
<entry><type>boolean</type></entry>
5918+
<entry>Test for finite date (not +/-infinity)</entry>
5919+
<entry><literal>isfinite(date '2001-02-16')</literal></entry>
5920+
<entry><literal>true</literal></entry>
5921+
</row>
5922+
59155923
<row>
59165924
<entry><literal><function>isfinite</function>(<type>timestamp</type>)</literal></entry>
59175925
<entry><type>boolean</type></entry>
5918-
<entry>Test for finite time stamp (not equal to infinity)</entry>
5926+
<entry>Test for finite time stamp (not +/-infinity)</entry>
59195927
<entry><literal>isfinite(timestamp '2001-02-16 21:28:30')</literal></entry>
59205928
<entry><literal>true</literal></entry>
59215929
</row>

src/backend/utils/adt/date.c

Lines changed: 132 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.142 2008/07/07 18:09:46 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.143 2008/10/14 17:12:33 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -38,6 +38,7 @@
3838
#endif
3939

4040

41+
static void EncodeSpecialDate(DateADT dt, char *str);
4142
static int time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
4243
static int timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
4344
static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
@@ -147,6 +148,14 @@ date_in(PG_FUNCTION_ARGS)
147148
GetEpochTime(tm);
148149
break;
149150

151+
case DTK_LATE:
152+
DATE_NOEND(date);
153+
PG_RETURN_DATEADT(date);
154+
155+
case DTK_EARLY:
156+
DATE_NOBEGIN(date);
157+
PG_RETURN_DATEADT(date);
158+
150159
default:
151160
DateTimeParseError(DTERR_BAD_FORMAT, str, "date");
152161
break;
@@ -174,10 +183,14 @@ date_out(PG_FUNCTION_ARGS)
174183
*tm = &tt;
175184
char buf[MAXDATELEN + 1];
176185

177-
j2date(date + POSTGRES_EPOCH_JDATE,
178-
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
179-
180-
EncodeDateOnly(tm, DateStyle, buf);
186+
if (DATE_NOT_FINITE(date))
187+
EncodeSpecialDate(date, buf);
188+
else
189+
{
190+
j2date(date + POSTGRES_EPOCH_JDATE,
191+
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
192+
EncodeDateOnly(tm, DateStyle, buf);
193+
}
181194

182195
result = pstrdup(buf);
183196
PG_RETURN_CSTRING(result);
@@ -208,6 +221,20 @@ date_send(PG_FUNCTION_ARGS)
208221
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
209222
}
210223

224+
/*
225+
* Convert reserved date values to string.
226+
*/
227+
static void
228+
EncodeSpecialDate(DateADT dt, char *str)
229+
{
230+
if (DATE_IS_NOBEGIN(dt))
231+
strcpy(str, EARLY);
232+
else if (DATE_IS_NOEND(dt))
233+
strcpy(str, LATE);
234+
else /* shouldn't happen */
235+
elog(ERROR, "invalid argument for EncodeSpecialDate");
236+
}
237+
211238

212239
/*
213240
* Comparison functions for dates
@@ -280,6 +307,14 @@ date_cmp(PG_FUNCTION_ARGS)
280307
PG_RETURN_INT32(0);
281308
}
282309

310+
Datum
311+
date_finite(PG_FUNCTION_ARGS)
312+
{
313+
DateADT date = PG_GETARG_DATEADT(0);
314+
315+
PG_RETURN_BOOL(!DATE_NOT_FINITE(date));
316+
}
317+
283318
Datum
284319
date_larger(PG_FUNCTION_ARGS)
285320
{
@@ -306,6 +341,11 @@ date_mi(PG_FUNCTION_ARGS)
306341
DateADT dateVal1 = PG_GETARG_DATEADT(0);
307342
DateADT dateVal2 = PG_GETARG_DATEADT(1);
308343

344+
if (DATE_NOT_FINITE(dateVal1) || DATE_NOT_FINITE(dateVal2))
345+
ereport(ERROR,
346+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
347+
errmsg("cannot subtract infinite dates")));
348+
309349
PG_RETURN_INT32((int32) (dateVal1 - dateVal2));
310350
}
311351

@@ -318,6 +358,9 @@ date_pli(PG_FUNCTION_ARGS)
318358
DateADT dateVal = PG_GETARG_DATEADT(0);
319359
int32 days = PG_GETARG_INT32(1);
320360

361+
if (DATE_NOT_FINITE(dateVal))
362+
days = 0; /* can't change infinity */
363+
321364
PG_RETURN_DATEADT(dateVal + days);
322365
}
323366

@@ -329,6 +372,9 @@ date_mii(PG_FUNCTION_ARGS)
329372
DateADT dateVal = PG_GETARG_DATEADT(0);
330373
int32 days = PG_GETARG_INT32(1);
331374

375+
if (DATE_NOT_FINITE(dateVal))
376+
days = 0; /* can't change infinity */
377+
332378
PG_RETURN_DATEADT(dateVal - days);
333379
}
334380

@@ -342,18 +388,25 @@ date2timestamp(DateADT dateVal)
342388
{
343389
Timestamp result;
344390

391+
if (DATE_IS_NOBEGIN(dateVal))
392+
TIMESTAMP_NOBEGIN(result);
393+
else if (DATE_IS_NOEND(dateVal))
394+
TIMESTAMP_NOEND(result);
395+
else
396+
{
345397
#ifdef HAVE_INT64_TIMESTAMP
346-
/* date is days since 2000, timestamp is microseconds since same... */
347-
result = dateVal * USECS_PER_DAY;
348-
/* Date's range is wider than timestamp's, so must check for overflow */
349-
if (result / USECS_PER_DAY != dateVal)
350-
ereport(ERROR,
351-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
352-
errmsg("date out of range for timestamp")));
398+
/* date is days since 2000, timestamp is microseconds since same... */
399+
result = dateVal * USECS_PER_DAY;
400+
/* Date's range is wider than timestamp's, so check for overflow */
401+
if (result / USECS_PER_DAY != dateVal)
402+
ereport(ERROR,
403+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
404+
errmsg("date out of range for timestamp")));
353405
#else
354-
/* date is days since 2000, timestamp is seconds since same... */
355-
result = dateVal * (double) SECS_PER_DAY;
406+
/* date is days since 2000, timestamp is seconds since same... */
407+
result = dateVal * (double) SECS_PER_DAY;
356408
#endif
409+
}
357410

358411
return result;
359412
}
@@ -366,24 +419,30 @@ date2timestamptz(DateADT dateVal)
366419
*tm = &tt;
367420
int tz;
368421

369-
j2date(dateVal + POSTGRES_EPOCH_JDATE,
370-
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
371-
372-
tm->tm_hour = 0;
373-
tm->tm_min = 0;
374-
tm->tm_sec = 0;
375-
tz = DetermineTimeZoneOffset(tm, session_timezone);
422+
if (DATE_IS_NOBEGIN(dateVal))
423+
TIMESTAMP_NOBEGIN(result);
424+
else if (DATE_IS_NOEND(dateVal))
425+
TIMESTAMP_NOEND(result);
426+
else
427+
{
428+
j2date(dateVal + POSTGRES_EPOCH_JDATE,
429+
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
430+
tm->tm_hour = 0;
431+
tm->tm_min = 0;
432+
tm->tm_sec = 0;
433+
tz = DetermineTimeZoneOffset(tm, session_timezone);
376434

377435
#ifdef HAVE_INT64_TIMESTAMP
378-
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
379-
/* Date's range is wider than timestamp's, so must check for overflow */
380-
if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
381-
ereport(ERROR,
382-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
383-
errmsg("date out of range for timestamp")));
436+
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
437+
/* Date's range is wider than timestamp's, so check for overflow */
438+
if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
439+
ereport(ERROR,
440+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
441+
errmsg("date out of range for timestamp")));
384442
#else
385-
result = dateVal * (double) SECS_PER_DAY + tz;
443+
result = dateVal * (double) SECS_PER_DAY + tz;
386444
#endif
445+
}
387446

388447
return result;
389448
}
@@ -797,15 +856,19 @@ timestamp_date(PG_FUNCTION_ARGS)
797856
*tm = &tt;
798857
fsec_t fsec;
799858

800-
if (TIMESTAMP_NOT_FINITE(timestamp))
801-
PG_RETURN_NULL();
802-
803-
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
804-
ereport(ERROR,
805-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
806-
errmsg("timestamp out of range")));
859+
if (TIMESTAMP_IS_NOBEGIN(timestamp))
860+
DATE_NOBEGIN(result);
861+
else if (TIMESTAMP_IS_NOEND(timestamp))
862+
DATE_NOEND(result);
863+
else
864+
{
865+
if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
866+
ereport(ERROR,
867+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
868+
errmsg("timestamp out of range")));
807869

808-
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
870+
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
871+
}
809872

810873
PG_RETURN_DATEADT(result);
811874
}
@@ -840,15 +903,19 @@ timestamptz_date(PG_FUNCTION_ARGS)
840903
int tz;
841904
char *tzn;
842905

843-
if (TIMESTAMP_NOT_FINITE(timestamp))
844-
PG_RETURN_NULL();
845-
846-
if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
847-
ereport(ERROR,
848-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
849-
errmsg("timestamp out of range")));
906+
if (TIMESTAMP_IS_NOBEGIN(timestamp))
907+
DATE_NOBEGIN(result);
908+
else if (TIMESTAMP_IS_NOEND(timestamp))
909+
DATE_NOEND(result);
910+
else
911+
{
912+
if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) != 0)
913+
ereport(ERROR,
914+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
915+
errmsg("timestamp out of range")));
850916

851-
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
917+
result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
918+
}
852919

853920
PG_RETURN_DATEADT(result);
854921
}
@@ -869,16 +936,19 @@ abstime_date(PG_FUNCTION_ARGS)
869936
switch (abstime)
870937
{
871938
case INVALID_ABSTIME:
872-
case NOSTART_ABSTIME:
873-
case NOEND_ABSTIME:
874939
ereport(ERROR,
875940
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
876941
errmsg("cannot convert reserved abstime value to date")));
942+
result = 0; /* keep compiler quiet */
943+
break;
877944

878-
/*
879-
* pretend to drop through to make compiler think that result will
880-
* be set
881-
*/
945+
case NOSTART_ABSTIME:
946+
DATE_NOBEGIN(result);
947+
break;
948+
949+
case NOEND_ABSTIME:
950+
DATE_NOEND(result);
951+
break;
882952

883953
default:
884954
abstime2tm(abstime, &tz, tm, NULL);
@@ -1452,9 +1522,9 @@ datetime_timestamp(PG_FUNCTION_ARGS)
14521522
TimeADT time = PG_GETARG_TIMEADT(1);
14531523
Timestamp result;
14541524

1455-
result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
1456-
DateADTGetDatum(date)));
1457-
result += time;
1525+
result = date2timestamp(date);
1526+
if (!TIMESTAMP_NOT_FINITE(result))
1527+
result += time;
14581528

14591529
PG_RETURN_TIMESTAMP(result);
14601530
}
@@ -2304,11 +2374,18 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
23042374
TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
23052375
TimestampTz result;
23062376

2377+
if (DATE_IS_NOBEGIN(date))
2378+
TIMESTAMP_NOBEGIN(result);
2379+
else if (DATE_IS_NOEND(date))
2380+
TIMESTAMP_NOEND(result);
2381+
else
2382+
{
23072383
#ifdef HAVE_INT64_TIMESTAMP
2308-
result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
2384+
result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
23092385
#else
2310-
result = date * (double) SECS_PER_DAY + time->time + time->zone;
2386+
result = date * (double) SECS_PER_DAY + time->time + time->zone;
23112387
#endif
2388+
}
23122389

23132390
PG_RETURN_TIMESTAMP(result);
23142391
}

src/backend/utils/adt/xml.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.78 2008/10/09 15:49:04 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.79 2008/10/14 17:12:33 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -1632,6 +1632,11 @@ map_sql_value_to_xml_value(Datum value, Oid type)
16321632
char buf[MAXDATELEN + 1];
16331633

16341634
date = DatumGetDateADT(value);
1635+
/* XSD doesn't support infinite values */
1636+
if (DATE_NOT_FINITE(date))
1637+
ereport(ERROR,
1638+
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1639+
errmsg("date out of range")));
16351640
j2date(date + POSTGRES_EPOCH_JDATE,
16361641
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
16371642
EncodeDateOnly(&tm, USE_XSD_DATES, buf);

src/include/catalog/catversion.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
3838
* Portions Copyright (c) 1994, Regents of the University of California
3939
*
40-
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.497 2008/10/13 16:25:19 tgl Exp $
40+
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.498 2008/10/14 17:12:33 tgl Exp $
4141
*
4242
*-------------------------------------------------------------------------
4343
*/
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 200810131
56+
#define CATALOG_VERSION_NO 200810141
5757

5858
#endif

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy