C语言根据日期判断星期几(使用基姆拉尔森计算公式)

算法如下: 
基姆拉尔森计算公式
W= (d+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日为星期一

程序如下:

/*利用基姆拉尔森计算日期公式  w=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)*/

#include <stdio.h>const char * getWeekdayByYearday(int iY, int iM, int iD)
{int iWeekDay = -1; if (1 == iM || 2 == iM) {   iM += 12; iY--;}   iWeekDay = (iD + 1 + 2 * iM + 3 * (iM + 1) / 5 + iY + iY / 4 - iY / 100 + iY / 400) % 7;switch(iWeekDay){   case 0 : return "Sunday"; break;case 1 : return "Monday"; break;case 2 : return "Tuesday"; break;case 3 : return "Wednesday"; break;case 4 : return "Thursday"; break;case 5 : return "Friday"; break;                                                             case 6 : return "Saturday"; break;default : return NULL; break;}   return NULL;
}int main()
{int year,month,day;char ch='1';while(ch != '\033'){ printf("\n请输入日期:\n格式为:1900,1,1\n");scanf("%d,%d,%d",&year,&month,&day);const char * p = getWeekdayByYearday(year, month, day);printf("WeekDay : %s\n", p);ch = getchar();printf("\n");}
}

运行效果:

$ ./getweekdaybyday 请输入日期:
格式为:1900,1,1
2008,4,29
WeekDay : Tuesday请输入日期:
格式为:1900,1,1
2015,2,4
WeekDay : Wednesday请输入日期:
格式为:1900,1,1

编者注:用来算现在真实日期的星期是没有问题的。原理是根据已知公元1年1月1日的星期数来推算。如果在你的题目中约定了某天是星期几,你要注意那天的星期是否跟真实的星期相同,如果不同,需要考虑相差几天!

如果大家觉得不够过瘾,可以看看以下该公式的推导过程,让大家对历法有个更深刻的认识

下面我们完全按自己的思路由简单到复杂一步步进行推导……

推导之前,先作两项规定: 
①用 y, m, d, w 分别表示 年 月 日 星期(w=0-6 代表星期日-星期六 
②我们从 公元0年1月1日星期日 开始

一、只考虑最开始的 7 天,即 d = 1---7 变换到 w = 0---6 
很直观的得到: 
w = d-1

二、扩展到整个1月份 
模7的概念大家都知道了,也没什么好多说的。不过也可以从我们平常用的日历中看出来,在周历里边每列都是一个按7增长的等差数列,如1、8、15、22的星期都是相同的。所以得到整个1月的公式如下: 
w = (d-1) % 7 --------- 公式⑴

三、按年扩展 
由于按月扩展比较麻烦,所以将年扩展放在前面说

① 我们不考虑闰年,假设每一年都是 365 天。由于365是7的52倍多1天,所以每一年的第一天和最后一天星期是相同的。 
也就是说下一年的第一天与上一年的第一天星期滞后一天。这是个重要的结论,每过一年,公式⑴会有一天的误差,由于我们是从0年开始的,所以只须要简单的加上年就可以修正扩展年引起的误差,得到公式如下: 
w = (d-1 + y) % 7

② 将闰年考虑进去 
每个闰年会多出一天,会使后面的年份产生一天的误差。如我们要计算2005年1月1日星期几,就要考虑前面的已经过的2004年中有多少个闰年,将这个误差加上就可以正确的计算了。 
根据闰年的定义(能被4整但不能被100整除或能被400整),得到计算闰年的个数的算式:y/4 - y/100 + y/400。 
由于我们要计算的是当前要计算的年之前的闰年数,所以要将年减1,得到了如下的公式: 
w = [d-1+y + (y-1)/4-(y-1)/100+(y-1)/400] % 7 -----公式⑵

现在,我们得到了按年扩展的公式⑵,用这个公式可以计算任一年的1月份的星期

四、扩展到其它月 
考虑这个问题颇费了一翻脑筋,后来还是按前面的方法大胆假才找到突破口。

①现在我们假设每个月都是28天,且不考虑闰年 
有了这个假设,计算星期就太简单了,因为28正好是7的整数倍,每个月的星期都是一样的,公式⑵对任一个月都适用 :)

②但假设终究是假设,首先1月就不是28天,这将会造成2月份的计算误差。1月份比28天要多出3天,就是说公式⑵的基础上,2月份的星期应该推后3天。 
而对3月份来说,推后也是3天(2月正好28天,对3月的计算没有影响)。 
依此类推,每个月的计算要将前面几个月的累计误差加上。 
要注意的是误差只影响后面月的计算,因为12月已是最后一个月,所以不用考虑12月的误差天数,同理,1月份的误差天数是0,因为前面没有月份影响它。

由此,想到建立一个误差表来修正每个月的计算。 
================================================== 
月 误差 累计 模7 
1 3 0 0 
2 0 3 3 
3 3 3 3 
4 2 6 6 
5 3 8 1 
6 2 11 4 
7 3 13 6 
8 3 16 2 
9 2 19 5 
10 3 21 0 
11 2 24 3 
12 - 26 5 
(闰年时2月会有一天的误差,但我们现在不考虑) 
==================================================

我们将最后的误差表用一个数组存放 
在公式⑵的基础上可以得到扩展到其它月的公式

e[] = {0,3,3,6,1,4,6,2,5,0,3,5} 
w = [d-1+y + e[m-1] + (y-1)/4-(y-1)/100+(y-1)/400] % 7 --公式⑶

③上面的误差表我们没有考虑闰年,如果是闰年,2月会一天的误差,会对后面的3-12月的计算产生影响,对此,我们暂时在编程时来修正这种情况,增加的限定条件是如果当年是闰年,且计算的月在2月以后,需要加上一天的误差。大概代码是这样的:

w = (d-1 + y + e[m-1] + (y-1)/4 - (y-1)/100 + (y-1)/400); 
if(m>2 && (y%4==0 && y%100!=0 || y%400==0) && y!=0) 
++w; 
w %= 7;

现在,已经可以正确的计算任一天的星期了。 
注意:0年不是闰年,虽然现在大都不用这个条件,但我们因从公元0年开始计算,所以这个条件是不能少的。

④ 改进 
公式⑶中,计算闰年数的子项 (y-1)/4-(y-1)/100+(y-1)/400 没有包含当年,如果将当年包含进去,则实现了如果当年是闰年,w 自动加1。 
由此带来的影响是如果当年是闰年,1,2月份的计算会多一天误差,我们同样在编程时修正。则代码如下

w = (d-1 + y + e[m-1] + y/4 - y/100 + y/400); ---- 公式⑷ 
if(m<3 && (y%4==0 && y%100!=0 || y%400==0) && y!=0) 
--w; 
w %= 7;

与前一段代码相比,我们简化了 w 的计算部分。 
实际上还可以进一步将常数 -1 合并到误差表中,但我们暂时先不这样做。

至此,我们得到了一个阶段性的算法,可以计算任一天的星期了。

public class Week { 
public static void main(String[] args){ 
int y = 2005; 
int m = 4; 
int d = 25;

int e[] = new int[]{0,3,3,6,1,4,6,2,5,0,3,5}; 
int w = (d-1+e[m-1]+y+(y>>2)-y/100+y/400); 
if(m<3 && ((y&3)==0 && y%100!=0 || y%400==0) && y!=0){ 
--w; 

w %= 7;

System.out.println(w); 

}

五、简化 
现在我们推导出了自己的计算星期的算法了,但还不能称之为公式。 
所谓公式,应该给定年月日后可以手工算出星期几的,但我们现在的算法需要记住一个误差表才能进行计算,所以只能称为一种算法,还不是公式。 
下面,我们试图消掉这个误差表……

============================= 
消除闰年判断的条件表达式 
=============================

由于闰年在2月份产生的误差,影响的是后面的月份计算。如果2月是排在一年的最后的话,它就不能对其它月份的计算产生影响了。可能已经有人联想到了文章开头的公式中为什么1,2月转换为上年的13,14月计算了吧 :)

就是这个思想了,我们也将1,2月当作上一年的13,14月来看待。 
由此会产生两个问题需要解决: 
1>一年的第一天是3月1日了,我们要对 w 的计算公式重新推导 
2>误差表也发生了变化,需要得新计算

①推导 w 计算式 
1> 用前面的算法算出 0年3月1日是星期3 
前7天, d = 1---7 ===> w = 3----2 
得到 w = (d+2) % 7 
此式同样适用于整个三月份 
2> 扩展到每一年的三月份 
[d + 2 + y + (y-1)/4 - (y-1)/100 + (y-1)/400] % 7

②误差表 
================================================== 
月 误差 累计 模7 
3 3 0 0 
4 2 3 3 
5 3 5 5 
6 2 8 1 
7 3 10 3 
8 3 13 6 
9 2 16 2 
10 3 18 4 
11 2 21 0 
12 3 23 2 
13 3 26 5 
14 - 29 1 
==================================================

③得到扩展到其它月的公式 
e[] = {0,3,5,1,3,6,2,4,0,2,5,1} 
w = [d+2 + e[m-3] +y+(y-1)/4-(y-1)/100+(y-1)/400] % 7 
(3 <= m <= 14)

我们还是将 y-1 的式子进行简化 
w = [d+2 + e[m-3] +y+y/4-y/100+y/400] % 7 
(3 <= m <= 14)

这个式子如果当年是闰年,会告成多1的误差 
但我们将1,2月变换到上一年的13,14月,年份要减1,所以这个误差会自动消除,所以得到下面的算法:

int e[] = new int[]{0,3,5,1,3,6,2,4,0,2,5,1}; 
if(m < 3) { 
m += 12; 
--y; 

int w = (d+2 + e[m-3] +y+(y/4)-y/100+y/400) % 7; -----公式⑸

我们可以看到公式⑸与公式⑷几乎是一样的,仅仅是误差天和一个常数的差别 
常数的区别是由起始日期的星期不同引起的,0年1月1日星期日,0年3日1日星期三,有三天的差别,所以常数也从 -1 变成了 2。

现在,我们成功的消除了繁琐的闰年条件判断。

============================= 
消除误差表 
============================= 
假如存在一种m到e的函数映射关系,使得 
e[m-3] = f(m) 
则我们就可以用 f(m) 取代公式⑸中的子项 e[m-3],也就消除了误差表。

由于误差表只有12个项,且每一项都可以加减 7n 进行调整,这个函数关系是可以拼凑出来的。但是这个过程可能是极其枯燥无味的,我现在不想自己去推导它,我要利用前人的成果。所谓前人栽树,后人乘凉嘛 :)

文章开头开出的公式中的 2*m+3*(m+1)/5 这个子项引起了我的兴趣

经过多次试试验,我运行下面的代码

for(m=1; m<=14; ++m) 
System.out.print((-1+2*m+3*(m+1)/5)%7 + " "); 
System.out.println();

天哪,输出结果与我的误差表不谋而合,成功了,哈哈

2 4 0 3 5 1 3 6 2 4 0 2 5 1 
Press any key to continue...

上面就是输出结果,看它后面的12项,与我的误差表完全吻合!!!

现在就简单的,将 f(m) = -1 + 2*m + 3*(m+1)/5 代入公式⑸,得到

w = (d+1+2*m+3*(m+1)/5+y+(y/4)-y/100+y/400) % 7 ----公式6 
约束条件: m=1,m=2 时 m=m+12,y=y-1;

现在,我们得到了通用的计算星期的公式,并且“完全”是按自己的思想推导出来的(那个函数映射关系不算),只要理解了这个推导的步骤,即使有一天忘记了这个公式,也可以重新推导出来!

可能有人会注意到公式⑹与文章开头的公式相差一个常数 1,这是因为原公式使用数字0--6表示星期一到星期日,而我用0--6表示星期日到星期六。实际上是一样,你可以改成任意你喜欢的表示方法,只需改变这个常数就可以了。

六、验证公式的正确性。

一个月中的日期是连续的,只要有一天对的,模7的关系就不会错,所以一个月中只须验证一天就可以了,一天需要验12天。由于扩展到年和月只跟是否闰年有关系,就是说至少要验证一个平年和一个闰年,也就是最少得验证24次。 
我选择了 2005 年和 2008 年,验证每个月的1号。 
测试代码如下:

class test {                                                                                         public int GetWeek(int y, int m, int d) { if(m<3) { m += 12; --y; }   int w = (d+1+2*m+3*(m+1)/5+y+(y>>2)-y/100+y/400) % 7;  return w;  }
}
public class Week { public static void main(String[] args){ int y = 2005; int m = 1; int d = 1; test t = new test(); String week[] = new String[]{ "星期日","星期一","星期二","星期三","星期四","星期五","星期六" }; for(y=2005; y<=2008; y+=3) { for(m=1; m<=12; ++m) { String str = y + "-" + m + "-" + d + "\t" + week[t.GetWeek(y,m,d)]; System.out.println(str); } } }
} 

查万年历,检查程序的输出,完全正确。

七、后话

我们这个公式的推导是以0年3月1日为基础的,对该日以后的日期都是可以计算的。但是否可以扩展到公元前(1,2已属于公元前1年的13,14月了)呢?

虽然我对0年1月和2月、以及公元前1年(令y=-1)的12月作了验证是正确的,但我在推导这个公式时并未想到将其扩展到公元前,所以上面的推导过程没有足够理论依据可以证明其适用于公元前。(负数的取模在不同的编译器如C++中好象处理并不完全正确)。

另外一有点是对于0年是否存在的争议,一种折中的说法是0年存在,但什么也没有发生,其持续时间为0。还有在罗马的格利戈里历法中有10天是不存的(1582年10月5日至14持续时间为0),英国的历法中有11天(1752年9月3日至13日)是不存在的。感兴趣的朋友可以看看这里:

但是我们做的是数字计算,不管那一天是否存在,持续的时间是24小时还是23小时甚至是0小时,只要那个号码存在,就有一个星期与之对应。所以这个公式仍然是适用的。 
如果要计算的是时间段,就必须考虑这个问题了。

转载于:https://www.cnblogs.com/fengbohello/p/3264300.html

C语言根据日期(年,月,日)判断星期几(使用基姆拉尔森计算公式)相关推荐

  1. C语言根据年-月-日-判断星期几

    利用基姆拉尔森计算公式 W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7 在公式中d表示日期中的日数,m表示月份数,y表示年数. 注意:在公式中有个与其他公式不 ...

  2. C语言根据日期判断星期几(使用基姆拉尔森计算公式)

    C语言根据日期判断星期几(使用基姆拉尔森计算公式) 算法如下: 基姆拉尔森计算公式 W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7 在公式中d表示日期中的日数 ...

  3. 基姆拉尔森计算公式(根据日期判断星期几)

    //基姆拉尔森计算公式根据日期判断星期几 void CalculateWeekDay(int y, int m,int d){if(m==1||m==2) m+=12,y--;int iWeek = ...

  4. 根据日期判断星期几(使用基姆拉尔森计算公式)

    基姆拉尔森计算公式 W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7 在公式中d表示日期中的日数,m表示月份数,y表示年数. 注意:在公式中有个与其他公式不同的 ...

  5. C语言根据日期计算星期——基姆拉尔森计算公式

    公式 基姆拉尔森计算公式 w = ( day + 2month +3(month+1)/5 + year + year/4 - year/100 +year/400)%7 根据已知公元1年1月1日星期 ...

  6. 根据日期计算星期几 -- 基姆拉尔森计算公式

    首先看下百度百科的基姆拉尔森计算公式定义:  基姆拉尔森计算公式 W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7 在公式中d表示日期中的日数,m表示月份数,y ...

  7. HDU 6112 今夕何夕【2017百度之星】【日期模拟计算】【基姆拉尔森计算公式】【蔡勒公式】

    今夕何夕 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  8. c语言输入年月日输出星期几,基姆拉尔森计算公式 (根据输入的年月日输出星期几)...

    基姆拉尔森计算公式 W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7 在公式中d表示日期中的日数,m表示月份数,y表示年数. 注意:在公式中有个与其他公式不同的 ...

  9. 使用C语言根据年月日求星期(基姆拉尔森计算公式)。

    题目 题目: 根据年月日求星期. 以下是本篇文章正文内容,欢迎朋友们进行指正,一起探讨,共同进步.--来自考研路上的lwj 一.解题思路 思路: 本题主要使用这个算法求出把算法代入程序即可: 基姆拉尔 ...

最新文章

  1. ​专为初学者设计——最小的神经网络
  2. Unity提出ProtoRes模型:稀疏可变的输入也能构建完整人体姿态
  3. js 输出二维数组的最大值
  4. bzoj 2342: 双倍回文 回文自动机
  5. android AppCompatEditText 自定义下划线颜色
  6. symbian c++ 开发环境Carbide.c++搭建
  7. oracle关联视图查询满_对于复杂的SQL, Oracle是怎么做的?
  8. X^2 Mod P(51Nod-1014)
  9. IT巨头埃森哲遭 LockBit 勒索攻击,黑客威胁泄露数据
  10. 使用IConfigurationSectionHandler在web.config中增加自定义配置
  11. matlab 图像中4像素融合一个像素,python实现两张图片的像素融合
  12. 【基础知识】9、加州房价预测
  13. 周纪二 周显王元年(癸丑,公元前368年)——摘要
  14. Spoon Kettle 输入之获取文件名(Get file names)
  15. 图片查看器插件(带缩略图) - viewer.js
  16. mysql数据库之存储过程
  17. 用exclusion切断maven jar包的依赖传递
  18. 礼品卡配合U盘,美国一公司遭受BadUSB真实攻击
  19. EndNote20如何下载并安装中文参考文献格式GBT7714
  20. 线程安全、volatile关键字、原子性、并发包、死锁、线程池

热门文章

  1. c语言sgoto 标志位,如何在Go中设置TCP数据包的“不分段”标志位?(How to set “don't fragment” flag bit for TCP packet in Go?)...
  2. CBOW模型的学习、Trainer类的实现
  3. (递推1)兔子繁殖问题
  4. 地图自定义图标_如何在H5里添加地图导航?这份教程请收藏!
  5. python子进程kerberos_Anaconda3的python找不到kerberos凭证缓存
  6. 基于Java+SpringMvc+vue+element实现疫苗接种管理平台
  7. mysql数据超10亿条,大型主键:超过10亿行MySQL + InnoDB?
  8. python list tuple 消耗_Python内存消耗:dict VS元组列表
  9. pyqt5生成py的文件为什么是c 语言,如何使用PyQt5在python中创建文件对话框
  10. c语言规范标准中英文,C语言中英文翻译资料.doc