蔡勒(Zeller)公式理解Get(√)

article/2025/11/11 13:30:37

Preface

偶然做到日期相关题目,了解到Zeller公式。不甘心停留在使用阶段,便想掌握其推导过程。

只适用于格利戈里历法,也就是现今的公历。

 

1. Zeller公式

  • 标准形式

 

  • 计算1582年10月4日或之前日期 (罗马教皇决定在1582年10月4日后使用格利戈里历法)

 

  • 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:日
  • [ ]代表取整,即只要整数部分。

注意编程中MOD运算 ≠ 数学中MOD的运算, 所以若D为负数,则在求W之前可对D进行7的倍数的自增运算,让其成为正数,最后对7求余得到结果。

 

思考: 如何将给定日期换算成星期数呢?

  1. 找已知日期(年月日,周几)作为源
  2. 计算目标日期与源的间隔天数
  3. 将间隔天对7取余获取偏移量
  4. 将源的week加上偏移量
  5. 得到目标星期数


2. 推导过程

 

如你所想,Zeller公式并不神秘。通过以上思想的优化和扩展后,便得到Zeller公式。

从以上思想开始扩展。

1. 如何获取间隔天数?

D = D1 + D2 + D3 

  1. D1:从原点到原点所在年份末尾的天数。
  2. D2:原点所在年份和目标日期所在年份之间所有年份的天数和。
  3. D3:目标日期所在年份的第一天到目标日期的天数。

优化:

1. 若我们把源设定为12月31日则可省去D1的计算。于是 D = D2 + D3

2. 若源点正好是周日就正好了 可以省去偏移量的计算。

巧了: 我们发现公元元年的前一年12月31日恰好是周日,这是一个完美的源。

 

D2:

假设目标日期是y-m-d, 我们可以很简单的得出D2

D2 = (y - 1) * 365 + [\frac{y - 1}{4}] - [\frac{y-1}{100}] + [\frac{y-1}{400}]

因一年至少有365天,此外,闰年比平年多一天,所以需加上闰年中多出来的天数。

闰年规则: 四年一闰, 百年不闰, 四百年闰。故:[\frac{y - 1}{4}] - [\frac{y-1}{100}] + [\frac{y-1}{400}]

至此:D1 D2 解决

D3:先不考虑闰年,因2月份天数28天最少,因此,我们不妨把每个月的天数看作 “28 + Excess”的模式,m 月之前所有月份的 Excess 之和为 Accum(m),则 D3=28×(m−1)+Accum(m)+d,并且我们可以得到这样一张表格:

月份表
月份123456789101112
天数312831303130313130313031
Excess303232332323
Accum0336811131619212426

 

通过上表可发现Excess 3 月份开始是 3、2、3、2、3 的循环,因此,当 m≥3m≥3 时,Accum(m)的值的增幅也是 3、2、3、2、3 的循环。因为每 5 个月增加 13,所以把 \frac{13}{5} 作为系数;因为 Accum(m) 的值是离散的(都是整数),所以我们用取整运算符,得到:

f(x) = [\frac{13}{5}x]

将x的取值为1,2,3... 得到表

x4567891011121314
f(x)1013151820232628313336

可以发现,x≥4时 增幅也是3、2、3、2、3 的循环,也就是说x≥4 f(x)== m≥3 Accum(m) 的增幅相同,这意味着他们之间只相差一个常数。代值计算:

f(4)−Accum(3)=10−3=7

得到这个常数7,由此可得,当m≥3时 Accum(m) = f(m+1) - 7 = [\frac{13(m+1))}{5}] - 7

但这里考虑的是m≥3,我们可以通过分段函数来列式

D3 = d (m=1)

D3 = 31 + d (m=2)

D3 = (m-1) * 28 + [13(m-1)/5] - 7 + d + i (m >= 3)

闰年i = 1 平年则 i = 0。

继续优化D3算法:

我们继续分析,从 3 月份到 12 月份的 Excess 正好是两个 3、2、3、2、3 的循环,那么假如有第 13 月,想要继续保持这种循环规律,13 月的 Excess 值应该是 3。而 1 月份的 Excess 的值恰好是 3,所以我们不妨变通一下,把每年的 1 月、2月当作上一年的 13月、14 月。这样不仅仍然符合公式,而且 2 月份变成了上一年的最后一个月,也就是公式中 dd 的部分,于是平闰年的影响也去掉了,D3 的公式简化成了:

