Как разбирать строку даты в С++ 11 std:: chrono time_point или подобное?
Рассмотрим историческую строку даты формата:
Thu Jan 9 12:35:34 2014
Я хочу проанализировать такую строку в виде представления даты С++, а затем вычислить количество времени, которое прошло с тех пор.
Из результирующей продолжительности мне нужен доступ к числу секунд, минут, часов и дней.
Можно ли это сделать с новым пространством имен С++ 11 std::chrono
? Если нет, как мне сегодня это заняться?
Я использую g++ - 4.8.1, хотя предположительно ответ должен быть нацелен только на спецификацию С++ 11.
Ответы
Ответ 1
std::tm tm = {};
std::stringstream ss("Jan 9 2014 12:35:34");
ss >> std::get_time(&tm, "%b %d %Y %H:%M:%S");
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
GCC до версии 5 не реализует std::get_time
. Вы также должны написать:
std::tm tm = {};
strptime("Thu Jan 9 2014 12:35:34", "%a %b %d %Y %H:%M:%S", &tm);
auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
Ответ 2
Новый ответ на старый вопрос. Обоснование нового ответа: вопрос был отредактирован из его первоначальной формы, потому что инструменты в то время не могли точно обрабатывать то, что спрашивалось. И полученный принятый ответ дает немного отличающееся поведение от того, о чем просил исходный вопрос.
Я не пытаюсь записать принятый ответ. Это хороший ответ. Просто C API настолько запутан, что неизбежны такие ошибки.
Первоначальный вопрос был для разбора "Thu, 9 Jan 2014 12:35:34 +0000"
. Очевидно, что целью было проанализировать метку времени, представляющую время UTC. Но strptime
(который не является стандартным C или C++, но является POSIX) не анализирует конечное смещение UTC, указывающее, что это метка времени UTC (оно отформатирует его с %z
, но не анализирует его).
Затем вопрос был отредактирован, чтобы задать вопрос "Thu Jan 9 12:35:34 2014"
. Но вопрос не был отредактирован, чтобы уточнить, была ли это метка времени UTC или метка времени в текущем местном часовом поясе компьютера. Принятый ответ неявно предполагает, что метка времени представляет текущий локальный часовой пояс компьютера из-за использования std::mktime
.
std::mktime
не только преобразует тип поля tm
в последовательный тип time_t
, но также выполняет настройку смещения от локального часового пояса компьютера до UTC.
Но что, если мы хотим проанализировать метку времени UTC как первоначальный (неотредактированный) вопрос?
Это можно сделать сегодня с помощью этой новой бесплатной библиотеки с открытым исходным кодом.
#include "date.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace date;
istringstream in{"Thu, 9 Jan 2014 12:35:34 +0000"};
sys_seconds tp;
in >> parse("%a, %d %b %Y %T %z", tp);
}
Эта библиотека может анализировать %z
. И date::sys_seconds
это просто определение типа для:
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
Вопрос также задает:
Из полученной продолжительности мне нужен доступ к числам секунд, минут, часов и дней.
Эта часть осталась без ответа. Вот как вы делаете это с этой библиотекой. Для этой части я также собираюсь использовать вторую библиотеку только для заголовков "chrono_io.h"
:
#include "chrono_io.h"
#include "date.h"
#include <iostream>
#include <sstream>
int
main()
{
using namespace std;
using namespace date;
istringstream in{"Thu, 9 Jan 2014 12:35:34 +0000"};
sys_seconds tp;
in >> parse("%a, %d %b %Y %T %z", tp);
auto tp_days = floor<days>(tp);
auto hms = make_time(tp - tp_days);
std::cout << "Number of days = " << tp_days.time_since_epoch() << '\n';
std::cout << "Number of hours = " << hms.hours() << '\n';
std::cout << "Number of minutes = " << hms.minutes() << '\n';
std::cout << "Number of seconds = " << hms.seconds() << '\n';
}
floor<days>
обрежут секунды точности time_point
к точности дней time_point
. Если вы time_point
с точностью до time_point
из tp
, у вас останется duration
, представляющая время с полуночи (UTC).
Заводская функция make_time
принимает любую duration
(в данном случае время с полуночи) и создает тип поля {hours, minutes, seconds}
с геттерами для каждого поля. Если у длительности точность меньше, чем в секундах, этот тип поля также будет иметь геттер для подсекунд.
Наконец, используя "chrono_io.h"
, можно просто распечатать все эти длительности. Этот пример выводит:
Number of days = 16079[86400]s
Number of hours = 12h
Number of minutes = 35min
Number of seconds = 34s
[86400]s
представляет единицы duration
с точностью до суток. Таким образом, 2014-01-09 - это 16079 дней после 1970-01-01.
Ответ 3
Это скорее C-ish и не столь изящное решение, как простой ответ, но я думаю, что это может сработать. Этот ответ, вероятно, неверен, но я оставлю его, поэтому кто-то может опубликовать исправления.
#include <iostream>
#include <ctime>
int main ()
{
struct tm timeinfo;
std::string buffer = "Thu, 9 Jan 2014 12:35:00";
if (!strptime(buffer.c_str(), "%a, %d %b %Y %T", &timeinfo))
std::cout << "Error.";
time_t now;
struct tm timeinfo2;
time(&now);
timeinfo2 = *gmtime(&now);
time_t seconds = difftime(mktime(&timeinfo2), mktime(&timeinfo));
time(&seconds);
struct tm result;
result = *gmtime ( &seconds );
std::cout << result.tm_sec << " " << result.tm_min << " "
<< result.tm_hour << " " << result.tm_mday;
return 0;
}
Ответ 4
Случаи покрыты (код ниже):
-
с даты до сих пор
long int min0 = getMinutesSince( "2005-02-19 12:35:00" );
-
с эпохи до сих пор
long int min1 = getMinutesSince1970( );
-
между двумя датами + часами (с эпохи до заданной даты)
long int min0 = getMinutesSince1970Until( "2019-01-18 14:23:00" );
long int min1 = getMinutesSince1970Until( "2019-01-18 14:27:00" );
cout << min1 - min0 << endl;
Полный код:
#include <iostream>
#include <chrono>
#include <sstream>
#include <string>
#include <iomanip>
using namespace std;
// ------------------------------------------------
// ------------------------------------------------
long int getMinutesSince1970Until( string dateAndHour ) {
tm tm = {};
stringstream ss( dateAndHour );
ss >> get_time(&tm, "%Y-%m-%d %H:%M:%S");
chrono::system_clock::time_point tp = chrono::system_clock::from_time_t(mktime(&tm));
return
chrono::duration_cast<chrono::minutes>(
tp.time_since_epoch()).count();
} // ()
// ------------------------------------------------
// ------------------------------------------------
long int getMinutesSince1970() {
chrono::system_clock::time_point now = chrono::system_clock::now();
return
chrono::duration_cast<chrono::minutes>( now.time_since_epoch() ).count();
} // ()
// ------------------------------------------------
// ------------------------------------------------
long int getMinutesSince( string dateAndHour ) {
tm tm = {};
stringstream ss( dateAndHour );
ss >> get_time(&tm, "%Y-%m-%d %H:%M:%S");
chrono::system_clock::time_point then =
chrono::system_clock::from_time_t(mktime(&tm));
chrono::system_clock::time_point now = chrono::system_clock::now();
return
chrono::duration_cast<chrono::minutes>(
now.time_since_epoch()-
then.time_since_epoch()
).count();
} // ()
// ------------------------------------------------
// ------------------------------------------------
int main () {
long int min = getMinutesSince1970Until( "1970-01-01 01:01:00" );
cout << min << endl;
long int min0 = getMinutesSince1970Until( "2019-01-18 14:23:00" );
long int min1 = getMinutesSince1970Until( "2019-01-18 14:27:00" );
if ( (min1 - min0) != 4 ) {
cout << " something is wrong " << endl;
} else {
cout << " it appears to work !" << endl;
}
min0 = getMinutesSince( "1970-01-01 01:00:00" );
min1 = getMinutesSince1970( );
if ( (min1 - min0) != 0 ) {
cout << " something is wrong " << endl;
} else {
cout << " it appears to work !" << endl;
}
} // ()