> 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