Created
June 15, 2019 02:56
-
-
Save john-yuan/fa6ff4aa6c4d1c266a672ded279b813b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 获取指定月份日历视图中的 42 个日期对象。 | |
* | |
* @param {number} year 完整的年份数字,如 `2019` 即代表 2019 年。 | |
* @param {number} month 月份数字,如 `11` 即代表 11 月。 | |
* @param {number} [startWeekDay=0] 可选的参数,默认为 `0`(星期日),表示日历中的第一列为星期几(一般为星期日或者星期一)。其中 `0` 表 | |
* 示星期日,`1` 表示星期一,后面以此类推。 | |
* @returns {Date[]} 返回一个数组,包含 42 个 `Date` 对象。 | |
*/ | |
function getCalendarDateList(year, month, startWeekDay) { | |
// 一个日历视图中有 42 个日期,每一行有 7 个日期,一共有 6 行,一共有 42 个格子。 | |
// 为了找到这 42 个日期,我们只需找到日历中第一个日期即可,后面的日期均可由第一个日期推导出来。 | |
// 为了方便描述,我们把日历中不属于本月的日期(如上个月的 31 号和下个月的 1 号等)的格子称为灰色格子。 | |
// 为了找到本月日历中的第一个日期对象,我们需要先求得本月日历前面有多少个灰色格子,现在假设前面的灰色格子数量为 x 个。 | |
// 根据已知条件 year 和 month,我们可以求得本月 1 号为星期几,并将结果保存到变量 firstDateWeekDay 中。 | |
// 如果 firstDateWeekDay 大于等于 startWeekDay,那么 x = firstDateWeekDay - startWeekDay。 | |
// 如果 firstDateWeekDay 小于 startWeekDay,那么 x = firstDateWeekDay + 7 - startWeekDay。 | |
// 以上便可以求出 x 的值,用本月 1 号的日期对象来减去 x 天即可得到日历中的第一个格子的日期。 | |
var x; // 日历中前面的灰色格子数量 | |
var firstDate; // 本月 1 号日期 | |
var firstDateWeekDay; // 本月 1 号为星期几,范围是 { 0, 1, 2, 3, 4, 5, 6 } | |
var calendarDate; // 日历中第一个格子对应的日期对象 | |
var calendarDateList = []; // 日历日期列表 | |
// 处理默认参数 startWeekDay | |
if (!startWeekDay) { | |
startWeekDay = 0; | |
} else if (typeof startWeekDay !== 'number') { | |
startWeekDay = parseInt(startWeekDay, 10); | |
} | |
if (isNaN(startWeekDay)) { | |
startWeekDay = 0; | |
} | |
// 确保 startWeekDay 大于等于 0 | |
while (startWeekDay < 0) { | |
startWeekDay += 7; | |
} | |
// 确保 startWeekDay 小于等于 6 | |
while (startWeekDay > 6) { | |
startWeekDay -= 7; | |
} | |
firstDate = new Date(year, month - 1, 1, 8); // 本月 1 号早上 8 点 | |
firstDateWeekDay = firstDate.getDay(); | |
if (firstDateWeekDay >= startWeekDay) { | |
x = firstDateWeekDay - startWeekDay; | |
} else { | |
x = firstDateWeekDay + 7 - startWeekDay; | |
} | |
calendarDate = new Date(firstDate.getTime() - x * 86400000); // firstDate 减去 x 天 | |
while (calendarDateList.length < 42) { | |
calendarDateList.push(calendarDate); | |
calendarDate = new Date(calendarDate.getTime() + 86400000); // 计算下一天 | |
} | |
return calendarDateList; | |
} | |
/** | |
* | |
* @param {number} year 年份数字 | |
* @param {number} month 月份数字 | |
* @param {number} startWeekDay 日历中第一列为星期几,`0` 代表星期天,`1` 代表星期一,以此类推 | |
* @param {(date: Date, type: number, index: number) => void} onEachDate 处理每个日期的回调函数,`type` 为 `-1` 代表上个月的日 | |
* 期,`type` 为 `0` 代表本月日期,`type` 为 `1` 表示下月日期 | |
*/ | |
function handleEachDateOfTheMonth(year, month, startWeekDay, onEachDate) { | |
var dateList = getCalendarDateList(year, month, startWeekDay); | |
var i; | |
var date; | |
var prevMonth; | |
var nextMonth; | |
var type; | |
month = month - 1; | |
prevMonth = (month - 1) < 1 ? 12 : (month - 1); | |
nextMonth = (month + 1) > 12 ? 1 : (month + 1); | |
for (i = 0; i < 42; i += 1) { | |
date = dateList[i]; | |
month = date.getMonth(); | |
if (month === prevMonth) { | |
type = -1; | |
} else if (month === nextMonth) { | |
type = 1; | |
} else { | |
type = 0; | |
} | |
onEachDate(date, type, i); | |
} | |
return dateList; | |
} | |
function buildCalendarView(year, month, startWeekDay) { | |
var rootNode = document.createElement('div'); | |
var rowNode; | |
var dateList = handleEachDateOfTheMonth(year, month, startWeekDay, function (date, type, index) { | |
var dateNode = document.createElement('div'); | |
var className = 'date-item'; | |
var dateNum = date.getDate(); | |
var textNode = document.createTextNode(dateNum + ''); | |
if (index % 7 === 0) { | |
rowNode = document.createElement('div'); | |
rowNode.className = 'date-row date-row-' + (index / 7); | |
rootNode.appendChild(rowNode); | |
} | |
if (type === -1) { | |
className += ' date-item-previous'; | |
} else if (type === 1) { | |
className += ' date-item-next'; | |
} else { | |
className += ' date-item-current'; | |
} | |
dateNode.setAttribute('data-date-index', index); | |
dateNode.className = className; | |
dateNode.appendChild(textNode); | |
rowNode.appendChild(dateNode); | |
}); | |
return { | |
rootNode: rootNode, | |
dateList: dateList | |
}; | |
} | |
/**********************************************************************************************************************/ | |
// 以下为测试: | |
function testGetCalendarList(year, month, startWeekDay, expected) { | |
var firstCalendarDate = getCalendarDateList(year, month, startWeekDay)[0]; | |
var year = firstCalendarDate.getFullYear(); | |
var month = firstCalendarDate.getMonth() + 1; | |
var date = firstCalendarDate.getDate(); | |
var actual = year + '/' + month + '/' + date; | |
var passed = actual === expected; | |
var throwErrorAsync = function (err) { | |
setTimeout(function () { | |
throw err; | |
}); | |
}; | |
console.log('----------- T E S T -----------'); | |
console.log('year: ' + year); | |
console.log('month: ' + month); | |
console.log('startWeekDay: ' + startWeekDay); | |
console.log('expected: ' + expected); | |
console.log('actual: ' + actual); | |
if (passed) { | |
console.log('test passed: ' + passed); | |
} else { | |
console.error('test passed: ' + passed); | |
throwErrorAsync(new Error('testGetCalendarList(' + | |
year + ', ' + | |
month + ', ' + | |
startWeekDay + ', "' + | |
expected + '"): ' + | |
actual + ' (actual) is not equal to ' + | |
expected + ' (expected)')); | |
} | |
console.log(''); | |
} | |
// 以星期天开头的日历测试 | |
testGetCalendarList(2019, 6, 0, '2019/5/26'); | |
testGetCalendarList(2019, 1, 0, '2018/12/30'); | |
testGetCalendarList(2018, 7, 0, '2018/7/1'); // 特殊日期,第一个格子为本月 1 号 | |
// 以星期一开头的日历测试 | |
testGetCalendarList(2019, 6, 1, '2019/5/27'); | |
testGetCalendarList(2019, 1, 1, '2018/12/31'); | |
testGetCalendarList(2019, 4, 1, '2019/4/1'); // 特殊日期,第一个格子为本月 1 号 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment