C语言 计算两个日期之间的工作日的函数不按预期工作

lnlaulya  于 2023-11-16  发布在  其他
关注(0)|答案(2)|浏览(227)

编辑:包括现在所有的代码,显然问题可能在countDays()之外。

我正在写一个函数,它接受两个日期作为输入,并返回一个带有两个变量的结构:m_TotalDays个(两个日期之间的总天数)和m_WorkDays(总天数减去周末和特定的国家法定假日)。但是,当两个给定日期的年份不同时,m_WorkDays的计算是错误的,而对于年份相同的人,它给出的答案比正确答案少一天。
该函数还使用了另外两个函数isWorkDay()totalDays(),但是,我确信问题不是来自这些函数。我相信错误一定是在countDays()中的for循环中,但我似乎没有找到问题可能在哪里。

#include <stdio.h>
#include <stdbool.h>

int m_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

typedef struct
{
    int m_TotalDays;
    int m_WorkDays;
} TResult;

bool isDayValid(int y, int m, int d);
bool isLeapYear(int y);
int totalDays(int y, int m, int d);
bool isWorkDay(int y, int m, int d);
TResult countDays (int y1, int m1, int d1, int y2, int m2, int d2);

int main()
{
    int y1 = 2023;
    int m1 = 1;
    int d1 = 1;

    int y2 = 2023;
    int m2 = 12;
    int d2 = 31;

    countDays(y1, m1, d1, y2, m2, d2);
}

bool isLeapYear(int y) {
    if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)
        return true;
    else
        return false;
}

bool isDayValid(int y, int m, int d)
{
    int months_31[7] = { 1, 3, 5, 7, 8, 10, 12 };
    int months_30[4] = { 4, 6, 9, 11 };

    if (y < 2000)    //year is invalid
        return false;
    if (m < 1 || m > 12)    //month is invalid
        return false;
    for (int i = 0; i < 7; i++)     //day is invalid
        if (m == months_30[i])
            if (d > 31)
                return false;
    for (int i = 0; i < 4; i++)
        if (m == months_30[i])
            if (d > 30)
                return false;
    if (!(isLeapYear(y)) && m == 2 && d > 28)    //if february > 28 on a normal year
        return false;
    else if ((isLeapYear(y)) && m == 2 && d > 29)    //if february > 29 on a leap year
        return false;
    else
        return true;
}

int totalDays(int y, int m, int d)
{
    if (isDayValid(y, m, d))
    {
        int leap_count = 0;
        int years_index = y - 2000;
        for (int i = 2000; i < y; i++) {
            if (isLeapYear(i))
                leap_count++;
        }
        int add_years = leap_count + 365 * (years_index);

        int add_months = 0;
        if (isLeapYear(y))
            m_days[1] = 29;

        for (int i = 0; i < m - 1; i++)
            add_months += m_days[i];

        int add_days = d - 1;

        int total_days = add_years + add_months + add_days;

        //printf("Days since: %d\n", total_days);
        return total_days;
    }
}

bool isWorkDay(int y, int m, int d)
{
    int day_in_week = totalDays(y, m, d) % 7;

    //printf("Day in week: %d\n", day_in_week);

    if ((day_in_week == 0) || (day_in_week == 1))
    {
        return false;
    }
    if ((m == 1 && d == 1) ||
        (m == 5 && (d == 1 || d == 8)) ||
        (m == 7 && (d == 5 || d == 6)) ||
        (m == 9 && d == 28) ||
        (m == 10 && d == 28) ||
        (m == 11 && d == 17) ||
        (m == 12 && (d == 24 || d == 25 || d == 26)))
    {
        //printf("not work day\n"); 
        return false; 
    } 
    //printf("work day\n");
    return true;
}

