commit c4f2035d213783953294005a7b69ac1e96294d12 Author: Peter J. Holzer Date: Sat Jun 4 11:43:12 2016 +0200 Compute offset of local timezone from UTC for every day since 1880 diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..573253b --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,2 @@ +CFLAGS = -std=c11 -Wall -ftrapv +all: timezone-history diff --git a/timezone-history.c b/timezone-history.c new file mode 100644 index 0000000..646ae6e --- /dev/null +++ b/timezone-history.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include + +time_t start_time(void) { + // return the start of our time range + // we want 12:00 local time on January 1st of the first year >= 1880 + // we can represent (1880, because time zones based on GMT were + // enacted in 1885, so we start a little bit earlier if we can) + for (int y = 1880; y < 2000; y++) { + struct tm tm; + tm.tm_year = y - 1900; + tm.tm_mon = 0; + tm.tm_mday = 1; + tm.tm_hour = 12; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + + time_t t = mktime(&tm); + if (t != (time_t)-1) { + return t; + } + } + assert(0); // no date before 2000 representable? mktime is broken +} + +time_t end_time(void) { + // return the end of our time range + // 12:00 local time on dec. 31 of the current year seems ok. + // we might want to extend that a few years into the future, but + // it's hard to say how much: Sometimes DST changes are only + // published weeks before they happen, so even the end of the year + // is a bit optimistic. + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + + struct tm tm; + tm.tm_year = tmp->tm_year; + tm.tm_mon = 11; + tm.tm_mday = 31; + tm.tm_hour = 12; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + + t = mktime(&tm); + if (t != (time_t)-1) { + return t; + } + assert(0); // end of current year not representable? mktime is broken +} + +int_least32_t diff(struct tm utm, struct tm ltm) { + int_least32_t d; + // local time should be close to noon, timezones are somwhere in + // the +/- 14 hours range, so the UTC date can differ by at most one + // date. Therefore we just check whether the UTC date is earlier or + // later than the local date. + if (ltm.tm_year > utm.tm_year) { + d = 86400; + } else if (ltm.tm_year < utm.tm_year) { + d = -86400; + } else { + if (ltm.tm_mon > utm.tm_mon) { + d = 86400; + } else if (ltm.tm_mon < utm.tm_mon) { + d = -86400; + } else { + if (ltm.tm_mday > utm.tm_mday) { + d = 86400; + } else if (ltm.tm_mday < utm.tm_mday) { + d = -86400; + } else { + d = 0; + } + } + } + d += (ltm.tm_hour - utm.tm_hour) * 3600 + + (ltm.tm_min - utm.tm_min) * 60 + + (ltm.tm_sec - utm.tm_sec); + return d; +} + +int main(void) { + time_t start = start_time(); + time_t end = end_time(); + for (time_t t = start; t <= end; t += 24*3600) { + struct tm utm = *(gmtime(&t)); + struct tm ltm = *(localtime(&t)); + int_least32_t d = diff(utm, ltm); + printf("%.0f %.0f\n", (double)t, (double)d); + } +}