今天写到一个类需要用到求两个日期之间相差的天数。我只好写一个时间累。之前写过一个但是用了许多许多的判断。代码页找不到了,需要重写。这次重写我希望找到简单的办法。按照计算机计时的思想,我可以计算两个日期到某一个指定日期的天数,然后在求差。网上找了一下算法,终于在yaoweijq的这篇文字里面找到了算法。大家可以去他的这个文章看一看实现。点击打开链接,下面是我类中重载减法运算符,用于计算两个日期之间的相隔天数。
int CDate::operator-(const CDate d)const{ int days; int nm = (_Month + 9) % 12; // int ny = _Year - nm / 10; int nd = 365 * ny + ny / 4 - ny / 100 + ny / 400 + (nm * 306 + 5) / 10 + (_Day - 1); int nm1 = (d._Month + 9) % 12; int ny1 = d._Year - nm1 / 10; int nd1 = 365 * ny1 + ny1 / 4 - ny1 / 100 + ny1 / 400 + (nm1 * 365 + 5) / 10 + (d._Day - 1); days = nd - nd1; return days; }
其中的nd就是当前日期到0年3月1日之间的天数。
相信看过前面链接里的文章的同学有一部分已经明白了。没有明白页没关系我来讲述一下,今天有点忙早上这个类还没写完就被同学叫去写单片机程序了。吃饭的时候把不明白的东西思考了一下,基本明白了。这个算法是分别计算两个日期至0年3月1日的天数,然后求差。至于为什么选择3月1日。我也不知道。
废话不在说了,下面分析一下。
int nm = (_Month + 9) % 12;
这一句用于检测当月是否在3月1日之前,因为我们知道是否为闰年是有2月来决定决定的,如果在2月以前我们就不用考虑本年,这个道理应该明白吧。
仔细看1月2月两月 求得的结果是10,11 。3月至12月nm为0~9。
看到这里我想你很迷糊为什么这么做。那我们来看第二句吧。
int ny = _Year - nm / 10;
这一句,ny 用于存储年,nm/10 这一步 1,2月两个月得到的结果为1 其他月份是0。所以这里在3月1日之前的我们把年减去一。因为我们是从3月1日开始计算的,当前不满一年。
int nd = 365 * ny + ny / 4 - ny / 100 + ny / 400 + (nm * 306 + 5) / 10 + (_Day - 1);
这一句相当犀利,不得不佩服算法的作者。
365*ny我们先把每一年当做平年。+ny/4 四年一闰年。还有后面-ny/100 ,100年不闰年。+ny/400 四百年闰年。这个是大家都应该明白的吧。看(nm * 306 + 5) / 10 这一个,思考了整个吃饭时间。
为什么要是306呢。365-31-28=306 (是一年的天数减去一月和二月的天数,为什么要这么做?因为我们这里元旦是3月1号。因为前面我们计算了到0年3月1日之间的天数现在我们需要做的就是计算当前日期到本年的3月1号的天数)(nm * 306 + 5) / 10 为什么这样。我不妨把它带入各个月份来看(别忘了这个int nm = (_Month + 9) % 12;
)
这一句用于检测当月是否在3月1日之前,因为我们知道是否为闰年是有2月来决定决定的,如果在2月以前我们就不用考虑本年,这个道理应该明白吧。
仔细看1月2月两月 求得的结果是10,11 。3月至12月nm为0~9。
看到这里我想你很迷糊为什么这么做。那我们来看第二句吧。
int ny = _Year - nm / 10;
这一句,ny 用于存储年,nm/10 这一步 1,2月两个月得到的结果为1 其他月份是0。所以这里在3月1日之前的我们把年减去一。因为我们是从3月1日开始计算的,当前不满一年。
int nd = 365 * ny + ny / 4 - ny / 100 + ny / 400 + (nm * 306 + 5) / 10 + (_Day - 1);
这一句相当犀利,不得不佩服算法的作者。
365*ny我们先把每一年当做平年。+ny/4 四年一闰年。还有后面-ny/100 ,100年不闰年。+ny/400 四百年闰年。这个是大家都应该明白的吧。看(nm * 306 + 5) / 10 这一个,思考了整个吃饭时间。
为什么要是306呢。365-31-28=306 (是一年的天数减去一月和二月的天数,为什么要这么做?因为我们这里元旦是3月1号。因为前面我们计算了到0年3月1日之间的天数现在我们需要做的就是计算当前日期到本年的3月1号的天数)(nm * 306 + 5) / 10 为什么这样。我不妨把它带入各个月份来看(别忘了这个int nm = (_Month + 9) % 12;
)
我们看一下每个月对应得nm是多少看一下(nm*306+5)/10 是多少
nm (nm*306+5)/10
1月-----nm =10 ---------- 306 = 265+31
2月-----nm =11 ---------- 336 = 306+30
3月-----nm =0
---------- 0
---------- 0
4月-----nm =1
----------- 31=0+31
----------- 31=0+31
5月-----nm =2 ------------ 61=31+30
6月-----nm =3
----------- 92= 61+31
----------- 92= 61+31
7月-----nm =4 ------------122= 92+30
8月-----nm =5 ------------153 = 122+31
9月-----nm =6 ------------ 184 = 153+31
10月---nm =7 ------------214 = 184+30
11月---nm =8 ------------ 245 = 214+31
12月---nm =9 ----------- 275 = 245+30
这里我们惊讶的发现用这样的方法居然可以算出 每个月份的1号到3月1日之间的天数。如果你自己算一遍的时候你会发现,这里的+5 起到了至关重要的作用,也不知道为什么每当该月是大月的时候这个5就可以帮我们来导致进位,比如9月 ,9月的nm是6(6*306)=1836 加上5以后就是 1841了 这样除以10以后就是 184了。
不知道这种巧合是有缘由的还是,这个方法提出者的精心推导。
最后 加上当前的 日期 ,+(_Day-1) 减一为什么你自己想 7月7日距离7月1日 是不是 7-1?
ok 时间仓促,就写到这里,如有问题希望指正。如有不懂,再看一遍。
以下是我的测试。常规的日期没有问题。
我们换一个极端的。
也没有问题,初步证明这个方法还是比较可靠地。
如果发现有问题,请告诉我。谢谢。