C语言 将天数添加到日期后,结果不正确

kr98yfug  于 2023-01-25  发布在  其他
关注(0)|答案(1)|浏览(161)

使用下面的函数,简单地将天数添加到日期(yyyymmdd),在几年中都能很好地工作。

int dateplusdays(int datein, int days) {
    int year, month, day;
    int dateout;
    struct tm date;
    time_t secs;

    year = (int)floor(datein / 10000.0);
    month = (int)floor(datein / 100.0) - year * 100;
    day = datein - month * 100 - year * 10000;

    date.tm_sec = 0;
    date.tm_min = 0;
    date.tm_hour = 12;
    date.tm_year = year - 1900;
    date.tm_mon = month - 1;
    date.tm_mday = day;
    date.tm_isdst = -1;
    
    secs = mktime(&date) + days * 86400;
    date = *localtime(&secs);

    dateout = (date.tm_year + 1900) * 10000 + (date.tm_mon + 1) * 100 + date.tm_mday;
    
    return dateout;
}

我用这个测试代码从1900到2100进行了压力测试。没有错误!

for (i = 19000101; i < 21001231; i++) {
    int a = dateplusdays(i, 0); // make date out of i
    if (i == a) { // check for valid date
        int b = dateplusdays(a, 1);
        int c = dateplusdays(b, 1);
        if (b == c)
            printf("i:%d a:%d b:%d c:%d\n", i, a, b, c);
    }
}

现在......当将date.tm_hour12更改为0时,我在非常特定的日期上得到了184个错误,这些错误完全不规则地分布在1900 - 2100年的范围内(例如,30.10.2022加上1天会得到30.10.2022)。

i:19160930 a:19160930 b:19161001 c:19161001
i:19161001 a:19161001 b:19161001 c:19161001
...
i:20221029 a:20221029 b:20221030 c:20221030
i:20221030 a:20221030 b:20221030 c:20221030
...
i:20381030 a:20381030 b:20381031 c:20381031
i:20381031 a:20381031 b:20381031 c:20381031

最重要的是,只关注9月至12月。

geohei@vm92:~/Devel$ ./dateplusdays | cut -c7-8 | sort | uniq -c
     47 09
    131 10
      6 11

我错过了什么?

dwbf0jvd

dwbf0jvd1#

您错过了冬季和夏季时间之间的切换-标准时间和夏令时,或者使用任何其他术语。选择12:00:00作为一天中的时间并不是偶然的。
你应该使用date.tm_day = day + days;来得到正确的答案(而不是加上days * 86400)。mktime()函数将日期标准化。注意,在某些日子里有82800秒和90000秒,这是因为从UTC切换到了时区偏移量。
旁注:你到底为什么要用浮点数而不是整数除法呢?
嗯:我尝试了你的代码与最低限度的额外(头部,一个实际的main()函数),只有得到输出时,我改变了12在代码中的0-运行在MacBook Pro与macOS大苏尔11.7.2。
你引用的日期接近10月底,时钟“回落”的时候。夏令时于1918年在美国首次引入,1921年后停止。1945年重新引入一年,然后在1965年“永久”。规则在1991年改变,所以秋季时间开关从“10月的最后一个星期日”改为“11月的第一个星期日”,所以我从你身上看到了不同的价值观。(Spring时间转换的规则也同时从“四月的第一个星期天”变成了“三月的第二个星期天”。)我猜你住在北美以外的地方(或者至少在美国以外)。
下面是一些修改过的代码,利用了mktime()的能力,使用了midnight。它还使用ISO 8601符号来格式化数据。但是,除非使用-DUSE_BROKEN_CODE作为编译时选项进行编译,否则它不会产生任何输出。

#include <stdio.h>
#include <time.h>

static int dateplusdays(int datein, int days)
{
    struct tm date = { 0 };

    int year = datein / 10000;
    int month = (datein / 100) % 100;
    int day = datein % 100;

    date.tm_sec = 0;
    date.tm_min = 0;
    date.tm_hour = 0;
    date.tm_year = year - 1900;
    date.tm_mon = month - 1;
    date.tm_isdst = -1;

#ifdef USE_BROKEN_CODE
    date.tm_mday = day;
    time_t secs = mktime(&date) + days * 86400;
#else
    date.tm_mday = day + days;
    time_t secs = mktime(&date);
#endif /* USE_BROKEN_CODE */

    date = *localtime(&secs);

    int dateout = (date.tm_year + 1900) * 10000 + (date.tm_mon + 1) * 100 + date.tm_mday;

    return dateout;
}

