使用适当的 tm_mday 将两个 time_t 之间的差异转换为 tm
Posted
技术标签:
【中文标题】使用适当的 tm_mday 将两个 time_t 之间的差异转换为 tm【英文标题】:get difference between two time_t into a tm with proper tm_mday 【发布时间】:2012-04-09 20:38:29 【问题描述】:根据输入数据的数量,我有一个可以在几秒或几天内运行的程序。在我的程序结束时,我想打印经过的“时钟墙”时间:如果小于一分钟,则以秒为单位,如果小于一小时,则以分钟和秒为单位,如果是,则以小时-分钟-秒为单位少于 1 天,否则以天-时-分-秒为单位。这是我正在使用的代码:
#include <cstdio>
#include <ctime>
#include <unistd.h> // for sleep
int main (int argc, char ** argv)
time_t startRawTime, endRawTime;
time (&startRawTime);
printf ("%s", ctime (&startRawTime));
sleep (3); // any preprocessing of input data
time (&endRawTime);
printf ("%s", ctime (&endRawTime));
printf ("%.0fs\n", difftime (endRawTime, startRawTime));
time_t elapsed = static_cast<time_t>(difftime (endRawTime, startRawTime));
struct tm * ptm = gmtime (&elapsed);
printf ("%id %ih %im %is\n", ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
return 0;
这是打印的内容:
Mon Apr 9 14:43:16 2012
Mon Apr 9 14:43:19 2012
3s
1d 0h 0m 3s
当然最后一行是错误的(应该是“0d”)。似乎可以通过打印ptm->tm_mday - 1
轻松解决。但是,当两个日期之间确实经过了一天时,ptm->tm_mday
也将是“1”。所以在这种情况下,我不想让它显示为“0d”。
那么有没有办法正确处理这个问题?还是应该将difftime
的结果作为双精度数(即秒数),然后计算自己的秒/分/小时/天数?
备注:我的代码只用在Linux上,用gcc -lstdc++
编译。
【问题讨论】:
"compiled with gcc -lstdc++" -- 为什么不使用g++
呢?它负责确定用于编译 C++ 代码的选项。
这真是滥用时间库。例如,一个 tm 结构代表一个特定的日期,其中 tm_mday 是一个月中的某天,而不是天数。 <chrono>
是一个类型安全的时间库,可以防止你犯这种错误。
【参考方案1】:
time_t
值表示特定的时间点。 difftime
的结果是两个时刻之间的间隔,以秒为单位。这是完全不同的事情。
在您的代码中,difftime()
返回 3.0
,因为两个指定时间之间有 3 秒。将其转换为time_t
会给你在纪元后 3 秒的时间;在大多数系统上,这将是 1970 年 1 月 1 日格林威治标准时间午夜后 3 秒。tm_mday
的值为 1,因为那是该月的第一天。
您可以通过从 tm_mday
值中减去 1 来完成这项工作,因为 tm_mday
是基于 1 而不是基于 0。但是对于更长的时间间隔,您仍然会得到毫无意义的结果。例如,间隔 31.5 天会给你 2 月 1 日中午,因为 1 月有 31 天;这与您尝试获取的信息无关。
只需将difftime()
的结果视为double
(因为它就是这样)并通过简单的算术计算天数、小时数、分钟数和秒数。
(由于某些可移植性的损失,您可以直接减去 time_t
值而不是使用 difftime()
。这将使一些算术更容易一些,但它会在 time_t
值的系统上中断不是自某个纪元以来的整数秒数。difftime()
的存在是有原因的。)
当然最后一行是错误的(应该是“0d”)。好像可以 通过打印“ptm->tm_mday - 1”可以轻松解决。但是,ptm->tm_mday 当两者之间确实经过了一天时,也将是“1” 日期。所以在这种情况下,我不想让它显示为“0d”。
这是不正确的;如果时间间隔刚刚超过 1 天,ptm->tm_mday
将是 2
。您可以通过对代码稍作修改来验证这一点:
time (&endRawTime);
endRawTime += 86400; // add this line
printf ("%s", ctime (&endRawTime));
当我进行此更改时,我会得到以下输出:
Mon Apr 9 13:56:49 2012
Tue Apr 10 13:56:52 2012
86403s
2d 0h 0m 3s
这可以通过从ptm->tm_mday
中减去 1 来纠正。但同样,这不是正确的方法。
【讨论】:
谢谢,有兴趣的朋友,这里有一个计算小时、分钟和秒的方法***.com/a/2419597/597069【参考方案2】:这是一个使用 <chrono>
库的示例,这是一个类型安全的计时库,可以防止您犯下您所犯的错误。在 chrono 中,time_points 和 durations 不可互换,如果您尝试以这种方式使用它们,则会出现编译器错误。
#include <chrono>
#include <iostream>
#include <thread>
#include <cassert>
template<typename Rep,typename Period>
void print_duration(std::chrono::duration<Rep,Period> t)
assert(0<=t.count() && "t must be >= 0");
// approximate because a day doesn't have a fixed length
typedef std::chrono::duration<int,std::ratio<60*60*24>> days;
auto d = std::chrono::duration_cast<days>(t);
auto h = std::chrono::duration_cast<std::chrono::hours>(t - d);
auto m = std::chrono::duration_cast<std::chrono::minutes>(t - d - h);
auto s = std::chrono::duration_cast<std::chrono::seconds>(t - d - h - m);
if(t>=days(1))
std::cout << d.count() << "d ";
if(t>=std::chrono::hours(1))
std::cout << h.count() << "h ";
if(t>=std::chrono::minutes(1))
std::cout << m.count() << "m ";
std::cout << s.count() << "s";
int main()
auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(std::chrono::seconds(3));
auto finish = std::chrono::steady_clock::now();
print_duration(finish-start);
std::cout << '\n';
GCC 的注意事项
旧版本的 GCC 有 monotonic_clock 而不是 stable_clock。 4.7 有 stable_clock。
为了访问 std::this_thread::sleep_for,您可能出于某种原因必须定义 _GLIBCXX_USE_NANOSLEEP。
【讨论】:
谢谢,我不知道 chrono,但显然我需要更新版本的 gcc @wfoolhill 为 gcc 添加了一些注释。如果您至少使用 GCC 4.5,您应该能够使用 chrono。我不确定是否支持比这更早,除非它肯定不在 4.2 中。以上是关于使用适当的 tm_mday 将两个 time_t 之间的差异转换为 tm的主要内容,如果未能解决你的问题,请参考以下文章
如何将包含时间的字符串变量转换为 c++ 中的 time_t 类型?