Преобразование строки времени в std:: time t с помощью std::get time: wrong result
Я пытаюсь сортировать фотографии в хронологическом порядке. Поэтому я извлекаю время в виде строки из данных EXIF, которые затем преобразую в std::time_t
. Однако иногда я получаю неверный результат. Я свел проблему к этому минимальному примеру. Он имеет три временные строки, одну секунду друг от друга:
#include <vector>
#include <string>
#include <iostream>
#include <ctime>
#include <iomanip>
#include <sstream>
int main()
{
std::vector<std::string> vec;
vec.push_back("2016:07:30 09:27:06");
vec.push_back("2016:07:30 09:27:07");
vec.push_back("2016:07:30 09:27:08");
for ( auto & i : vec )
{
struct std::tm tm;
std::istringstream iss;
iss.str(i);
iss >> std::get_time(&tm,"%Y:%m:%d %H:%M:%S");
std::time_t time = mktime(&tm);
std::cout << i << " time = " << time << std::endl;
}
}
составлен с clang++ -std=c++14 test.cpp
.
вывод:
2016:07:30 09:27:06 time = 1469867226
2016:07:30 09:27:07 time = 1469863627
2016:07:30 09:27:08 time = 1469863628
что, очевидно, неправильно, они должны быть 1
apart, что верно только для последние два?
редактировать
так как, кажется, есть ошибка в clang's
std::get_time
(иstd::put_time
), вот мои версии:$ clang --version Apple LLVM version 8.1.0 (clang-802.0.42) Target: x86_64-apple-darwin16.7.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
2 ответов
как я сказал в своем комментарии, все ваши выходные данные неверны. Кажется, что это ошибка в clang. Я искал на страница Bugzilla проекта LLVM но ничего не нашли. Может быть, вы хотите отправить отчет об ошибке с вашим примером кода.
обходным путем является разбор строки вручную с помощью std::sscanf()
и заполнить std::tm
структура. Следующий код делает это для вашей входной строки i
. Увидеть все пример с выводом std::get_time()
и std::sscanf()
способ на ideone. Если строка отличается, адаптируйте это решение к вашим потребностям.
это решение использует %d:%d:%d %d:%d:%d
в строке формата. Вы также можете использовать %4d:%2d:%2d %2d:%2d:%2d
который будет принимать только числа, длина которых меньше или равна числу между %
и d
.
выход:
2016:07:30 09:27:06 | sscanf() time = 1469870826
2016:07:30 09:27:06 | std::get_time() time = 1469870826
2016:07:30 09:27:07 | sscanf() time = 1469870827
2016:07:30 09:27:07 | std::get_time() time = 1469870827
2016:07:30 09:27:08 | sscanf() time = 1469870828
2016:07:30 09:27:08 | std::get_time() time = 1469870828
код:
#include <vector>
#include <string>
#include <iostream>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <cstring>
int main()
{
std::vector<std::string> vec;
vec.push_back("2016:07:30 09:27:06");
vec.push_back("2016:07:30 09:27:07");
vec.push_back("2016:07:30 09:27:08");
for (auto & i : vec)
{
struct std::tm tm;
/* std::sscanf() method: */
std::memset(&tm, 0, sizeof(tm));
if (6 != std::sscanf(i.c_str(), "%d:%d:%d %d:%d:%d",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec))
{
return -1;
}
/* correct the numbers according to:
* see: http://en.cppreference.com/w/cpp/chrono/c/tm */
--tm.tm_mon;
tm.tm_year -= 1900;
/* mktime determines if Daylight Saving Time was in effect
* see: http://en.cppreference.com/w/cpp/chrono/c/mktime */
tm.tm_isdst = -1;
std::time_t time = std::mktime(&tm);
std::cout << i << " | sscanf() time = " << time << std::endl;
/************************************************************/
/* std::get_time() method: */
std::istringstream iss;
iss.str(i);
iss >> std::get_time(&tm, "%Y:%m:%d %H:%M:%S");
time = std::mktime(&tm);
std::cout << i << " | std::get_time() time = " << time << std::endl;
}
}
необходимо вычесть один из tm_mon
потому что это начинается с 0. Дальше tm_year
участник лет с 1900. Вижу описание std::tm
здесь.
Далее необходимо установить tm_isdst
-1, чтобы позволить std::mktime()
определить, если Летнее Время действовал.
преобразование std::time_t
метка времени Linux обратно в std::string
вашего данного формата:%Y:%m:%d %H:%M:%S
можно использовать std::strftime()
С std:: localtime (). см. живой пример на ideone.
выход:
time = 1469870826 | 2016:07:30 09:27:06
код:
#include <vector>
#include <string>
#include <iostream>
#include <ctime>
int main()
{
char buff[20];
time_t timestamp = 1469870826;
std::strftime(buff, sizeof(buff), "%Y:%m:%d %H:%M:%S", std::localtime(×tamp));
std::string timeStr(buff);
std::cout << "time = " << timestamp << " | " << timeStr;
}
вот еще одна библиотека, которую вы можете использовать чтобы обойти ошибку. И эта библиотека портативна. Если у вас возникнут какие-то проблемы, я все исправлю в течение нескольких часов. Это всего лишь один заголовок, без источников, и поэтому очень легко "установить". Просто поместите заголовок где-нибудь в свой путь включения:
#include "date.h"
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
int main()
{
std::vector<std::string> vec;
vec.push_back("2016:07:30 09:27:06");
vec.push_back("2016:07:30 09:27:07");
vec.push_back("2016:07:30 09:27:08");
for ( auto & i : vec )
{
date::sys_seconds tm;
std::istringstream iss{i};
iss >> date::parse("%Y:%m:%d %H:%M:%S", tm);
std::cout << i << " time = " << tm.time_since_epoch().count() << std::endl;
std::cout << date::format("%Y:%m:%d %H:%M:%S\n", tm);
}
}
выход:
2016:07:30 09:27:06 time = 1469870826
2016:07:30 09:27:06
2016:07:30 09:27:07 time = 1469870827
2016:07:30 09:27:07
2016:07:30 09:27:08 time = 1469870828
2016:07:30 09:27:08