日期计算相关问题
闰年判别,吉姆拉尔森日期公式(星期几判别),天数差,据天数差计算具体日期
下面有高斯日历和求国庆为星期几两个题
<1> 闰年判别
- 能被4整除但不能被100整除的年份
- 能被400整除的年份
private static boolean isLeap(int year) {
if ((year % 4 == 0 && year % 100 != 0)|| year % 400 == 0) {
return true;
}
return false;
}
<2> 吉姆拉尔森公式
w
=
(
d
a
y
+
2
∗
m
o
n
t
h
+
3
∗
(
m
o
n
t
h
+
1
)
5
+
y
e
a
r
+
y
e
a
r
4
−
y
e
a
r
100
+
y
e
a
r
400
)
m
o
d
7
+
1
w\:= \:(day\:+2\:\ast\:month\:+\:\frac{3\:\ast\:(month+1)}{5}+\:year\:+\frac{year}{4}\:-\:\frac{year}{100}\:+ \:\frac{year}{400})\:mod\:7\:+\:1
w
=
(
d
a
y
+
2
∗
m
o
n
t
h
+
5
3
∗
(
m
o
n
t
h
+
1
)
+
y
e
a
r
+
4
y
e
a
r
−
1
0
0
y
e
a
r
+
4
0
0
y
e
a
r
)
m
o
d
7
+
1
注:
- 当月数为1月或2月时以上一年的13和14月计算, 年数减1
带入所求星期的
年份
(year)
月
(month)
日
(day) 即可。所给结果w是多少就是星期几(公式原理很容易搜到,没彻底弄清楚,不过对我来说能用就好)
private static int calWhatDay(int y, int m, int d) {
if (m == 1 || m == 2) {
y--;
m += 12;
}
return (d + 2*m + 3*(m+1)/5 + y + y/4 - y/100 + y/400) % 7 + 1;
}
<3>天数差计算
s
=
365
∗
(
y
−
1
)
+
[
y
−
1
4
]
−
[
y
−
1
100
]
+
[
y
−
1
400
]
+
c
s= 365\:*\:(y – 1) + [\frac{y-1}{4}]- [\frac{y-1}{100}]+ [\frac{y-1}{400}] +c
s
=
3
6
5
∗
(
y
−
1
)
+
[
4
y
−
1
]
−
[
1
0
0
y
−
1
]
+
[
4
0
0
y
−
1
]
+
c
注:
- 中括号的意思是取整
-
c为所给日期
月
转换为以
日
为单位 和 所给
日
的
天数之和
-
计算的天数s为所给
年份
(year)
月
(month)
日
(day)与公元
1年 1月 1日
之间的差值
//differ(新年月日) - differ(旧年月日)即为天数差
private static int differ (int y, int m, int d) {
int sum = 0;
int[] daysData = {0,31,28,31,30,31,30,31,31,30,31,30,31};
if (isLeap(y) ) {
daysData[2] = 29;
}
for (int i = 1; i <= m; i++) {
sum += daysData[i];
}
sum += d;
return 365 * (y-1) + (y-1)/4 - (y-1)/100 + (y-1)/400 + sum;
}
题目练习
-
1949年的国庆节(10月1日)是星期六。
今年(2012)的国庆节是星期一。
那么,从建国到现在,有几次国庆节正好是星期日呢?
只要答案,不限手段!
可以用windows日历,windows计算器,Excel公式。。。。。当然,也可以编程!
解题思路:
- 直接使用吉姆拉尔森日期公式自1949年起对每个国庆进行遍历判断
public static void main(String[] args) {
int sum = 0;
for (int i = 1949; i < 2020; i++) {
if (calWhatDay(i ,10, 1) == 7) {
System.out.println(i);
sum++;
}
}
System.out.println(sum);
}
private static int calWhatDay(int y, int m, int d) {
if (m == 1 || m == 2) {
y--;
m += 12;
}
return (d + 2*m + 3*(m+1)/5 + y + y/4 - y/100 + y/400) % 7 + 1;
}
<4>根据天数差求具体日期
2. 大数学家高斯有个好习惯:无论如何都要记日记。
他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210
后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?
高斯出生于:1777年4月30日。
在高斯发现的一个重要定理的日记上标注着:5343,因此可算出那天是:1791年12月15日。
高斯获得博士学位的那天日记上标着:8113
请你算出高斯获得博士学位的年月日。
思路分析:
- 第一思路可以根据天数差8113 约等于 22 * 365 + 83 预估也就是在1777 ~ 1810年之间。所以可以直接使用天数差计算公式,三层循环对1777~1810期间年月日每一天遍历判断天数差是否为8112,来确定天数,但是感觉运算量有点大,所以不做实现另想其他办法
-
以前做过一个类似的题不过解决方案不太好这里有链接有兴趣的可以康康
用户输入: 年 月 日 及天数 程序运行后打印出具体的日期
- 因此作出优化简化了方案
解题方案:
- 将起始年记为1月1日开始,剩余月份及天数转加至总天数差(这主要是为了方便计算)
- 将确定日期的任务化简至确定在某一年中
- 再次化简将确定日期的任务确定在某个月中
- 然后就能自然而然确定天数
反思:
相比于以前的做法简化过程主要体现在了确定年份那里,感觉很奇特为什以前想不到,其他就没什么独特的地方了
public static void main(String[] args) {
//平年以及闰年每个月的天数以及一年的天数
int[][] daysData = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 365},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 366}};
//分别输入起始年, 月, 日, 以及相差的天数
//这道题标记为5343是自出生起的第5343天,实际相差天数应为5342
Scanner in = new Scanner(System.in);
int year = in.nextInt();
int month = in.nextInt();
int day = in.nextInt();
int differDays = in.nextInt();
//计算起始年已有天数,将月传化为单位日的总天数
int leap = isLeap(year);
for (int i = 0; i < month-1; i++) {
differDays += daysData[leap][i];
}
differDays += day;
//在相差天数大于365或355时逐年作差确定年份
while (differDays >= daysData[leap][12]) {
differDays -= daysData[leap][12];
leap = isLeap(++year);
}
//确定年份后,逐月作差,确定月份以及日期
int i = 0;
while (differDays > daysData[leap][i]) {
differDays -= daysData[leap][i];
i++;
}
System.out.println(year + "年" + (i+1) + "月" + differDays + "日");
}
//判断是否为闰年,为方便调用二维数组返回值直接用1,0表示
private static int isLeap(int year) {
if ((year % 4 == 0 && year % 100 != 0)|| year % 400 == 0) {
return 1;
}else {
return 0;
}
}