summaryrefslogtreecommitdiff
path: root/time/mktime.c
diff options
context:
space:
mode:
Diffstat (limited to 'time/mktime.c')
-rw-r--r--time/mktime.c18
1 files changed, 13 insertions, 5 deletions
diff --git a/time/mktime.c b/time/mktime.c
index c3c539526a..7fa5ccb23a 100644
--- a/time/mktime.c
+++ b/time/mktime.c
@@ -133,11 +133,19 @@ ydhms_tm_diff (year, yday, hour, min, sec, tp)
int year, yday, hour, min, sec;
const struct tm *tp;
{
- time_t ay = year + (time_t) (TM_YEAR_BASE - 1);
- time_t by = tp->tm_year + (time_t) (TM_YEAR_BASE - 1);
- time_t intervening_leap_days =
- (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400);
- time_t years = ay - by;
+ /* Compute intervening leap days correctly even if year is negative.
+ Take care to avoid int overflow. time_t overflow is OK, since
+ only the low order bits of the correct time_t answer are needed.
+ Don't convert to time_t until after all divisions are done, since
+ time_t might be unsigned. */
+ int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
+ int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
+ int a100 = a4 / 25 - (a4 % 25 < 0);
+ int b100 = b4 / 25 - (b4 % 25 < 0);
+ int a400 = a100 >> 2;
+ int b400 = b100 >> 2;
+ int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
+ time_t years = year - (time_t) tp->tm_year;
time_t days = (365 * years + intervening_leap_days
+ (yday - tp->tm_yday));
return (60 * (60 * (24 * days + (hour - tp->tm_hour))