/** * Composable для форматирования дат * Автоматически использует таймзону сервера из serverSettings * * Использование: * import { useDateFormat } from '@/composables/useDateFormat' * const { formatShort, formatRelative, getDaysLeftText } = useDateFormat() */ import { serverSettings } from '../api' // Константы месяцев const MONTHS_SHORT = ['янв', 'фев', 'мар', 'апр', 'мая', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'] const MONTHS_FULL = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря'] // Для дат типа "1 май" (именительный падеж) const MONTHS_SHORT_NOM = ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'] export function useDateFormat() { /** * Парсинг даты с учётом таймзоны сервера */ const parseDate = (dateStr) => { if (!dateStr) return null return serverSettings.parseDate(dateStr) } /** * Короткий формат: "1 янв 2025" */ const formatShort = (dateStr) => { const date = parseDate(dateStr) if (!date) return '' const day = date.getDate() return `${day} ${MONTHS_SHORT_NOM[date.getMonth()]} ${date.getFullYear()}` } /** * Полный формат: "1 января 2025" */ const formatFull = (dateStr) => { const date = parseDate(dateStr) if (!date) return '' const day = date.getDate() return `${day} ${MONTHS_FULL[date.getMonth()]} ${date.getFullYear()}` } /** * С временем: "01 янв 2025, 14:30" */ const formatDateTime = (dateStr) => { const date = parseDate(dateStr) if (!date) return '—' const day = date.getDate().toString().padStart(2, '0') const year = date.getFullYear() const hours = date.getHours().toString().padStart(2, '0') const minutes = date.getMinutes().toString().padStart(2, '0') return `${day} ${MONTHS_SHORT[date.getMonth()]} ${year}, ${hours}:${minutes}` } /** * Расчёт дней между датами * Положительное число = дней осталось, отрицательное = просрочено */ const getDaysUntil = (dateStr) => { const target = parseDate(dateStr) if (!target) return null const today = new Date() today.setHours(0, 0, 0, 0) target.setHours(0, 0, 0, 0) return Math.round((target - today) / (1000 * 60 * 60 * 24)) } /** * Расчёт дней назад */ const getDaysAgo = (dateStr) => { const days = getDaysUntil(dateStr) return days !== null ? -days : null } /** * Текст для дедлайна: "Сегодня", "Завтра", "Осталось: 5 дн.", "Просрочено: 3 дн." */ const getDaysLeftText = (dateStr) => { const days = getDaysUntil(dateStr) if (days === null) return '' if (days < 0) return `Просрочено: ${Math.abs(days)} дн.` if (days === 0) return 'Сегодня' if (days === 1) return 'Завтра' return `Осталось: ${days} дн.` } /** * Статус дедлайна для CSS классов: 'overdue', 'soon', '' */ const getDueDateStatus = (dateStr) => { const days = getDaysUntil(dateStr) if (days === null) return '' if (days < 0) return 'overdue' if (days <= 2) return 'soon' return '' } /** * Относительная дата в прошлом: "Сегодня", "Вчера", "3 дня назад", или полная дата */ const formatRelative = (dateStr) => { if (!dateStr) return '' const daysAgo = getDaysAgo(dateStr) if (daysAgo === null) return '' if (daysAgo === 0) return 'Сегодня' if (daysAgo === 1) return 'Вчера' if (daysAgo >= 2 && daysAgo <= 4) return `${daysAgo} дня назад` if (daysAgo >= 5 && daysAgo <= 14) return `${daysAgo} дней назад` return formatShort(dateStr) } /** * Время назад для комментариев: "только что", "5 мин. назад", "2 ч. назад", "3 дн. назад" * Для старых дат — "1 янв 14:30" */ const formatTimeAgo = (dateStr) => { const date = parseDate(dateStr) if (!date) return '' const now = new Date() const diff = now - date const minutes = Math.floor(diff / 60000) const hours = Math.floor(diff / 3600000) const days = Math.floor(diff / 86400000) if (minutes < 1) return 'только что' if (minutes < 60) return `${minutes} мин. назад` if (hours < 24) return `${hours} ч. назад` if (days < 7) return `${days} дн. назад` const day = date.getDate() const time = date.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' }) return `${day} ${MONTHS_SHORT[date.getMonth()]} ${time}` } return { // Константы MONTHS_SHORT, MONTHS_FULL, // Парсинг parseDate, // Форматирование formatShort, formatFull, formatDateTime, formatRelative, formatTimeAgo, // Расчёты getDaysUntil, getDaysAgo, getDaysLeftText, getDueDateStatus } }