如何解析包含分数时间的日期时间字符串?

时间:2016-06-16 10:42:36

标签: c++ datetime tm strptime format-specifiers

我有一个日期时间字符串:

  

20:48:01.469 UTC 2016年3月31日

我想使用struct tm将此时间字符串表示转换为strptime,但我的格式字符串不起作用。

是否有小数秒的格式说明符?可能是%S%s或其他什么?

代码段如下:

tm tmbuf;
const char *str = "20:48:01.469 UTC MAR 31 2016"
const char *fmt = "%H:%M:%s %Z %b %d %Y";
strptime(str,fmt,&tmbuf);

2 个答案:

答案 0 :(得分:3)

使用这个free, open source C++11/14 library,这是处理解析小数秒的另一种方法:

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

int main()
{
    using namespace date;
    using namespace std::chrono;
    std::istringstream str("20:48:01.469 UTC MAR 31 2016");
    sys_time<milliseconds> tp;
    parse(str, "%T %Z %b %d %Y", tp);
    std::cout << tp << '\n';
}

输出:

2016-03-31 20:48:01.469

即,使用此工具%S%T正常工作。不使用标志来控制精度,而是使用std::chrono::time_point的精度。

如果你想找出你解析的时区缩写,那也是可能的:

std::istringstream str("20:48:01.469 UTC MAR 31 2016");
sys_time<milliseconds> tp;
std::string abbrev;
parse(str, "%T %Z %b %d %Y", tp, abbrev);
std::cout << tp << ' ' << abbrev << '\n';

输出:

2016-03-31 20:48:01.469 UTC

这就是说,这个库建立在std::get_time之上,因此具有相同的可移植性问题,Jonathan的优秀(和赞成)答案暗示:只有libc ++目前以不区分大小写的方式解析月份名称。希望这将在不太遥远的未来发生变化。

libstdc++ bug report.

VSO#232129错误报告。

如果你必须处理UTC以外的时区,一般来说,没有确定的方法可以做到这一点,因为在任何时候,不止一个时区可以使用相同的缩写。因此UTC偏移可能不明确。但是here is a short article关于如何使用这个库将缩写缩小到候选时区列表,你可能会从中选择一个独特的时区。

答案 1 :(得分:1)

请注意,表示最小时间增量的tm成员为tm_sec,这是int,仅在以下范围内定义:

  

自C ++ 11以来[0,60]分钟后的秒数

因此,您无法在tm中存储一小段时间,您只需要丢弃小数点后的数字。

As suggested by Karsten Koop你可以读两年,第二个%Y会踩到第一个:

auto fmt = "%H:%M:%S.%Y %Z %b %d %Y";

Live Example

那就是说,我建议使用strptime使用get_time 它是一个POSIX函数,使用像Live Example这样的标准函数会更好。这有一个小缺点; get_time没有时区知识,但tm也没有,tm_isdst除外:

  

夏令时标志。如果DST生效,则该值为正,否则为零;如果没有可用信息则为负

因此,如果你坚持这样的话,你可能需要独立分配tm_isdst

tm tmbuf;
stringstream str("20:48:01.469 UTC MAR 31 2016");

str >> get_time(&tmbuf, "%H:%M:%S.%Y UTC %b %d %Y");

Live Example

我的get_time回答有点虚伪,因为当我谈到标准化的重要性时,我只能让它在libc ++上运行。因此,我认为我发布了一个更通用的解决方案,它也会丢弃时区,所以您再次需要独立设置tm_isdst

tm tmbuf{};
stringstream str("20:48:01.469 UTC MAR 31 2016");
string tm_mon;

str >> get_time(&tmbuf, "%T");

str.ignore(std::numeric_limits<std::streamsize>::max(), 'C');

str >> tm_mon >> get_time(&tmbuf, "%d %Y");

for (const auto& i : { "JAN"s, "FEB"s, "MAR"s, "APR"s, "MAY"s, "JUN"s, "JUL"s, "AUG"s, "SEP"s, "OCT"s, "NOV"s, "DEC"s }) {
    if (equal(cbegin(tm_mon), cend(tm_mon), cbegin(i), cend(i), [](const unsigned char a, const unsigned char b) { return toupper(a) == b; })) break;
    ++tmbuf.tm_mon;
}

enter image description here

这有两个关键依赖项:

  1. 时区始终以字符'C'结尾(必须为大写)
  2. 与我initializer_list
  3. 中的一个相匹配的月份缩写