Вот полная, но, к сожалению, неудовлетворительная, но переносимая программа среди самых последних версий реализаций libc ++, libstdc ++, VS, которая анализирует строку в формате, который вы показываете, в std::chrono::system_clock::time_point
.
Я не смог найти DateTime
, о котором вы говорите. Однако std::chrono::system_clock::time_point
- это структура типа DateTime. std::chrono::system_clock::time_point
- это счетчик некоторой продолжительности времени (секунды, микросекунды, наносекунды и т. Д.), Начиная с некоторой неопределенной эпохи. И вы можете запросить std::chrono::system_clock::time_point
, чтобы узнать, какова его временная продолжительность. И, как оказалось, в каждой реализации измеряется время, прошедшее с Нового 1970 года, без учета дополнительных секунд.
#include <chrono>
#include <iostream>
#include <limits>
#include <locale>
#include <sstream>
template <class Int>
// constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const Int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<Int>(doe) - 719468;
}
using days = std::chrono::duration
<int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
namespace std
{
namespace chrono
{
template<class charT, class traits>
std::basic_istream<charT,traits>&
operator >>(std::basic_istream<charT,traits>& is, system_clock::time_point& item)
{
typename std::basic_istream<charT,traits>::sentry ok(is);
if (ok)
{
std::ios_base::iostate err = std::ios_base::goodbit;
try
{
const std::time_get<charT>& tg = std::use_facet<std::time_get<charT> >
(is.getloc());
std::tm t = {};
const charT pattern[] = "%Y-%m-%dT%H:%M:%S";
tg.get(is, 0, is, err, &t, begin(pattern), end(pattern)-1);
if (err == std::ios_base::goodbit)
{
charT sign = {};
is.get(sign);
err = is.rdstate();
if (err == std::ios_base::goodbit)
{
if (sign == charT('+') || sign == charT('-'))
{
std::tm t2 = {};
const charT pattern2[] = "%H:%M";
tg.get(is, 0, is, err, &t2, begin(pattern2), end(pattern2)-1);
if (!(err & std::ios_base::failbit))
{
auto offset = (sign == charT('+') ? 1 : -1) *
(hours{t2.tm_hour} + minutes{t2.tm_min});
item = system_clock::time_point{
days{days_from_civil(t.tm_year+1900, t.tm_mon+1,
t.tm_mday)} +
hours{t.tm_hour} + minutes{t.tm_min} + seconds{t.tm_sec} -
offset};
}
else
{
err |= ios_base::failbit;
}
}
else
{
err |= ios_base::failbit;
}
}
else
{
err |= ios_base::failbit;
}
}
else
{
err |= ios_base::failbit;
}
}
catch (...)
{
err |= std::ios_base::badbit | std::ios_base::failbit;
}
is.setstate(err);
}
return is;
}
} // namespace chrono
} // namespace std
int
main()
{
std::istringstream infile("2005-08-15T15:52:01+04:00");
std::chrono::system_clock::time_point tp;
infile >> tp;
std::cout << tp.time_since_epoch().count() << '\n';
}
Это было протестировано на libc ++, libstdc ++ - 5.0 и VS-2015 и дает соответственно:
1124106721000000
1124106721000000000
11241067210000000
В libc ++ это количество микросекунд с Нового 1970 года без учета дополнительных секунд. В libstdc ++ - 5.0 это количество наносекунд, а в VS-2015 - 100 наносекунд.
Проблема с этим решением заключается в том, что оно включает в себя вставку функции в пространство имен std. В будущем комитет C ++ может решить вставить эту же функцию в то же пространство имен, что может сделать ваш код недействительным.
Еще одна проблема с этим кодом в том, что он ужасно сложен. Жаль, что в стандарте нет более простого решения.
Другая проблема с этим кодом заключается в том, что он не использует более простые шаблоны синтаксического анализа «% F», «% T» и «% z», задокументированные в стандарте C (хотя и задокументированные как шаблоны форматирования). Я экспериментально обнаружил, что их использование непереносимо.
Еще одна проблема с этим кодом заключается в том, что для него потребуется gcc-5.0. Если вы используете gcc-4.9, вам не повезло. Вам придется разбирать вещи самостоятельно. Мне не удалось протестировать реализации VS до VS-2015. libc ++ должен быть в порядке (хотя даже libc ++ не поддерживает "% z").
При желании вы можете преобразовать std::chrono::system_clock::time_point
обратно в «разбитую» структуру с помощью формул здесь. Однако, если это ваша конечная цель, было бы более эффективно изменить приведенный выше код для анализа непосредственно в вашу «разбитую» структуру, а не в std::chrono::system_clock::time_point
.
Отказ от ответственности: только очень легкие испытания. Я рад дополнить этот ответ любыми сообщениями об ошибках.
Обновить
За годы, прошедшие с тех пор, как я впервые дал этот ответ, я написал библиотеку, которая выполняет все вышеперечисленные вычисления с гораздо более кратким синтаксисом.
#include "date/date.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
std::istringstream infile{"2005-08-15T15:52:01+04:00"};
sys_seconds tp; // This is a system_clock time_point with seconds precision
infile >> parse("%FT%T%Ez", tp);
std::cout << tp.time_since_epoch() << " is " << tp << '\n';
}
Вы можете найти "date.h"
здесь. Это бесплатная библиотека с открытым исходным кодом только для заголовков. По этой ссылке также есть ссылки на полная документация, а для "date.h"
даже видеоурок. Хотя видеоурок был создан до реализации функции parse
.
Результат вышеупомянутой программы:
1124106721s is 2005-08-15 11:52:01
который дает как секунды с эпохи (1970-01-01 00:00:00 UTC), так и дату / время в UTC (с учетом смещения).
Если вам нужно считать дополнительные секунды с начала эпохи, другую библиотеку по адресу эта же ссылка на GitHub доступна, но не только для заголовка и требует небольшого количества установки < / а>. Но его использование - это простая модификация указанной выше программы:
#include "date/tz.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace date;
std::istringstream infile{"2005-08-15T15:52:01+04:00"};
utc_seconds tp; // This is a utc_clock time_point with seconds precision
infile >> parse("%FT%T%Ez", tp);
std::cout << tp.time_since_epoch() << " is " << tp << '\n';
}
И вот результат:
1124106743s is 2005-08-15 11:52:01
Разница в коде состоит в том, что "tz.h"
теперь включается вместо "date.h"
, а utc_seconds
анализируется вместо sys_seconds
. utc_seconds
по-прежнему std::chrono::time_point
, но теперь он основан на часах с учётом дополнительной секунды. Программа выводит ту же дату / время, но количество секунд, прошедших с начала эпохи, теперь на 22 секунды больше, поскольку это количество дополнительных секунд, вставленных между 1970-01-01 и 2005-08-15.
person
Howard Hinnant
schedule
28.02.2015