std :: string中的std :: chrono :: time_point

时间:2018-09-04 23:03:07

标签: c++ boost chrono

我正在尝试将日期(以std::string的形式)转换为std::chrono::time_point。为此,我使用加速日期时间。 在下面,您可以找到一个最小的工作示例。 但是,我不明白为什么我认为无效的某些输入字符串似乎不会以任何方式引发异常。 我不知道这是怎么回事。

#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的回答和其回答下方的评论)!显然,最好使用Howard Hinnant的date/time库。

2 个答案:

答案 0 :(得分:1)

您没有解释您的期望以及原因,因此我只描述您的程序的作用。

使用“ 19700101”和“ 19700103”解析“ 1970”跳过一个字符,解析“ 10”跳过一个字符并找到字符串的末尾,因此得出结论,两者均为1970年10月。

对于“ 19700301”,它解析“ 1970”,跳过一个字符解析“ 30”,并且由于第30个月是胡说八道而抛出。

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

答案 1 :(得分: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<<;
  • date::parse更改为std::chrono::parse

更新

为帮助您解释结果:

  • 1970年1月1日00:00:00之后的1514764800s是2018年1月1日00:00:00
  • 1970年1月1日00:00:00之后的23587200s是1970年10月1日00:00:00

(通常忽略leap秒)