You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

计算两日期间工作日的函数不符合预期问题排查

问题描述

编辑说明:现已附上全部代码,问题可能出在countDays()函数外部。

我正在编写一个函数,接收两个日期作为输入,返回一个包含两个变量的结构体TResult: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;
}

问题定位与修复

核心错误点

  1. 日期重复递增:countDays的循环里同时写了if (d1 < d2) d1++;和后续的日期边界判断递增,导致单次循环日期被加两次,直接跳过一半日期的检查,这是跨年份计算错误的根源。
  2. 闰年处理缺失:递增日期时没有判断当前年份是否为闰年,直接用全局m_days数组的固定值,会导致闰年2月的日期计算错误。
  3. isDayValid数组误用:检查31天月份时错误使用了months_30数组,应该用months_31。
  4. 全局数组污染:totalDays函数修改了全局m_days的2月天数,会影响其他依赖该数组的逻辑。

修复后的完整关键代码

修复countDays函数

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;
        // 使用独立变量保存当前遍历的日期,避免修改原参数逻辑
        int curr_y = y1, curr_m = m1, curr_d = d1;

        for (int i = 0; i < res.m_TotalDays; i++) {
            // 检查当前日期是否为非工作日
            if (!isWorkDay(curr_y, curr_m, curr_d)) {
                nonWorkDays++;
            }
            // 单次递增日期,处理闰年2月
            int max_day = m_days[curr_m - 1];
            if (curr_m == 2 && isLeapYear(curr_y)) {
                max_day = 29;
            }
            if (curr_d < max_day) {
                curr_d++;
            } else {
                curr_d = 1;
                if (curr_m < 12) {
                    curr_m++;
                } else {
                    curr_m = 1;
                    curr_y++;
                }
            }
        }
        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;
}

修复isDayValid函数的数组误用

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;
    // 修复:检查31天月份时用months_31数组
    for (int i = 0; i < 7; i++)     
        if (m == months_31[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)    
        return false;
    else if ((isLeapYear(y)) && m == 2 && d > 29)    
        return false;
    else
        return true;
}

修复totalDays的全局数组污染问题

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;
        // 使用局部数组,避免修改全局变量
        int local_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
        if (isLeapYear(y))
            local_days[1] = 29;

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

        int add_days = d - 1;

        int total_days = add_years + add_months + add_days;

        return total_days;
    }
    // 增加无效日期的返回值,避免未定义行为
    return -1;
}

内容的提问来源于stack exchange,提问作者Nare Avetisyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:40:56