TResult countDays(int y1, int m1, int d1, int y2, int m2, int d2)
{
    TResult res = { 0, 0 };

    if (y2 > y1 || (y2 == y1 && (m2 > m1 || (m2 == m1 && d2 >= d1))))
    {
        int total_days1 = totalDays(y1, m1, d1);
        int total_days2 = totalDays(y2, m2, d2);
        res.m_TotalDays = (total_days2 - total_days1) + 1;

        int nonWorkDays = 0;

        for (int i = 1; i <= res.m_TotalDays; i++) {
            if (!(isWorkDay(y1, m1, d1))) {
                nonWorkDays++;
            }
            if (d1 < d2)
                d1++;
            if (d1 < m_days[m1 - 1]) {
                d1++;
            } else {
                d1 = 1;
                if (m1 < 12) {
                    m1++;
                } else {
                    m1 = 1;
                    y1++;
                }
            }
        }
        res.m_WorkDays = res.m_TotalDays - nonWorkDays;

        printf("m_TotalDays: %d\n", res.m_TotalDays);
        printf("m_WorkDays: %d\n", res.m_WorkDays);

        return res;
    }
    TResult invalidRes = { -1, -1 };
    return invalidRes;
}

字符串

des4xlb0

des4xlb01#

至少这些问题:

totalDays()更改m_days[]

使用if (isLeapYear(y)) m_days[1] = 29;时,m_days[]表被更改。如果totalDays()只被调用一次,这是可以的,但如果是多次调用,当后续调用不是闰年时,更改表会产生问题。
OP先前开放问题中的替代代码already posted

缺少返回

totalDays()中,当isDayValid(y, m, d)为false时,函数不返回任何值。这意味着代码没有在启用所有警告的情况下编译。保存时间. Enable them all

密码错误

通过启用所有警告,快速发现以下代码错误。

for (int i = 0; i < 7; i++)
    // if (m == months_30[i])
    if (m == months_31[i])

字符串

eanckbw9

eanckbw92#

你对闰年的处理是不正确的:你修改全局数组m_days作为函数totalDays()的副作用,如果该函数在闰年至少被调用一次,则使二月有29天,这可能会为后续调用产生错误的结果,并在countDays中导致问题,因为你使用m_days数组枚举跨越该期间的所有天数,如果第一年和最后一年都不是闰年,则可能忽略2月29日的事件,否则计算额外的事件。
还有其他更简单的错误,例如d1递增一次if (d1 < d2),另一次if (d1 < m_days[m1 - 1]).
另外,m_TotalDaysm_WorkDays同时包括第一天和最后一天。这是预期的行为吗?
闰年问题的一个更简单的解决方案是使用函数int monthDays(int y, int m),而不是不可能总是正确的全局数组。
以下是修改后的版本:

#include <stdio.h>
#include <stdbool.h>

typedef struct {
    int m_TotalDays;
    int m_WorkDays;
} TResult;

int monthDays(int y, int m);
bool isDayValid(int y, int m, int d);
bool isLeapYear(int y);
int totalDays(int y, int m, int d);
bool isWorkDay(int y, int m, int d);
TResult countDays(int y1, int m1, int d1, int y2, int m2, int d2);

int main(int argc, char *argv[]) {
    int y1 = 2023;
    int m1 = 1;
    int d1 = 1;

    int y2 = 2023;
    int m2 = 12;
    int d2 = 31;

    if (argc > 1) {
        if (sscanf(argv[1], "%d/%d/%d", &d1, &m1, &y1) != 3)
            return 1;
        if (argc > 2) {
            if (sscanf(argv[2], "%d/%d/%d", &d2, &m2, &y2) != 3)
                return 1;
        }
    }
    TResult res = countDays(y1, m1, d1, y2, m2, d2);

    printf("%d/%d/%d - %d/%d/%d: ", d1, m1, y1, d2, m2, y2);
    if (res.m_TotalDays < 0) {
        printf("invalid dates\n");
        return 1;
    } else {
        printf("m_TotalDays: %d, m_WorkDays: %d\n",
               res.m_TotalDays, res.m_WorkDays);
        return 0;
    }
}

int monthDays(int y, int m) {
    static int const m_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    if (m < 1 || m > 12)
        return -1;
    if (m == 2)
        return 28 + isLeapYear(y);
    else
        return m_days[m - 1];
}

bool isLeapYear(int y) {
    return (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0));
}

bool isDayValid(int y, int m, int d) {
    return (y >= 2000 && m >= 1 && m <= 12 && d >= 1 && d <= monthDays(y, m));
}

int totalDays(int y, int m, int d) {
    if (isDayValid(y, m, d)) {
        int years_count = y - 2000;
        int total_days = 365 * years_count;
        for (int i = 2000; i < y; i++) {
            if (isLeapYear(i))
                total_days++;
        }
        for (int i = 1; i < m; i++)
            total_days += monthDays(y, i);

        total_days += d - 1;
        //printf("totalDays(%d, %d, %d) -> %d\n", y, m, d, total_days);
        return total_days;
    } else {
        return -1;
    }
}

