关于计算两日期之间经过多少天的超巧妙算法

首先声明:本文引自一博主原创博客

原创地址: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,本次解析到此结束,

慢慢领悟此代码的玄妙之处吧

再见。嘿嘿

再次声明

代码不是我的

转载自https://blog.csdn.net/chinaeran/article/details/43601699