📄 根据年月日求得星期几的几种方法.txt
字号:
根据年月日求得星期几的几种方法- -
最近在看代码,无意中看到一个由年月日求出星期几的函数,于是在网上搜索了一下,找到了以下的一些公式。
一:常用公式
W = [Y-1] + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D
Y是年份数,D是这一天在这一年中的累积天数,也就是这一天在这一年中是第几天。
二:蔡勒(Zeller)公式
w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
公式中的符号含义如下,w:星期;c:世纪;y:年(两位数); m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算);d:日;[ ]代表取整,即只要整数部分。
相比于通用通用计算公式而言,蔡勒(Zeller)公式大大降低了计算的复杂度。
三:对蔡勒(Zeller)公式的改进
作者:冯思琮
相比于另外一个通用通用计算公式而言,蔡勒(Zeller)公式大大降低了计算的复杂度。不过,笔者给出的通用计算公式似乎更加简洁(包括运算过程)。现将公式列于其下:
W=[y/4]+r (y/7)-2r(c/4)+m'+d
公式中的符号含义如下,r ( )代表取余,即只要余数部分;m'是m的修正数,现给出1至12月的修正数1'至12'如下:(1',10')=6;(2',3',11')=2;(4',7')=5;5'=0;6'=3;8'=1;(9',12')=4(注意:在笔者给出的公式中,y为润年时1'=5;2'=1)。其他符号与蔡勒(Zeller)公式中的含义相同。
四:
W= (d+1+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7
在公式中d表示日期中的日数,m表示月份数,y表示年数。
注意:在公式中有个与其他公式不同的地方:
把一月和二月看成是上一年的十三月和十四月,例:如果是2004-1-10则换算成:2003-13-10来代入公式计算。
对上述公式的理解:
首先1年1月1日是星期1,那么每加1天,就是相对这一天的天数对于7的一个取模。 即 D % 7, 为了让结果0表示星期日,1表示星期一,
取(D+1)%7。那么这个D怎么确定呢,首先一般的一年为365天,365%7=1 那么表示对于一年的第一天同最后一天,他们的星期是一样的。
也就是对于365天的年来说,1月1日是星期1,那么12月31日也是星期1。所以第二年的1/1,就是前一年的后一个星期。4年为一个闰年,多
一天,那么闰年的情况要+2。假如每年都是365天,那就好办了,只要 (Y-1 + D)%7,(D表示一年中的第几天,Y表示年),但是有闰年的存在,
所以需要再加上Y年的时候,经过了多少个闰年,我们知道闰年的计算方法就是 Y可以整除4,且不能整除100,对于Y年,需要Y-1,因为Y年的
时候,Y这一年还没有过完,所以 (Y-1)/4-(Y-1)/100+(Y-1)/400计算的是1-Y-1年中闰年的个数。因为闰年比一般的年多了1天,所以要加
上这个数。这样就可以理解第一个基本公式了。 W = [Y-1] + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D
****************************************************************************************************************************
一个计算星期几的简单程序2006-09-03 16:06通常我们只知道生活当天的前后几天是星期几,即便是翻日历,也只能知道有限日期的星期数。那么有没有一种方法可以让我们知道任何一天是星期几呢?有,下面我将向大家介绍一种方法,用以编写万年历的程序。
首先我们必须约定一些法则,我们用Y、M、D分别表示年、月、日,用数字0-6分别表示星期日-星期六,这样我们就可以开始推导我们的公式了。
我们知道2002年9月1号为星期日,如果我们要想知道2002年9月10号为星期几,可以这样算:(0+(10-1))%7=(0+9)%7=2,即星期二。同样可算得2002年9月20号为:(0+(20-1))%7=(0+19)%7=5,即星期五。但是这样算需要把日期减1,不太方便,为了解决这个问题,我们可以假设每个月有一个0号,由于2002年9月1号为星期日,那么2002年9月0号为星期六,这样算9月10号,只需代入10既(6+10)%7=2。事实上,9月0号也就是8月31号,每个月0号的星期数实际上就是每个月1号的前一天的星期数。我把这个星期数称之为每个月的代码。有了这个代码,要算这个月任一天的星期数都好办了。
以上讨论的是一年中每个月的代码,事实上对于每年也有一个代码,这个代码就是每年1月0号(即1月1号的前一天)的星期数,也就是一月份的代码。如果我们能够找到每年的代码之间的关系,那么要计算万年历就易如反掌了。
(一)推算年的代码公式
我们都知道,平年一年有365天,即52周多1天。闰年为366天即52周多2天。我们先只考虑平年的情况。
假设第N年的代码为W,则第N+1年的代码为(W+1)%7,而第N+K年的代码则为(W+K)%7。这是因为从第N年到第N+K年共经过了K年,每过一年也就是过了52周余1天,经过K年也就是过了52*K周余K天,将多余的天数K加上第N年的代码W再对7取模,所得也就是第N+K年的代码了。
下面我们把闰年也考虑进来。判断闰年的规则是,能被4整除,并能被100和400同时整除的年份就是闰年。所以从第N年到第N+K年间共有K/4 -K/100+K/400个闰年,而每个闰年有52周余2天,要比平年多余了1天,即共多余了K/4-K/100+K/400天。我们应该把这些天也加进去,所以第N+K年的代码应为(W+K+K/4-K/100+K/400)%7。
这样子是不是就考虑完全了呢?并非如此,我们还有两点没考虑到。第一点是第N年是不是闰年。如果第N年是闰年的话,它本身就是52周余2天,而我们在上面却是把它当作平年来计算的,少算了1天,应加上。所以在第N年为闰年的时候上式应为(W+(K+1)+K/4-K/100+K/400)%7。第二点是第N+K年是不是闰年。如果第N+K年是闰年,虽然它有52周余2天,但只有在算第N+(K+1)年的时候,才需要多加它那一天,而在算第N+K年的时候不需要多加这1天,因此我们必须将上式改为(W+(K+1)+(K-1)/4-(K-1)/100+(K-1)/400)%7(注意千万不能改为(W+(K+1)+(K/4-K/100+K/400-1))%7=(W+K+K/4-K/100+K/400)%7)。
由此我们可以得出当第N年为闰年时,第N+K年的代码计算式为:
A=(W+(K+1)+(K-1)/4-(K-1)/100+(K-1)/400)%7
为了方便计算,我们可以取N为0,也就是假设公元元年的代码为W。因为公元元年也是闰年,符合上式,那么当我们输入的年份为Y时,此时就有K=Y,也就是说第Y年的代码为
A=(W+(Y+1)+(Y-1)/4-(Y-1)/100+(Y-1)/400)%7
接下来的问题就是W究竟是一个什么数了,下面我们就来解决这个问题。
我们已经知道2002年1月1号为星期二,它的前一天为星期一,那也就是说2002年的代码就是1,由此我们可得
(W+(2002+1)+(2002-1)/4-(2002-1)/100+(2002-1)/400)%7=1
即
(W+2488)%7=1
(W+3)%7=1
这样我们就可求得W=5。我们的公式就变成了如下形式
A=(5+(Y+1)+(Y-1)/4-(Y-1)/100+(Y-1)/400)%7
有了这个公式,我们就可以算公元后任意一年的代码了,但还不能算公元前的,我们还需要再改进一下,得出公式(1):
(1)Ayear= Y>0?(5+(Y+1)+(Y-1)/4-(Y-1)/100+(Y-1)/400)%7:(5+Y+Y/4-Y/100+Y/400)%7
这样就OK了。不过这又导致了另一个问题:Y<0时,算得的A有可能小于0。这个问题我们暂且留下,待会再解决。
(二)推算月的代码公式
年的问题解决了,月怎么办呢?请看下表:
月份 代码 差值
一月 A 0
二月 A+3 3
三月 A+3 3
四月 A+6 6
五月 A+1 1
六月 A+4 4
七月 A+6 6
八月 A+2 2
九月 A+5 5
十月 A 0
十一月 A+3 3
十二月 A+5 5
表中的A为当年的代码。由这个表我们可以看出,月与月之间也有一定的关系。由此我们可以推出下面的公式(2):
(2)Amonth=M>2?(Ayear+2*(M+1)+3*(M+1)/5)%7:(Ayear+2*(M+2)+3*(M+2)/5)%7
但是上表所反映的仅为平年的情况,若Y为闰年,则在M大于2时,每个月的代码还需再加1。这可用一个IF语句解决:
(3)if (((Y%4==0 && Y%100!==0)(Y%400==0))&&M>2) Amonth = (Amonth+1)%7;
现在我们回到公式(1)中的问题。如果Y<0时,使得Ayear<0,那么Ayear最小也只能到-6。大家可以看到,当我们将Ayear代入公式(2)时,问题就自然解决了。
(三)计算日期
有了上面的公式,当我们输入日期后,就很容易算出当天为星期几了,而且可以计算变量允许范围内的任意一天的星期数。
(4)A = (Amonth+D)%7
(四)写程序
下面给出该万年历的程序,约莫估计,它可从公元前数十亿年算到公元后数十亿年(即从负十位数到正十位数),而且无一错漏。
(程序中并没有对输入的月份和日期进行出错处理)
#include <stdio.h>
char *week[] = {"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"};
void main()
{
int Y;
int M;
int D;
int A;
printf("\nEnter year:");
scanf("%d",&Y);
printf("\nEnter month:");
scanf("%d",&M);
printf("\nEnter date:");
scanf("%d",&D);
//下面的四条语句用来计算输入日期的星期数,是程序的核心部分,缺一不可
A = Y>0?(5+(Y+1)+(Y-1)/4-(Y-1)/100+(Y-1)/400)%7:(5+Y+Y/4-Y/100+Y/400)%7;
A = M>2?(A+2*(M+1)+3*(M+1)/5)%7:(A+2*(M+2)+3*(M+2)/5)%7;
if (((Y%4 == 0 && Y%100 != 0) Y%400 == 0) && M>2)
{
A =(A+1)%7;
}
A=(A+D)%7;
printf("\nI's a %s.\n\n",week[A]);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -