t for the epoch in pure ISO C?

Using nothing but the C standard library (plain ISO C, no POSIX, and thus no assumption that time_t is represented in "seconds since the epoch"), what is the simplest way to get a time_t value corresponding to 01 Jan 1970 00:00:00 UTC ?

The UTC part is the key; otherwise, just using mktime on a properly initialized struct tm would trivially solve the problem.

Alternatively (this is actually the "point" of the question), how does one portably determine the number of POSIX seconds between a given time_t value, eg the current time obtained via time(0) , and the epoch? By "POSIX seconds" I mean the definition used in POSIX's "Seconds Since the Epoch" which does not use leap seconds. If this sounds too complicated, just go back to the question as originally stated in the first paragraph and assume the epoch is representable in time_t .


Here's an entry for a way to do it, "simplest" if nobody beats it:

  • call mktime on a struct tm for 02 Jan 1970 00:00:00
  • call mktime on a struct tm for 31 Dec 1969 00:00:00 . This could reasonably return -1, in which case treat it as 0.
  • Binary search between the two for a value of time_t that, when passed to gmtime , results in 01 Jan 1970 00:00:00
  • Assumes that no local time is ever more than 24 hours different from UTC, which I'm pretty sure is a fact. We could widen the boundaries if necessary, in extremis we could search between 0 and time(0) .

    The binary search could be improved on, for example by interpolation. But who knows, maybe some crazy time zone (or broken tzinfo data) could cause a daylight savings change in December/January. I doubt that happened for any real time zone. But it's not forbidden by the C standard, only by common sense.

    If it wasn't for that, I think we could calculate based on gmtime(mktime(01 Jan)) (to get the time zone) and a comparison of 01 Jan 1970 00:00:00 vs 01 Jan 1970 00:00:01 (to get the precision of time_t ).


    Your problem is rather fundamental: ISO C punts on time zones almost entirely, simply providing mktime() and the localtime() and gmtime() conversions, with a hook for daylight savings. (They implement, you decide.)

    So there seems like only two things you can do:

  • assume that time_t is seconds-since-epoch UTC and use gmtime() to verify that, and panic or alert if it ever fails; or
  • rely on a more comprehensive standard than ISO C

  • Step 1: Choose any time_t (the current time will work just fine) as a reference point; call it t0 .

    Step 2: Call gmtime on t0 and compute the difference between the result and the epoch in a broken-down struct tm form.

    Step 3: Call localtime on t0 and apply the broken-down difference from step 2 to the resulting struct tm . Then call mktime on it to get back a time_t .

    The result should be a time_t representing the epoch.

    My first attempt to implement this had problems when the local time offsets are not constant over time, for example in localities where daylight time has been added or abandoned or which switched from observing one zone to another. This seems to be because the data in the struct tm on which the time zone information is based gets changed. Here is the original implementation, with its problems:

    time_t get_epoch(time_t t0)
    {
        struct tm gmt = *gmtime(&t0);
        struct tm tmp = *localtime(&t0);
        tmp.tm_sec -= gmt.tm_sec;
        tmp.tm_min -= gmt.tm_min;
        tmp.tm_hour -= gmt.tm_hour;
        tmp.tm_mday -= gmt.tm_mday-1;
        tmp.tm_mon -= gmt.tm_mon;
        tmp.tm_year -= gmt.tm_year-70;
        return mktime(&tmp);
    }
    

    and an improved version, where posix_time is a function to compute the seconds since the epoch for a given struct tm using the POSIX formulae (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_15), with additional work to handle years before 1970, etc. if needed:

    time_t get_epoch(time_t t0)
    {
        struct tm gmt = *gmtime(&t0);
        struct tm tmp = *localtime(&t0);
        long long offset = posix_time(&gmt);
        while (offset > INT_MAX) {
            offset -= INT_MAX;
            tmp.tm_sec -= INT_MAX;
            mktime(&tmp);
        }
        while (offset < -INT_MAX+61) {
            offset -= -INT_MAX+61;
            tmp.tm_sec -= -INT_MAX+61;
            mktime(&tmp);
        }
        tmp.tm_sec -= offset;
        return mktime(&tmp);
    }
    

    For C89 compatibility, long long would have to be dropped and the number of mktime calls needed increases dramatically; offset could not be computed as a single value, but a loop would be needed to call mktime multiple times per year.

    链接地址: http://www.djcxy.com/p/67474.html

    上一篇: IE通过函数调用更快运行?

    下一篇: t为纯ISO C的时代?