Вот полный, но, к сожалению неудовлетворительными, и в то же переносимая программа среди самых последних версий 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++ это подсчет микросекунд с New Years 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
обратно в структуру с разбивкой по мере необходимости через formulas here. Однако, если это ваша конечная цель, было бы более эффективно модифицировать приведенный выше код, чтобы анализировать непосредственно в вашей «разбитой» структуре, а не в std::chrono::system_clock::time_point
.
Отказ от ответственности: Только очень легко проверено. Я рад обновить этот ответ с помощью любых отчетов об ошибках.
Update
В последующие годы я впервые дал этот ответ я написал библиотеку, которая делает все вычисления выше с гораздо более кратким синтаксисом.
#include "chrono_io.h"
#include "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';
}
Вы можете найти "chrono_io.h"
и "date.h"
here. Это бесплатные библиотеки с открытым исходным кодом, только заголовки. На this link есть также ссылки на full documentation, а для "date.h"
- даже video tutorial. Хотя video tutorial был создан до реализации функции parse
.
Выход выше программы:
1124106721s is 2005-08-15 11:52:01
, который дает как секунды с начала эпохи (1970-01-01 00:00:00 UTC) и дата/время в UTC (беря учитывается).
С учетом того, что вам нужно засчитывать прыжки секунд с эпохи, another library в этой же ссылке GitHub доступен, но не только для заголовка и требует небольшого количества installation. Но использование его является простой модификацией вышеуказанной программы:
#include "chrono_io.h"
#include "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.