static void print_date(int date)
{
    printf("%.4d-%.2d-%.2d", date / 10000, (date / 100) % 100, date % 100);
}

int main(void)
{
    for (int i = 19000101; i < 21001231; i++)
    {
        int a = dateplusdays(i, 0); // make date out of i
        if (i == a)   // check for valid date
        {
            int b = dateplusdays(a, 1);
            int c = dateplusdays(b, 1);
            if (b == c)
            {
                printf("i: %d  a: ", i);
                print_date(a);
                printf("  b: ");
                print_date(b);
                printf("  c: ");
                print_date(c);
                putchar('\n');
            }
        }
    }

    return 0;
}

我住在美国科罗拉多州,所以当我编译这个程序时,我得到了这样的结果:

i: 19181026  a: 1918-10-26  b: 1918-10-27  c: 1918-10-27
i: 19181027  a: 1918-10-27  b: 1918-10-27  c: 1918-10-27
i: 19191025  a: 1919-10-25  b: 1919-10-26  c: 1919-10-26
i: 19201030  a: 1920-10-30  b: 1920-10-31  c: 1920-10-31
i: 19201031  a: 1920-10-31  b: 1920-10-31  c: 1920-10-31
i: 19210521  a: 1921-05-21  b: 1921-05-22  c: 1921-05-22
i: 19210522  a: 1921-05-22  b: 1921-05-22  c: 1921-05-22
i: 19450929  a: 1945-09-29  b: 1945-09-30  c: 1945-09-30
i: 19450930  a: 1945-09-30  b: 1945-09-30  c: 1945-09-30
i: 19651030  a: 1965-10-30  b: 1965-10-31  c: 1965-10-31
i: 19651031  a: 1965-10-31  b: 1965-10-31  c: 1965-10-31
i: 19661029  a: 1966-10-29  b: 1966-10-30  c: 1966-10-30
i: 19661030  a: 1966-10-30  b: 1966-10-30  c: 1966-10-30
…
i: 19881029  a: 1988-10-29  b: 1988-10-30  c: 1988-10-30
i: 19881030  a: 1988-10-30  b: 1988-10-30  c: 1988-10-30
i: 19891028  a: 1989-10-28  b: 1989-10-29  c: 1989-10-29
i: 19891029  a: 1989-10-29  b: 1989-10-29  c: 1989-10-29
i: 19901027  a: 1990-10-27  b: 1990-10-28  c: 1990-10-28
i: 19901028  a: 1990-10-28  b: 1990-10-28  c: 1990-10-28
i: 19911026  a: 1991-10-26  b: 1991-10-27  c: 1991-10-27
i: 19911027  a: 1991-10-27  b: 1991-10-27  c: 1991-10-27
i: 19921024  a: 1992-10-24  b: 1992-10-25  c: 1992-10-25
i: 19921025  a: 1992-10-25  b: 1992-10-25  c: 1992-10-25
…
i: 20201031  a: 2020-10-31  b: 2020-11-01  c: 2020-11-01
i: 20201101  a: 2020-11-01  b: 2020-11-01  c: 2020-11-01
i: 20211106  a: 2021-11-06  b: 2021-11-07  c: 2021-11-07
i: 20211107  a: 2021-11-07  b: 2021-11-07  c: 2021-11-07
i: 20221105  a: 2022-11-05  b: 2022-11-06  c: 2022-11-06
i: 20221106  a: 2022-11-06  b: 2022-11-06  c: 2022-11-06
…
i: 20371031  a: 2037-10-31  b: 2037-11-01  c: 2037-11-01
i: 20371101  a: 2037-11-01  b: 2037-11-01  c: 2037-11-01
i: 20381106  a: 2038-11-06  b: 2038-11-07  c: 2038-11-07
i: 20381107  a: 2038-11-07  b: 2038-11-07  c: 2038-11-07
i: 20391105  a: 2039-11-05  b: 2039-11-06  c: 2039-11-06
i: 20391106  a: 2039-11-06  b: 2039-11-06  c: 2039-11-06
…
i: 20981101  a: 2098-11-01  b: 2098-11-02  c: 2098-11-02
i: 20981102  a: 2098-11-02  b: 2098-11-02  c: 2098-11-02
i: 20991031  a: 2099-10-31  b: 2099-11-01  c: 2099-11-01
i: 20991101  a: 2099-11-01  b: 2099-11-01  c: 2099-11-01
i: 21001106  a: 2100-11-06  b: 2100-11-07  c: 2100-11-07
i: 21001107  a: 2100-11-07  b: 2100-11-07  c: 2100-11-07

相关问题