@@ -160,13 +160,20 @@ date_in(PG_FUNCTION_ARGS)
160160 break ;
161161 }
162162
163+ /* Prevent overflow in Julian-day routines */
163164 if (!IS_VALID_JULIAN (tm -> tm_year , tm -> tm_mon , tm -> tm_mday ))
164165 ereport (ERROR ,
165166 (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
166167 errmsg ("date out of range: \"%s\"" , str )));
167168
168169 date = date2j (tm -> tm_year , tm -> tm_mon , tm -> tm_mday ) - POSTGRES_EPOCH_JDATE ;
169170
171+ /* Now check for just-out-of-range dates */
172+ if (!IS_VALID_DATE (date ))
173+ ereport (ERROR ,
174+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
175+ errmsg ("date out of range: \"%s\"" , str )));
176+
170177 PG_RETURN_DATEADT (date );
171178}
172179
@@ -209,8 +216,7 @@ date_recv(PG_FUNCTION_ARGS)
209216 /* Limit to the same range that date_in() accepts. */
210217 if (DATE_NOT_FINITE (result ))
211218 /* ok */ ;
212- else if (result < - POSTGRES_EPOCH_JDATE ||
213- result >= JULIAN_MAX - POSTGRES_EPOCH_JDATE )
219+ else if (!IS_VALID_DATE (result ))
214220 ereport (ERROR ,
215221 (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
216222 errmsg ("date out of range" )));
@@ -258,6 +264,7 @@ make_date(PG_FUNCTION_ARGS)
258264 errmsg ("date field value out of range: %d-%02d-%02d" ,
259265 tm .tm_year , tm .tm_mon , tm .tm_mday )));
260266
267+ /* Prevent overflow in Julian-day routines */
261268 if (!IS_VALID_JULIAN (tm .tm_year , tm .tm_mon , tm .tm_mday ))
262269 ereport (ERROR ,
263270 (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
@@ -266,6 +273,13 @@ make_date(PG_FUNCTION_ARGS)
266273
267274 date = date2j (tm .tm_year , tm .tm_mon , tm .tm_mday ) - POSTGRES_EPOCH_JDATE ;
268275
276+ /* Now check for just-out-of-range dates */
277+ if (!IS_VALID_DATE (date ))
278+ ereport (ERROR ,
279+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
280+ errmsg ("date out of range: %d-%02d-%02d" ,
281+ tm .tm_year , tm .tm_mon , tm .tm_mday )));
282+
269283 PG_RETURN_DATEADT (date );
270284}
271285
@@ -427,11 +441,21 @@ date_pli(PG_FUNCTION_ARGS)
427441{
428442 DateADT dateVal = PG_GETARG_DATEADT (0 );
429443 int32 days = PG_GETARG_INT32 (1 );
444+ DateADT result ;
430445
431446 if (DATE_NOT_FINITE (dateVal ))
432- days = 0 ; /* can't change infinity */
447+ PG_RETURN_DATEADT (dateVal ); /* can't change infinity */
448+
449+ result = dateVal + days ;
450+
451+ /* Check for integer overflow and out-of-allowed-range */
452+ if ((days >= 0 ? (result < dateVal ) : (result > dateVal )) ||
453+ !IS_VALID_DATE (result ))
454+ ereport (ERROR ,
455+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
456+ errmsg ("date out of range" )));
433457
434- PG_RETURN_DATEADT (dateVal + days );
458+ PG_RETURN_DATEADT (result );
435459}
436460
437461/* Subtract a number of days from a date, giving a new date.
@@ -441,11 +465,21 @@ date_mii(PG_FUNCTION_ARGS)
441465{
442466 DateADT dateVal = PG_GETARG_DATEADT (0 );
443467 int32 days = PG_GETARG_INT32 (1 );
468+ DateADT result ;
444469
445470 if (DATE_NOT_FINITE (dateVal ))
446- days = 0 ; /* can't change infinity */
471+ PG_RETURN_DATEADT (dateVal ); /* can't change infinity */
472+
473+ result = dateVal - days ;
474+
475+ /* Check for integer overflow and out-of-allowed-range */
476+ if ((days >= 0 ? (result > dateVal ) : (result < dateVal )) ||
477+ !IS_VALID_DATE (result ))
478+ ereport (ERROR ,
479+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
480+ errmsg ("date out of range" )));
447481
448- PG_RETURN_DATEADT (dateVal - days );
482+ PG_RETURN_DATEADT (result );
449483}
450484
451485/*
@@ -464,14 +498,18 @@ date2timestamp(DateADT dateVal)
464498 TIMESTAMP_NOEND (result );
465499 else
466500 {
467- #ifdef HAVE_INT64_TIMESTAMP
468- /* date is days since 2000, timestamp is microseconds since same... */
469- result = dateVal * USECS_PER_DAY ;
470- /* Date's range is wider than timestamp's, so check for overflow */
471- if (result / USECS_PER_DAY != dateVal )
501+ /*
502+ * Date's range is wider than timestamp's, so check for boundaries.
503+ * Since dates have the same minimum values as timestamps, only upper
504+ * boundary need be checked for overflow.
505+ */
506+ if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE ))
472507 ereport (ERROR ,
473508 (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
474509 errmsg ("date out of range for timestamp" )));
510+ #ifdef HAVE_INT64_TIMESTAMP
511+ /* date is days since 2000, timestamp is microseconds since same... */
512+ result = dateVal * USECS_PER_DAY ;
475513#else
476514 /* date is days since 2000, timestamp is seconds since same... */
477515 result = dateVal * (double ) SECS_PER_DAY ;
@@ -495,6 +533,16 @@ date2timestamptz(DateADT dateVal)
495533 TIMESTAMP_NOEND (result );
496534 else
497535 {
536+ /*
537+ * Date's range is wider than timestamp's, so check for boundaries.
538+ * Since dates have the same minimum values as timestamps, only upper
539+ * boundary need be checked for overflow.
540+ */
541+ if (dateVal >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE ))
542+ ereport (ERROR ,
543+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
544+ errmsg ("date out of range for timestamp" )));
545+
498546 j2date (dateVal + POSTGRES_EPOCH_JDATE ,
499547 & (tm -> tm_year ), & (tm -> tm_mon ), & (tm -> tm_mday ));
500548 tm -> tm_hour = 0 ;
@@ -504,14 +552,18 @@ date2timestamptz(DateADT dateVal)
504552
505553#ifdef HAVE_INT64_TIMESTAMP
506554 result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC ;
507- /* Date's range is wider than timestamp's, so check for overflow */
508- if ((result - tz * USECS_PER_SEC ) / USECS_PER_DAY != dateVal )
509- ereport (ERROR ,
510- (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
511- errmsg ("date out of range for timestamp" )));
512555#else
513556 result = dateVal * (double ) SECS_PER_DAY + tz ;
514557#endif
558+
559+ /*
560+ * Since it is possible to go beyond allowed timestamptz range because
561+ * of time zone, check for allowed timestamp range after adding tz.
562+ */
563+ if (!IS_VALID_TIMESTAMP (result ))
564+ ereport (ERROR ,
565+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
566+ errmsg ("date out of range for timestamp" )));
515567 }
516568
517569 return result ;
@@ -1053,7 +1105,17 @@ abstime_date(PG_FUNCTION_ARGS)
10531105
10541106 default :
10551107 abstime2tm (abstime , & tz , tm , NULL );
1108+ /* Prevent overflow in Julian-day routines */
1109+ if (!IS_VALID_JULIAN (tm -> tm_year , tm -> tm_mon , tm -> tm_mday ))
1110+ ereport (ERROR ,
1111+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
1112+ errmsg ("abstime out of range for date" )));
10561113 result = date2j (tm -> tm_year , tm -> tm_mon , tm -> tm_mday ) - POSTGRES_EPOCH_JDATE ;
1114+ /* Now check for just-out-of-range dates */
1115+ if (!IS_VALID_DATE (result ))
1116+ ereport (ERROR ,
1117+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
1118+ errmsg ("abstime out of range for date" )));
10571119 break ;
10581120 }
10591121
@@ -1678,7 +1740,13 @@ datetime_timestamp(PG_FUNCTION_ARGS)
16781740
16791741 result = date2timestamp (date );
16801742 if (!TIMESTAMP_NOT_FINITE (result ))
1743+ {
16811744 result += time ;
1745+ if (!IS_VALID_TIMESTAMP (result ))
1746+ ereport (ERROR ,
1747+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
1748+ errmsg ("timestamp out of range" )));
1749+ }
16821750
16831751 PG_RETURN_TIMESTAMP (result );
16841752}
@@ -2550,11 +2618,29 @@ datetimetz_timestamptz(PG_FUNCTION_ARGS)
25502618 TIMESTAMP_NOEND (result );
25512619 else
25522620 {
2621+ /*
2622+ * Date's range is wider than timestamp's, so check for boundaries.
2623+ * Since dates have the same minimum values as timestamps, only upper
2624+ * boundary need be checked for overflow.
2625+ */
2626+ if (date >= (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE ))
2627+ ereport (ERROR ,
2628+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
2629+ errmsg ("date out of range for timestamp" )));
25532630#ifdef HAVE_INT64_TIMESTAMP
25542631 result = date * USECS_PER_DAY + time -> time + time -> zone * USECS_PER_SEC ;
25552632#else
25562633 result = date * (double ) SECS_PER_DAY + time -> time + time -> zone ;
25572634#endif
2635+
2636+ /*
2637+ * Since it is possible to go beyond allowed timestamptz range because
2638+ * of time zone, check for allowed timestamp range after adding tz.
2639+ */
2640+ if (!IS_VALID_TIMESTAMP (result ))
2641+ ereport (ERROR ,
2642+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
2643+ errmsg ("date out of range for timestamp" )));
25582644 }
25592645
25602646 PG_RETURN_TIMESTAMP (result );
0 commit comments