@@ -32,8 +32,16 @@ static int days_in_month[13] = { 31, 31, 28, 31, 30, 31, 30, 31, 3
3232static void do_range_limit (timelib_sll start , timelib_sll end , timelib_sll adj , timelib_sll * a , timelib_sll * b )
3333{
3434 if (* a < start ) {
35- * b -= (start - * a - 1 ) / adj + 1 ;
36- * a += adj * ((start - * a - 1 ) / adj + 1 );
35+ /* We calculate 'a + 1' first as 'start - *a - 1' causes an int64_t overflows if *a is
36+ * LONG_MIN. 'start' is 0 in this context, and '0 - LONG_MIN > LONG_MAX'. */
37+ timelib_sll a_plus_1 = * a + 1 ;
38+
39+ * b -= (start - a_plus_1 ) / adj + 1 ;
40+
41+ /* This code add the extra 'adj' separately, as otherwise this can overflow int64_t in
42+ * situations where *b is near LONG_MIN. */
43+ * a += adj * ((start - a_plus_1 ) / adj );
44+ * a += adj ;
3745 }
3846 if (* a >= end ) {
3947 * b += * a / adj ;
@@ -462,9 +470,15 @@ void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi)
462470 do_adjust_relative (time );
463471 do_adjust_special (time );
464472
465- time -> sse =
466- (timelib_epoch_days_from_time (time ) * SECS_PER_DAY ) +
467- timelib_hms_to_seconds (time -> h , time -> i , time -> s );
473+ /* You might be wondering, why this code does this in two steps. This is because
474+ * timelib_epoch_days_from_time(time) * SECS_PER_DAY with the lowest limit of
475+ * timelib_epoch_days_from_time() is less than the range of an int64_t. This then overflows. In
476+ * order to be able to still allow for any time in that day that only halfly fits in the int64_t
477+ * range, we add the time element first, which is always positive, and then twice half the value
478+ * of the earliest day as expressed as unix timestamp. */
479+ time -> sse = timelib_hms_to_seconds (time -> h , time -> i , time -> s );
480+ time -> sse += timelib_epoch_days_from_time (time ) * (SECS_PER_DAY / 2 );
481+ time -> sse += timelib_epoch_days_from_time (time ) * (SECS_PER_DAY / 2 );
468482
469483 // This modifies time->sse, if needed
470484 do_adjust_timezone (time , tzi );
0 commit comments