[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: os.time and UTC conversions (with code)
- From: William Ahern <william@...>
- Date: Wed, 3 Oct 2012 15:29:04 -0700
There's no portable way, in either C or even POSIX, to convert a struct tm
into a time_t value without the system applying the local timezone offset.
struct tm is presumed to express localtime.
Some systems provide timegm(), but others don't. I'm writing a comprehensive
binding to OpenSSL (bignum, X.509, SSL sessions, etc) for my cqueues
project, and I needed (well, wanted) to convert ASN.1 time objects to a
timestamp.
Below is the code for a portable timegm, which turned out to be surprisingly
simple (eventually). Assuming a double with a 53-bit significand, it can
generate the unix timestamp for any time between -9999-01-01 and 9999-12-31,
and then some.
I realize this code presumes Unix-style timestamps. But I figured it might
still be a worthwhile addition to Lua, to provide optional symmetry with the
"!" format specifier to os.date. Are there actually any systems around that
don't use similar timestamps? A different epoch isn't an issue. The hard
part would be dealing with leap seconds--which POSIX conveniently defines
away--or some other encoding scheme.
#define CLAMP(i, min, max) (((i) < (min))? (min) : ((i) > (max))? (max) : (i))
static _Bool isleap(int year) {
if (year >= 0)
return !(year % 4) && ((year % 100) || !(year % 400));
else
return isleap(-(year + 1));
} /* isleap() */
static int yday(int year, int mon, int mday) {
static const int past[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
int yday = past[CLAMP(mon, 0, 11)] + CLAMP(mday, 1, 31) - 1;
return yday + (mon > 1 && isleap(year));
} /* yday() */
static int tm_yday(const struct tm *tm) {
return (tm->tm_yday)? tm->tm_yday : yday(1900 + tm->tm_year, tm->tm_mon, tm->tm_mday);
} /* tm_yday() */
static int leaps(int year) {
if (year >= 0)
return (year / 400) + (year / 4) - (year / 100);
else
return -(leaps(-(year + 1)) + 1);
} /* leaps() */
static double tm2unix(const struct tm *tm, int gmtoff) {
int year = tm->tm_year + 1900;
double ts;
ts = 86400.0 * 365.0 * (year - 1970);
ts += 86400.0 * (leaps(year - 1) - leaps(1969));
ts += 86400 * tm_yday(tm);
ts += 3600 * tm->tm_hour;
ts += 60 * tm->tm_min;
ts += CLAMP(tm->tm_sec, 0, 59);
ts += (year < 1970)? gmtoff : -gmtoff;
return ts;
} /* tm2unix() */