c++11:使用HowardHinnant/date.h解析ISO8601格式字符串,并解决时区问题

C++11提供了std::get_time函数用于解析时间格式字符串,解析成功后将时间保存在std::tm结构中。
但是对于ISO8601标准中有毫秒精度的字符串比如('2014-11-12T19:12:14.505+0800')是不支持的。
如何解析这种有毫秒精度的时间字符串呢?

HowardHinnant/date

通过stakoverflow上的这个贴子:

《how do I parse an iso 8601 date (with optional milliseconds) to a struct tm in C++?》
https://stackoverflow.com/questions/26895428/how-do-i-parse-an-iso-8601-date-with-optional-milliseconds-to-a-struct-tm-in-c

我知道github上的开源代码HowardHinnant/date提供了这种支持.核心代码只有一个date.h头文件。include进来就可以使用。

下面是从贴子中抄来的使用HowardHinnant/date对ISO8601标准时间字符串解析的示例代码:

#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;
    // 这里输出显示为UTC时间
    cout << parse8601(istringstream{
    
    "2014-11-12T19:12:14.505Z"}) << '\n';
    cout << parse8601(istringstream{
    
    "2014-11-12T12:12:14.505-5:00"}) << '\n';
}

注意事项

  1. 要注意的是如果字符串中没有指定时区,HowardHinnant/date在解析时会自动解析成UTC时间,而不是本地时间。这与std::get_time是不一样的。
  2. 第二个问题是如果字符串中没有日期,只有时间(如12:12:14),HowardHinnant/date不能正确解析,所以建议是要判断日期格式,如果日期字符串没有只有时间,还是用std::get_time来解析.

时区问题

对于第一个问题其实HowardHinnant/date也提供了解决方案。进一步阅读HowardHinnant/date的源码,发现date::parse函数有好几个重载函数。上面的示例中只是用了参数最少最简单的一个,下面date::parse函数这个有4个参数的版本,后面两个参数都是用于时区设置的,如果正确指定了时区参数,解析出来的时间就是当前时区的。

template <class Parsable, class CharT, class Traits, class Alloc>
inline
auto
parse(const std::basic_string<CharT, Traits, Alloc>& format, Parsable& tp,
      std::basic_string<CharT, Traits, Alloc>& abbrev, std::chrono::minutes& offset)
  • abbrev 指定提供时区名字缩写,如CST–中国标准时间(但这个参数似乎无效,我没有深究)
  • offset 相对GMT时间以分钟为单位时区偏移,比如北京时间东八区就是8x60=480。

这个第二个参数就是指定当前时区与GMT时间的偏移.默认这个参数为0,如果指定了该参数,则转换后的时间是以当前时区计算的时间。

所以上面的例子代码做如下修改,就可以支持将未指定时区的字符串解析为本地时间.说明参见代码中的中文注释。

// 增加一个时区偏移参数 offset
static date::sys_time<std::chrono::milliseconds>
parse8601(std::istream&& is,int offset = 0/** 时区偏移 */)
{
    
    
    std::string save;
    is >> save;
    std::istringstream in{
    
    save};
    date::sys_time<std::chrono::milliseconds> tp;
    // 将 offset 传递给data:parse
    in >> date::parse("%FT%TZ", tp,std::string(), std::chrono::minutes(offset));
    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;
    // 这里输出显示为UTC时间
    cout << parse8601(istringstream{
    
    "2014-11-12T19:12:14.505Z"}) << '\n';
    cout << parse8601(istringstream{
    
    "2014-11-12T12:12:14.505-5:00"}) << '\n';
    /** 
     * 下面这个字符串没有指定时区,我们希望它解析为当前时间,
     * 所以offset使用北京时间(+08:00)的时区偏移
     */
    cout << parse8601(istringstream{
    
    "2014-11-12T12:12:14.505"},480) << '\n';
}

上面的例子中480仅对+08:00时区有效,如何自动获取当前时区的偏移呢?
参见我的上一篇博客:

《c,c++:获取当前时区偏移》

Guess you like

Origin blog.csdn.net/10km/article/details/120432666