公元年每一天是星期几

 在之前一篇文章中我讲了判断2015年中每一天是星期几的方法

这不禁让我思考如果不限定年份,如何判断每一天是星期几呢?

首先我们明确一下总的判断方法:

公元元年1月1日至指定日期的总天数%7 

余数为几就是星期几

 难点:求公元元年1月1日至指定日期的总天数

考虑到年、月、日三个变量,发现求总天数应当是分为三个部分

  1. 公元元年1月1日至指定年份前一年的总天数
  2. 指定年份1月1日至指定月份前一月的总天数
  3. 指定月份1日至指定日的总天数

 然后发现求公元元年1月1日至指定年份前一年的总天数不能直接用“年数*365”,还要考虑到平年、闰年的问题,则第一个部分再细分为两个小部分

  1. 平年年数*365
  2. 闰年年数*366

此时发现需要解决平年数、闰年数的统计问题

我们判断闰年,则平年数就等于指定年份减去闰年数

这里引入润年的概念

地球绕太阳运行的周期为365天5小时48分46秒(合365.24219天),即一回归年(tropical year)。公历的平年只有365天,比回归年短约0.2422天,所余下的时间约为每四年累积一天,故在第四年的2月末加1天,使当年的时间长度变为366天,这一年就是闰年。现行公历中每400年有97个闰年。按照每四年一个闰年计算,平均每年就要多算出0.0078天,这样,每128年就会多算出1天,经过400年就会多算出3天多。因此,每400年中要减少3个闰年。所以公历规定:年份是整百数时,必须是400的倍数才是闰年;不是400的倍数的世纪年,即使是4的倍数也不是闰年。

这就是通常说的:四年一闰,百年不闰,四百年再闰。例如:2000年是闰年,2100年则是平年。

也就是:能被4整除但不能被100整除或能被400整除的年份为润年

知道了闰年的判断方法,则统计闰年数十分简单,定义变量i,让i从1循环至指定年份前一年,每次循环判断i是否为闰年,如果是则润年数加1(初始值为0)

至此第一部分解决

第二部分,指定年份1月1日至指定月份前一月的总天数

定义数组,将每个月份的天数存进去,定义变量i,让i从1循环到指定月份的前一个月,每次循环每个月的天数加到总天数中(循环前总天数为公元元年1月1日至指定年份前一年的总天数)

至此第二部分解决

第三部分,发现不需要算,指定几就是几。

最后直接算就好

#include <stdio.h>
int main()
{
	int year, month, day, sumday, result;
	while (1)
	{
        int sumleapyear = 0;
		printf("请输入年:");
		scanf_s("%d", &year);
		//统计公元元年至去年中有多少个闰年
		for (int i = 1; i <= year - 1; i++)
		{
			if (i % 4 == 0 && i % 100 != 0 || i % 400 == 0)
				sumleapyear++;
		}
		//统计公元元年1月1日至去年12月31日共有多少天
		//公元元年至去年中的平年数*365+闰年数*366
		sumday = (year - 1 - sumleapyear) * 365 + sumleapyear * 366;
		int February;
		if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
			February = 29;
		else
			February = 28;
		int a[13] = { 0,31,February,31,30,31,30,31,31,30,31,30,31 };
		printf("请输入月:");
		scanf_s("%d", &month);
		//统计公元元年1月1日至去年12月31日总天数+今年1月1日至今年上一个月最后一天的总天数
		for (int i = 1; i <= month - 1; i++)
			sumday += a[i];
		printf("请输入日:");
		scanf_s("%d", &day);
		//公元1月1日至今的总天数求余7
		result = (sumday + day) % 7;
		if (result != 0)
			printf("%d年%d月%d日是星期%d\n", year, month, day, result);
		else
			printf("%d年%d月%d日是星期日\n", year, month, day);
	}
	return 0;
}

这种算法默认公元元年1月1日是星期一 

但你可能会思考到一个问题,公元元年1月1日真的是星期1吗?

我也思考了,发现星期是公元321年3月7日才提出来的,这里引入星期的概念

星期,又叫周,是一个时间单位,也是制定工作日休息日的依据。

