/** * 计算两个日期之间相差的天数 * @param date1 开始日期 * @param date2 结束日期 * @returns 相差天数(绝对值) */ export function diffDays(date1: string | Date = new Date(), date2: string | Date = new Date()): number { const d1 = new Date(date1); const d2 = new Date(date2); // 转为时间戳并取绝对值 const diff = Math.abs(d1.getTime() - d2.getTime()); // 一天的毫秒数 return Math.floor(diff / (1000 * 60 * 60 * 24)); } /** * 计算两个日期之间的自然日相差天数 * 规则:同一天 = 0,昨天 = 1,前天 = 2,以此类推 * 忽略时分秒,纯日期对比 * @param date1 日期1,默认为当前日期 * @param date2 日期2,默认为当前日期 * @returns 自然日相差天数(非负整数) * @example getNaturalDayDiff(今日, 今日) => 0 * @example getNaturalDayDiff(今日, 昨日) => 1 */ export function getNaturalDayDiff(date1: string | Date = new Date(), date2: string | Date = new Date()): number { const getDateTimestamp = (date: string | Date) => { const d = new Date(date); d.setHours(0, 0, 0, 0); return d.getTime(); }; const time1 = getDateTimestamp(date1); const time2 = getDateTimestamp(date2); return Math.floor(Math.abs(time1 - time2) / (1000 * 60 * 60 * 24)); } /** * 格式化两个时间的差值,自动显示 年/月/天/时/分/秒 * @param startTime 开始时间(Date / 时间戳 / 字符串) * @param endTime 结束时间,默认当前时间 * @returns 如:2年3个月、15天、2小时30分、50秒 */ export function formatTimeDiff(startTime: string | number | Date, endTime: string | number | Date = new Date()): string { const start = new Date(startTime).getTime(); const end = new Date(endTime).getTime(); // 毫秒差值(绝对值) let diff = Math.abs(end - start); if (diff < 1000) return '0秒'; // 单位定义 const second = 1000; const minute = 60 * second; const hour = 60 * minute; const day = 24 * hour; const month = 30 * day; // 粗略按月计算 const year = 365 * day; // 计算 const years = Math.floor(diff / year); diff -= years * year; const months = Math.floor(diff / month); diff -= months * month; const days = Math.floor(diff / day); diff -= days * day; const hours = Math.floor(diff / hour); diff -= hours * hour; const minutes = Math.floor(diff / minute); diff -= minutes * minute; const seconds = Math.floor(diff / second); // 拼接显示 const res: string[] = []; if (years > 0) res.push(years + '年'); if (months > 0) res.push(months + '个月'); if (days > 0) res.push(days + '天'); if (hours > 0) res.push(hours + '小时'); if (minutes > 0) res.push(minutes + '分钟'); if (seconds > 0 && res.length === 0) res.push(seconds + '秒'); return res.join(''); } /** * 生成指定日期所在周的日期数组(周一到周日) * @param date 基准日期,默认今天 * @param format 返回格式 * @returns 一周日期数组 */ export function generateWeekDays(date: Date | string | number = new Date(), format: 'name' | 'date' | 'full' = 'name'): string[] { const weekNames = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; const baseDate = new Date(date); // 计算本周一的日期(getDay() 0=周日,1=周一) const day = baseDate.getDay(); const monday = new Date(baseDate); monday.setDate(baseDate.getDate() - (day === 0 ? 6 : day - 1)); // 生成周一到周日7天 const week = []; for (let i = 0; i < 7; i++) { const current = new Date(monday); current.setDate(monday.getDate() + i); if (format === 'name') { week.push(weekNames[i]); } else if (format === 'date') { // YYYY-MM-DD 格式 const year = current.getFullYear(); const month = String(current.getMonth() + 1).padStart(2, '0'); const day = String(current.getDate()).padStart(2, '0'); week.push(`${year}-${month}-${day}`); } else { // 完整格式:周一 (2026-04-20) const year = current.getFullYear(); const month = String(current.getMonth() + 1).padStart(2, '0'); const day = String(current.getDate()).padStart(2, '0'); week.push(`${weekNames[i]} (${year}-${month}-${day})`); } } return week; } /** * 根据数字 1-7 生成对应星期 * @param num 1=周一,2=周二...7=周日 * @returns 周一 / 周二 ... / 周日 */ export function numToWeek(num: number): string { const weekMap = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; return weekMap[num - 1] || ''; } /** * 根据数字 1-7 生成对应星期 * @param num 1=周一,2=周二...7=周日 * @returns 周一 / 周二 ... / 周日 */ export function numToWeek2(num: number): string { const weekMap = ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期日']; return weekMap[num - 1] || ''; } // 生成过去7天的日期 export const getLast7Days = (days = 7) => { const dates = []; for (let i = days - 1; i >= 0; i--) { dates.push(new Date(new Date().getTime() - i * 24 * 60 * 60 * 1000).toLocaleDateString()); } return dates; }; /** * 高性能生成从指定结束日期向前追溯 N 天的连续日期数组 * 规则:0=今天,1=昨天,2=前天,以此类推 * 支持自定义日期格式(YYYY/MM/DD/HH/mm/ss)或传入格式化函数 * 优化:正则模板仅编译1次、时间戳计算、数组预分配,无性能损耗 * @category 日期工具 * @param days 向前追溯天数,0=今天,1=昨天,默认 30 天 * @param endDate 结束日期,支持 Date 对象 / 日期字符串,默认当前日期 * @param format 日期格式化规则,支持模板字符串或自定义格式化函数,默认 'YYYY-MM-DD' * 支持模板占位符:YYYY(年)、MM(月)、DD(日)、HH(时)、mm(分)、ss(秒) * @returns 格式化后的日期字符串数组(按时间正序排列) * @example * // 获取今天 * getLastNDays(0) * @example * // 获取昨天 * getLastNDays(1) * @example * // 最近7天(含今天) * getLastNDays(7) */ export function getLastNDays( days: number = 30, endDate: Date | string = new Date(), format: string | ((date: Date) => string) = 'YYYY-MM-DD' ): string[] { // 非法天数校验 if (!Number.isInteger(days) || days < 0) { console.warn('getLastNDays: days 必须是大于等于 0 的整数'); return []; } // 安全解析结束日期,并【重置时分秒为 00:00:00】— 关键修复 const end = new Date(endDate); if (isNaN(end.getTime())) { console.warn('getLastNDays: 无效日期,已自动使用当前时间'); end.setTime(Date.now()); } // ✅ 修复:统一把时间设为 00:00:00,避免跨天计算错误 end.setHours(0, 0, 0, 0); const endTime = end.getTime(); const oneDay = 86400000; // 提前编译格式化函数(核心性能优化:正则只编译一次) let formatter: (date: Date) => string; if (typeof format === 'function') { formatter = format; } else { formatter = (date: Date) => { const YYYY = date.getFullYear().toString(); const MM = String(date.getMonth() + 1).padStart(2, '0'); const DD = String(date.getDate()).padStart(2, '0'); const HH = String(date.getHours()).padStart(2, '0'); const mm = String(date.getMinutes()).padStart(2, '0'); const ss = String(date.getSeconds()).padStart(2, '0'); return format.replace(/YYYY/g, YYYY).replace(/MM/g, MM).replace(/DD/g, DD).replace(/HH/g, HH).replace(/mm/g, mm).replace(/ss/g, ss); }; } // 生成日期 const result: string[] = []; for (let i = 0; i <= days; i++) { const currentDate = new Date(endTime - i * oneDay); result.push(formatter(currentDate)); } // 正序返回 return result.reverse(); } /** * 格式化 Date 对象为指定格式的字符串 * 高性能正则解析,支持任意分隔符,仅编译一次模板 * @param date 要格式化的日期,默认:当前时间 new Date() * @param format 格式化模板,默认:YYYY-MM-DD * 支持占位符:YYYY(年)、MM(月)、DD(日)、HH(时)、mm(分)、ss(秒) * @returns 格式化后的日期字符串 * @category 日期工具 * @example * formatDate() // 今天 → 2025-12-20 * @example * formatDate(new Date(), 'YYYY年MM月DD日') * @example * formatDate(new Date(), 'YYYY-MM-DD HH:mm:ss') */ export function formatDate(date: Date | string = new Date(), format: string = 'YYYY-MM-DD'): string { // 解析并校验日期 const d = new Date(date); if (isNaN(d.getTime())) { console.warn('formatDate: 无效日期,返回空字符串'); return ''; } // 获取并补零 const YYYY = d.getFullYear().toString(); const MM = String(d.getMonth() + 1).padStart(2, '0'); const DD = String(d.getDate()).padStart(2, '0'); const HH = String(d.getHours()).padStart(2, '0'); const mm = String(d.getMinutes()).padStart(2, '0'); const ss = String(d.getSeconds()).padStart(2, '0'); // 正则替换(通用、无需穷举) return format.replace(/YYYY/g, YYYY).replace(/MM/g, MM).replace(/DD/g, DD).replace(/HH/g, HH).replace(/mm/g, mm).replace(/ss/g, ss); }