关于计算两日期之间经过多少天的超巧妙算法
首先声明:本文引自一博主原创博客
原创地址:https://blog.csdn.net/chinaeran/article/details/43601699
昨天呢,刚刚阅读了这个代码,大部分都还可以看懂,有一两个地方属实难懂,但细细思来,方知博主此代码超神奇。简直巧妙至极。
所以来细细解析一下此代码。
话不多说。我们先来看一下原文。
#include <stdio.h>
#include <stdlib.h>
int day_diff(int year_start, int month_start, int day_start
, int year_end, int month_end, int day_end)
{
int y2, m2, d2;
int y1, m1, d1;
m1 = (month_start + 9) % 12;
y1 = year_start - m1/10;
d1 = 365*y1 + y1/4 - y1/100 + y1/400 + (m1*306 + 5)/10 + (day_start - 1);
m2 = (month_end + 9) % 12;
y2 = year_end - m2/10;
d2 = 365*y2 + y2/4 - y2/100 + y2/400 + (m2*306 + 5)/10 + (day_end - 1);
return (d2 - d1);
}
int main(void)
{
printf("%d\n", day_diff(2015, 1, 1, 2015, 1, 8));
printf("%d\n", day_diff(2015, 1, 29, 2015, 2, 9));
return 0;
}
这里呢是原文代码。
可以看到非常简短,接下来,就让你真正见识到这个代码的巧妙之处。
接下来我们先来看一下楼主的解析
算法解析:
该算法总体思想是计算给定日期到 0年3月1日的天数,然后相减,获取天数的间隔。
m1 = (month_start + 9) % 12; 用于判断日期是否大于3月(2月是判断闰年的标识),还用于纪录到3月的间隔月数。
y1 = year_start - m1/10; 如果是1月和2月,则不包括当前年(因为是计算到0年3月1日的天数)。
d1 = 365*y1 + y1/4 - y1/100 + y1/400 + (m1*306 + 5)/10 + (day_start - 1);
其中 365*y1 是不算闰年多出那一天的天数,
y1/4 - y1/100 + y1/400 是加所有闰年多出的那一天,
(m2*306 + 5)/10 用于计算到当前月到3月1日间的天数,306=365-31-28(1月和2月),5是全年中不是31天月份的个数
(day_start - 1) 用于计算当前日到1日的间隔天数
————————————————
我们由简到繁,慢慢分析:
其中关于最后的 (day_start - 1)就不用多说了吧。
我们主要来讲讲没别的地方。
先来看这里:
d1 = 365*y1 + y1/4 - y1/100 + y1/400
本文思路在这在说一下:即计算每个日期距离0年三月一日的相差天数,在做差即得两日期之间天数。
好了言归正传所谓闰年四年一闰,百年不闰,四百又多一闰。
我们从0年3月一日开始算到我们输入哪一年的3月一日截止先计算整年的天数总和。
0年的二月已过。所以考虑从1年到截止年的闰年个数。
用y1/4算出可被4整除的年数,我们记为疑是闰年年数。后我们减去y1/100,去掉其中的不是闰年年数,但是由于四百年又多一闰所以我们又加上了y1/400;
这样就可以完美的算出所经过的年的总天数,关于这里的年数还涉及后面的(- m1/10)一项,这个我们后面来细细解答,这里是本文最巧妙的地方之一,,先留个悬念哈。
好了我们接着往下看。
m1 = (month_start + 9) % 12;
这个就比较巧妙了,需要与
(m1*306 + 5)/10和(- m1/10)两项结合来分析。这里就到了作者算法最为精妙的部分了,不得不说这个代码真的神奇。
OK,继续。来看(- m1/10),和m1 = (month_start + 9) % 12;的结合。
先来看,怎么样才能让余数大于10呢,1,2,这两个结果对吧。如果是m1大于10 的话呢,可知年数会减一。
简单点来说呢就是用计算年总天数的计算到当年3月一号的天数总和,
如果当年不到三月,即1,2月,我们就往前推一年,计算到上一年的3月一号的天数,
至于多出来的天数我们接着看,别急。
县来看如果是1,n那么结果就是10,
即为一月份,意思是距离上一年的3月一号过去了10个月,对吧,
即上一年的3,4,5,6,7,8,9,10,11,12
其中一共有多少天呢,其中3.5.7.8.10.11位31天每月。
即结果为30*10+6
带入 (m1*306 + 5)/10即得真的为306;是不是很神奇。
我们将这个公式一一带入算一下。
下面呢 ,我们先看m1.再看月份,再看天数。
2,,,,,余11,4,5,6,7,8,9,10,11,12,1对吧,带入刚好为337;
3,,,,,余00
4,,,,,余13带入得31
5,,,,,余二3,4带入61;
6,,,,,余三3,4,5带入92
7,,,,,余四3,4,5,6带入122
8.,,,,,余五3,4,5,6,7带入153
9,,,,,余六,3,4,5,6,7,8带入184
10,,,,,余七,3,4,5,6,7,8,9滴入214
11,,,,,余8,3,4,5,6,7,8,9,10带入245
12,,,,,余9,,3,4,5,6,7,8,9,10,11带入275
OK,是不是发现都对,是不是很神奇。
我也这样觉得,我来为大家分析其玄妙之处
先来分析月份问题。
随余数的增加月份为3 4 5 6 7 8 9 10 11 12 1
先来看为31天的月份有,3 5 7 8 9 10 12 1
看一下其中大部分30,31天的月份间隔,对吧,
简单看一下即可看出由于306后面的6的原因及后面5的作用下
我们把它稍微化简一下化为(m1*300+5+m1*6)/10
m1*300的我们直接滤掉
(5+6*m1)/10;
可以看到
1得1
2得1
3得2
最容易的得知的就是每加二回多一天即上面的初步规律30,31间隔出现
其中有两个特例,7到8,12到2
包含7,和7,8月份的余数分别为5,6
可以得出符合,
再看1到12你会发现一样符合,
到底为什么呢
看6
多出来的6是最最中心的地方
月份每加一,在其原来的31,30紧挨着变化的情况下会多出来一个1
而当月份为5,10时,会凑乘5,而后再过一个月,按30,31来看为 偶数,是30天,不过由于6多出来的五和多一月多出来的6结合,又会造就一个31天,而且和前一个31天还是挨着的
所以,7月到8月的特例就包含了。
再看12月到1月的情况12月时余数为10,一月为11.
是不是又凑成一个5,后面一加有多出来一天。
OK,本次解析到此结束,
慢慢领悟此代码的玄妙之处吧
再见。嘿嘿
再次声明
代码不是我的