使用适当的 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-&gt;tm_mday - 1 轻松解决。但是,当两个日期之间确实经过了一天时,ptm-&gt;tm_mday 也将是“1”。所以在这种情况下,我不想让它显示为“0d”。

那么有没有办法正确处理这个问题?还是应该将difftime 的结果作为双精度数(即秒数),然后计算自己的秒/分/小时/天数?

备注:我的代码只用在Linux上,用gcc -lstdc++编译。

【问题讨论】:

"compiled with gcc -lstdc++" -- 为什么不使用g++呢?它负责确定用于编译 C++ 代码的选项。 这真是滥用时间库。例如,一个 tm 结构代表一个特定的日期,其中 tm_mday 是一个月中的某天,而不是天数。 &lt;chrono&gt; 是一个类型安全的时间库,可以防止你犯这种错误。 【参考方案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-&gt;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-&gt;tm_mday 中减去 1 来纠正。但同样,这不是正确的方法。

【讨论】:

谢谢,有兴趣的朋友,这里有一个计算小时、分钟和秒的方法***.com/a/2419597/597069【参考方案2】:

这是一个使用 &lt;chrono&gt; 库的示例,这是一个类型安全的计时库,可以防止您犯下您所犯的错误。在 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 类型?

C/C++输入两个任意日期求相隔天数

将纪元转换为 time_t

C语言计算两个时间之间的时间差,精确到秒。 现在我用的是time_t来计算的,但是我发现这个tim

从 time_t 和 tm 转换的错误值

使用 Visual Studio 获取 time_t 的最大值