如何将 iso 8601 日期(可选毫秒)解析为 C 中的 struct tm?

新手上路,请多包涵

我有一个字符串,它应该以 ISO 8601 格式指定日期和时间,其中可能有也可能没有毫秒,我想从中获取 struct tm 以及任何可能的毫秒值已指定(如果字符串中不存在,则可以假定为零)。

检测字符串是否格式正确以及将用户指定的字符串转换为 struct tm 和毫秒值会涉及什么?

如果不是毫秒问题,我可能只使用 C 函数 strptime() ,但我不知道当秒包含小数点时该函数的定义行为应该是什么。

作为最后一个警告,如果可能的话,我非常喜欢一个不依赖于仅在 Boost 中找到的函数的解决方案(但我很高兴接受 C++11 作为先决条件)。

输入看起来像:

 2014-11-12T19:12:14.505Z

或者

2014-11-12T12:12:14.505-5:00

Z 在这种情况下,表示 UTC,但可以使用任何时区,并将表示为与 GMT 的 + 或 - 小时/分钟偏移量。秒字段的小数部分是可选的,但它可能存在的事实就是为什么我不能简单地使用 strptime()std::get_time() ,它们没有描述任何特定的定义行为如果在字符串的秒部分中找到这样的字符。

原文由 markt1964 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.5k
2 个回答

您可以使用 Csscanfhttp://www.cplusplus.com/reference/cstdio/sscanf/ )来解析它:

 const char *dateStr = "2014-11-12T19:12:14.505Z";
int y,M,d,h,m;
float s;
sscanf(dateStr, "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &s);

如果您有 std::string 可以这样调用( http://www.cplusplus.com/reference/string/string/c_str/ ):

 std::string dateStr = "2014-11-12T19:12:14.505Z";
sscanf(dateStr.c_str(), "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &s);

如果它应该处理不同的时区,您需要使用 sscanf 返回值 - 解析参数的数量:

 int tzh = 0, tzm = 0;
if (6 < sscanf(dateStr.c_str(), "%d-%d-%dT%d:%d:%f%d:%dZ", &y, &M, &d, &h, &m, &s, &tzh, &tzm)) {
    if (tzh < 0) {
       tzm = -tzm;    // Fix the sign on minutes.
    }
}

然后你可以填写 tmhttp://www.cplusplus.com/reference/ctime/tm/ )结构:

 tm time = { 0 };
time.tm_year = y - 1900; // Year since 1900
time.tm_mon = M - 1;     // 0-11
time.tm_mday = d;        // 1-31
time.tm_hour = h;        // 0-23
time.tm_min = m;         // 0-59
time.tm_sec = (int)s;    // 0-61 (0-60 in C++11)

它也可以用 std::get_timehttp://en.cppreference.com/w/cpp/io/manip/get_time )来完成,因为 C++11 正如@Barry在评论中提到的 如何做我将 iso 8601 日期(可选毫秒)解析为 C++ 中的 struct tm?

原文由 k06a 发布,翻译遵循 CC BY-SA 4.0 许可协议

老问题的新答案。理由:更新的工具。

使用这个 免费的开源库,可以解析为 std::chrono::time_point<system_clock, milliseconds> ,它比 tm 具有优势,能够保持毫秒精度。如果你真的需要,你可以通过 system_clock::to_time_t 继续使用 C API(一路上会丢失毫秒)。

 #include "date.h"
#include <iostream>
#include <sstream>

date::sys_time<std::chrono::milliseconds>
parse8601(std::istream&& is)
{
    std::string save;
    is >> save;
    std::istringstream in{save};
    date::sys_time<std::chrono::milliseconds> tp;
    in >> date::parse("%FT%TZ", tp);
    if (in.fail())
    {
        in.clear();
        in.exceptions(std::ios::failbit);
        in.str(save);
        in >> date::parse("%FT%T%Ez", tp);
    }
    return tp;
}

int
main()
{
    using namespace date;
    using namespace std;
    cout << parse8601(istringstream{"2014-11-12T19:12:14.505Z"}) << '\n';
    cout << parse8601(istringstream{"2014-11-12T12:12:14.505-5:00"}) << '\n';
}

这输出:

 2014-11-12 19:12:14.505
2014-11-12 17:12:14.505

请注意,两个输出都是 UTC。 parse 使用 -5:00 偏移量将本地时间转换为 UTC。如果你真的想要 local time ,还有一种方法可以解析成一种名为 date::local_time<milliseconds> 的类型,然后它会解析但忽略偏移量。如果需要,甚至可以将偏移量解析为 chrono::minutes (使用 parse 过载 minutes& )。

解析的精度由您传入的 chrono::time_point 的精度控制,而不是由格式字符串中的标志控制。偏移量可以是 +/-hhmm%z+/-[h]h:mm%Ez 的风格。

原文由 Howard Hinnant 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题