@@ -29,8 +29,17 @@ struct
{ 919973892L, "EST+5EDT,M4.1.0/2,M10.5.0/2",
"1999/02/25 15:18:12 dst=0 zone=EST" },
- /* Test Atlantic Standard / Atlantic Daylight transitions in 2024 and 2038,
- with explicit DST rule. */
+ /* Test Atlantic Standard / Atlantic Daylight transitions in 1966,
+ 2024 and 2038, with explicit DST rule. See Bug#32395 for why
+ 1966 is being tested. */
+ { -120074401, "AST4ADT,M3.2.0,M11.1.0",
+ "1966/03/13 01:59:59 dst=0 zone=AST" },
+ { -120074400, "AST4ADT,M3.2.0/2,M11.1.0/2",
+ "1966/03/13 03:00:00 dst=1 zone=ADT" },
+ { -99514801, "AST4ADT,M3.2.0/02:00,M11.1.0/02:00",
+ "1966/11/06 01:59:59 dst=1 zone=ADT" },
+ { -99514800, "AST4ADT,M3.2.0/02:00:00,M11.1.0/02:00:00",
+ "1966/11/06 01:00:00 dst=0 zone=AST" },
{ 1710050399, "AST4ADT,M3.2.0,M11.1.0",
"2024/03/10 01:59:59 dst=0 zone=AST" },
{ 1710050400, "AST4ADT,M3.2.0/2,M11.1.0/2",
@@ -56,15 +56,15 @@ typedef struct
/* We cache the computed time of change for a
given year so we don't have to recompute it. */
__time64_t change; /* When to change to this zone. */
- int computed_for; /* Year that CHANGE is computed for.
- If INT_MIN, CHANGE is unspecified. */
+ long long int computed_for; /* Year that CHANGE is computed for.
+ If LLONG_MIN, CHANGE is unspecified. */
} tz_rule;
/* tz_rules[0] is standard, tz_rules[1] is daylight. */
static tz_rule tz_rules[2];
-static void compute_change (tz_rule *rule, int year) __THROW;
+static void compute_change (tz_rule *rule, long long int year) __THROW;
static void tzset_internal (int always);
/* List of buffers containing time zone strings. */
@@ -341,7 +341,7 @@ parse_rule (const char **tzp, int whichrule)
secs = 2 * 60 * 60;
tzr->secs = secs;
- tzr->computed_for = INT_MIN;
+ tzr->computed_for = LLONG_MIN;
*tzp = tz;
return true;
}
@@ -442,7 +442,7 @@ tzset_internal (int always)
when the change described by RULE will occur and
put it in RULE->change, saving YEAR in RULE->computed_for. */
static void
-compute_change (tz_rule *rule, int year)
+compute_change (tz_rule *rule, long long int year)
{
__time64_t t;
@@ -450,17 +450,15 @@ compute_change (tz_rule *rule, int year)
return;
/* First set T to January 1st, 0:00:00 GMT in YEAR. */
- if (year > 1970)
- t = ((year - 1970) * 365
- + /* Compute the number of leapdays between 1970 and YEAR
- (exclusive). There is a leapday every 4th year ... */
- + ((year - 1) / 4 - 1970 / 4)
- /* ... except every 100th year ... */
- - ((year - 1) / 100 - 1970 / 100)
- /* ... but still every 400th year. */
- + ((year - 1) / 400 - 1970 / 400)) * SECSPERDAY;
- else
- t = 0;
+ t = (((year - 1970) * 365
+ /* Compute the number of leap days between 1970 and YEAR (exclusive).
+ There is a leap day every 4th year ... */
+ + (((year - 1) >> 2) - 1970 / 4)
+ /* ... except every 100th year ... */
+ - ((year - 1) / 100 - ((year - 1) % 100 < 0) - 1970 / 100)
+ /* ... but still every 400th year. */
+ + ((year - 1) / 400 - ((year - 1) % 400 < 0) - 1970 / 400))
+ * SECSPERDAY);
switch (rule->type)
{
@@ -484,7 +482,7 @@ compute_change (tz_rule *rule, int year)
/* Mm.n.d - Nth "Dth day" of month M. */
{
unsigned int i;
- int d, m1, yy0, yy1, yy2, dow;
+ int d, m1;
const unsigned short int *myday =
&__mon_yday[__isleap (year)][rule->m];
@@ -493,10 +491,12 @@ compute_change (tz_rule *rule, int year)
/* Use Zeller's Congruence to get day-of-week of first day of month. */
m1 = (rule->m + 9) % 12 + 1;
- yy0 = (rule->m <= 2) ? (year - 1) : year;
- yy1 = yy0 / 100;
- yy2 = yy0 % 100;
- dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+ long long int yy0 = year - (rule->m <= 2);
+ int yy1 = yy0 / 100 - (yy0 % 100 < 0);
+ int yy2 = yy0 % 100 + (yy0 % 100 < 0 ? 100 : 0);
+ int dow = (((13 * m1 - 1) / 5 + 1 + yy2 + (yy2 >> 2)
+ + (yy1 >> 2) - 2 * yy1)
+ % 7);
if (dow < 0)
dow += 7;
@@ -530,8 +530,11 @@ compute_change (tz_rule *rule, int year)
void
__tz_compute (__time64_t timer, struct tm *tm)
{
- compute_change (&tz_rules[0], 1900 + tm->tm_year);
- compute_change (&tz_rules[1], 1900 + tm->tm_year);
+ _Static_assert (INT_MAX <= LLONG_MAX - 1900,
+ "long long int must be wider than int");
+ long long int year = 1900LL + tm->tm_year;
+ compute_change (&tz_rules[0], year);
+ compute_change (&tz_rules[1], year);
/* Distinguish between northern and southern hemisphere.
For the latter the daylight saving time ends in the next year. */
@@ -574,6 +577,11 @@ weak_alias (__tzset, tzset)
struct tm *
__tz_convert (__time64_t timer, int use_localtime, struct tm *tp)
{
+ /* Check that __time64_t is wider than int,
+ so that __tz_convert cannot fail due to integer overflow. */
+ _Static_assert (sizeof (int) < sizeof (__time64_t),
+ "__time64_t must be wider than int");
+
int leap_correction;
bool leap_extra_sec;