0%

Zeller公式

Zeller日期公式

计算原理

选定一个日期,并且知道它是周几,作为参考点,计算相差的天数。

$ D_1 $ 表示从参考点到参考点年末的天数。

$ D_2 $ 表示从参考点所在年份到当前年份的所有天数。

$ D_3 $ 表示从当前年份第一天到当前年份当前日期的天数。

如果参考点选在 12 月 31 日那么就不需要计算 $ D_1 $ 了,因为 $ D_1 = 0 $ 。

$ D $ 模除 7 然后需要加上参考点周几的偏移量,如果参考点为周日就不需要偏移量了。公元元年前一年的12月31日刚好是周日,刚好满足所有要求。

所以 $ D\mod7 $ 的余数就是周几。

计算 $ D_2 $

也就是相差的年数为 $ y-1 $ ,乘以天数 $ 365 $ ,然后加上闰年的个数。(闰年:能被 $ 4 $ 整除但是不能被 $ 100 $ 整除,能被 $ 400 $ 整除)

计算 $ D_3 $

不考虑闰年的话,每年中 2 月份天数最少,为 28 天。因此,可以把月份的天数看做 28 + 增量的形式。

月份123456789101112
天数312831303130313130313031
增量303232332323
前缀和(m月之前)0336811131619212426

因此

这是以公元前1年的 12 月 31 日为起点的。 $ D-1 $ 为从1,1,1开始的天数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// weekday=(id+1)%7;{Sun=0,Mon=1,...} getId(1, 1, 1) = 0
int getDays(int y, int m, int d)
{
if(m < 3) y--, m += 12;
return 365 * (y - 1) + y / 4 - y / 100 + y / 400 + 28 * (m - 1) + 13 * (m + 1) / 5 - 8 + d;
}
// 当y小于0时, 将除法改为向下取整后即可保证正确性, 或统一加400的倍数年
auto date(int id)
{
int x=id+1789995, n, i, j, y, m, d;
n = 4 * x / 146097; x -= (146097 * n + 3) / 4;
i = (4000 * (x + 1)) / 1461001; x -= 1461 * i / 4 - 31;
j = 80 * x / 2447; d = x - 2447 * j / 80; x = j / 11;
m = j + 2 - 12 * x; y = 100 * (n - 49) + i + x;
return make_tuple(y, m, d);
}