bool isWorkDay(int y, int m, int d) {
    int day_in_week = totalDays(y, m, d) % 7;

    //printf("Day in week: %d\n", day_in_week);
    // Jan 1st 2000 was a Saturday
    if (day_in_week == 0 || day_in_week == 1) {
        return false;
    }
    // enumerate the national holidays in the Tcheck Republic
    if ((m == 1 && d == 1)
    ||  (m == 5 && (d == 1 || d == 8))
    ||  (m == 7 && (d == 5 || d == 6))
    ||  (m == 9 && d == 28)
    ||  (m == 10 && d == 28)
    ||  (m == 11 && d == 17)
    ||  (m == 12 && (d == 24 || d == 25 || d == 26))) {
        return false;
    } else {
        return true;
    }
}

TResult countDays(int y1, int m1, int d1, int y2, int m2, int d2) {
    TResult res = { -1, -1 };
    int total_days1 = totalDays(y1, m1, d1);
    int total_days2 = totalDays(y2, m2, d2);

    if (total_days1 >= 0 && total_days2 >= 0 && total_days2 >= total_days1) {
        // include both the first and last days: is this correct?
        res.m_TotalDays = (total_days2 - total_days1) + 1;
        res.m_WorkDays = 0;

        for (int i = 0; i < res.m_TotalDays; i++) {
            if (isWorkDay(y1, m1, d1)) {
                res.m_WorkDays++;
            }
            // enumerate the next day
            if (d1 < monthDays(y1, m1)) {
                d1++;
            } else {
                d1 = 1;
                if (m1 < 12) {
                    m1++;
                } else {
                    m1 = 1;
                    y1++;
                }
            }
        }
    }
    return res;
}

字符串
示例输出:

$ for i in {2000..2023} ; do ./231111-days 1/1/$i 31/12/$i ; done
1/1/2000 - 31/12/2000: m_TotalDays: 366, m_WorkDays: 252
1/1/2001 - 31/12/2001: m_TotalDays: 365, m_WorkDays: 252
1/1/2002 - 31/12/2002: m_TotalDays: 365, m_WorkDays: 253
1/1/2003 - 31/12/2003: m_TotalDays: 365, m_WorkDays: 253
1/1/2004 - 31/12/2004: m_TotalDays: 366, m_WorkDays: 255
1/1/2005 - 31/12/2005: m_TotalDays: 365, m_WorkDays: 254
1/1/2006 - 31/12/2006: m_TotalDays: 365, m_WorkDays: 252
1/1/2007 - 31/12/2007: m_TotalDays: 365, m_WorkDays: 252
1/1/2008 - 31/12/2008: m_TotalDays: 366, m_WorkDays: 254
1/1/2009 - 31/12/2009: m_TotalDays: 365, m_WorkDays: 252
1/1/2010 - 31/12/2010: m_TotalDays: 365, m_WorkDays: 254
1/1/2011 - 31/12/2011: m_TotalDays: 365, m_WorkDays: 254
1/1/2012 - 31/12/2012: m_TotalDays: 366, m_WorkDays: 253
1/1/2013 - 31/12/2013: m_TotalDays: 365, m_WorkDays: 253
1/1/2014 - 31/12/2014: m_TotalDays: 365, m_WorkDays: 253
1/1/2015 - 31/12/2015: m_TotalDays: 365, m_WorkDays: 252
1/1/2016 - 31/12/2016: m_TotalDays: 366, m_WorkDays: 254
1/1/2017 - 31/12/2017: m_TotalDays: 365, m_WorkDays: 252
1/1/2018 - 31/12/2018: m_TotalDays: 365, m_WorkDays: 252
1/1/2019 - 31/12/2019: m_TotalDays: 365, m_WorkDays: 253
1/1/2020 - 31/12/2020: m_TotalDays: 366, m_WorkDays: 253
1/1/2021 - 31/12/2021: m_TotalDays: 365, m_WorkDays: 254
1/1/2022 - 31/12/2022: m_TotalDays: 365, m_WorkDays: 254
1/1/2023 - 31/12/2023: m_TotalDays: 365, m_WorkDays: 252

相关问题