Index: timestamp.c =================================================================== RCS file: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v retrieving revision 1.179 diff -c -r1.179 timestamp.c *** timestamp.c 6 Jul 2007 04:15:59 -0000 1.179 --- timestamp.c 8 Jul 2007 19:45:04 -0000 *************** *** 3044,3050 **** if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 && timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0) { ! 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; --- 3044,3051 ---- if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 && timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0) { ! /* form the symbolic difference */ ! 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; *************** *** 3064,3069 **** --- 3065,3081 ---- tm->tm_year = -tm->tm_year; } + /* propagate any negative fields into the next higher field */ + while (fsec < 0) + { + #ifdef HAVE_INT64_TIMESTAMP + fsec += USECS_PER_SEC; + #else + fsec += 1.0; + #endif + tm->tm_sec--; + } + while (tm->tm_sec < 0) { tm->tm_sec += SECS_PER_MINUTE; *************** *** 3082,3097 **** tm->tm_mday--; } ! while (tm->tm_mday < 0) { 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--; } } --- 3094,3130 ---- tm->tm_mday--; } ! /* ! * day-to-month conversion is tricky because variable. For each ! * decrement in tm_mon, we should adjust tm_mday by the length of ! * the next-to-last month(s) of the original time interval. ! * This corresponds to the notion that interval addition will add ! * months first, then days. ! */ ! if (tm->tm_mday < 0) { + int end_year; + int end_mon; + if (dt1 < dt2) { ! end_year = tm2->tm_year; ! end_mon = tm2->tm_mon; } else { ! end_year = tm1->tm_year; ! end_mon = tm1->tm_mon; ! } ! ! while (tm->tm_mday < 0) ! { ! if (--end_mon <= 0) ! { ! end_mon = MONTHS_PER_YEAR; ! end_year--; ! } ! tm->tm_mday += day_tab[isleap(end_year)][end_mon - 1]; tm->tm_mon--; } } *************** *** 3158,3163 **** --- 3191,3197 ---- if (timestamp2tm(dt1, &tz1, tm1, &fsec1, &tzn, NULL) == 0 && timestamp2tm(dt2, &tz2, tm2, &fsec2, &tzn, NULL) == 0) { + /* form the symbolic difference */ fsec = fsec1 - fsec2; tm->tm_sec = tm1->tm_sec - tm2->tm_sec; tm->tm_min = tm1->tm_min - tm2->tm_min; *************** *** 3178,3183 **** --- 3212,3228 ---- tm->tm_year = -tm->tm_year; } + /* propagate any negative fields into the next higher field */ + while (fsec < 0) + { + #ifdef HAVE_INT64_TIMESTAMP + fsec += USECS_PER_SEC; + #else + fsec += 1.0; + #endif + tm->tm_sec--; + } + while (tm->tm_sec < 0) { tm->tm_sec += SECS_PER_MINUTE; *************** *** 3196,3211 **** tm->tm_mday--; } ! while (tm->tm_mday < 0) { 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--; } } --- 3241,3277 ---- tm->tm_mday--; } ! /* ! * day-to-month conversion is tricky because variable. For each ! * decrement in tm_mon, we should adjust tm_mday by the length of ! * the next-to-last month(s) of the original time interval. ! * This corresponds to the notion that interval addition will add ! * months first, then days. ! */ ! if (tm->tm_mday < 0) { + int end_year; + int end_mon; + if (dt1 < dt2) { ! end_year = tm2->tm_year; ! end_mon = tm2->tm_mon; } else { ! end_year = tm1->tm_year; ! end_mon = tm1->tm_mon; ! } ! ! while (tm->tm_mday < 0) ! { ! if (--end_mon <= 0) ! { ! end_mon = MONTHS_PER_YEAR; ! end_year--; ! } ! tm->tm_mday += day_tab[isleap(end_year)][end_mon - 1]; tm->tm_mon--; } }