一个有趣的算法, 对于给定的日期,可以快速算出是周几.

这个算法并不是多么高明, 我看中的主要是改进的过程

因为格里历(公历)取代儒略历的原因,所以只适用于 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))
		}
	}
}