HardcoreHenry
HardcoreHenry

Reputation: 6387

Converting seconds since epoch to time_t

I have a string representing seconds since epoch, and I need to convert it to a human readable string. I've seen a few posts online that suggest simply casting an integer to time_t as so:

time_t time = (time_t)(atoi(secs_since_epoch_str));

But, if I look up the definition of time_t:

typedef /* unspecified */ time_t;

Although not defined by the C standard, this is almost always an integral 
value holding the number of seconds (not counting leap seconds) since 00:00,
Jan 1 1970 UTC, corresponding to POSIX time.

So, this is not guaranteed to work. I'm wondering if there's a proper way of doing this?

Upvotes: 3

Views: 1711

Answers (2)

chux
chux

Reputation: 153368

Converting seconds since epoch to time_t
I have a string representing seconds since epoch, and I need to convert it to a human readable string.

Add the seconds offset to a .tm_sec member of an epoch struct tm and form the time_t.


  1. Convert the string to an wide integer. (Here I assume the string represents a whole number of seconds.) It may be wider than long. Error handling omitted for brevity.
    long long offset = strtoll(string, NULL, 10);
    // or pedantically
    intmax_t offset = strtoimax(string, NULL, 10);
  1. Consider cases where time_t is not certainly a count of seconds (e.g. maybe nanoseconds) and maybe the epoch is not Jan 1, 1970 - it is more interesting. Form a timestamp for the epoch.
    time_t epoch = 0;
    struct tm *tm_ptr = localtime(&epoch);
    if (tm_ptr == NULL) Handle_Error();
    struct tm tm = *tm_ptr;
  1. Add to the struct tm members. Perhaps segmented the offset addition into parts to avoid int overflow. More code needed when int is 16-bit and offset is large (e. g. 88+ years).
    tm.tm_sec += offset%60;
    offset /= 60;
    tm.tm_min += offset%60;
    offset /= 60;
    tm.tm_hour += offset%24;
    offset /= 24;
    tm.tm_mday += offset;
  1. Call mktime() to bring the timestamp into the primary range and print. Note than since time_t might be some integer type or some floating point type, covert to a FP type to print.
    time_t target = mktime(&tm);
    printf("%Lg\n", (long double) target);
    if (target == -1) Handle_Error();
    printf("%s", asctime(&tm));

Upvotes: 3

Luis Colorado
Luis Colorado

Reputation: 12668

So, this is not guaranteed to work. I'm wondering if there's a proper way of doing this?

Hm.... I don't exactly know what you mean with the above conclussion. time_t is a unix specific type, that has evolved into POSIX standard. If you receive it as a string, then you must deal with the possibility of it being a 64bit number (E.g. FreeBSD uses a 64bit time_t)

About the epoch time, it wasn't always that epoch (first versions of unix used 01.01.1972, but I think none of us has seen one of such systems ---I run a unix v7 for pdp-11 at home, and it has a 32bit time ---no time_t type at that time, it's a long int--- and it is based on actual epoch time)

You must be careful, as since its conception, unix time has been a signed value, and so it starts at 1970-01-01, but you can specify dates before that, just by using negative values, as in:

$ LANG=C date -u -r -2147483648
Fri Dec 13 20:45:52 UTC 1901
$ _

This is why it will run out in

$ LANG=C date -u -r 2147483647
Tue Jan 19 03:14:07 UTC 2038
$ _

On other side, your problem is ambiguous, because you don't state the final string data in which locale and timezone (or format) has to be converted to. The best is that you use the standard unix time functions (look for gmtime(3), localtime(3) and the struct tm associated type)

A common way to do it is:

    time_t now = time(NULL);
    struct tm *now_gmt = gmtime(&now);

and then, if you are not interested in a specific format for the time, to use:

    printf("%s", asctime(now_gmt));

Or you can operate on the struct tm fields (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday or tm_isdst ---this last to know if you are in winter or summer time)

So everything is solved in POSIX.

And last, if you have a huge number (for a 64bit value) to convert into time_t, the best to do is to convert it with:

#include <stdint.h>

...

    int64_t val;

    sscanf(the_string_value, "%lld", &val); /* use a 64bit type */
    time_t my_time_t = (time_t) val; /* then cast it to time_t */

(I used a cast because it can be that time_t be smaller than int64_t, and this avoids a warning from the compiler)

that will work for 64bit and 32bit integers. Once you have converted it into a time_t, all the above things remain valid.

Upvotes: 1

Related Questions