【问题标题】:Calculating time difference in C计算C中的时间差
【发布时间】:2016-02-02 01:12:40
【问题描述】:

我尝试使用 <time.h> 库,但似乎 <time.h> 并不真正支持早于 1900 年甚至可能超过 x 年的时间。

我的问题是:

  1. 我可以使用<time.h> 来计算时间差(以秒、小时或天为单位)吗,例如:

    2744 年 5 月 1 日和 566 年 1 月 24 日

  2. <time.h> 是否支持闰年?意思是如果我计算上面的差异 - 那会计算闰年吗?我显然可以做出类似的东西:

    int toDays(struct tm *date)
    {
        int days = date->tm_yday;
        int year;
        for (year = 1; year < date->tm_year; ++year)
        {
            days += isLeapYear(year) ? 366 : 365;
        }
        return days;
    }
    

但话又说回来 - tm_year 从 1900 开始计数,对吗?

说实话,我对这个结构并不了解,我可能会自己写一个,除非有人可以帮助我。当我想像问题 1 那样计算年份时,使用 &lt;time.h&gt; 是否有意义?

【问题讨论】:

  • 这取决于您的操作系统和库,以及您机器的“比特性”。 32 位系统可能会在 1902..2037 范围之外出现问题(基于 1970-01-01 00:00:00 +00:00 作为纪元的 32 位有符号整数)。 64 位系统通常具有 64 位 time_t 类型,并且其日期范围是巨大的(宇宙死亡类型规模)。是的,函数知道闰年。他们是否了解世界上哪个地区从儒略历变为公历(如果确实如此)的所有变幻莫测,这是一个单独的讨论。时区也很复杂!
  • @OleksandrKravchuk:为什么要重复?这大约是整秒,而不是毫秒或纳秒,不是吗?
  • 2744y566y中的y有什么意义?你的意思是这些年是在第 28 和第 6 千年吗?或者年份是公元 2744 年和公元 566 年(其中 CE 也称为 AD;它是公历)?还是别的什么?
  • 如果您要及时回到 566 年 1 月 24 日这样的日期,您应该注意早期日期不使用公历。

标签: c time standard-library


【解决方案1】:
  1. time.h 函数的工作数据范围因系统而异。假设他们在 1970-2037 年工作是合理的。许多系统在更广泛的范围内工作。 OP 似乎早在 1900 年就可以使用。

  2. 当今使用的主要日历:公历始于 1582 年。其在世界范围内的采用各不相同。所以像 566 年 1 月 24 日这样的日期需要限定。

让我们假设 OP 的 time 工作时间超过 2000 到 2400 并且所有日期都是 Gregorian, projected 根据需要向后和向前:

使用 400 公历年是 400*365+97 天的“技巧”(这是 7 的倍数)。

所以要使用 OP 的函数获得天数,希望在 2000-2400 的 400 年范围内有效:

long long day_number(int year, int, month, int day) {
  #define DaysPer400Year (400LL*365 + 97)
  long long yearll = year - 2000LL;
  year = yearll % 400;
  int century400 =  yearll/400;
  if (year < 0) {
    year += 400;
    century400--;
  }
  long long number = century400 * DaysPer400Year;
  number += OP_day_number(year + 2000, month, day);
  return number;
}

