> If you're willing to compile and run the attached simple C program,
> please let me know its output and your platform's details - ideally
> off-list - I'll give a summary in a few days' time.

Thanks to those who responded, I now know:
 * that all the platforms below do the same thing if tm_isdst is 0 or 1 (Yay !)
 * with .tm_isdst = -1, there is diversity of handling.

When tm_isdst is 0 or 1, in the fall back it gets preserved;
in the spring forward, it gets flipped and tm_hour is changed
in the way it usually would for such a flip.

OSX 10.10, Xcode 6.02
 * varies within the hour (see below)
 * at half past: handled as if you'd claimed tm_isdst = 0 (see above)

MacBook, Darwin 15.0.0
Ubuntu 12.04, glibc 2.15
Debian/stretch, glibc 2.19
 * clear tm_isdst and move an hour earlier in spring
 * set tm_isdst and preserve hour in autumn

MinGW
MSVC 2015
 * set tm_isdst and move an hour later in spring
 * clear tm_isdst and preserve hour in autumn.

My initial test was, it turns out, naive.  Apparently some platforms
vary handling of -1 across the hour.  I also bit the bullet and worked
out how to auto-detect DST transitions.  One response indicates
asctime_r() isn't portable.  So I attach a new version of mktime.c, that
needs no manual configuration and is, I hope, a little more portable.  I
note that setting the TZ environment variable (on platforms that honour
it, at least) can be used to discover what your O/S thinks happens in
other time-zones than your local one.

As before, if anyone feels inclined to give this a whirl, I'd again
appreciate any responses, along with descriptions of the system they
come from (e.g. uname -a output).

    Eddy
/* Report on current Time-Zone's DST transitions, if any. */
#include <time.h>
#include <stdio.h>
#include <stdlib.h>

static const struct tm summer = { 0,0,0, 21, 5,115, 0 };
static const struct tm winter = { 0,0,0, 21,11,115, 0 };

/* Half-way between two struct tm, ignoring their .tm_isdst: */
static struct tm centre(struct tm lo, struct tm hi) {
    /* Round up, as lo is typically hh:59:59 */
    lo.tm_sec = (lo.tm_sec + hi.tm_sec + 1) / 2;

    lo.tm_min = lo.tm_min + hi.tm_min;
    if (lo.tm_min % 2)
        lo.tm_sec += 30;
    lo.tm_min /= 2;

    while (lo.tm_sec >= 60) {
        lo.tm_sec -= 60;
        lo.tm_min += 1;
    }

    lo.tm_min += 30 * (hi.tm_hour - lo.tm_hour);
    while (lo.tm_min > 60) {
        lo.tm_min -= 60;
        lo.tm_hour += 1;
    }
    /* Axiom: same day, so lo.tm_hour <= hi.tm_hour < 24 still. */
    return lo;
}

