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 */
3838#endif
3939
4040
41+ static void EncodeSpecialDate (DateADT dt ,char * str );
4142static int time2tm (TimeADT time ,struct pg_tm * tm ,fsec_t * fsec );
4243static int timetz2tm (TimeTzADT * time ,struct pg_tm * tm ,fsec_t * fsec ,int * tzp );
4344static int tm2time (struct pg_tm * tm ,fsec_t fsec ,TimeADT * result );
@@ -147,6 +148,14 @@ date_in(PG_FUNCTION_ARGS)
147148GetEpochTime (tm );
148149break ;
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+
150159default :
151160DateTimeParseError (DTERR_BAD_FORMAT ,str ,"date" );
152161break ;
@@ -174,10 +183,14 @@ date_out(PG_FUNCTION_ARGS)
174183* tm = & tt ;
175184char 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
182195result = pstrdup (buf );
183196PG_RETURN_CSTRING (result );
@@ -208,6 +221,20 @@ date_send(PG_FUNCTION_ARGS)
208221PG_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)
280307PG_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+
283318Datum
284319date_larger (PG_FUNCTION_ARGS )
285320{
@@ -306,6 +341,11 @@ date_mi(PG_FUNCTION_ARGS)
306341DateADT dateVal1 = PG_GETARG_DATEADT (0 );
307342DateADT 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+
309349PG_RETURN_INT32 ((int32 ) (dateVal1 - dateVal2 ));
310350}
311351
@@ -318,6 +358,9 @@ date_pli(PG_FUNCTION_ARGS)
318358DateADT dateVal = PG_GETARG_DATEADT (0 );
319359int32 days = PG_GETARG_INT32 (1 );
320360
361+ if (DATE_NOT_FINITE (dateVal ))
362+ days = 0 ;/* can't change infinity */
363+
321364PG_RETURN_DATEADT (dateVal + days );
322365}
323366
@@ -329,6 +372,9 @@ date_mii(PG_FUNCTION_ARGS)
329372DateADT dateVal = PG_GETARG_DATEADT (0 );
330373int32 days = PG_GETARG_INT32 (1 );
331374
375+ if (DATE_NOT_FINITE (dateVal ))
376+ days = 0 ;/* can't change infinity */
377+
332378PG_RETURN_DATEADT (dateVal - days );
333379}
334380
@@ -342,18 +388,25 @@ date2timestamp(DateADT dateVal)
342388{
343389Timestamp 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
358411return result ;
359412}
@@ -366,24 +419,30 @@ date2timestamptz(DateADT dateVal)
366419* tm = & tt ;
367420int 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
388447return result ;
389448}
@@ -797,15 +856,19 @@ timestamp_date(PG_FUNCTION_ARGS)
797856* tm = & tt ;
798857fsec_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
810873PG_RETURN_DATEADT (result );
811874}
@@ -840,15 +903,19 @@ timestamptz_date(PG_FUNCTION_ARGS)
840903int tz ;
841904char * 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
853920PG_RETURN_DATEADT (result );
854921}
@@ -869,16 +936,19 @@ abstime_date(PG_FUNCTION_ARGS)
869936switch (abstime )
870937{
871938case INVALID_ABSTIME :
872- case NOSTART_ABSTIME :
873- case NOEND_ABSTIME :
874939ereport (ERROR ,
875940(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
876941errmsg ("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
883953default :
884954abstime2tm (abstime ,& tz ,tm ,NULL );
@@ -1452,9 +1522,9 @@ datetime_timestamp(PG_FUNCTION_ARGS)
14521522TimeADT time = PG_GETARG_TIMEADT (1 );
14531523Timestamp 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
14591529PG_RETURN_TIMESTAMP (result );
14601530}
@@ -2304,11 +2374,18 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
23042374TimeTzADT * time = PG_GETARG_TIMETZADT_P (1 );
23052375TimestampTz 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
23132390PG_RETURN_TIMESTAMP (result );
23142391}