mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-30 22:43:12 +03:00
[BZ #541]
2004-11-10 Paul Eggert <eggert@cs.ucla.edu> [BZ #541] * time/mktime.c (SHR): New macro, which is a portable substitute for >> that should work even on Crays. (TIME_T_MIDPOINT, ydhms_diff, __mktime_internal): Use it. Problem reported by Mark D. Baushke in <http://lists.gnu.org/archive/html/bug-gnulib/2004-11/msg00071.html>.
This commit is contained in:
@ -46,6 +46,21 @@
|
|||||||
# define mktime my_mktime
|
# define mktime my_mktime
|
||||||
#endif /* DEBUG */
|
#endif /* DEBUG */
|
||||||
|
|
||||||
|
/* Shift A right by B bits portably, by dividing A by 2**B and
|
||||||
|
truncating towards minus infinity. A and B should be free of side
|
||||||
|
effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
|
||||||
|
INT_BITS is the number of useful bits in an int. GNU code can
|
||||||
|
assume that INT_BITS is at least 32.
|
||||||
|
|
||||||
|
ISO C99 says that A >> B is implementation-defined if A < 0. Some
|
||||||
|
implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
|
||||||
|
right in the usual way when A < 0, so SHR falls back on division if
|
||||||
|
ordinary A >> B doesn't seem to be the usual signed shift. */
|
||||||
|
#define SHR(a, b) \
|
||||||
|
(-1 >> 1 == -1 \
|
||||||
|
? (a) >> (b) \
|
||||||
|
: (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
|
||||||
|
|
||||||
/* The extra casts work around common compiler bugs. */
|
/* The extra casts work around common compiler bugs. */
|
||||||
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
|
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
|
||||||
/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
|
/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
|
||||||
@ -60,14 +75,13 @@
|
|||||||
#ifndef TIME_T_MAX
|
#ifndef TIME_T_MAX
|
||||||
# define TIME_T_MAX TYPE_MAXIMUM (time_t)
|
# define TIME_T_MAX TYPE_MAXIMUM (time_t)
|
||||||
#endif
|
#endif
|
||||||
#define TIME_T_MIDPOINT (((TIME_T_MIN + TIME_T_MAX) >> 1) + 1)
|
#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1)
|
||||||
|
|
||||||
/* Verify a requirement at compile-time (unlike assert, which is runtime). */
|
/* Verify a requirement at compile-time (unlike assert, which is runtime). */
|
||||||
#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
|
#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
|
||||||
|
|
||||||
verify (time_t_is_integer, (time_t) 0.5 == 0);
|
verify (time_t_is_integer, (time_t) 0.5 == 0);
|
||||||
verify (twos_complement_arithmetic, -1 == ~1 + 1);
|
verify (twos_complement_arithmetic, -1 == ~1 + 1);
|
||||||
verify (right_shift_propagates_sign, -1 >> 1 == -1);
|
|
||||||
/* The code also assumes that signed integer overflow silently wraps
|
/* The code also assumes that signed integer overflow silently wraps
|
||||||
around, but this assumption can't be stated without causing a
|
around, but this assumption can't be stated without causing a
|
||||||
diagnostic on some hosts. */
|
diagnostic on some hosts. */
|
||||||
@ -133,12 +147,12 @@ ydhms_diff (long int year1, long int yday1, int hour1, int min1, int sec1,
|
|||||||
|
|
||||||
/* Compute intervening leap days correctly even if year is negative.
|
/* Compute intervening leap days correctly even if year is negative.
|
||||||
Take care to avoid integer overflow here. */
|
Take care to avoid integer overflow here. */
|
||||||
int a4 = (year1 >> 2) + (TM_YEAR_BASE >> 2) - ! (year1 & 3);
|
int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3);
|
||||||
int b4 = (year0 >> 2) + (TM_YEAR_BASE >> 2) - ! (year0 & 3);
|
int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3);
|
||||||
int a100 = a4 / 25 - (a4 % 25 < 0);
|
int a100 = a4 / 25 - (a4 % 25 < 0);
|
||||||
int b100 = b4 / 25 - (b4 % 25 < 0);
|
int b100 = b4 / 25 - (b4 % 25 < 0);
|
||||||
int a400 = a100 >> 2;
|
int a400 = SHR (a100, 2);
|
||||||
int b400 = b100 >> 2;
|
int b400 = SHR (b100, 2);
|
||||||
int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
|
int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
|
||||||
|
|
||||||
/* Compute the desired time in time_t precision. Overflow might
|
/* Compute the desired time in time_t precision. Overflow might
|
||||||
@ -322,14 +336,16 @@ __mktime_internal (struct tm *tp,
|
|||||||
int LOG2_YEARS_PER_BIENNIUM = 1;
|
int LOG2_YEARS_PER_BIENNIUM = 1;
|
||||||
|
|
||||||
int approx_requested_biennia =
|
int approx_requested_biennia =
|
||||||
((year_requested >> LOG2_YEARS_PER_BIENNIUM)
|
(SHR (year_requested, LOG2_YEARS_PER_BIENNIUM)
|
||||||
- ((EPOCH_YEAR - TM_YEAR_BASE) >> LOG2_YEARS_PER_BIENNIUM)
|
- SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)
|
||||||
+ (mday >> ALOG2_DAYS_PER_BIENNIUM)
|
+ SHR (mday, ALOG2_DAYS_PER_BIENNIUM)
|
||||||
+ (hour >> ALOG2_HOURS_PER_BIENNIUM)
|
+ SHR (hour, ALOG2_HOURS_PER_BIENNIUM)
|
||||||
+ (min >> ALOG2_MINUTES_PER_BIENNIUM)
|
+ SHR (min, ALOG2_MINUTES_PER_BIENNIUM)
|
||||||
+ (LEAP_SECONDS_POSSIBLE ? 0 : sec >> ALOG2_SECONDS_PER_BIENNIUM));
|
+ (LEAP_SECONDS_POSSIBLE
|
||||||
|
? 0
|
||||||
|
: SHR (sec, ALOG2_SECONDS_PER_BIENNIUM)));
|
||||||
|
|
||||||
int approx_biennia = t0 >> ALOG2_SECONDS_PER_BIENNIUM;
|
int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM);
|
||||||
int diff = approx_biennia - approx_requested_biennia;
|
int diff = approx_biennia - approx_requested_biennia;
|
||||||
int abs_diff = diff < 0 ? - diff : diff;
|
int abs_diff = diff < 0 ? - diff : diff;
|
||||||
|
|
||||||
@ -347,7 +363,7 @@ __mktime_internal (struct tm *tp,
|
|||||||
/* Overflow occurred. Try repairing it; this might work if
|
/* Overflow occurred. Try repairing it; this might work if
|
||||||
the time zone offset is enough to undo the overflow. */
|
the time zone offset is enough to undo the overflow. */
|
||||||
time_t repaired_t0 = -1 - t0;
|
time_t repaired_t0 = -1 - t0;
|
||||||
approx_biennia = repaired_t0 >> ALOG2_SECONDS_PER_BIENNIUM;
|
approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);
|
||||||
diff = approx_biennia - approx_requested_biennia;
|
diff = approx_biennia - approx_requested_biennia;
|
||||||
abs_diff = diff < 0 ? - diff : diff;
|
abs_diff = diff < 0 ? - diff : diff;
|
||||||
if (overflow_threshold < abs_diff)
|
if (overflow_threshold < abs_diff)
|
||||||
|
Reference in New Issue
Block a user