【问题标题】:How to parse ISO8601 into a time_t?如何将 ISO8601 解析为 time_t?
【发布时间】:2019-06-10 16:09:58
【问题描述】:

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

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

1991-02-03T04:05:06.000-07:00
(我不需要担心“Z”或隐含的本地时间变体)

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

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

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

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

获取 TZ,清除 TZ,tzset()mktime(),重置 TZ,tzset()
(有一个 timegm(),但不是标准的)

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

【问题讨论】:

  • 请记住,time_t 通常是整数类型,并且会计算整秒。分数与它并不真正相关。
  • 是的,在这种情况下我不关心小数秒,但未来的读者可能会使用 struct timespec 或类似的,所以我想我会说跳过 或处理.

标签: c timezone locale iso8601


【解决方案1】:

我可以使用strptime() 来解析小数秒,但手册页提到了setlocale(),所以我担心我需要对此做些什么。是吗?

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

就我个人而言,我会完全手动完成,使用strtokstrtol,填写struct tm 的相应字段。您不必填写tm_ydaytm_wday 即可使用mktime

我假设我可以修改从 strptime 获得的 tm_min 与 TZ 偏移的分钟数。对吗?

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

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

我可以在开始时清除一次 TZ 和 tzset() 吗?

在“预期方式”有效的系统上,是的。但是,“预期方式”不能保证有效,事实上,如果您没有timegm,我希望它不会有效。

使用timegm。如果你没有timegmget it from gnulib

【讨论】:

  • 我的印象是mktime() 会为您进行标准化/修复。
  • 另外,出于好奇,什么样的疯狂平台会让 strptime %S 消耗小数秒?标准要求 [00,60] 中的值,并且 tm_sec 必须是 int。
【解决方案2】:

事实证明,编写自己的极简主义“mktime w/o timezone 废话”并不难。

#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 的回答中的建议结合起来,只用“手动”解析字符串,而不是处理strptime()mktime()struct tm 的可能特质。

【讨论】:

    猜你喜欢
    • 2012-01-30
    • 2019-03-02
    • 1970-01-01
    • 2012-08-20
    • 1970-01-01
    • 2012-06-28
    • 2018-01-12
    • 2012-12-08
    • 2011-07-24
    相关资源
    最近更新 更多