一个有趣的算法, 对于给定的日期,可以快速算出是周几.
这个算法并不是多么高明, 我看中的主要是改进的过程
因为格里历(公历)取代儒略历的原因,所以只适用于 1582/10/15 以后
这里假设第一年一月一日是周一, (巧了, 正好可以完美契合, 不用再外加 offset 修正了)
package algorithm
/*
计算年份会产生的 offset
主要是二月, 如果:
1. 一月二月, 其实只计算这一年之前的 offset 就可以了
在调用的时候需要 将year-1传入
2. 二月以后, 需要计算因为二月产生的偏差
在调用的时候需要 将真实的year传入
但是这里还会产生一个问题: 年份的offset多了1
这就需要在month_util()中解决了: 二月以后(不含二月)以后的 offset-1
*/
func year_util(y int) int {
/*
1. y 是计算年份产生的 offset
365 = 7 * 52 + 1
所以过了y年, 就会造成 offset = y
2. y/4 - y/100 + y/400 是计算二月产生的 offset
四年一闰 百年不闰 四百年再一闰
*/
return y + y/4 - y/100 + y/400
}
/*
计算月份会产生的 offset
*/
func month_util() []int {
days_in_month := []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
sum, offset := 0, make([]int, len(days_in_month))
for idx, days := range days_in_month {
/*
这里需要响应year_util()中提到的问题
*/
temp := sum % 7
// 这里 idx > 1 其实是月份大于二月份
if idx > 1 {
/*
这里offset 可能是 0
(其实可以枚举出来, 当九月的时候就会导致是0)
此时 offset = -1
所以 令 offset = 6
类似补码的思想, 这里只是为了都是正整数操作
*/
temp--
if temp < 0 {
temp = 6
}
}
offset[idx] = temp
sum += days
}
return offset
}
func tomohiko_sakamoto(y, m, d int) int {
// 当小于二月份的时候, 既没有当年 年份产生的offset, 也没有 二月产生的 offset
// 故而 y--
if m <= 2 {
y--
}
return (year_util(y) + month_util()[m-1] + d) % 7
}
Test
package algorithm
import (
"testing"
)
type date struct {
y, m, d, w int
}
func TestTomohikoSakamoto(t *testing.T) {
check := []date{
/* 年,月,日,星期 */
{0000, 3, 1, 3},
{1600, 3, 1, 3},
{2000, 3, 1, 3},
{2001, 3, 1, 4},
{2004, 3, 1, 1},
{2008, 3, 1, 6},
{2042, 3, 1, 6},
/* 平年3月1日 */
{1700, 3, 1, 1}, /* 注意:1700年是平年 */
{1800, 3, 1, 6}, /* 注意:1800年是平年 */
{1900, 3, 1, 4}, /* 注意:1900年是平年 */
{1901, 3, 1, 5},
{1918, 3, 1, 5},
{1958, 3, 1, 6},
{1988, 3, 1, 2},
{1999, 3, 1, 1},
{2100, 3, 1, 1}, /* 注意:2100年是平年 */
{2101, 3, 1, 2},
{2102, 3, 1, 3},
{2103, 3, 1, 4},
{2104, 3, 1, 6}, /* 注意:2104年是闰年 */ /* 不是周2 */
{9999, 3, 1, 1},
/* 2004年的重大节日 */
{2004, 1, 1, 4}, /* 元旦 */
{2004, 1, 22, 4}, /*春节*/
{2004, 2, 5, 4}, /*宵节*/
{2004, 3, 8, 1}, /*妇女节*/
{2004, 3, 12, 5}, /*植树节*/
{2004, 3, 15, 1}, /*投诉节*/
{2004, 4, 4, 0}, /*清明节*/
{2004, 5, 1, 6}, /*五一节*/
{2004, 5, 4, 2}, /*青年节*/
{2004, 5, 9, 0}, /*母亲节*/
{2004, 6, 1, 2}, /*儿童节*/
{2004, 6, 22, 2}, /*端午节*/
{2004, 7, 1, 4}, /*建党节*/
{2004, 8, 1, 0}, /*建军节*/
{2004, 8, 8, 0}, /*父亲节*/
{2004, 9, 10, 5}, /*教师节*/
{2004, 9, 28, 2}, /*中秋节*/
{2004, 10, 1, 5}, /*国庆节*/
{2004, 10, 6, 3}, /*老人节*/
{2004, 10, 22, 5}, /*重阳节*/
{2004, 11, 7, 0}, /*立冬节*/
{2004, 12, 21, 2}, /*冬至节*/
/* 二十世纪 */
{1900, 1, 1, 1}, /*元旦*/
{1900, 3, 1, 4}, /* 公式测试关键点 */
{1918, 1, 21, 1}, /*大寒*/
{1918, 2, 11, 1}, /*春节*/
{1927, 1, 1, 6}, /*元旦 */
{1948, 1, 18, 0}, /*腊八节*/
{1949, 10, 1, 6}, /*国庆*/
{1962, 5, 1, 2}, /*劳动节*/
{1999, 12, 31, 5},
/* 二十一世纪 */
{2000, 1, 1, 6}, /*元旦*/
{2018, 1, 20, 6}, /*大寒*/
{2018, 2, 16, 5}, /*春节*/
{2027, 1, 1, 5}, /*元旦*/
{2048, 1, 22, 3}, /*腊八节*/
{2099, 12, 31, 4},
/* 二十二世纪 */
{2100, 1, 1, 5},
{2100, 1, 31, 0},
{2100, 2, 1, 1},
{2100, 2, 28, 0},
{2100, 3, 1, 1},
{2100, 3, 31, 3},
{2100, 12, 31, 5},
{2101, 1, 1, 6},
{2101, 1, 31, 1},
{2101, 2, 1, 2},
{2101, 2, 28, 1},
{2101, 3, 1, 2},
{2101, 3, 31, 4},
{2101, 12, 31, 6},
{2104, 2, 29, 5}, /* 不是周1 */
{2104, 3, 1, 6}, /* 不是周2 */
{2199, 12, 31, 2},
}
for _, val := range check {
if tomohiko_sakamoto(val.y, val.m, val.d) != val.w {
t.Errorf("%d %d %d Expected %d but got %d", val.y, val.m, val.d, val.w, tomohiko_sakamoto(val.y, val.m, val.d))
}
}
}