和中国的节日不同,美国的节假日通常是选择某个月的第几个星期几这种形式,因此每一年的放假日期都不相同。具体规则如下:
现在给出一个年份,请你帮忙生成当年节日的日期。
输入描述:
输入包含多组数据,每组数据包含一个正整数year(2000≤year≤9999)
输出描述:
对应每一组数据,以“YYYY-MM-DD”格式输出当年所有的节日日期,每个日期占一行。 每组数据之后输出一个空行作为分隔。
题目中一共有两种类型的节日:固定日期
的节日和非固定日期
的节日
(1)对于前者直接输出
即可
(2)对于后者我们就需要找到一个基准时间来求某月的第几个星期几,这里的基准时间设置为该月的1号
比如今天是2022年3月1日,想要求2022年3月的第三个星期一是几号,就可以根据3月1日是星期几,从而进行推断3月的第三个星期一是几号
那么,现在要求的问题就是2022年3月1日是星期几,如果我明确知晓2022年2月21日是星期一,那么根据2月21日和3月1日相差几天,结果%上一个7,就可以知道3月1日是星期几了(相差8天,那么1+8%7=2,3月1日是星期二)。在这里为了求出相差几天更加的方便简洁,采用的基准时间为公元前1年12月31日,该日为星期天
。
再求和公元前1年12月31日相差几天的过程中需要注意闰年要多一天。
(1)给定年月日(Y-M-D)求和公元前1年12月31日相差几天(days)
总公式
:days = (Y-M-D)-(0000-12-31)= 一共过去的完整的年的天数 + 最后一年(第Y年)过去的天数
分公式1
:一共过去的完整的年的天数 =(Y-1)* 365 + 这些完整的年中闰年数 * 1
分公式2
:闰年数 = (Y - 1)/ 4 - (Y - 1) / 100 + (Y - 1) / 400
分公式3
:最后一年过去的天数 = 经过的完整的月的天数之和+D+是否是闰年?1 : 0(过了2月的前提,否则不用加上最后一项)
分公式4
:闰年 = (Y % 400 == 0)||(Y % 4 == 0 && Y % 100 != 0)
由上面的公式可以推出
days = (Y-M-D)-(0000-12-31)=(Y-1)*365 + (Y-1)/4 - (Y-1)/100 +
(Y-1)/400 + 完整月的天数 + D + 是否是闰年?1:0
至此就可以算出相差的天数,(Y-M-D)是星期(days % 7 == 0 ? 7 :days%7)
由于days的结果需要 % 7,而 365 等于364 + 1,364 等于 52 * 7,days 公式可以简化为:
days = (Y-1) + (Y-1)/4 - (Y-1)/100 + (Y-1)/400 + 完整月的天数 + D + 是否是闰年?1:0
(2)根据某月1日是星期X(已求出)来知晓当月的第W个星期Q是几号
根据上面的分析我们可以求助某月1日是星期几,比如 1 月1 日已经被求出是星期六,想要求1月的第三个星期一:马丁·路德·金纪念日,我们可以经过找规律来得出结论:
推出结论:1 号为星期X时,第三个星期一的日期是 1 + 2 * 7 +(7-X+1)% 7
再分析一下11月的第四个星期四:感恩节,已知11月1 日是周二,那么可以通过找规律得到:
结论
:1号为星期X时,第W个星期Q的日期是1 + (W-1) * 7 + (7-X+Q) % 7
所有日期中还有一个比较特殊的,就是5月的最后一个星期一:阵亡将士纪念日
可以将 6 月1 日当做 5 月32日,已知 6 月1日为星期X,那么5月的最后一个星期一可以推出:
结论
:6月1日为星期X时,5月的最后一个星期一的日期是 32 - (X == 1 ?7 : X - 1)
import java.util.*;
public class Main {
//来判断是否是闰年
public static boolean isYear(int Y) {
return ((Y % 400 == 0)||(Y % 4 == 0 && Y % 100 != 0));
}
//来算算第Y年已经过了多少天了
private static final int[] mouth = {31,28,31,30,31,30,31,31,30,31,30,31};//按照平年的来算
public static int yDay(int Y,int M,int D) {
int ret = 0;
for(int i = 0;i < M-1;i ++) {
ret += mouth[i];
}
//如果月份过了2月就来判断一下是否是闰年,是的话加一
if(M > 2 && isYear(Y)) {
ret ++;
}
return ret + D;
}
//来算算从0000-12-31到Y-M-D一共隔的天数(简化过的结果)
public static int count(int Y,int M,int D) {
return (Y - 1) + (Y - 1)/ 4 - (Y - 1) / 100 + (Y - 1) / 400 + yDay(Y,M,D);
}
//来算算某年某月的一号是星期几
public static int One(int Y,int M,int D) {
int X = count(Y,M,D)%7;
if(X == 0){
X = 7;
}
return X;
}
//来算算第W个星期Q的日期
public static int m1(int X,int W,int Q) {
return 1 + (W-1) * 7 + (7-X+Q) % 7;
}
//来算算5月的最后一个星期一的日期
public static int m2(int X) {
int d = (X == 1? 7:X-1);
return 32 - d;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while(scan.hasNextInt()) {
int Y = scan.nextInt();
System.out.printf("%d-01-01\n",Y);//1月1日
int X = 0;
X = One(Y,1,1);
System.out.printf("%d-01-%02d\n",Y,m1(X,3,1));//1月的第三个星期一
X = One(Y,2,1);
System.out.printf("%d-02-%02d\n",Y,m1(X,3,1));//2月的第三个星期一
X = One(Y,6,1);
System.out.printf("%d-05-%02d\n",Y,m2(X));//5月的最后一个星期一
System.out.printf("%d-07-04\n",Y);//7月4日
X = One(Y,9,1);
System.out.printf("%d-09-%02d\n",Y,m1(X,1,1));//9月的第一个星期一
X = One(Y,11,1);
System.out.printf("%d-11-%02d\n",Y,m1(X,4,4));//11月的第四个星期四
System.out.printf("%d-12-25\n",Y);
System.out.println();//12月25日
}
}
}
完!