提问人:Nare Avetisyan 提问时间:11/10/2023 最后编辑:chqrlieNare Avetisyan 更新时间:11/11/2023 访问量:115
计算两个日期之间的工作日未按预期工作的函数
Function calculating work days between two dates not working as expected
问:
编辑:现在包括所有代码,显然问题可能出在外面。countDays()
我正在编写一个函数,该函数将两个日期作为输入,并返回一个包含两个变量的结构:(两个日期之间的总天数)和(总天数减去周末和特定的国定假日)。但是,当两个给定日期的年份不同时,计算是错误的,对于年份相同的年份,它给出的日期比正确答案少一天。m_TotalDays
m_WorkDays
m_WorkDays
该函数还使用了其他 2 个函数,但是,我确信问题不是来自这些函数。我相信错误一定在循环中,但我似乎没有找到问题可能出在哪里。isWorkDay()
totalDays()
for
countDays()
#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;
}
答:
至少存在以下问题:
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])
您对闰年的处理不正确:您修改全局数组作为函数的副作用,如果该函数在闰年至少调用一次,则使 2 月有 29 天,这可能会为后续调用产生错误的结果,并导致在使用数组枚举跨期间的所有天数时出现问题。 如果第一年和最后一年都不是闰年,则可能会省略 2 月 29 日的事件,否则会计算额外的事件。m_days
totalDays()
countDays
m_days
还有其他更简单的错误,例如一次又一次地递增......d1
if (d1 < d2)
if (d1 < m_days[m1 - 1])
此外,还包括第一天和最后一天。这是预期的行为吗?m_TotalDays
m_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
上一个:向文件添加活动
评论
for (int i=0; i<=res.m_TotalDays; i++) {
i = 0
i < total
i <= total
<=
i = 1
for
nonWorkDays
mktime
struct tm
tm_sec