使用 std::get_time 将时间字符串转换为 std::time_t:错误的结果
Convert time-string to std::time_t using 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
分开的,只有最后两个才是正确的?
Edit
Since there seems to be a bug in clang's std::get_time
(and std::put_time
), here is my version information:
$ 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
正如我在评论中所说,您的所有输出都是错误的。好像是clang的bug。我在 the Bugzilla page of the LLVM project 上进行了搜索,但一无所获。也许您想提交带有示例代码的错误报告。
解决方法是使用 std::sscanf()
手动解析字符串并填充 std::tm
结构。以下代码为您的输入字符串 i
执行此操作。看全example with the output of std::get_time()
and the std::sscanf()
method at 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 年以来的年份。请参阅 description of std::tm
here。
进一步需要将tm_isdst
设置为-1让std::mktime()
判断夏令时是否生效。
转换std::time_t
Linux timestamp back to a std::string
of your given format: %Y:%m:%d %H:%M:%S
you can use std::strftime()
with std::localtime(). See live example on 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;
}
Here is another library you could use 来解决这个错误。而且这个库是可移植的。如果您有任何问题,我会在数小时内解决。它只是一个单一的header,没有来源,所以很容易"install"。只需将 header 放在包含路径中的某处:
#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
我正在尝试按时间顺序对照片进行排序。因此,我从 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
分开的,只有最后两个才是正确的?
Edit
Since there seems to be a bug in clang's
std::get_time
(andstd::put_time
), here is my version information:$ 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
正如我在评论中所说,您的所有输出都是错误的。好像是clang的bug。我在 the Bugzilla page of the LLVM project 上进行了搜索,但一无所获。也许您想提交带有示例代码的错误报告。
解决方法是使用 std::sscanf()
手动解析字符串并填充 std::tm
结构。以下代码为您的输入字符串 i
执行此操作。看全example with the output of std::get_time()
and the std::sscanf()
method at 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 年以来的年份。请参阅 description of std::tm
here。
进一步需要将tm_isdst
设置为-1让std::mktime()
判断夏令时是否生效。
转换std::time_t
Linux timestamp back to a std::string
of your given format: %Y:%m:%d %H:%M:%S
you can use std::strftime()
with std::localtime(). See live example on 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;
}
Here is another library you could use 来解决这个错误。而且这个库是可移植的。如果您有任何问题,我会在数小时内解决。它只是一个单一的header,没有来源,所以很容易"install"。只需将 header 放在包含路径中的某处:
#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