D3=(m−1)×28+[13(m+1)5]−7+d, 3≤m≤14

 

由D = D2 + D3, 得

D=(y-1)*365+[(y-1)/4]-[(y-1)/100]+[(y-1)/400]+(m-1)*28+[\frac{13(m+1)}{5}]-7+d,3\leq m\leq 14

但!

注意!这个公式离正确形式还差一小步。因为在当前的公式中,每年的 1 月和 2 月被当作上一年的 13 月和 14 月计算,因此当前公式中计算闰日的部分([y−14]−[y−1100]+[y−1400])存在错误。举一个具体的例子,比如计算公元 4 年(闰年)3 月 1 日的星期数。在当前公式中,公元 4 年的 2 月被算作了公元 3 年的 14 月(换句话说公元 3 年变成了闰年),而公式中计算闰日的部分没有考虑这点,依然将公元 3 年当成平年计算,因此少算了一天。因此,计算闰日的部分应当改进,公式如下:

D=(y-1)*365+[y/4]-[y/100]+[y/400]+(m-1)*28+[\frac{13(m+1)}{5}]-7+d,3\leq m\leq 14

 

计算出D的值后,对7取模则可得到星期数。

根据同余定理,D 除以 7 所得的余数等于 D 式的各项分别除以 7 所得余数之和(余数之和大于等于 7 时,再对 7 取余),因此可以消去 D 式中能被 7 整除的项,进行化简:

D=(y-1)+[y/4]-[y/100]+[y/400]+(m-1)*28+[\frac{13(m+1)}{5}]-7+d

对以上公式再次简练,对年份进行处理。通过上面公式计算出每个世纪第一年3月1日的星期数,得出下表

年份1,401,801,..2001101, 501, 901, … , 2101201, 601, 1001, … , 2201301, 701, 1101, … ,2301
星期4205

 

又发现,每隔四个世纪,星期就会重复。因为在数学上-2 和 5 除以 7 的余数相同,所以我们不妨把这个重复序列中的 5 改为 -2。这样,4、2、0、-2 恰好构成了一个等差数列。利用等差公式,我们可以得到计算每个世纪第一年的 3 月 1 日星期数的公式

W = 4 - 2 (c mod 4) // 其中c是世纪数 - 1

将此公式与上面公式联立,带入特定日期 3月1日 得出

((y-1)+[(y-1)/4]-[(y-1)/100]+[(y-1)/400]+11)mod7=4-2(c mod 4)

利用同余原理 ,变换。

(y-1)+[(y-1)/4]-[(y-1)/100]+[(y-1)/400]\equiv -2(c mod 4) (mod7)

故此,我们可以知道在每个世纪的第一年,(y−1)+[y−1 / 4]−[y−1 / 100]+[y−1 / 400]可以被 −2(cmod4) 同余替换。进而计算 D的公式得到如下形式:

 

D=-2(cmod4)+[13(m+1)/5]+d

 

 

注意!现在的计算公式只能适用于每个世纪的第一年。但是,有个这个公式,再加上计算一个世纪中闰日的部分,我们就可以很容易地得到计算这个世纪其他年份的日期的星期数的公式了。令 c 等于世纪数减一,y 等于世纪中的年份数(如 1994 年,则 c = 19,y = 94)。因为一个世纪中只有一百年,所以不用考虑“四百年又闰”的情况;因为每百年,即每个世纪最后一年的 y = 00,而 [y/4](y=0)=0,所以 [y4] 既可以计算四年一闰的情况,又满足百年不闰的要求 。综合这些情况,与得到公式(2)的过程类似,我们可以得到:

D=-2(cmod4)+(y-1)+[y/4]+[13(m+1)/5]+d

c是年份的前两位-1,y是年份的后两位

最后,我们来把公式中的取模运算改成四则运算。设商为 q,余数为 r,则:

4q + r = c

又∵ q = [c/4] , r = c mod 4

所以 c mod 4  = c - 4 * [c / 4]

 

