计算两个日期之间的工作日未按预期工作的函数

Function calculating work days between two dates not working as expected

提问人:Nare Avetisyan 提问时间:11/10/2023 最后编辑:chqrlieNare Avetisyan 更新时间:11/11/2023 访问量:115

问:

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

我正在编写一个函数,该函数将两个日期作为输入,并返回一个包含两个变量的结构:(两个日期之间的总天数)和(总天数减去周末和特定的国定假日)。但是,当两个给定日期的年份不同时,计算是错误的,对于年份相同的年份,它给出的日期比正确答案少一天。m_TotalDaysm_WorkDaysm_WorkDays

该函数还使用了其他 2 个函数,但是,我确信问题不是来自这些函数。我相信错误一定在循环中,但我似乎没有找到问题可能出在哪里。isWorkDay()totalDays()forcountDays()

#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;
}
C 函数

评论

1赞 Some programmer dude 11/10/2023
你如何处理闰日?
0赞 Nare Avetisyan 11/10/2023
@Someprogrammerdude添加了我用来计算闰年的函数。我在 totalDays() 中使用它,其中我计算给定日期和 2000-01-01 之间的天数(我正在处理的任务指定应从该日期开始计算年份)
0赞 Adrian Mole 11/10/2023
仔细看。一般来说,如果在 开始循环,终止条件应为 (not);该测试应用于从 .因此,对于 42 TotalDays,您的循环将运行 43 次,因此,您可能会将该值递增一次。for (int i=0; i<=res.m_TotalDays; i++) {i = 0i < totali <= total<=i = 1fornonWorkDays
0赞 Adrian Mole 11/10/2023
...不过,很难说这是否是唯一没有更多代码的问题。即使你看起来很有信心你的其他函数可以工作,但如果没有它们,没有人可以测试你的代码 - 以及任何修复。
0赞 KamilCuk 11/10/2023
您是否考虑过仅使用一天的秒数来使用 and 和递增?mktimestruct tmtm_sec

答:

1赞 chux - Reinstate Monica 11/10/2023 #1

至少存在以下问题:

totalDays() 更改 m_days[]

随着表的改变。如果只调用一次,这是可以的,但对于多次调用,当后续调用不是闰年时,使用表进行更改会产生问题。if (isLeapYear(y)) m_days[1] = 29;m_days[]totalDays()

替代代码已发布在 OP 之前的开放性问题中。

缺少退货

在 中,当为 false 时,该函数不返回任何值。这意味着代码未在启用所有警告的情况下编译。节省时间。全部启用totalDays()isDayValid(y, m, d)

代码错误

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

  for (int i = 0; i < 7; i++)
    // if (m == months_30[i])
    if (m == months_31[i])
0赞 chqrlie 11/11/2023 #2

您对闰年的处理不正确:您修改全局数组作为函数的副作用,如果该函数在闰年至少调用一次,则使 2 月有 29 天,这可能会为后续调用产生错误的结果,并导致在使用数组枚举跨期间的所有天数时出现问题。 如果第一年和最后一年都不是闰年,则可能会省略 2 月 29 日的事件,否则会计算额外的事件。m_daystotalDays()countDaysm_days

还有其他更简单的错误,例如一次又一次地递增......d1if (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