如何将 ISO8601 解析为 time_t?

How to parse ISO8601 into a time_t?

将 ISO8601 日期时间解析为 time_t 的正确方法是什么?

输入字符串是 ISO8601 日期时间的特定变体:

1991-02-03T04:05:06.000-07:00
(I do not need to worry about the 'Z' or implied localtime variants)

我可以使用 strptime() 来解析小数秒,但是手册页提到了 setlocale() 所以我担心我需要对此做些什么。 可以吗? 还是仅用于月份和日期名称?

似乎没有任何方法可以跳过(或处理)strptime 中的小数秒,而且我的版本似乎也不支持“%z”(而且 tm_gmtoff是非标准的),所以我一直在解析输入的小数秒和时区偏移 'by hand'。很简单。

所以我假设我可以修改 tm_min 我从 strptime 得到的 TZ 偏移分钟数。 正确吗?

然后我们来到mktime()。强制它在 UTC 中运行的预期方式似乎是:

get TZ, clear TZ, tzset(), mktime(), reset TZ, tzset()
(there is a timegm() but is non-standard)

我要处理很多这样的字符串,我不关心这个程序中的任何其他时间处理,所以这似乎是很多毫无价值的开销,我可以清除吗TZ和tzset()开头有过一次吗?

I can use strptime() to parse up to the fractional seconds, but the man page mentions setlocale() so I worry that I need to do something with that. Do I?

是的,不幸的是;根据区域设置,%S 可能会消耗小数秒,并且可能会查找小数逗号而不是小数点。

就个人而言,我会完全手动完成此操作,使用 strtokstrtol,填写 struct tm 的适当字段。您不必为 mktime 填写 tm_ydaytm_wday

I assume I can just modify tm_min I got from strptime with the number of minutes of TZ offset. Correct?

这比看起来要难。根据时区偏移量的实际值,您需要调整 tm_hr 以及 tm_min (实际上,常见情况是整数小时,因此您需要调整 tm_hr 而不是 tm_min),如果调整将时间推到一天的边界上,您将需要标准化小时并确定日、月和年。

您还必须确保 tm_isdsttm_gmtoff 为零且 tm_zone 为 NULL。

Can I just clear TZ and tzset() once at the beginning?

在 "expected way" 工作的系统上,是的。但是,"expected way" 不能保证有效,事实上,如果您没有 timegm,我希望它 not 有效。

使用timegm。如果你没有 timegm, get it from gnulib.

事实证明,编写自己的极简主义 'mktime w/o timezone nonsense' 并不难。

#define divis(y,n) (((y) % (n)) == 0)
#define isLY(y) (divis((y),4) && (!divis((y),100) || divis((y),400)))
#define nLY(y)  (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)

static int      mdays[]         = {31,28,31,30,31,30,31,31,30,31,30,31};

/*
 * mktime implicitly applies the local timezone, this doesn't.
 * This also only works with valid values, no checking or normalizing happens.
 */
static time_t epoch (
        struct tm *     tm
) {
        int     years           = tm->tm_year + 1900;
        int     months          = tm->tm_mon;
        int     days            = tm->tm_mday - 1;
        int     hours           = tm->tm_hour;
        int     minutes         = tm->tm_min;
        int     seconds         = tm->tm_sec;

        if ((months > 1) && isLY(years)) ++days;
        while (months-- > 0) days += mdays[months];
        days += (365 * (years - 1970)) + nLY(years);
        return (time_t)((86400 * days) + (3600 * hours) + (60 * minutes) + seconds);
}

所以,我将把它与@zwol 的回答中的建议结合起来,只解析字符串 'by hand' 而不必处理 strptime()mktime()struct tm 可能的特质。