跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
|注册
博客列表

目录

  1. 项目背景
  2. 项目需求
  3. 功能性需求
  4. 非功能性需求
  5. 技术原理
  6. 什么是 Doomsday(末日)
  7. 年锚点(Century Anchor Day)
  8. 闰年规则说明
  9. 实现思路
  10. 算法总体流程
  11. 月份 Doomsday 对照表
  12. 代码实现
  13. 代码解读
  14. IsLeapYear
  15. GetCenturyAnchor
  16. GetYearDoomsday
  17. GetMonthDoomsday
  18. DayOfWeek
  19. 总结
  20. 常见问题
  21. 扩展方向
Go / Golang算法

Go 语言实现 Doomsday 末日算法

由 John Horton Conway 提出的 Doomsday 末日算法,用于快速计算任意日期是星期几。内容涵盖算法原理、世纪锚点、闰年规则及月份对照表,并提供完整的 Go 语言实现代码。代码不依赖标准库 time 包,包含闰年判断、年份锚点计算及星期推算逻辑,适合算法学习与面试准备。

板砖工程师发布于 2026/3/23更新于 2026/4/1629K 浏览

项目背景

在计算机科学与数学领域中,有一类非常有意思、也非常'优雅'的算法:心算友好型算法。它们不依赖复杂计算,却能通过规律和结构,快速得到结果。

Doomsday Algorithm(末日算法),正是其中最著名的代表之一。

该算法由传奇计算机科学家 John Horton Conway 提出,用于快速计算任意日期是星期几。末日算法的特点是规则清晰、可解释性强,既适合人脑心算,也非常适合程序实现,是算法思维、数学建模、时间系统理解的绝佳案例。

在工程实践中,虽然我们可以直接调用标准库,但自实现有助于理解历法、闰年、模运算,也是面试中经常考察的「日期算法原理」。

本文将使用 Go 语言,完整实现一套可教学、可运行、可验证的 Doomsday 末日算法。

项目需求

功能性需求

  1. 输入任意合法日期(年、月、日)
  2. 输出该日期对应的星期
  3. 支持公历(Gregorian Calendar)
  4. 正确处理闰年规则
  5. 提供完整示例可直接运行

非功能性需求

  1. 不依赖 time 包的 Weekday 计算
  2. 算法步骤清晰、易于讲解
  3. 适合博客、课堂、面试讲解
  4. 所有代码放在单一代码块中

技术原理

什么是 Doomsday(末日)

在 Doomsday 算法中,**Doomsday(末日)**指的是:某一年中,一组'固定日期'都落在同一个星期几。

例如(非闰年):

  • 4 月 4 日
  • 6 月 6 日
  • 8 月 8 日
  • 10 月 10 日
  • 12 月 12 日

它们在同一年中,星期几是完全一致的。

年锚点(Century Anchor Day)

每个世纪都有一个固定的'锚点星期',例如:

  • 1900–1999:星期三
  • 2000–2099:星期二

这是算法中非常关键的一步。

闰年规则说明

公历闰年规则:

  1. 能被 400 整除 → 闰年
  2. 能被 100 整除但不能被 400 整除 → 平年
  3. 能被 4 整除但不能被 100 整除 → 闰年

实现思路

算法总体流程

  1. 计算世纪锚点星期
  2. 计算当年 Doomsday 星期
  3. 找到该月的 Doomsday 日期
  4. 根据日期差值推算目标星期

月份 Doomsday 对照表

月份平年闰年
11 月 3 日1 月 4 日
22 月 28 日2 月 29 日
33 月 14 日3 月 14 日
44 月 4 日4 月 4 日
55 月 9 日
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog

更多推荐文章

查看全部
  • 基于 AI 的 B 站充电视频页面结构解析方案
  • jsPDF 中文显示解决方案:字体引入与配置指南
  • 前端微前端:大型应用的模块化解决方案
  • 前端微前端:大型应用的模块化解决方案
  • 前端微前端:大型应用的模块化解决方案
  • 双指针经典算法题解析
  • Git 多人协作开发流程与分支管理
  • Mac mini 部署 Clawdbot AI Agent 并接入 Claude Code

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

