std :: chrono :: time_point из std :: string - PullRequest
       17

std :: chrono :: time_point из std :: string

0 голосов
/ 05 сентября 2018

Я пытаюсь преобразовать дату (в форме 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 и комментарии ниже его ответа)! Очевидно, лучше всего использовать библиотеку Говарда Хиннанта дата / время .

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Вот как будет выглядеть ваш код, если вы воспользуетесь бесплатной библиотекой даты / времени Говарда Хиннанта с открытым исходным кодом :

#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.

Обновление

Чтобы помочь вам интерпретировать ваши результаты:

  • 1514764800s после 1970-01-01 00:00:00 - 2018-01-01 00: 00: 00
  • 23587200 с после 1970-01-01 00:00:00 - 1970-10-01 00: 00: 00

(все пренебрегают високосными секундами, что является нормой)

0 голосов
/ 05 сентября 2018

Вы не объяснили, что вы ожидали и почему, поэтому я просто опишу, что делает ваша программа.

С "19700101" и "19700103" он анализирует "1970", пропускает символ, анализирует "10", пропускает символ и находит конец строки, так что он приходит к выводу, что оба - первое октября 1970 года.

С "19700301" он анализирует "1970", пропускает символ, анализирует "30" и подбрасывает, так как месяц 30 - нонсенс.

Также в вашем выходном описании есть опечатка, вы добавляете «Cannot parse date», а не «Cannot parse string».

...