求解方法有很多,这里用小学生翻日历的笨方法。大致思路如下:定义三个变量year、month、day,用来记录日期,再定义一个变量days,用来计算天数。现在有一本日历放在面前,假设当前日期是2005年3月5日,开始翻日历,翻一页,变量day就加1,日期变成2005年3月6日,继续翻……,翻到了2005年3月31日,此时变量month是3,day是31,继续翻页,变成了3月32日,这时候,我们发现32大于3月份的天数(31),于是,将month加1,并且day置为1。特殊情况,如果是2005年12月31日,如果翻一页日历,就会变成2005年12月32日,先变为2005年13月1日(month加1,并且day置为1),然后变为2006年1月1日(year加1,并且month置为1)。
为什么不整年整年的366或者365天累加呢?那样程序运行效率明显高很多。原因有三:
(1)上面这种原始的写法的好处是很容易把它改成火星上的程序,只需要修改三处:MONTH的值、daysOfMonth的值、IsLeapYear函数体,程序其它逻辑不需要做任何修改。
(2)以上这种很笨的写法,循环体执行一次的时间是纳秒(10的负9次方秒)级别的,即使一百万年的间隔,循环几亿次(10的8次方),整个程序运行时间也不需要1秒钟。
(3)这种一页一页翻日历的思路,不但可以求两天的时间间隔,还可以求从某一天开始往前或者往后经过若干天的日期。前者(求两天的时间间隔)用while循环,后者(从某一天开始往前或者往后经过若干天的日期)用for循环,循环体里的逻辑基本完全一致。
进一步的,这种面向过程的写法可以改成面向对象的,如果利用C++的运算符重载,重载减号运算符,求某两天的间隔天数的形式将非常简单,像下面这样:Date(2023, 9, 29) - Date(2005, 3, 5)。
面向过程的代码如下:
#include
using namespace std;
#define MONTH 12
int daysOfMonth[2][MONTH + 1] = { {-1,31,28,31,30,31,30,31,31,30,31,30,31}, //每行多定义一个元素,第一个元素不用
{-1,31,29,31,30,31,30,31,31,30,31,30,31} };
struct Date
{
int year, month, day;
};
int DateDiff(Date beginDate, Date endDate);
int IsLeapYear(int year);
int main()
{
Date date1 = { 2005,3,5 };
Date date2 = { 2023,9,29 };
cout << "间隔" << DateDiff(date1, date2) << "天" << endl;
return 0;
}
int DateDiff(Date beginDate, Date endDate)
{
int year = beginDate.year;
int month = beginDate.month;
int day = beginDate.day;
int days = 0;
while (!(year == endDate.year && month == endDate.month && day == endDate.day))
{
day++;
if (day > daysOfMonth[IsLeapYear(year)][month])
{
day = 1;
month++;
if (month > MONTH)
{
month = 1;
year++;
}
}
days++;
}
return days;
}
int IsLeapYear(int year)
{
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
return 1;
else
return 0;
}