星期作为时间周期最早起源于巴比伦。世界通行的星期制是罗马皇帝君士坦丁大帝在公元321年3月7日正式确立的。

在中国古代称“七曜”。七曜在中国夏商周时期,是指日、月及五大行星等七个主要星体,是当时天文星象的重要组织成份,后来借用作七天为一周的时间单位,故称“星期”。 

 那公元321年3月7日是星期1?

推算一下还真是

 则公元元年1月1日肯定为星期1

 但这是按照格里历(公历)算的,公历1582年前使用的是儒略历

儒略历

儒略历  (Julian calendar)是由罗马共和国独裁官儒略·凯撒(又译盖乌斯·尤里乌斯·凯撒)采纳埃及亚历山大的数学家兼天文学家索西琴尼的计算后,于公元前45年1月1日起执行的取代旧罗马历法的一种历法。

儒略历中,一年被划分为12个月,大小月交替;四年一闰,平年365日,闰年366日为在当年二月底增加一闰日,年平均长度为365.25日。

格里历

现行西历即格里历,又译国瑞历、额我略历、格列高利历、格里高利历,称西元。是由意大利医生兼哲学家里利乌斯(Aloysius Lilius)改革儒略历制定的历法,由教皇格列高利十三世在1582年颁行。

格里历与儒略历大致一样,但格里历特别规定,除非能被400整除,所有的世纪年(能被100整除)都不设闰日;如此,每四百年,格里历仅有97个闰年,比儒略历减少3个闰年。格里历的历年平均长度为365.2425日,接近平均回归年的365.242199074日,即约每3300年误差一日,也更接近春分点回归年的365.24237日,即约每8000年误差一日;而儒略历的历年为365.25日,约每128年就误差一日。到1582年时,儒略历的春分日(3月21日)与地球公转到春分点的实际时间已相差10天。因此,格里历开始实行时,将儒略历1582年10月4日星期四的次日,改为格里历1582年10月15日星期五,即有10天被删除,但原星期的周期保持不变。 

公元1582年10月15日

 我们使用儒略历,在已知儒略历1582年10月4日是星期四的情况下往前推

这里我们更改一下闰年的判断方法,改为

能被4整除的都是润年

更改为如下代码后我们再次运行,输入1582年10月4日

#include <stdio.h>
int main()
{
	int year, month, day, sumday;
	while (1)
	{
        int sumleapyear = 0;
		printf("请输入年:");
		scanf_s("%d", &year);
		//统计公元元年至去年中有多少个闰年
		for (int i = 1; i <= year - 1; i++)
		{
			if (i % 4 == 0)
				sumleapyear++;
		}
		printf("距元年共有%d个润年\n",sumleapyear);
		//统计公元元年1月1日至去年12月31日共有多少天
		//公元元年至去年中的平年数*365+闰年数*366
		sumday = (year - 1 - sumleapyear) * 365 + sumleapyear * 366;
		int February;
		if (year % 4 == 0)
			February = 29;
		else
			February = 28;
		int a[13] = { 0,31,February,31,30,31,30,31,31,30,31,30,31 };
		printf("请输入月:");
		scanf_s("%d", &month);
		//统计公元元年1月1日至去年12月31日总天数+今年1月1日至今年上一个月最后一天的总天数
		for (int i = 1; i <= month - 1; i++)
			sumday += a[i];
		printf("请输入日:");
		scanf_s("%d", &day);
		//公元1月1日至今的总天数求余7
		printf("距元年元旦共有%d天\n",sumday+day);
		printf("距元年元旦共有%d周,余%d天\n", (sumday+day-(sumday + day) % 7)/7, (sumday + day) % 7);
	}
}

运行结果为

 也就是公元元年1月6号也是星期四,如此往前推6天,

公元元年1月5号为星期三

公元元年1月4号为星期二

公元元年1月3号为星期一

公元元年1月2号为星期日

公元元年1月1号为星期六

其实在写的时候我就想到整个运算过程是有一定规律的,或许可以推导出一个通用公式来,在写完之后我去查了有关资料,发现真的有两个通用公式,基姆拉尔森计算公式蔡勒公式