5 月 9 日
66 月 6 日6 月 6 日
77 月 11 日7 月 11 日
88 月 8 日8 月 8 日
99 月 5 日9 月 5 日
1010 月 10 日10 月 10 日
1111 月 7 日11 月 7 日
1212 月 12 日12 月 12 日

代码实现

// =========================================
// 文件:doomsday.go
// 描述:Go 语言实现 Doomsday 末日算法
// =========================================
package main

import "fmt"

var weekdays = []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}

// IsLeapYear 判断是否为闰年
func IsLeapYear(year int) bool {
	if year%400 == 0 {
		return true
	}
	if year%100 == 0 {
		return false
	}
	return year%4 == 0
}

// GetCenturyAnchor 获取世纪锚点(0=Sunday)
func GetCenturyAnchor(year int) int {
	century := year / 100
	// Conway 公式:(5 * (century % 4) + 2) % 7
	return (5*(century%4) + 2) % 7
}

// GetYearDoomsday 计算某一年的 Doomsday
func GetYearDoomsday(year int) int {
	anchor := GetCenturyAnchor(year)
	y := year % 100
	a := y / 12
	b := y % 12
	c := b / 4
	return (anchor + a + b + c) % 7
}

// GetMonthDoomsday 获取某月的 Doomsday 日期
func GetMonthDoomsday(month int, leap bool) int {
	doomsdays := [][]int{
		{3, 4},   // Jan
		{28, 29}, // Feb
		{14, 14}, // Mar
		{4, 4},   // Apr
		{9, 9},   // May
		{6, 6},   // Jun
		{11, 11}, // Jul
		{8, 8},   // Aug
		{5, 5},   // Sep
		{10, 10}, // Oct
		{7, 7},   // Nov
		{12, 12}, // Dec
	}
	if leap {
		return doomsdays[month-1][1]
	}
	return doomsdays[month-1][0]
}

// DayOfWeek 使用 Doomsday 算法计算星期
func DayOfWeek(year, month, day int) string {
	doomsday := GetYearDoomsday(year)
	leap := IsLeapYear(year)
	monthDoomsday := GetMonthDoomsday(month, leap)
	diff := day - monthDoomsday
	weekday := (doomsday + diff%7 + 7) % 7
	return weekdays[weekday]
}

func main() {
	year, month, day := 2024, 10, 1
	fmt.Printf("%d-%02d-%02d is %s\n", year, month, day, DayOfWeek(year, month, day))
}

代码解读

IsLeapYear

判断给定年份是否为公历闰年。

GetCenturyAnchor

计算世纪对应的锚点星期,是 Doomsday 算法的基础。

GetYearDoomsday

根据年份后两位,推算该年的 Doomsday 星期。

GetMonthDoomsday

返回指定月份对应的 Doomsday 日期。

DayOfWeek

综合 Doomsday 算法的所有步骤,计算目标日期的星期。

总结

Doomsday 末日算法是一种数学结构优美、规则高度总结的经典算法,非常适合教学与面试讲解。

通过 Go 语言实现后,可以清晰看到时间系统的规律、模运算在工程中的应用以及算法思想如何落地为代码。

常见问题

Q1:为什么不直接用 time.Weekday? A:为了理解算法原理,而不是依赖库。

Q2:算法支持 1582 年之前吗? A:本文实现基于公历,适用于现代日期。

Q3:结果如何验证? A:可与标准库 time 包对照测试。

扩展方向

  1. 支持历史历法(儒略历)
  2. 命令行日期查询工具
  3. 与 time 包结果对照测试
  4. 心算口诀与代码映射总结
  5. 封装为日期算法工具库
SpringBoot 结合 PostGIS 实现省级旅游口号管理实践
  • FPGA 实现 SATA 硬盘读写协议详解
  • C++ 类和对象基础概念详解
  • 易语言子程序高级应用:递归、回调与参数设计实战
  • AI 大模型落地基础:Prompt、Agent 与工具调用详解
  • Ubuntu 22.04 Ollama 离线部署
  • py-xiaozhi Python 语音客户端本地部署与配置指南
  • C++ std::map 容器用法详解
  • SpringBoot 集成 LangChain4j 本地调用 Ollama
  • Spring Boot 微服务架构:独立匹配系统设计及后端对接
  • C++ 内存管理核心技巧与最佳实践
  • Effective Modern C++ 条款 35:基于任务与基于线程编程的对比与实践