match historical behavior for tm_gmtoff member of struct tm
authorNatanael Copa <ncopa@alpinelinux.org>
Thu, 13 Aug 2015 15:28:39 +0000 (17:28 +0200)
committerRich Felker <dalias@aerifal.cx>
Fri, 14 Aug 2015 00:47:46 +0000 (00:47 +0000)
tm_gmtoff is a nonstandard field, but on historical systems which have
this field, it stores the offset of the local time zone from GMT or
UTC. this is the opposite of the POSIX extern long timezone object and
the offsets used in POSIX-form TZ strings, which represent the offset
from local time to UTC. previously we were storing these negated
offsets in tm_gmtoff too.

programs which only used this field indirectly via strftime were not
affected since strftime performed the negation for presentation.
however, some programs and libraries accesse tm_gmtoff directly and
were obtaining negated time zone offsets.

src/time/__tz.c
src/time/localtime_r.c
src/time/mktime.c
src/time/strftime.c

index 102c8bc764e1391720c3a341b8d3143c0d69bbee..8b84b9bd12cf9091268105357c0158357d6ab82f 100644 (file)
@@ -354,9 +354,9 @@ void __secs_to_zone(long long t, int local, int *isdst, long *offset, long *oppo
                size_t alt, i = scan_trans(t, local, &alt);
                if (i != -1) {
                        *isdst = types[6*i+4];
-                       *offset = -(int32_t)zi_read32(types+6*i);
+                       *offset = (int32_t)zi_read32(types+6*i);
                        *zonename = (const char *)abbrevs + types[6*i+5];
-                       if (oppoff) *oppoff = -(int32_t)zi_read32(types+6*alt);
+                       if (oppoff) *oppoff = (int32_t)zi_read32(types+6*alt);
                        UNLOCK(lock);
                        return;
                }
@@ -390,15 +390,15 @@ void __secs_to_zone(long long t, int local, int *isdst, long *offset, long *oppo
        }
 std:
        *isdst = 0;
-       *offset = __timezone;
-       if (oppoff) *oppoff = dst_off;
+       *offset = -__timezone;
+       if (oppoff) *oppoff = -dst_off;
        *zonename = __tzname[0];
        UNLOCK(lock);
        return;
 dst:
        *isdst = 1;
-       *offset = dst_off;
-       if (oppoff) *oppoff = __timezone;
+       *offset = -dst_off;
+       if (oppoff) *oppoff = -__timezone;
        *zonename = __tzname[1];
        UNLOCK(lock);
 }
index 1d43d9f4be41157b16fc8763ca06dbcc27058a8a..2e62c29fb2ac1bccaf5af0f045a5bbf5ffefc880 100644 (file)
@@ -11,7 +11,7 @@ struct tm *__localtime_r(const time_t *restrict t, struct tm *restrict tm)
                return 0;
        }
        __secs_to_zone(*t, 0, &tm->tm_isdst, &tm->__tm_gmtoff, 0, &tm->__tm_zone);
-       if (__secs_to_tm((long long)*t - tm->__tm_gmtoff, tm) < 0) {
+       if (__secs_to_tm((long long)*t + tm->__tm_gmtoff, tm) < 0) {
                errno = EOVERFLOW;
                return 0;
        }
index 0ab478029c3a2f9aded4555b7fc3decc0b201b0b..bad3f0765a089fd9f97e6f0d651b15fd19d949ed 100644 (file)
@@ -10,14 +10,14 @@ time_t mktime(struct tm *tm)
        __secs_to_zone(t, 1, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone);
 
        if (tm->tm_isdst>=0 && new.tm_isdst!=tm->tm_isdst)
-               t += opp - new.__tm_gmtoff;
+               t -= opp - new.__tm_gmtoff;
 
-       t += new.__tm_gmtoff;
+       t -= new.__tm_gmtoff;
        if ((time_t)t != t) goto error;
 
        __secs_to_zone(t, 0, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone);
 
-       if (__secs_to_tm(t - new.__tm_gmtoff, &new) < 0) goto error;
+       if (__secs_to_tm(t + new.__tm_gmtoff, &new) < 0) goto error;
 
        *tm = new;
        return t;
index 794fbe1d70727a057a99a4d709bd71a2a66b8a99..e945bb7d1631cae5d3f181a0874d1b1414f4cc49 100644 (file)
@@ -126,7 +126,7 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *
                fmt = "%H:%M";
                goto recu_strftime;
        case 's':
-               val = __tm_to_secs(tm) + tm->__tm_gmtoff;
+               val = __tm_to_secs(tm) - tm->__tm_gmtoff;
                width = 1;
                goto number;
        case 'S':
@@ -178,7 +178,7 @@ const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *
                        return "";
                }
                *l = snprintf(*s, sizeof *s, "%+.2d%.2d",
-                       (-tm->__tm_gmtoff)/3600,
+                       (tm->__tm_gmtoff)/3600,
                        abs(tm->__tm_gmtoff%3600)/60);
                return *s;
        case 'Z':