Skip to content

Instantly share code, notes, and snippets.

@john-yuan
Created June 15, 2019 02:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save john-yuan/fa6ff4aa6c4d1c266a672ded279b813b to your computer and use it in GitHub Desktop.
Save john-yuan/fa6ff4aa6c4d1c266a672ded279b813b to your computer and use it in GitHub Desktop.
/**
* 获取指定月份日历视图中的 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