关于计算两日期之间经过多少天的超巧妙算法
Posted cndccm
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于计算两日期之间经过多少天的超巧妙算法相关的知识,希望对你有一定的参考价值。
首先声明:本文引自一博主原创博客
原创地址: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
", day_diff(2015, 1, 1, 2015, 1, 8));
printf("%d
", 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
以上是关于关于计算两日期之间经过多少天的超巧妙算法的主要内容,如果未能解决你的问题,请参考以下文章
SOS 如何在oracle 中计算两日期之间有多少天???并且这两日期都是已经赋了日期的变量: