计算两日期间工作日的函数不符合预期问题排查
问题描述
编辑说明:现已附上全部代码,问题可能出在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; }
问题定位与修复
核心错误点
- 日期重复递增:
countDays的循环里同时写了if (d1 < d2) d1++;和后续的日期边界判断递增,导致单次循环日期被加两次,直接跳过一半日期的检查,这是跨年份计算错误的根源。 - 闰年处理缺失:递增日期时没有判断当前年份是否为闰年,直接用全局
m_days数组的固定值,会导致闰年2月的日期计算错误。 isDayValid数组误用:检查31天月份时错误使用了months_30数组,应该用months_31。- 全局数组污染:
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
相关产品推荐
相关产品推荐