/* Find the transitions, if any. */
static int hasDST(struct tm *spring, struct tm *autumn) {
    struct tm *output[2] = { spring, autumn };
    struct tm dst = summer; /* When we might have daylight-saving time */
    struct tm std = winter; /* When we definitely have standard time */
    std.tm_year -= 1; /* bracket the first half of 2015 */
    dst.tm_isdst = std.tm_isdst = -1;

    time_t tdst = mktime(&dst), tstd = mktime(&std);
    if (dst.tm_isdst == std.tm_isdst) return 0; /* No transition */
    int south = std.tm_isdst; /* Southern hemisphere */
    if (south) { /* Swap dst<->std, tdst<->tstd */
        tdst ^= tstd; tstd ^= tdst; tdst ^= tstd;
        dst = std;
        std = summer; /* misnamed: June is winter */
    }
    for (int i = 0; i < 2; i++) {
        if (i) { /* bracket the second half of 2015 */
            if (south) {
                std = summer;
                dst = winter;
            } else {
                std = winter;
                dst = summer;
            }
            tstd = mktime(&std);
            tdst = mktime(&dst);
            if (std.tm_isdst || !dst.tm_isdst) {
                fputs("DST-ness changed this year !\n", stderr);
                exit(1);
            }
        } /* else: initialization taken care of before loop. */

        struct tm mid, *cut;
        time_t mean = (tdst + tstd) / 2;
        while (mean != tdst && mean != tstd &&
               0 != (cut = localtime(&mean))) {
            mid = *cut;
            if (mid.tm_isdst == -1) {
                fprintf(stderr, "Indeterminate DST-ness after localtime(%s)\n",
                        asctime(&mid));
                exit(1);
            } else if (mid.tm_isdst) {
                dst = mid;
                tdst = mean;
            } else {
                std = mid;
                tstd = mean;
            }
            /* Compute new mean */
            mean = (tdst + tstd) / 2;
        }
        if (cut == 0) {
            perror("Failed localtime");
            fprintf(stderr, "at t = %ld\n", (long)mean);
            exit(1);
        }

        /* Post-process to get middle of the transition: */
        if (std.tm_isdst || !dst.tm_isdst)
            fputs("Failed chop :-(\n", stderr);
        else if (std.tm_mday != dst.tm_mday ||
                 std.tm_year != dst.tm_year || std.tm_mon != dst.tm_mon ||
                 std.tm_wday != dst.tm_wday || std.tm_yday != dst.tm_yday)
            fprintf(stderr,
                    "DST transition straddles day; handling clumsily\n"
                    "Standard: %s\n"
                    "     DST: %s\n", asctime(&std), asctime(&dst));

        else if (std.tm_hour < dst.tm_hour)
            mid = centre(std, dst);
        else if (dst.tm_hour < std.tm_hour)
            mid = centre(dst, std);
        else if (std.tm_min < dst.tm_min)
            mid = centre(std, dst);
        else if (dst.tm_min < std.tm_min ||
                 dst.tm_sec < std.tm_sec)
            mid = centre(dst, std);
        else
            mid = centre(std, dst);

        mid.tm_isdst = -1;
        /* Save to correct season: */
        *output[i ^ south] = mid;
    }
    return 1;
}

static void report(time_t got) {
    if (got == (time_t) -1) puts("Rejected.");
    else printf("Accepted: %ld\n", (long) got);
}

static int get_dst(const struct tm *when, int minute) {
    struct tm probe = *when;
    probe.tm_isdst = -1;
    probe.tm_min = minute;
    return (mktime(&probe) < 0) ? -1 : probe.tm_isdst;
}

static int show(const struct tm *when, int dst, int min, const char *claim) {
    struct tm test = *when;
    test.tm_isdst = dst;
    test.tm_min = min;
    report(mktime(&test));
    printf("%s (-> %d): %s", claim, test.tm_isdst, asctime(&test));
    return test.tm_isdst;
}

static void study(const struct tm *when) {
    printf("Initial: %s\n", asctime(when));

    const int got = show(when, -1, when->tm_min, "Ignorant of DST");
    const int early = get_dst(when, 1), later = get_dst(when, 59);
    if (early != later) { // Find the transition
        int lo = (early == got) ? when->tm_min : 1;
        int hi = (later == got) ? when->tm_min : 59;
        while (lo + 1 < hi) {
            int mid = (lo + hi) / 2;
            int here = get_dst(when, mid);
            if (here == early) lo = mid;
            else if (here == later) hi = mid;
            else {
                fprintf(stderr, "Three .tm_isdst values !\n"
                        " %d (@%d), %d (@%d), %d (@%d)\n",
                        early, lo, mid, here, later, hi);
                break;
            }
        }

        show(when, -1, lo, "Ignorant of DST");
        show(when, -1, hi, "Ignorant of DST");
    }
    show(when, 0, when->tm_min, "Claiming no DST");
    puts(""); /* Blank line */
    show(when, 1, when->tm_min, "Claiming DST");
    puts(""); /* Blank line */
}

int main(void) {
    const char *TZ = getenv("TZ");
    printf("Studying DST transitions in %s\n",
           TZ ? TZ : "system default time-zone");
    tzset(); /* Just to be entirely sure it *does* get called. */

    struct tm spring, autumn;
    if (hasDST(&spring, &autumn)) {
        puts("\nTesting spring forward");
        study(&spring);
        puts("\n\nTesting fall backward");
        study(&autumn);
    } else
        puts("No DST.");

    return 0;
}
_______________________________________________
Development mailing list
Development@qt-project.org
http://lists.qt-project.org/mailman/listinfo/development

Reply via email to