【讨论】:

    【解决方案2】:

    如果您有 64 位系统,例如 Mac OS X(使用 10.11 El Capitan),则可以:

    #include <inttypes.h>
    #include <stdio.h>
    #include <time.h>
    
    int main(void)
    {
        int y1 = 27440;
        int m1 = 5;
        int d1 = 1;
        int y2 = 5660;
        int m2 = 1;
        int d2 = 24;
        struct tm tm1 = { .tm_year = y1 - 1900, .tm_mon = m1 - 1, .tm_mday = d1 };
        struct tm tm2 = { .tm_year = y2 - 1900, .tm_mon = m2 - 1, .tm_mday = d2 };
        time_t t1 = mktime(&tm1);
        time_t t2 = mktime(&tm2);
        size_t dt = t1 - t2;       // Dodgy assignment…I get away with it, but…
    
        char buffer[128];
        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm1);
        printf("t1 = %20s\n", buffer);
        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm2);
        printf("t2 = %20s\n", buffer);
    
        printf("t1 = %" PRIdMAX "\n", (intmax_t)t1);
        printf("t2 = %" PRIdMAX "\n", (intmax_t)t2);
        printf("dt = %zu seconds\n", dt);
        printf("dt = %zu hours\n", dt / 3600);
        printf("dt = %zu days\n", dt / (24 * 3600));
        return 0;
    }
    

    我得到的输出是:

    t1 = 27440-05-01 00:00:00
    t2 =  5660-01-24 00:00:00
    t1 = 803766009600
    t2 = 116447184000
    dt = 687318825600 seconds
    dt = 190921896 hours
    dt = 7955079 days
    

    试图回顾过去,事情并不是那么好。将两个年份值除以 10 收益率:

    t1 =  2744-05-01 00:00:00
    t2 =  0566-01-24 00:00:00
    t1 = 24435504000
    t2 = -1
    dt = 24435504001 seconds
    dt = 6787640 hours
    dt = 282818 days
    

    注意-1 表示错误;系统不愿意使用第一个千年中的日期(这使得dt 的计算不准确)。 AFAICT,在 Mac 上,mktime() 不会比 32 位有符号值更早回溯——它接受 1902-01-01 但拒绝 1901-01-01。 32 位限制为:

    -2147483647 = Fri Dec 13 12:45:53 1901  (US/Pacific)
    

    测试代码:

    static int test_year(int year)
    {
        struct tm tm1 = { .tm_year = year - 1900, .tm_mon = 0, .tm_mday = 1 };
        time_t t1 = mktime(&tm1);
        return (t1 != -1);
    }
    
    static void early_year(void)
    {
        int y_lo =  566;
        int y_hi = 1902;
        assert(test_year(y_lo) == 0);
        assert(test_year(y_hi) == 1);
    
        while (y_lo != y_hi)
        {
            int y_md = (y_lo + y_hi) / 2;
            printf("lo = %4d; hi = %4d; md = %4d\n", y_lo, y_hi, y_md);
            if (test_year(y_md) == 0)
                y_lo = y_md + 1;
            else
                y_hi = y_md - 1;
        }
        printf("Valid back to %4d\n", y_lo);
    }
    

    调用该代码的结果:

    lo =  566; hi = 1902; md = 1234
    lo = 1235; hi = 1902; md = 1568
    lo = 1569; hi = 1902; md = 1735
    lo = 1736; hi = 1902; md = 1819
    lo = 1820; hi = 1902; md = 1861
    lo = 1862; hi = 1902; md = 1882
    lo = 1883; hi = 1902; md = 1892
    lo = 1893; hi = 1902; md = 1897
    lo = 1898; hi = 1902; md = 1900
    lo = 1901; hi = 1902; md = 1901
    Valid back to 1902
    

    YMMV,俗话说;这将取决于您正在使用的系统。请注意,时间越往后走,时钟和日历就越不可靠。忽略 1712 年 2 月 30 日(在瑞典)这样的细节,你会得到 1584 到 20 世纪之间的各种日期,这些日期是各国从儒略历转换为公历的日期(1752 年是英国及其殖民地的转换日期,例如)。人们通常向后应用“公历”。

    【讨论】:

    • 使用struct tm tm1 = { .tm_year = y1 - 1900, .tm_mon = m1 - 1, .tm_mday = d1 }; ... time_t t1 = mktime(&amp;tm1);,是否会将其他字段初始化为0?
    • @chux: 是的——缺少初始化器等同于默认(实际上为零)初始化器。
    • difftime(t1,t2)/(24*60*60L) 避免了“狡猾的分配”。
    【解决方案3】:

    你可以试试 difftime() 函数。

    首先,您必须定义两个可以保存所需数据的结构,例如

    struct tm start_date, end_date;
    

    然后根据您的日期用数据填充结构。

    然后使用difftime()作为

    seconds = difftime(mktime(&end_date),mktime(&start_date))
    

    以下示例将帮助您理解流程。

    #include<stdio.h>
    #include<time.h>
    int main()
    {
       time_t now;
       struct tm start_date, end_date;
       start_date = *localtime(&now);
       end_date = *localtime(&now);
    
       start_date.tm_year = 1013;
       end_date.tm_year = 1015;
    
       unsigned long int diff = difftime(mktime(&end_date), mktime(&start_date));
       printf("DIFF: [%lu]",diff);
       return(0);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多