带入公式, 最后得出 Zeller公式

D=[\frac{c}{4}]-2c+y-1+[\frac{y-1}{4}]+[\frac{13(m+1)}{5}]+d

 

3. 例子

下面以中华人民共和国成立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周年国庆)是星期五。
再比如计算2013年3月7日,过程如下:
w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
 =13+[13/4]+[20/4]-2*20+[26*(3+1)/10]+7-1
 =-3 (除以7余4,注意对负数的取模运算!)

 

文章引用: 

例子

推导

百度百科


http://chatgpt.dhexx.cn/article/t7Pew0Un.shtml

相关文章

C语言——蔡勒(Zeller)公式的使用

C语言——蔡勒公式的使用 蔡勒公式简介: 蔡勒(Zeller)公式,是一个计算星期的公式,随便给一个日期,就能用这个公式推算出是星期几。 计算公式: 核心公式: w(y[y/4][c/4]-2c[26(m1…

c++ operator百样操作符重载(详解)

目录 一、operator &#xff1a;等号判断重载 二、operator &#xff1a; 等号赋值重载 三、operator ! : 不等于重载 四、operator> &#xff1a; 大于号 或者 小于号 重载 五、operator << &#xff1a;输入重定向重载 六、operator &#xff1a;加号重载 …

操作符重载!看这篇就够了!

实现一个操作符重载的方式通常有两种情况&#xff1a; 将操作符重载实现为类的成员函数。操作符重载实现为非类的成员函数(即全局函数)。 将操作符重载实现为类的成员函数 在类体中声明(定义)需要重载的操作符&#xff0c;声明方式跟普通的成员函数一样&#xff0c;只不过操作符…

C++基本操作符重载

基本操作符重载 基本操作符重载reference 基本操作符重载 操作符重载指的是将 C 提供的操作符进行重新定义&#xff0c;使之满足我们所需要的一些功能。 在 C 中可以重载的操作符有&#xff1a; - * / % ^ & | ~ ! < > - * / % ^ & | <…

【Groovy】map 集合 ( map 集合操作符重载 | + 操作符重载 | 代码示例 )

文章目录 一、map 集合 " " 操作符重载二、代码示例 一、map 集合 " " 操作符重载 对 map 集合使用 " " 操作符 , 操作符两侧都是 map 集合 , 调用的是 map 集合的 plus 方法 , plus 函数有 2 2 2 个参数 : 第一个参数 , Map<K, V> l…

【Groovy】map 集合 ( map 集合操作符重载 | - 操作符重载 | 代码示例 )

文章目录 一、map 集合 " - " 操作符重载二、完整代码示例 一、map 集合 " - " 操作符重载 对 map 集合 使用 " - " 操作符 , 相当于调用了 map 集合的 minus 方法 , 该方法传入 2 2 2 个参数 : Map<K,V> self 参数 : 相当于 " - &…

【C++】操作符重载

注意&#xff1a;操作符重载可以当做“自定义类的运算” 1 为什么需要操作符重载&#xff1f; 对于基础的变量&#xff0c;int等&#xff0c;不需要重载就知道如何做&#xff0c;但是对于自定义类&#xff0c;就无法进行运算&#xff0c;比如复数类。 2 操作符重载总结 1&…

C++ 操作符重载

输出操作符"<<" 和输入操作运算符">>" 操作符重载&#xff0c;也叫运算符重载&#xff0c;是C的重要组成部分&#xff0c;它可以让程序更加的简单易懂&#xff0c;简单的运算符使用可以使复杂函数的理解更直观。 操作符重载可对 已有的运算…

C++-操作符重载

定义&#xff1a; Salesitem.h /* * This file contains code from "C Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * copyright and warranty notices given in that book: * * "Copyrig…

c++操作符重载

转自https://www.cnblogs.com/xudong-bupt/p/3557525.html 1.什么是操作符重载 可以使用分词将操作符重载理解为&#xff1a;操作符重载。 C中的操作符很多&#xff0c;如&#xff0c;-&#xff0c;*&#xff0c;\等等。 C中的重载也是C中面向对象多态的体现。 简单说操作符重…

操作符重载——C/C++学习笔记

此篇文章来自于网上&#xff0c;作为自己学习中的笔记&#xff0c;若有侵权行为&#xff0c;请告之&#xff0c;24小时之内必删除&#xff01;下面就转入正题吧&#xff01; 一、什么是操作符重载&#xff1f; 一看到重载&#xff0c;很容易就让人联想到成员函数重载&#xff0…

什么是操作符重载

一、什么是操作符重载 操作符重载可以分为两部分&#xff1a;“操作符”和“重载”。说到重载想必都不陌生了吧&#xff0c;这是一种编译时多态&#xff0c;重载实际上可以分为函数重载和操作符重载。运算符重载和函数重载的不同之处在于操作符重载重载的一定是操作符。我们不妨…

前端知识点总结 Vue JS CSS

前端知识点 MVVM和MVC的区别什么是Vue生命周期钩子函数触发顺序 VueVue优点父子通信&#xff0c;兄弟通信指令V-if和V-show区别 Vue-loaderVue-key的作用v-modalVue data必须是函数的问题Vue slot Vue-router多个router-viewroute与router的区别导航守卫懒加载 ES6JavaScript同…

JavaScript进阶之手写Promise

前言 Promise异步编程的一种解决方案&#xff0c;比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现&#xff0c;ES6 将其写进了语言标准&#xff0c;统一了用法&#xff0c;原生提供了Promise对象。这里手写一次&#xff0c;希望能和大家一起彻底…

【数据库】MongoDB数据库详解

目录 一&#xff0c;数据库管理系统 1&#xff0c; 什么是数据库 2&#xff0c;什么是数据库管理系统 二&#xff0c; NoSQL 是什么 1&#xff0c;NoSQL 简介 2&#xff0c;NoSQL数据库 3&#xff0c;NoSQL 与 RDBMS 对比 三&#xff0c;MongoDB简介 1&#xff0c; MongoDB 是什…

面试06,[长亮科技]()(offer)、[荔枝]()FM(在确定部门和薪资)、[涂鸦智能]()(第一轮电话面半小时,待后续)、华资软件(HR面)、[广州速游]()(已挂)。至于公司怎么样不加以言论。

作者&#xff1a;Carson-Zhao 链接&#xff1a;https://ac.nowcoder.com/discuss/522002?type2&order0&pos16&page1&ncTraceId&channel-1&source_iddiscuss_tag_nctrack 来源&#xff1a;牛客网 总结一下这几天的面试吧&#xff01;从19号到现在23号…

SQL Foundation(1--13)

1&#xff1a;关系数据库的由来&#xff1a; IBM的工程师Dr E F codd 的关系型数据库模型发表于1970 论文名称&#xff1a; A relational Model of data for Large Shared Data Bank (这个在wiki 和google上可以搜到) SQL: Structured query language&#xff1a; oracle官方…

一、快速入门 MongoDB 数据库

文章目录 一、NoSQL 是什么1.1 NoSQL 简史1.2 NoSQL 的种类及其特性1.3 NoSQL 特点1.4 NoSQL 的优缺点1.5 NoSQL 与 SQL 数据库的比较 二、MongoDB 基础知识2.1 MongoDB 是什么2.2 MongoDB 的体系结构2.3 MongoDB 的特点2.4 MongoDB 键特性2.5 MongoDB 的核心服务和工具2.6 Mon…

数据库总结(考研复试和期末复习皆可用)

数据库总结 点击下载该文档 密码&#xff1a;cqoq 本人自制了简答题的速记卡片 地址&#xff0c;大家可以参考使用。[下载Markji App 使用] 第一章 绪论 1.1 数据库系统概述 数据库管理系统(DBMS)的功能: 数据定义功能数据组织、存储和管理数据库操纵功能数据库的事务和运行…

一步步教你轻松学KNN模型算法

一步步教你轻松学KNN模型算法 ( 白宁超 2018年7月24日08:52:16 ) 导读&#xff1a;机器学习算法中KNN属于比较简单的典型算法&#xff0c;既可以做聚类又可以做分类使用。本文通过一个模拟的实际案例进行讲解。整个流程包括&#xff1a;采集数据、数据格式化处理、数据分析、数…