/** * 日期工具函数 */ const lunar = require('./lunar') /** * 格式化日期 * @param {Date} date - 日期对象 * @param {String} format - 格式字符串,默认 'YYYY-MM-DD' * @returns {String} 格式化后的日期字符串 */ function formatDate(date, format = 'YYYY-MM-DD') { if (!date) return '' const d = new Date(date) const year = d.getFullYear() const month = String(d.getMonth() + 1).padStart(2, '0') const day = String(d.getDate()).padStart(2, '0') const hour = String(d.getHours()).padStart(2, '0') const minute = String(d.getMinutes()).padStart(2, '0') const second = String(d.getSeconds()).padStart(2, '0') return format .replace('YYYY', year) .replace('MM', month) .replace('DD', day) .replace('HH', hour) .replace('mm', minute) .replace('ss', second) } /** * 计算距离今天还有多少天 * @param {Date} targetDate - 目标日期 * @returns {Number} 天数差 */ function getDaysUntil(targetDate) { const today = new Date() today.setHours(0, 0, 0, 0) const target = new Date(targetDate) target.setHours(0, 0, 0, 0) const diff = target.getTime() - today.getTime() return Math.ceil(diff / (1000 * 60 * 60 * 24)) } /** * 判断是否是今天 */ function isToday(date) { return getDaysUntil(date) === 0 } /** * 判断是否已过期 */ function isPast(date) { return getDaysUntil(date) < 0 } /** * 判断是否即将到来 */ function isUpcoming(date) { const days = getDaysUntil(date) return days > 0 && days <= 7 } /** * 获取星期几 */ function getWeekDay(date) { const weekdays = ['日', '一', '二', '三', '四', '五', '六'] const d = new Date(date) return weekdays[d.getDay()] } /** * 判断是否在本月 */ function isInThisMonth(date) { const today = new Date() const target = new Date(date) return today.getFullYear() === target.getFullYear() && today.getMonth() === target.getMonth() } /** * 获取本月的开始日期和结束日期 */ function getMonthRange(year, month) { const start = new Date(year, month - 1, 1) const end = new Date(year, month, 0) return { start, end } } /** * 获取日期对象(从YYYY-MM-DD字符串) */ function parseDate(dateString) { if (!dateString) return null const parts = dateString.split('-') return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2])) } /** * 算出某条纪念日在指定年份的公历日期 * 公历纪念日:每年同月同日,直接用录入时的 solarMonth/solarDay * 农历纪念日:把农历月日转回当年公历,因为农历对应的公历日期每年都不同 * @param {Object} anniv - 纪念日记录 * @param {Number} year - 目标公历年份 * @returns {Date} */ function getOccurrenceInYear(anniv, year) { if (anniv.isLunar && anniv.lunarMonth && anniv.lunarDay) { const isLeap = !!anniv.isLeapMonth // 农历年和公历年不对齐:农历腊月通常落到下一个公历年(如农历2026年腊月十五 = 公历2027年1月)。 // 所以要同时试 year-1 / year 两个农历年,取真正落在公历 year 年内的那个; // 先试 year-1 保证返回的是该公历年内最早的一次。 for (const lunarYear of [year - 1, year]) { const d = lunar.lunarToSolar(lunarYear, anniv.lunarMonth, anniv.lunarDay, isLeap) if (d.getFullYear() === year) return d } // 兜底(理论上不会走到,农历年跨度必然覆盖整个公历年) return lunar.lunarToSolar(year, anniv.lunarMonth, anniv.lunarDay, isLeap) } return new Date(year, anniv.solarMonth - 1, anniv.solarDay) } /** * 算出某条纪念日的下一次发生(今年或明年),农历/公历都适用 * Why:首页、详情页、日历页原先各算一套,农历纪念日只有日历页算对了。 * 统一走这里,避免三处口径不一致。 * @param {Object} anniv - 纪念日记录 * @returns {{ date: Date, daysUntil: Number }} */ function getNextOccurrenceOf(anniv) { const currentYear = new Date().getFullYear() let date = getOccurrenceInYear(anniv, currentYear) let daysUntil = getDaysUntil(date) if (daysUntil < 0) { date = getOccurrenceInYear(anniv, currentYear + 1) daysUntil = getDaysUntil(date) } return { date, daysUntil } } module.exports = { formatDate, getDaysUntil, getOccurrenceInYear, getNextOccurrenceOf, isToday, isPast, isUpcoming, getWeekDay, isInThisMonth, getMonthRange, parseDate }