Как разобрать строку даты и времени, содержащую дробное время?

У меня есть строка даты и времени:

20:48:01.469 UTC 31 марта 2016 г.

Я хотел бы преобразовать это строковое представление времени в struct tm, используя strptime, но моя строка формата не работает.

Есть ли спецификатор формата для дробных секунд? Возможно, %S, %s или что-то еще?

Фрагмент кода ниже:

tm tmbuf;
const char *str = "20:48:01.469 UTC MAR 31 2016"
const char *fmt = "%H:%M:%s %Z %b %d %Y";
strptime(str,fmt,&tmbuf);

person Dr. Debasish Jana    schedule 16.06.2016    source источник
comment
попробуй "%H:%M:%s.%f %Z %b %d %Y"   -  person EdChum    schedule 16.06.2016
comment
Одна маленькая поправочка: "%H:%M:%S.%f %Z %b %d %Y"   -  person GMichael    schedule 16.06.2016
comment
@Michael Но %H:%M:%S.%f %Z %b %d %Y также не извлекается должным образом, он показывает минуты и секунды (целая часть), но больше ничего   -  person Dr. Debasish Jana    schedule 16.06.2016
comment
%H:%M:%S.%Y %Z %b %d %Y у меня работает. Это преобразует нежелательные миллисекунды в год, который затем перезаписывается реальным годом. Поведение может зависеть от среды выполнения (например, %f не поддерживается повсеместно).   -  person Karsten Koop    schedule 16.06.2016
comment
@Доктор. Debasish Jana Насколько я знаю встроенного чтения долей секунды нет. По крайней мере, такого разбора я еще не видел. Вы должны проанализировать всю строку вручную, если вам нужна такая точность. Если вам это не нужно, воспользуйтесь рекомендацией Karsten Koop.   -  person GMichael    schedule 16.06.2016
comment
@KarstenKoop, но %H:%M:%S.%Y %Z %b %d %Y также не смог извлечь   -  person Dr. Debasish Jana    schedule 16.06.2016
comment
Подождите, вы хотите извлечь миллисекунды? В struct tm для них нет поля.   -  person Karsten Koop    schedule 16.06.2016
comment
@KarstenKoop без миллисекунд и часового пояса, извлечение работает, но данные идут с ними, и я хотел бы игнорировать эти части, если это необходимо   -  person Dr. Debasish Jana    schedule 16.06.2016
comment
@KarstenKoop Ваше решение было тем, с которым я тоже пошел. Поскольку вы получили его первым, я связал ваш комментарий в мом ответе, но поскольку это не сработало для OP Я также предоставил пару альтернативных решений.   -  person Jonathan Mee    schedule 16.06.2016


Ответы (2)


Используя эту бесплатную библиотеку C++11/14 с открытым исходным кодом, можно иметь дело с разбором дробных секунд:

#include "tz.h"
#include <iostream>
#include <sstream>

int main()
{
    using namespace date;
    using namespace std::chrono;
    std::istringstream str("20:48:01.469 UTC MAR 31 2016");
    sys_time<milliseconds> tp;
    parse(str, "%T %Z %b %d %Y", tp);
    std::cout << tp << '\n';
}

Выход:

2016-03-31 20:48:01.469

То есть с этим инструментом %S и %T просто работают. Точность контролируется не флагами, а точностью std::chrono::time_point.

Если вы хотите узнать, какую аббревиатуру часового пояса вы проанализировали, это также возможно:

std::istringstream str("20:48:01.469 UTC MAR 31 2016");
sys_time<milliseconds> tp;
std::string abbrev;
parse(str, "%T %Z %b %d %Y", tp, abbrev);
std::cout << tp << ' ' << abbrev << '\n';

Выход:

2016-03-31 20:48:01.469 UTC

При этом эта библиотека построена поверх std::get_time и, таким образом, имеет ту же проблему переносимости, на которую ссылается отличный (и получивший одобрение) ответ Джонатана: только libc++ в настоящее время анализирует имена месяцев без учета регистра. Надеюсь, что это изменится в не столь отдаленном будущем.

отчет об ошибке libstdc++.

Отчет об ошибке VSO#232129.

Если вам приходится иметь дело с часовыми поясами, отличными от UTC, как правило, нет надежного способа сделать это, потому что в любой момент одна и та же аббревиатура может использоваться более чем в одном часовом поясе. Таким образом, смещение UTC может быть неоднозначным. Однако вот короткая статья о том, как использовать эту библиотеку для сузьте аббревиатуру до списка часовых поясов-кандидатов, из которых у вас может быть некоторая специальная логика для выбора уникального часового пояса.

