【问题标题】:Formatted elapsed time in CC中格式化的经过时间
【发布时间】:2017-08-23 17:47:55
【问题描述】:

我正在编写一个函数,除其他外,它必须打印出经过的时间计数器。它通过引用接收开始时间_start,并将其与current 进行比较,两者都键入为time_t。我想使用strftime() 以 ISO 8601 格式打印出观察到的时间增量。这是我尝试做的:

// Negative start time implies program has not begun
if (*_start > 0) {
    time_t elapsed = current - *_start;
    strftime(time_str, sizeof(time_str) - 1, "%T", localtime(&elapsed));
    printf("%s\n", time_str);
}

这是我运行程序后立即得到的输出

01:00:00
01:00:00
01:00:01
01:00:01
01:00:01

秒工作正常,如果我让它运行更长时间,它们会按预期增加,分钟也是如此,但是小时从 01 开始而不是 00。为什么会发生这种情况?我怎样才能让小时数开始归零,比如分钟数?

【问题讨论】:

  • 通过使用localtime,您实际上得到了自 1970 年 1 月 1 日午夜以来的日期您的本地时区。例如,在美国西海岸,如果elapsed_time = 0 然后strftime 表示16:00:00,因为localtime(0) 这里是Wed Dec 31 16:00:00 1969。我猜你是在欧洲大陆时间。您可以改用 gmtime,但这最多只能工作 24 小时。

标签: c time c99 strftime


【解决方案1】:

time_t 通常(见编辑)存储绝对时间戳(自 1970 年 1 月 1 日午夜 UTC 以来的秒数)。通过计算current - *_start,您将获得以秒为单位的经过时间(根据需要),但是通过将其传递给localtimestrftime,您是在告诉计算机从程序启动以来经过的时间并将其视为自 UTC 1-1-1970 午夜以来经过的时间。

我猜这恰好是您系统本地时区的 01:00:00。

我不知道打印经过时间的 C99 函数,但自己写一个并不难。

void format_elapsed_time(char *time_str, int total_seconds_elapsed) {
    int hours_elapsed = total_seconds_elapsed / 3600;
    int minutes_elapsed = (total_seconds_elapsed % 3600) / 60;
    int seconds_elapsed = total_seconds_elapsed % 60;
    sprintf(time_str, "%02i:%02i:%02i", hours_elapsed, minutes_elapsed, seconds_elapsed);
}

编辑:正如@chux 指出的那样,time_t 不必将时间戳存储为自 1970 年 1 月 1 日以来的秒数。详情请见this answer

【讨论】:

  • 顺便说一句,这不会处理闰秒 :-) 我不知道你为什么不只是建议 gmtime 而不是 localtime
  • time_t 很常见,因为自 1970 年 1 月 1 日以来的秒数,但 C 未指定。time_t 可以是任何真实类型,包括从任何纪元开始的 double
  • @paxdiablo - 部分是因为我没想到。 :-) 部分是因为取一个经过的时间值并将其称为绝对时间戳,并指望gmtime + strftime 通常会做我想做的事情,感觉有点恶心。部分原因是我知道有时它不会做我想做的事(例如,自程序启动以来已经过了 24 小时)。
【解决方案2】:

要方便地找到 2 个 time_t 之间经过的秒数,请使用 difftime() 减去 2 个 time_t 值,在 C 中未指定以秒为单位。

double difftime(time_t time1, time_t time0); C11dr §7.26.2.2
difftime 函数将以秒为单位的差值返回为double

double elapsed_seconds = difftime(current, *_start);
printf("elapsed_seconds: %f\n", elapsed_seconds);

【讨论】:

    【解决方案3】:

    如果你想使用这个方法,你应该知道time()返回UTC。如果然后从 local 时间中减去它(由localtime() 返回),时区将对结果产生影响(您的特定时区可能会从 UTC 中删除一小时)。

    考虑以下完整的程序,类似于您的 sn-p:

    #include <stdio.h>
    #include <time.h>
    #include <unistd.h>
    
    int main(void) {
        time_t start = time(0);
        char time_str[100];
        for (int i = 5; i > 0; i--) {
            sleep (1);
            time_t elapsed = time(0) - start;
            strftime(time_str, sizeof(time_str) - 1, "%T", localtime(&elapsed));
            printf("%s\n", time_str);
        }
    
        return 0;
    }
    

    还要考虑到我的特定时区与 UTC 相差 8 小时:

    pax> date ; date -u
    Thursday 30 March  10:25:57 AWST 2017
    Thursday 30 March  02:25:57 UTC 2017
    

    当我运行它时,我看到:

    08:00:01
    08:00:02
    08:00:03
    08:00:04
    08:00:05
    

    您可以在此处看到与 UTC 的 8 小时时差会影响该值。 fix 实际上非常简单:根本不要使用本地时间。如果你用gmtime() 替换localtime() 调用,你会得到你期望的输出:

    00:00:01
    00:00:02
    00:00:03
    00:00:04
    00:00:05
    

    【讨论】:

    • 次要:当我读到“如果包含终止空字符在内的结果字符总数不超过 maxsize,则 strftime 函数返回放入由 s 指向的数组中的字符数,不包括终止空字符”strftime(..., sizeof(time_str) - 0,...); 就足够了。你的想法?
    【解决方案4】:

    我意识到这个问题已经有了答案,但我想我可能会稍微转移一下焦点并扩展已接受的答案。由于 OP 指定他们正在处理 经过的时间,因此可以使用 clock() 函数完全避免处理绝对时间戳。

    clock_t start = clock();
    .
    .
    clock_t end = clock();
    double elapsed = (end - start) / (double)CLOCKS_PER_SEC;
    

    然后您可以调用名义上修改的 format_elapsed_time() 函数,如下所示:

    void format_elapsed_time(char *time_str, double elapsed) {
        int h, m, s, ms;
    
        h = m = s = ms = 0;
        ms = elapsed * 1000; // promote the fractional part to milliseconds
        h = ms / 3600000;
        ms -= (h * 3600000);
        m = ms / 60000;
        ms -= (m * 60000);
        s = ms / 1000;
        ms -= (s * 1000);
        sprintf(time_str, "%02i:%02i:%02i.%03i", h, m, s, ms);
    }
    

    【讨论】:

      猜你喜欢
      • 2011-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 1970-01-01
      相关资源
      最近更新 更多