88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.155 2005/10/15 02:49:30 momjian Exp $
11+ * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.156 2005/10/25 17:13:07 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -1224,8 +1224,10 @@ interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
12241224{
12251225#ifdef HAVE_INT64_TIMESTAMP
12261226int64 time ;
1227+ int64 tfrac ;
12271228#else
12281229double time ;
1230+ double tfrac ;
12291231#endif
12301232
12311233tm -> tm_year = span .month /MONTHS_PER_YEAR ;
@@ -1234,17 +1236,23 @@ interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
12341236time = span .time ;
12351237
12361238#ifdef HAVE_INT64_TIMESTAMP
1237- tm -> tm_hour = time /USECS_PER_HOUR ;
1238- time -= tm -> tm_hour * USECS_PER_HOUR ;
1239- tm -> tm_min = time /USECS_PER_MINUTE ;
1240- time -= tm -> tm_min * USECS_PER_MINUTE ;
1241- tm -> tm_sec = time /USECS_PER_SEC ;
1242- * fsec = time - (tm -> tm_sec * USECS_PER_SEC );
1239+ tfrac = time /USECS_PER_HOUR ;
1240+ time -= tfrac * USECS_PER_HOUR ;
1241+ tm -> tm_hour = tfrac ;/* could overflow ... */
1242+ tfrac = time /USECS_PER_MINUTE ;
1243+ time -= tfrac * USECS_PER_MINUTE ;
1244+ tm -> tm_min = tfrac ;
1245+ tfrac = time /USECS_PER_SEC ;
1246+ * fsec = time - (tfrac * USECS_PER_SEC );
1247+ tm -> tm_sec = tfrac ;
12431248#else
12441249recalc :
1245- TMODULO (time ,tm -> tm_hour , (double )SECS_PER_HOUR );
1246- TMODULO (time ,tm -> tm_min , (double )SECS_PER_MINUTE );
1247- TMODULO (time ,tm -> tm_sec ,1.0 );
1250+ TMODULO (time ,tfrac , (double )SECS_PER_HOUR );
1251+ tm -> tm_hour = tfrac ;/* could overflow ... */
1252+ TMODULO (time ,tfrac , (double )SECS_PER_MINUTE );
1253+ tm -> tm_min = tfrac ;
1254+ TMODULO (time ,tfrac ,1.0 );
1255+ tm -> tm_sec = tfrac ;
12481256time = TSROUND (time );
12491257/* roundoff may need to propagate to higher-order fields */
12501258if (time >=1.0 )
@@ -1935,55 +1943,68 @@ timestamp_mi(PG_FUNCTION_ARGS)
19351943result -> month = 0 ;
19361944result -> day = 0 ;
19371945
1946+ /* this is wrong, but removing it breaks a lot of regression tests */
19381947result = DatumGetIntervalP (DirectFunctionCall1 (interval_justify_hours ,
19391948IntervalPGetDatum (result )));
1949+
19401950PG_RETURN_INTERVAL_P (result );
19411951}
19421952
1943- /*interval_justify_hours()
1944- *Adjust interval so 'time' contains less than a whole day, and
1945- *'day' contains an integral number of days.This is useful for
1953+ /*
1954+ *interval_justify_hours()
1955+ *
1956+ *Adjust interval so 'time' contains less than a whole day, adding
1957+ *the excess to 'day'. This is useful for
19461958 *situations (such as non-TZ) where '1 day' = '24 hours' is valid,
1947- *e.g. interval subtraction and division. The SQL standard requires
1948- *such conversion in these cases, but not the conversion of days to months.
1959+ *e.g. interval subtraction and division.
19491960 */
19501961Datum
19511962interval_justify_hours (PG_FUNCTION_ARGS )
19521963{
19531964Interval * span = PG_GETARG_INTERVAL_P (0 );
19541965Interval * result ;
1966+ #ifdef HAVE_INT64_TIMESTAMP
1967+ int64 wholeday ;
1968+ #else
1969+ double wholeday ;
1970+ #endif
19551971
19561972result = (Interval * )palloc (sizeof (Interval ));
19571973result -> month = span -> month ;
1974+ result -> day = span -> day ;
19581975result -> time = span -> time ;
19591976
19601977#ifdef HAVE_INT64_TIMESTAMP
1961- result -> time += span -> day * USECS_PER_DAY ;
1962- TMODULO (result -> time ,result -> day ,USECS_PER_DAY );
1978+ TMODULO (result -> time ,wholeday ,USECS_PER_DAY );
19631979#else
1964- result -> time += span -> day * (double )SECS_PER_DAY ;
1965- TMODULO (result -> time ,result -> day , (double )SECS_PER_DAY );
1980+ TMODULO (result -> time ,wholeday , (double )SECS_PER_DAY );
19661981#endif
1982+ result -> day += wholeday ;/* could overflow... */
19671983
19681984PG_RETURN_INTERVAL_P (result );
19691985}
19701986
1971- /*interval_justify_days()
1972- *Adjust interval so 'time' contains less than 30 days, and
1973- *adds as months.
1987+ /*
1988+ *interval_justify_days()
1989+ *
1990+ *Adjust interval so 'day' contains less than 30 days, adding
1991+ *the excess to 'month'.
19741992 */
19751993Datum
19761994interval_justify_days (PG_FUNCTION_ARGS )
19771995{
19781996Interval * span = PG_GETARG_INTERVAL_P (0 );
19791997Interval * result ;
1998+ int32 wholemonth ;
19801999
19812000result = (Interval * )palloc (sizeof (Interval ));
2001+ result -> month = span -> month ;
19822002result -> day = span -> day ;
19832003result -> time = span -> time ;
19842004
1985- result -> day += span -> month * DAYS_PER_MONTH ;
1986- TMODULO (result -> day ,result -> month ,DAYS_PER_MONTH );
2005+ wholemonth = result -> day /DAYS_PER_MONTH ;
2006+ result -> day -= wholemonth * DAYS_PER_MONTH ;
2007+ result -> month += wholemonth ;
19872008
19882009PG_RETURN_INTERVAL_P (result );
19892010}
@@ -2282,28 +2303,35 @@ interval_mul(PG_FUNCTION_ARGS)
22822303
22832304result = (Interval * )palloc (sizeof (Interval ));
22842305
2285- result -> month = span -> month * factor ;
2286- result -> day = span -> day * factor ;
2306+ month_remainder = span -> month * factor ;
2307+ day_remainder = span -> day * factor ;
2308+ result -> month = (int32 )month_remainder ;
2309+ result -> day = (int32 )day_remainder ;
2310+ month_remainder -= result -> month ;
2311+ day_remainder -= result -> day ;
22872312
2288- /* Compute remainders */
2289- month_remainder = span -> month * factor - result -> month ;
2290- day_remainder = span -> day * factor - result -> day ;
2313+ /*
2314+ * The above correctly handles the whole-number part of the month and
2315+ * day products, but we have to do something with any fractional part
2316+ * resulting when the factor is nonintegral. We cascade the fractions
2317+ * down to lower units using the conversion factors DAYS_PER_MONTH and
2318+ * SECS_PER_DAY. Note we do NOT cascade up, since we are not forced to
2319+ * do so by the representation. The user can choose to cascade up later,
2320+ * using justify_hours and/or justify_days.
2321+ */
22912322
2292- /* Cascade fractions to lower units */
22932323/* fractional months full days into days */
22942324month_remainder_days = month_remainder * DAYS_PER_MONTH ;
2295- result -> day += month_remainder_days ;
2325+ result -> day += ( int32 ) month_remainder_days ;
22962326/* fractional months partial days into time */
2297- day_remainder += month_remainder_days - (int )month_remainder_days ;
2327+ day_remainder += month_remainder_days - (int32 )month_remainder_days ;
22982328
22992329#ifdef HAVE_INT64_TIMESTAMP
23002330result -> time = rint (span -> time * factor + day_remainder * USECS_PER_DAY );
23012331#else
23022332result -> time = span -> time * factor + day_remainder * SECS_PER_DAY ;
23032333#endif
23042334
2305- result = DatumGetIntervalP (DirectFunctionCall1 (interval_justify_hours ,
2306- IntervalPGetDatum (result )));
23072335PG_RETURN_INTERVAL_P (result );
23082336}
23092337
@@ -2334,29 +2362,29 @@ interval_div(PG_FUNCTION_ARGS)
23342362(errcode (ERRCODE_DIVISION_BY_ZERO ),
23352363errmsg ("division by zero" )));
23362364
2337- result -> month = span -> month /factor ;
2338- result -> day = span -> day /factor ;
2339- result -> time = span -> time /factor ;
2365+ month_remainder = span -> month /factor ;
2366+ day_remainder = span -> day /factor ;
2367+ result -> month = (int32 )month_remainder ;
2368+ result -> day = (int32 )day_remainder ;
2369+ month_remainder -= result -> month ;
2370+ day_remainder -= result -> day ;
23402371
2341- /* Compute remainders */
2342- month_remainder = span -> month / factor - result -> month ;
2343- day_remainder = span -> day / factor - result -> day ;
2372+ /*
2373+ * Handle any fractional parts the same way as in interval_mul.
2374+ */
23442375
2345- /* Cascade fractions to lower units */
23462376/* fractional months full days into days */
23472377month_remainder_days = month_remainder * DAYS_PER_MONTH ;
2348- result -> day += month_remainder_days ;
2378+ result -> day += ( int32 ) month_remainder_days ;
23492379/* fractional months partial days into time */
2350- day_remainder += month_remainder_days - (int )month_remainder_days ;
2380+ day_remainder += month_remainder_days - (int32 )month_remainder_days ;
23512381
23522382#ifdef HAVE_INT64_TIMESTAMP
2353- result -> time + =rint (day_remainder * USECS_PER_DAY );
2383+ result -> time = rint (span -> time / factor + day_remainder * USECS_PER_DAY );
23542384#else
2355- result -> time += day_remainder * SECS_PER_DAY ;
2385+ result -> time = span -> time / factor + day_remainder * SECS_PER_DAY ;
23562386#endif
23572387
2358- result = DatumGetIntervalP (DirectFunctionCall1 (interval_justify_hours ,
2359- IntervalPGetDatum (result )));
23602388PG_RETURN_INTERVAL_P (result );
23612389}
23622390