person Howard Hinnant    schedule 16.06.2016
comment
Должен сказать, что я ожидал, что вы бросите ответ здесь, прежде чем я закончу все свои правки. Когда ваша библиотека станет стандартом? - person Jonathan Mee; 16.06.2016
comment
@JonathanMee: Ты поймал меня на том, что я дремлю. ;-) Стандартизация - это ледниковый процесс, чреватый неудачами. Я отправил open-std.org/ jtc1/sc22/wg21/docs/papers/2016/p0355r0.html всего несколько недель назад. Он будет рассмотрен на следующей неделе в Оулу. Я понятия не имею, каков будет результат. Но я могу заверить вас, что единодушного согласия не будет. <chrono> сам по себе был очень спорным и трудным для стандартизации для C++11. - person Howard Hinnant; 16.06.2016
comment
О хват! Удачи вам, сэр. Я обнаружил, что каким-то образом тяготею к этим проблемам со временем, потому что я потратил на них много времени, я думаю, но я продолжаю думать, чувак, если бы у меня была только библиотека @HowardHinnant, я мог бы ответить на этот вопрос проще. - person Jonathan Mee; 16.06.2016
comment
@JonathanMee: Пожалуйста, не стесняйтесь использовать мою библиотеку, чтобы ответить на эти вопросы! :-) Портирован на Mac, Linux и Windows. Windows скоро будет обновлена, чтобы танцевать автоматическую загрузку. - person Howard Hinnant; 16.06.2016
comment
Вы знаете, мне очень интересно изучать и объяснять вещи, которые ожидаются в стандарте. Стандарт действительно нуждается в этой библиотеке, поэтому я уверен, что вы, по крайней мере, уедете из Оулу с номером выпуска. Дайте мне знать, когда вы его получите, чтобы я мог начать ссылаться на него! - person Jonathan Mee; 16.06.2016

Обратите внимание, что элемент tm, обозначающий наименьшее приращение времени, равен tm_sec, который является int, который определен только в диапазоне:

Секунды после минуты [0,60] с C++11

Таким образом, вы не сможете сохранить долю секунды в tm, вам просто нужно будет отбросить число после запятой.

Как предложено Карстеном Купом можно просто прочитать год дважды, второй %Y затмит первый:

auto fmt = "%H:%M:%S.%Y %Z %b %d %Y";

Живой пример


При этом я бы рекомендовал против использовать strptime< /a> это функция POSIX, использующая стандартную функцию, такую ​​как get_time было бы предпочтительнее. У этого есть один небольшой недостаток; get_time не знает часовых поясов, как и tm, за исключением tm_isdst, который является:

Флаг перехода на летнее время. Значение положительное, если действует летнее время, нулевое, если нет, и отрицательное, если нет доступной информации.

Таким образом, вам, возможно, придется назначить tm_isdst независимо, если вы сделаете что-то вроде этого:

tm tmbuf;
stringstream str("20:48:01.469 UTC MAR 31 2016");

str >> get_time(&tmbuf, "%H:%M:%S.%Y UTC %b %d %Y");

Живой пример


Мой get_time ответ был немного лицемерным, потому что, говоря о важности стандартизации, я мог заставить его работать только на libc++. Поэтому я решил опубликовать более универсальное решение, которое также отбросит часовой пояс, поэтому вам снова нужно будет установить tm_isdst независимо:

tm tmbuf{};
stringstream str("20:48:01.469 UTC MAR 31 2016");
string tm_mon;

str >> get_time(&tmbuf, "%T");

str.ignore(std::numeric_limits<std::streamsize>::max(), 'C');

str >> tm_mon >> get_time(&tmbuf, "%d %Y");

for (const auto& i : { "JAN"s, "FEB"s, "MAR"s, "APR"s, "MAY"s, "JUN"s, "JUL"s, "AUG"s, "SEP"s, "OCT"s, "NOV"s, "DEC"s }) {
    if (equal(cbegin(tm_mon), cend(tm_mon), cbegin(i), cend(i), [](const unsigned char a, const unsigned char b) { return toupper(a) == b; })) break;
    ++tmbuf.tm_mon;
}

Живой пример

Это имеет 2 ключевые зависимости:

  1. Что часовой пояс всегда заканчивается символом 'C' (он должен быть в верхнем регистре)
  2. Что аббревиатуры месяца совпадают с одним из тех, что указаны в моем initializer_list
person Jonathan Mee    schedule 16.06.2016