基姆拉尔森计算公式

本公式用来计算指定的年月日是星期几

算法如下:

基姆拉尔森计算公式 (C++与VB.Net整数除法和取余运算符不同)

W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7 //C++计算公式

W = (D + 2 * M + 3 * (M + 1) \ 5 + Y + Y \ 4 - Y \ 100 + Y \ 400+1) Mod 7 'VB.Net计算公式

在公式中d表示日期中的日数,m表示月份数,y表示年数。

w表示星期,w的取值范围是0~6,0代表星期日,1~6星期一到星期六。

注意:在公式中有个与其他公式不同的地方:

把一月和二月看成是上一年的十三月和十四月,例:如果是2004-1-10则换算成:2003-13-10来代入公式计算。

C实现

#include
void main() {
    int y=2013,m=1,d=1;
    int w;
    char *weekday[7]= {
        "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
    }
    ;
    if (m==1 || m==2) {
        m=(m==1?13:14);
        y=y-1;
        //此处表示把1,2月计算到上一年的13,14月<修改日期:2014.5.13 by 杨康佳>
    }
    w=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7;
    printf("%s\n",weekday[w]);
}

 蔡勒公式

或者是:

 

若要计算的日期是在1582年10月4日或之前,公式则为

以1582年10月4日为例:

1582年10月4日后:w = (d + 1+ 2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7;

1582年10月4日前:w = (d+1+2*m+3*(m+1)/5+y+y/4+5) % 7;

或者1752年9月3日为例

1752年9月3日后:w = (d + 2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7;(这个公式应该是跟正常的相差1的,也就是算出来相差了一天)

1752年9月3日前:w = (d+2*m+3*(m+1)/5+y+y/4+5) % 7;

注:罗马教皇决定在1582年10月4日后使用格利戈里历法;而英国则是在1752年9月3日后才接受使用格利戈里历法。

注意:

当年的1,2月要当成上一年的13,14月进行计算

w:星期; w对7取模得:0-星期日,1-星期一,2-星期二,3-星期三,4-星期四,5-星期五,6-星期六

c:世纪(注:一般情况下,在公式中取值为已经过的世纪数,也就是年份除以一百的结果,而非正在进行的世纪,也就是现在常用的年份除以一百加一;不过如果年份是公元前的年份且非整百数的话,c应该等于所在世纪的编号,如公元前253年,是公元前3世纪,c就等于-3)

y:年(一般情况下是后两位数,如果是公元前的年份且非整百数,y应该等于cMOD100+100)

m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算)

d:日

[ ]代表取整,即只要整数部分。

下面以中华人民共和国成立100周年纪念日那天(2049年10月1日)来计算是星期几,过程如下:

w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1

=49+[49/4]+[20/4]-2×20+[26×(10+1)/10]+1-1

=49+[12.25]+5-40+[28.6]

=49+12+5-40+28

=54 (除以7余5)

即2049年10月1日(100周年国庆)是星期五。

再比如计算2006年4月4日,过程如下:

w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1

=6+[6/4]+[20/4]-2*20+[26*(4+1)/10]+4-1

=-12 (除以7余5,注意对负数的取模运算!实际上应该是星期二而不是星期五)

w=(-12%7+7)%7=2;

C实现

#include<stdio.h>
int main()
{
 int year, month, day;
 while (scanf("%d %d %d", &year, &month, &day) != EOF)
 {
  if (month == 1 || month == 2)//判断month是否为1或2 
   year--, month += 12;
  int c = year / 100;
  int y = year - c * 100;
  int week = y + y / 4 + c / 4 - 2 * c + 26 * (month + 1) / 10 + day - 1;
  while (week < 0)
   week += 7;
  week %= 7;
  switch (week)
  {
  case 1:printf("Monday\n"); break;
  case 2:printf("Tuesday\n"); break;
  case 3:printf("Wednesday\n"); break;
  case 4:printf("Thursday\n"); break;
  case 5:printf("Friday\n"); break;
  case 6:printf("Saturday\n"); break;
  case 0:printf("Sunday\n"); break;
  }
 }
 return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值