const formatTime = (seconds) => { const m = Math.floor(seconds / 60) const s = seconds % 60 return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}` } const formatDuration = (seconds) => { if (seconds < 60) return `${seconds}秒` const m = Math.floor(seconds / 60) const s = seconds % 60 return s > 0 ? `${m}分${s}秒` : `${m}分钟` } const getDaysInMonth = (year, month) => new Date(year, month, 0).getDate() const getMonthCalendar = (year, month) => { const firstDay = new Date(year, month - 1, 1) const daysInMonth = new Date(year, month, 0).getDate() const startDayOfWeek = firstDay.getDay() const weeks = [] let week = [null, null, null, null, null, null, null] for (let d = 1; d <= daysInMonth; d++) { const dow = (startDayOfWeek + d - 1) % 7 week[dow] = d if (dow === 6 || d === daysInMonth) { weeks.push(week.slice()) week = [null, null, null, null, null, null, null] } } return weeks } /** * Format a Date as `YYYY-MM-DD HH:MM:SS` in local time. * * Used as the canonical `record.date` string so each record has a * precise timestamp (e.g. "2026-06-11 14:30:45"). Use `_dateOnly()` * to strip back to YYYY-MM-DD for date-level comparisons (streak, * leaderboard day filter, etc.). */ const formatDate = (date) => { const y = date.getFullYear() const mo = String(date.getMonth() + 1).padStart(2, '0') const d = String(date.getDate()).padStart(2, '0') const h = String(date.getHours()).padStart(2, '0') const mi = String(date.getMinutes()).padStart(2, '0') const s = String(date.getSeconds()).padStart(2, '0') return `${y}-${mo}-${d} ${h}:${mi}:${s}` } /** Strip the time portion of a formatDate string → "YYYY-MM-DD". */ const _dateOnly = (s) => (typeof s === 'string' && s.length >= 10) ? s.substring(0, 10) : '' module.exports = { formatTime, formatDuration, getDaysInMonth, getMonthCalendar, formatDate, dateOnly: _dateOnly }