【问题标题】:std::chrono::time_point from std::string来自 std::string 的 std::chrono::time_point
【发布时间】:2018-09-04 23:03:06
【问题描述】:

我正在尝试将日期(以std::string 的形式)转换为std::chrono::time_point。为此,我使用 Boost Date Time。 下面,您可以找到一个最小的工作示例。 但是,我不明白为什么在我看来某些无效的输入字符串似乎不会以某种方式引发异常。 我无法弄清楚这里发生了什么。

#include <iostream>
#include <chrono>
#include <sstream>
#include <boost/date_time.hpp>

using Clock = std::chrono::system_clock;
using TimePoint = std::chrono::time_point<Clock>;

TimePoint timePointFromString(const std::string& date, const std::string& format) {
  // local takes care of destructing time_input_facet
  auto loc = std::locale(std::locale::classic(), new boost::posix_time::time_input_facet(format));

  std::stringstream ss{date};
  ss.imbue(loc);

  boost::posix_time::ptime pt;
  ss >> pt;

  if (!ss.good()) {
    throw std::runtime_error("Cannot parse string");
  }

  boost::posix_time::ptime time_t_epoch{boost::gregorian::date(1970, 1, 1)};
  boost::posix_time::time_duration diff = pt - time_t_epoch;

  Clock::duration duration{diff.total_nanoseconds()};

  return TimePoint{duration};
}

int main() {
  std::string format{"%Y-%m-%d"};

  std::vector<std::string> strings {"2018", "2018-", "19700101", "19700103", "19700301"};
  for (const auto& s: strings) {
    auto tp = timePointFromString(s, format);
    std::cout << s << ": " << TimePoint::clock::to_time_t(tp) << std::endl;
  }
}

输出:

2018: 1514764800
2018-: 1514764800
19700101: 23587200
19700103: 23587200
terminate called after throwing an instance of 'std::runtime_error'
  what():  Cannot parse string

更新:我误解了这段代码,认为它会进行某种模式匹配。事实并非如此(请参阅Öö Tiib 答案和他的答案下方的 cmets)!显然,最好使用 Howard Hinnant 的 date/time 库。

【问题讨论】:

    标签: c++ boost chrono


    【解决方案1】:

    如果您使用Howard Hinnant's free, open-source date/time library,您的代码将如下所示:

    #include "date/date.h"
    #include <chrono>
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <vector>
    
    std::chrono::system_clock::time_point
    timePointFromString(const std::string& date, const std::string& format)
    {
        std::stringstream ss{date};
        std::chrono::system_clock::time_point pt;
        ss >> date::parse(format, pt);
        if (ss.fail())
            throw std::runtime_error("Cannot parse date");
        return pt;
    }
    
    int
    main()
    {
        std::string format{"%Y-%m-%d"};
        std::vector<std::string> strings{"2018", "2018-", "19700101", "19700103", "19700301",
                                         "1970-03-01"};
        for (const auto& s: strings)
        {
            try
            {
                auto tp = timePointFromString(s, format);
                using date::operator<<;
                std::cout << s << ": " << tp << '\n';
            }
            catch (std::exception const& e)
            {
                std::cout << s << ": " << e.what() << '\n';
            }
        }
    }
    

    输出将是:

    2018: Cannot parse date
    2018-: Cannot parse date
    19700101: Cannot parse date
    19700103: Cannot parse date
    19700301: Cannot parse date
    1970-03-01: 1970-03-01 00:00:00.000000
    

    我在向量末尾添加了一个有效的字符串/日期,以显示它使用此format 接受的内容。 1970-03-01 00:00:00.0... 上的尾随零的数量将根据您平台的 std::chrono::system_clock::time_point 的精度而有所不同。

    此外,这段代码应该很容易移植到 C++20:

    • 删除#include "date/date.h"
    • 删除using date::operator&lt;&lt;;
    • date::parse 更改为std::chrono::parse

    更新

    为了帮助您解释结果:

    • 1970-01-01 00:00:00 之后的 1514764800s 是 2018-01-01 00:00:00
    • 1970-01-01 00:00:00 之后的 23587200s 是 1970-10-01 00:00:00

    (所有忽略闰秒,这是常态)

    【讨论】:

    • 感谢您的建议!我想我会使用那个库。
    【解决方案2】:

    你没有解释你的期望和原因,所以我只描述你的程序做了什么。

    对于“19700101”和“19700103”,它解析“1970”跳过一个字符解析“10”跳过一个字符并找到字符串的结尾,因此它得出结论都是1970年10月。

    使用“19700301”,它解析“1970”跳过一个字符解析“30”并抛出,因为第30个月是无意义的。

    您的输出描述也有错字,您抛出“无法解析日期”而不是“无法解析字符串”。

    【讨论】:

    • 好的,我明白了,但是:1)为什么当字符串太短时没有抛出异常(ss.good()呢)? 2)为什么会跳过字符?最后,我将如何更改代码以确保这些案例抛出?也许检查字符串的大小?
    • 因为它不会验证您的字符串是否与特定模式匹配。相反,它会验证它实际从中读取的内容。当它只找到一年并且字符串结束时,假设第一个一月,当它只找到年份和月份时,假设一个月的第一天。我不知道为什么它是这样设计的。如果你想做模式验证,那么使用正则表达式或者使用 Howard Hinnant 的库,它比 boost date time 更好,而且它的代码也更容易阅读。
    猜你喜欢
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-04
    • 2013-12-05
    • 1970-01-01
    相关资源
    最近更新 更多