> Yet another idea for an alternative. Compile by entering 'sh xtimegm.c' > and then run ./xtimegm > > Simple cases seems to work. Dst change may (or then may not) give one > hour difference to the expected. The test "coverage" could be easily > expanded to that ;) > > Hmm, I also found this: > http://lists.gnu.org/archive/html/bug-gnulib/2003-09/msg00004.html > which does 2 mktime's and one gmtime_r (without allocating tg!)... > Pick any of these 3 -- or something different (e.g. less NIH if there is) Of these two, I would probably lean slightly toward the latter, in that it relies more on libc for the time zone handling logic. But in general, this seems to me like a case where an explicit implementation like mine is less prone to failure, because it doesn't need to worry about time zones at all. The other approaches rely on letting libc do all the hard work of time zone manipulation, and then reading the tea leaves to find a way to undo it. I would guess that if some change in the time standards is going to break one of these implementations, it's going to be some new time zone specification, not a change in the number of days in a month. :) For what it's worth, I used the attached program to test my implementation, and it passed. Blake
#include <time.h> static int leapyear(int year) { return ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)); } /* * This is a simple implementation of timegm() which does what is needed * by parse-time-string.c -- just turns the "struct tm" into a GMT time_t. * It does not normalize any of the fields of the "struct tm", nor does * it set tm_wday or tm_yday. */ static time_t timegm(struct tm *tm) { int monthlen[2][12] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, }; int year, month, days; days = 365 * (tm->tm_year - 70); for (year = 70; year < tm->tm_year; year++) { if (leapyear(1900 + year)) { days++; } } for (month = 0; month < tm->tm_mon; month++) { days += monthlen[leapyear(1900 + year)][month]; } days += tm->tm_mday - 1; return ((((days * 24) + tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec); } #include <stdio.h> #include <stdlib.h> #include <unistd.h> void tm_test(int niter) { const int monthlen[2][12] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, }; int i; struct tm tm, *tmp; time_t t; for (i = 0; i < niter; i++) { tm.tm_year = (lrand48() % 67) + 70; tm.tm_mon = lrand48() % 12; do { t = (lrand48() % 31) + 1; } while (t > monthlen[leapyear(1900 + tm.tm_year)][tm.tm_mon]); tm.tm_mday = t; tm.tm_hour = lrand48() % 24; tm.tm_min = lrand48() % 60; tm.tm_sec = lrand48() % 60; t = timegm(&tm); tmp = gmtime(&t); if (tmp->tm_sec != tm.tm_sec || tmp->tm_min != tm.tm_min || tmp->tm_hour != tm.tm_hour || tmp->tm_mday != tm.tm_mday || tmp->tm_mon != tm.tm_mon || tmp->tm_year != tm.tm_year) { printf("%4d-%02d-%02d %02d:%02d:%02d -> " "%4d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); } } } void time_test(int niter) { int i; time_t st, et; struct tm *tmp; for (i = 0; i < niter; i++) { st = (time_t)lrand48(); tmp = gmtime(&st); et = timegm(tmp); if (st != et) { printf("%d -> %d (%4d-%02d-%02d %02d:%02d:%02d)\n", st, et, tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); } } } int main(void) { const int niter = 10000000; srand48(getpid()); tm_test(niter); time_test(niter); return (0); }