2199年7月2日是星期几?根据日期计算当天是星期几是一个有趣的问题,通常的思路是:选择任意一天作为我们计算的基础,比如2018年8月5日星期日,然后计算出目标日期与基础日期相差多少天,再推算目标日期是星期几。在没有遇见末日算法之前,我大概不会深入思考这个问题,然后按照上述思路草草解决。但是末日算法给我们提供了一种新的思路或者说优化了前述的算法。
末日算法假设每一年2月的最后一天是末日,因此每一年的3月7日,4月4日,5月9日,6月6日,7月11日,8月8日,9月5日,10月10日,11月7日,12月12日的星期必然和末日的星期相同,因为这些日期和末日相差的天数正好是7的倍数。除此之外,因为平年一年365天,闰年一年366天,所以每过一个平年,末日的星期数加1,每过一个闰年,末日的星期数加2(365 % 7 = 1, 366 % 7 = 2)。这样一来,只要我们知道某一年的末日是星期几,就可以按照末日算法的思路快速地计算出目标日期是星期几。
虽然末日算法最后还是要计算时间差,但是它极大地节省了计算量,也许这点计算量在现今的计算环境下算不了什么,但它让我看到了有意思的想法,如果我没有遇见末日算法,我可能不会继续深入思考这个问题,编程的秘密不仅仅是机械地解决问题,更在于理解逻辑和生活的本质。
const weekList = [
'星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'
];
const doomBase = {
year : 2000,
week : 2
};
const searchMap = {
'3' : 7,
'4' : 4,
'5' : 9,
'6' : 6,
'7' : 11,
'8' : 8,
'9' : 5,
'10' : 10,
'11' : 7,
'12' : 12
};
function isLeapYear( year ) {
return ( year % 4 === 0 && year % 100 !== 0 ) || year % 400 === 0;
}
function positiveModulo( a, b ) {
let result;
result = a % b;
if ( result < 0 ) {
result += b;
}
return result;
}
function getDoomDay( year ) {
let i, result;
result = doomBase.week;
if ( year < doomBase.year ) {
for ( i = doomBase.year - 1; i >= year; i-- ) {
result--;
if ( isLeapYear( i + 1 ) ) {
result--;
}
}
} else {
for ( i = doomBase.year + 1; i <= year; i++ ) {
result++;
if ( isLeapYear( i ) ) {
result++;
}
}
}
result = positiveModulo( result, 7 );
return result;
}
function getWeek( year, month, day ) {
let offset, result;
const doomDay = getDoomDay( year );
if ( month < 3 ) {
if ( isLeapYear( year ) ) {
offset = day - 29 - ( 2 - month ) * 31;
} else {
offset = day - 28 - ( 2 - month ) * 31;
}
} else {
offset = day - searchMap[ month ];
}
result = doomDay + offset;
result = positiveModulo( result, 7 );
return result;
}
function isValidateDate( year, month, day ) {
let result;
result = true;
const dayCountMap = {
'1' : 31,
'3' : 31,
'4' : 30,
'5' : 31,
'6' : 30,
'7' : 31,
'8' : 31,
'9' : 30,
'10' : 31,
'11' : 30,
'12' : 31
};
dayCountMap[ '2' ] = isLeapYear( year ) ? 29 : 28;
if ( month < 1 || month > 12 ) {
result = false;
}
if ( day > dayCountMap[ month ] ) {
result = false;
}
return result;
}
function getDateString( year, month, day ) {
return year + '年' + month + '月' + day + '日';
}
const testData = [
[ 2199, 7, 2 ],
[ 1994, 7, 5 ],
[ 1994, 5, 27 ],
[ 1994, 2, 28 ],
[ 1994, 1, 1 ],
[ 1994, 1, 31 ],
[ 1994, 2, 1 ],
[ 1994, 0, 1 ],
[ 1994, 2, 29 ]
];
const testFunction = () => {
testData.map( ( item ) => {
if ( !isValidateDate( ...item ) ) {
console.log( getDateString( ...item ) + '不是合法的日期!' );
} else {
console.log(
getDateString( ...item ) + '是' + weekList[ getWeek( ...item ) ]
);
}
} );
};
testFunction();