日期时间工具函数
2025/9/24大约 10 分钟
本文档整理了常用的日期时间处理工具函数。
日历函数
实现代码
/*
工具:生成指定月份的日历数据(含农历、是否今天、是否本月、星期)
依赖:chinese-lunar
导出:
- window.getMonthCalendar
- CommonJS: module.exports = { getMonthCalendar }
使用示例:
const grid = getMonthCalendar({ year: 2025, month: 9, weekStartsOn: 1 });
参数说明:
- year: 年份(默认当前年)
- month: 月份(1-12,默认当前月)
- weekStartsOn: 一周起始星期(0=周日,1=周一;默认1)
- fill6Weeks: 是否强制填充6行(默认true,以便UI固定高度)
返回:
{
year, month,
weeks: [
[
{ date: '2025-09-01', year: 2025, month: 9, day: 1, week: 1,
lunar: { year, month, day, leap, monthText, dayText },
isToday: false,
inCurrentMonth: true
},
],
]
}
*/
(function () {
function pad2(num) {
return num < 10 ? "0" + num : String(num);
}
function formatDateYmd(year, month, day) {
return year + "-" + pad2(month) + "-" + pad2(day);
}
function getLunarForDate(dateObj) {
try {
if (
typeof window !== "undefined" &&
window.chineseLunar &&
typeof window.chineseLunar.solarToLunar === "function"
) {
var lunar = window.chineseLunar.solarToLunar(dateObj);
return {
year: lunar.year,
month: lunar.month,
day: lunar.day,
leap: !!lunar.leap,
monthText: window.chineseLunar.monthName(
lunar.month,
false,
lunar.leap
),
dayText: window.chineseLunar.dayName(lunar.day),
};
}
} catch (e) {}
return null;
}
function buildMonthCalendar(options) {
options = options || {};
var today = new Date();
var year =
typeof options.year === "number" ? options.year : today.getFullYear();
var month =
typeof options.month === "number" ? options.month : today.getMonth() + 1;
var weekStartsOn = options.weekStartsOn === 0 ? 0 : 1; // 仅支持0或1,默认周一
var fill6Weeks = options.fill6Weeks !== false; // 默认true
// 当月第一天和最后一天
var firstDate = new Date(year, month - 1, 1);
var lastDate = new Date(year, month, 0); // 下月第0天即本月最后一天
var firstWeekday = firstDate.getDay(); // 0-6,周日为0
var lastDay = lastDate.getDate();
// 计算网格起始偏移
// 若周一起始:将周日(0)视为7,再减1得到偏移
var offset = 0;
if (weekStartsOn === 1) {
offset = (firstWeekday === 0 ? 7 : firstWeekday) - 1;
} else {
offset = firstWeekday; // 周日开头直接使用getDay
}
// 计算网格总单元数
var totalCells = offset + lastDay;
if (fill6Weeks) {
totalCells = Math.ceil(totalCells / 7) * 7;
if (totalCells < 42) totalCells = 42; // 固定6周
} else {
totalCells = Math.ceil(totalCells / 7) * 7; // 至少完整周
}
var days = [];
for (var i = 0; i < totalCells; i++) {
// 计算该单元对应的公历日期
var dayNumber = i - offset + 1; // 可能为0或负数,表示上月;超出lastDay表示下月
var cellDate;
var inCurrentMonth = true;
var cellYear = year;
var cellMonth = month;
var cellDay;
if (dayNumber < 1) {
// 上月
var prevDate = new Date(year, month - 1, 0); // 上月最后一天
cellDay = prevDate.getDate() + dayNumber; // 例如 dayNumber=-2 => 倒数第二天
cellMonth = prevDate.getMonth() + 1;
cellYear = prevDate.getFullYear();
inCurrentMonth = false;
cellDate = new Date(cellYear, cellMonth - 1, cellDay);
} else if (dayNumber > lastDay) {
// 下月
var nextDate = new Date(year, month, dayNumber - lastDay);
cellDay = nextDate.getDate();
cellMonth = nextDate.getMonth() + 1;
cellYear = nextDate.getFullYear();
inCurrentMonth = false;
cellDate = nextDate;
} else {
// 本月
cellDay = dayNumber;
cellDate = new Date(year, month - 1, cellDay);
}
var isToday =
cellYear === today.getFullYear() &&
cellMonth === today.getMonth() + 1 &&
cellDay === today.getDate();
var week = cellDate.getDay();
var lunar = getLunarForDate(cellDate);
days.push({
date: formatDateYmd(cellYear, cellMonth, cellDay),
year: cellYear,
month: cellMonth,
day: cellDay,
week: week, // 0-6,周日为0
lunar: lunar,
isToday: isToday,
inCurrentMonth: inCurrentMonth,
});
}
// 切成按周二维数组
var weeks = [];
for (var j = 0; j < days.length; j += 7) {
weeks.push(days.slice(j, j + 7));
}
return {
year: year,
month: month,
weekStartsOn: weekStartsOn,
weeks: weeks,
};
}
// 主函数:支持两种调用
// getMonthCalendar(9) => 当年9月
// getMonthCalendar({ year: 2025, month: 9, weekStartsOn: 1 })
function getMonthCalendar(arg) {
if (typeof arg === "number") {
return buildMonthCalendar({ month: arg });
}
return buildMonthCalendar(arg || {});
}
// 导出
if (typeof window !== "undefined") {
window.getMonthCalendar = getMonthCalendar;
}
if (typeof module !== "undefined" && module.exports) {
module.exports = { getMonthCalendar: getMonthCalendar };
}
})();
代码解析
该日历函数由多个辅助函数组成,实现了完整的日历数据生成逻辑:
辅助函数:
pad2(num)
:将数字格式化为两位数(小于 10 时前面补 0)formatDateYmd(year, month, day)
:格式化日期为 YYYY-MM-DD 格式getLunarForDate(dateObj)
:获取指定日期的农历信息(依赖外部 chinese-lunar 库)
核心逻辑:
buildMonthCalendar(options)
:构建日历数据的核心函数,处理日期计算和数据格式化getMonthCalendar(arg)
:主入口函数,支持两种调用方式(直接传入月份或传入配置对象)
关键算法:
- 计算当月第一天和最后一天
- 根据一周起始日计算日历网格的起始偏移
- 生成包含上月、本月和下月日期的完整日历网格
- 为每个日期添加公历、农历、是否当天等标记信息
- 将一维日期数组转换为按周分组的二维数组
使用方法
基本使用
// 获取当前月的日历数据
const currentMonthCalendar = getMonthCalendar();
// 获取指定月份的日历数据(当年9月)
const septemberCalendar = getMonthCalendar(9);
// 获取指定年份和月份的日历数据
const specificMonthCalendar = getMonthCalendar({
year: 2025,
month: 12,
});
高级配置
// 配置周日为一周的第一天
const calendarWithSundayStart = getMonthCalendar({
year: 2025,
month: 1,
weekStartsOn: 0, // 0表示周日,1表示周一
});
// 不强制填充6行(根据实际周数显示)
const calendarWithoutFixedRows = getMonthCalendar({
year: 2025,
month: 2,
fill6Weeks: false,
});
// 综合配置
const fullyConfiguredCalendar = getMonthCalendar({
year: 2025,
month: 3,
weekStartsOn: 0,
fill6Weeks: false,
});
使用示例
下面是一个使用该函数生成日历并渲染到页面的简单示例:
// 获取日历数据
const calendar = getMonthCalendar({ year: 2025, month: 9 });
// 创建日历表格
const calendarContainer = document.getElementById("calendar");
const table = document.createElement("table");
// 创建表头(星期)
const thead = document.createElement("thead");
const headerRow = document.createElement("tr");
const weekdays = ["日", "一", "二", "三", "四", "五", "六"];
if (calendar.weekStartsOn === 1) {
// 周一开头,调整顺序
weekdays.push(weekdays.shift());
}
weekdays.forEach((day) => {
const th = document.createElement("th");
th.textContent = day;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// 创建表体(日期)
const tbody = document.createElement("tbody");
calendar.weeks.forEach((week) => {
const row = document.createElement("tr");
week.forEach((day) => {
const td = document.createElement("td");
td.textContent = day.day;
// 添加样式类以便区分
if (!day.inCurrentMonth) {
td.classList.add("other-month");
}
if (day.isToday) {
td.classList.add("today");
}
// 如果有农历信息,添加农历日期
if (day.lunar) {
const lunarSpan = document.createElement("span");
lunarSpan.textContent = day.lunar.dayText;
lunarSpan.classList.add("lunar-date");
td.appendChild(lunarSpan);
}
row.appendChild(td);
});
tbody.appendChild(row);
});
table.appendChild(tbody);
calendarContainer.appendChild(table);
返回数据结构详解
函数返回一个包含以下属性的对象:
{
year: 2025, // 当前日历的年份
month: 9, // 当前日历的月份(1-12)
weekStartsOn: 1, // 一周的起始日(0=周日,1=周一)
weeks: [ // 按周分组的二维数组
[ // 第一周
{
date: '2025-08-31', // 日期字符串(YYYY-MM-DD格式)
year: 2025, // 年份
month: 8, // 月份(注意:非当前月的日期会显示实际月份)
day: 31, // 日
week: 0, // 星期几(0=周日)
lunar: {...}, // 农历信息(如果有)
isToday: false, // 是否今天
inCurrentMonth: false // 是否在当前显示的月份内
},
// 其他6天...
],
// 其他周...
]
}
注意事项
- 依赖说明:农历功能依赖外部的
chinese-lunar
库,使用前请确保正确引入 - 月份参数:函数接受的月份参数范围是 1-12,而不是 JavaScript Date 对象的 0-11
日期格式化函数
实现代码
/**
* 格式化日期为指定格式的字符串
* @param {Date} date - 日期对象
* @param {string} format - 格式化模板,支持的占位符:yyyy, MM, dd, HH, mm, ss
* @returns {string} 格式化后的日期字符串
*/
function formatDate(date, format = "yyyy-MM-dd") {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
return format
.replace("yyyy", year)
.replace("MM", pad2(month))
.replace("dd", pad2(day))
.replace("HH", pad2(hours))
.replace("mm", pad2(minutes))
.replace("ss", pad2(seconds));
function pad2(num) {
return num < 10 ? "0" + num : String(num);
}
}
/**
* 获取相对时间描述(如:刚刚、5分钟前、3小时前、昨天、3天前、2024-09-01)
* @param {Date|string} date - 日期对象或日期字符串
* @returns {string} 相对时间描述
*/
function formatRelativeTime(date) {
const now = new Date();
const target = typeof date === "string" ? new Date(date) : date;
const diff = now - target;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (seconds < 60) {
return "刚刚";
} else if (minutes < 60) {
return `${minutes}分钟前`;
} else if (hours < 24) {
return `${hours}小时前`;
} else if (days === 1) {
return "昨天";
} else if (days < 7) {
return `${days}天前`;
} else {
// 超过一周显示具体日期
return formatDate(target);
}
}
代码解析
formatDate函数:
- 接受两个参数:日期对象和格式化模板字符串(默认为"yyyy-MM-dd")
- 首先从日期对象中获取年、月、日、时、分、秒等各个部分的值
- 对于月、日、时、分、秒这些需要保证两位数的部分,使用内部的pad2函数进行格式化
- 通过字符串的replace方法,将格式化模板中的占位符(如yyyy、MM等)替换为实际的日期值
- 返回最终格式化后的字符串
formatRelativeTime函数:
- 接受一个参数:日期对象或日期字符串
- 首先获取当前时间和目标时间的差值(以毫秒为单位)
- 将毫秒差值转换为秒、分钟、小时、天等不同时间单位
- 根据时间差值的大小,返回不同的相对时间描述
- 少于1分钟:"刚刚"
- 少于1小时:"X分钟前"
- 少于1天:"X小时前"
- 等于1天:"昨天"
- 少于7天:"X天前"
- 超过7天:使用formatDate函数返回具体日期
使用方法
// 格式化日期
const now = new Date();
console.log(formatDate(now)); // 输出: 2024-09-11
console.log(formatDate(now, "yyyy年MM月dd日")); // 输出: 2024年09月11日
console.log(formatDate(now, "yyyy/MM/dd HH:mm:ss")); // 输出: 2024/09/11 15:30:45
// 格式化相对时间
const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);
console.log(formatRelativeTime(fiveMinutesAgo)); // 输出: 5分钟前
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
console.log(formatRelativeTime(yesterday)); // 输出: 昨天
const lastWeek = new Date(now.getTime() - 8 * 24 * 60 * 60 * 1000);
console.log(formatRelativeTime(lastWeek)); // 输出: 2024-09-03 (具体日期)
日期计算函数
实现代码
/**
* 计算两个日期之间的天数差
* @param {Date} startDate - 开始日期
* @param {Date} endDate - 结束日期
* @returns {number} 天数差
*/
function getDaysBetween(startDate, endDate) {
const start = new Date(startDate);
const end = new Date(endDate);
// 将时间设为同一天的开始,避免时间部分影响计算
start.setHours(0, 0, 0, 0);
end.setHours(0, 0, 0, 0);
const diffTime = Math.abs(end - start);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return diffDays;
}
/**
* 获取指定日期所在月份的第一天
* @param {Date} date - 日期对象
* @returns {Date} 月份第一天的日期对象
*/
function getFirstDayOfMonth(date) {
return new Date(date.getFullYear(), date.getMonth(), 1);
}
/**
* 获取指定日期所在月份的最后一天
* @param {Date} date - 日期对象
* @returns {Date} 月份最后一天的日期对象
*/
function getLastDayOfMonth(date) {
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
}
/**
* 判断是否为闰年
* @param {number} year - 年份
* @returns {boolean} 是否为闰年
*/
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
/**
* 添加指定天数到日期
* @param {Date} date - 基础日期
* @param {number} days - 要添加的天数
* @returns {Date} 计算后的日期
*/
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
/**
* 添加指定月份到日期
* @param {Date} date - 基础日期
* @param {number} months - 要添加的月份
* @returns {Date} 计算后的日期
*/
function addMonths(date, months) {
const result = new Date(date);
result.setMonth(result.getMonth() + months);
return result;
}
代码解析
getDaysBetween函数:
- 计算两个日期之间的天数差
- 首先创建两个日期对象的副本,并将它们的时间部分设置为当天的开始(00:00:00)
- 计算两个日期之间的毫秒差值,并取绝对值
- 将毫秒差值转换为天数,使用Math.ceil确保向上取整
getFirstDayOfMonth函数:
- 获取指定日期所在月份的第一天
- 使用Date构造函数,传入年份、月份和1(表示第一天)
- 注意JavaScript的月份是从0开始的,但这里我们直接使用date.getMonth()的结果
getLastDayOfMonth函数:
- 获取指定日期所在月份的最后一天
- 巧妙地使用了Date构造函数的特性:传入0作为日期值时,会返回上个月的最后一天
- 因此,我们传入当前月份+1和0,即可得到当前月份的最后一天
isLeapYear函数:
- 判断给定年份是否为闰年
- 使用闰年的判断规则:能被4整除但不能被100整除,或者能被400整除
addDays函数:
- 向指定日期添加指定天数
- 为了不修改原始日期对象,首先创建一个新的日期对象副本
- 使用setDate和getDate方法进行日期的加减操作
addMonths函数:
- 向指定日期添加指定月份
- 同样创建一个新的日期对象副本以避免修改原始对象
- 使用setMonth和getMonth方法进行月份的加减操作
使用方法
// 计算日期差
const start = new Date("2024-09-01");
const end = new Date("2024-09-11");
console.log(getDaysBetween(start, end)); // 输出: 10
// 获取月份的第一天和最后一天
const date = new Date("2024-09-11");
console.log(formatDate(getFirstDayOfMonth(date))); // 输出: 2024-09-01
console.log(formatDate(getLastDayOfMonth(date))); // 输出: 2024-09-30
// 判断闰年
console.log(isLeapYear(2024)); // 输出: true
console.log(isLeapYear(2023)); // 输出: false
// 日期加减
console.log(formatDate(addDays(date, 7))); // 输出: 2024-09-18
console.log(formatDate(addMonths(date, 3))); // 输出: 2024-12-11
更新日志
2025/9/28 09:03
查看所有更新日志
38e56
-于