/** * 集中式的日期字符串工具集。 * * 应用内所有日期值统一使用 'YYYY-MM-DD' (本地墙钟时间) 格式。 * 本模块是格式化与日期运算的唯一来源, 替代此前散落在 organize.js、 * recur.js、useCalendar.js、ical-io.js 中的重复实现。 */ /** 将数字补零为 2 位字符串。 */ export function pad2(n) { return String(n).padStart(2, '0') } /** 将 JS Date 转换为 'YYYY-MM-DD' (本地墙钟时间)。 */ export function toISODate(date) { return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}` } /** 将 'YYYY-MM-DD' 解析为 { year, month, day } (month 为 1 基)。 */ export function parseISODate(s) { const [y, m, d] = s.split('-').map(Number) return { year: y, month: m, day: d } } /** 由 'YYYY-MM-DD' 构造本地午夜 JS Date。 */ export function toDate(isoDate) { return new Date(isoDate + 'T00:00:00') } /** 在 ISO 日期字符串上增加 `days` 天, 返回新的 ISO 字符串。 */ export function addDays(isoDate, days) { const d = toDate(isoDate) d.setDate(d.getDate() + days) return toISODate(d) } /** 两个 ISO 日期字符串之间的整天差 (b - a)。 */ export function daysBetween(isoA, isoB) { return Math.round((toDate(isoB) - toDate(isoA)) / (24 * 3600 * 1000)) } /** 今天的日期, 格式为 'YYYY-MM-DD' (本地)。 */ export function todayISO() { return toISODate(new Date()) } /** * 当前 UTC 时间戳, 紧凑 RFC 5545 格式 (如 '20260711T085008Z')。 * 用于新事件的 DTSTAMP。 */ export function nowCompactTimestamp() { return new Date().toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z' } /** * 返回 isoDate 所在周的周一 (ISO 周, 周一为首日)。 * @param {string} isoDate - 'YYYY-MM-DD' * @returns {string} 周一的 'YYYY-MM-DD' */ export function startOfWeek(isoDate) { const d = toDate(isoDate) const dow = d.getDay() // 0=周日 .. 6=周六 const diff = dow === 0 ? -6 : 1 - dow // 回退到周一 d.setDate(d.getDate() + diff) return toISODate(d) } /** * 返回 isoDate 所在月的第一天。 * @param {string} isoDate - 'YYYY-MM-DD' * @returns {string} 当月 1 号的 'YYYY-MM-DD' */ export function startOfMonth(isoDate) { const { year, month } = parseISODate(isoDate) return `${year}-${pad2(month)}-01` } /** * 将 'HH:MM' 转换为当天分钟数 (0~1439)。 * @param {string} hhmm - 'HH:MM' * @returns {number} */ export function hhmmToMinutes(hhmm) { const [h, m] = hhmm.split(':').map(Number) return h * 60 + m }