Files
pinned_message_client/src/components/date.ts
T
2026-03-26 14:41:25 +07:00

188 lines
7.6 KiB
TypeScript

export function formatRussianDate(dateStr: string): string {
// 1. Вытаскиваем только саму дату (2026-03-24) с помощью регулярного выражения
// Это защитит нас от багов с часовыми поясами при парсинге
const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})/);
if (!match) {
throw new Error("Неверный формат входной строки");
}
// 2. Преобразуем строки в числа
const year = parseInt(match[1], 10);
const month = parseInt(match[2], 10);
const day = parseInt(match[3], 10);
// 3. Создаем объект даты (обратите внимание: в JS месяцы начинаются с 0, поэтому month - 1)
const dateObj = new Date(year, month - 1, day);
// 4. Словари для точного форматирования (родительный падеж для месяцев)
const monthsRu = [
"января", "февраля", "марта", "апреля", "мая", "июня",
"июля", "августа", "сентября", "октября", "ноября", "декабря"
];
// В JS 0 - это воскресенье, 1 - понедельник и т.д.
const weekdaysRu = [
"воскресенье", "понедельник", "вторник", "среда",
"четверг", "пятница", "суббота"
];
// 5. Получаем нужные значения
const monthName = monthsRu[dateObj.getMonth()];
const weekdayName = weekdaysRu[dateObj.getDay()];
// 6. Собираем итоговую строку
return `${weekdayName} ${day} ${monthName}`;
}
export function getRelativeDayName(dateStr: string): string {
// 1. Вытаскиваем только дату (например, 2026-03-24)
const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})/);
if (!match) {
throw new Error("Неверный формат входной строки");
}
const year = parseInt(match[1], 10);
const month = parseInt(match[2], 10) - 1; // Месяцы в JS начинаются с 0
const day = parseInt(match[3], 10);
// 2. Создаем объект целевой даты и сбрасываем время в 00:00:00
const targetDate = new Date(year, month, day, 0, 0, 0, 0);
// 3. Создаем объект СЕГОДНЯШНЕЙ даты и тоже сбрасываем время в 00:00:00
const today = new Date();
today.setHours(0, 0, 0, 0);
// 4. Вычисляем разницу в миллисекундах и переводим в дни
// Используем Math.round, чтобы избежать багов при переходе на летнее/зимнее время
const diffInMs = targetDate.getTime() - today.getTime();
const diffInDays = Math.round(diffInMs / (1000 * 60 * 60 * 24));
// 5. Возвращаем результат в зависимости от разницы в днях
switch (diffInDays) {
case 0:
return "сегодня";
case 1:
return "завтра";
case 2:
return "послезавтра"; // бонус :)
case -1:
return "вчера"; // бонус :)
default:
// Если это не сегодня и не завтра, можно вернуть пустую строку,
// либо просто количество дней, либо отформатированную дату (как в прошлом ответе)
return "";
}
}
/**
* Функция для склонения слов (1 минута, 2 минуты, 5 минут)
*/
function getDeclension(number: number, titles: [string, string, string]): string {
const cases = [2, 0, 1, 1, 1, 2];
return titles[
(number % 100 > 4 && number % 100 < 20)
? 2
: cases[(number % 10 < 5) ? number % 10 : 5]
];
}
/**
* Основная функция форматирования времени
*/
export function timeAgo(goDateStr: string): string {
// 1. Парсим строку регуляркой, чтобы вытащить дату, время и смещение пояса.
// Игнорируем "m=+..." и доли секунд, так как для "5 минут назад" они не нужны.
// Группы: 1-Дата, 2-Время, 3-Часовой пояс (например +0700)
const regex = /^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})(?:\.\d+)? ([+-]\d{4})/;
const match = goDateStr.match(regex);
if (!match) {
console.error("Неверный формат даты:", goDateStr);
return goDateStr; // Возвращаем как есть в случае ошибки
}
const datePart = match[1];
const timePart = match[2];
const timezone = match[3];
// 2. Приводим к формату ISO 8601, который понимает любой браузер:
// "2026-03-26T12:11:18+0700"
const isoString = `${datePart}T${timePart}${timezone}`;
const date = new Date(isoString);
const now = new Date();
// 3. Считаем разницу в секундах
const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
// Если дата в будущем (например, как в вашем примере 2026 год)
if (seconds < 0) {
return "в будущем";
}
// 4. Логика интервалов
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (seconds < 60) {
return "только что";
}
if (minutes < 60) {
return `${minutes} ${getDeclension(minutes, ['минуту', 'минуты', 'минут'])} назад`;
}
if (hours < 24) {
return `${hours} ${getDeclension(hours, ['час', 'часа', 'часов'])} назад`;
}
if (days < 7) {
return `${days} ${getDeclension(days, ['день', 'дня', 'дней'])} назад`;
}
// Если прошло больше недели, возвращаем просто дату
// Можно использовать Intl.DateTimeFormat для красоты
return date.toLocaleDateString("ru-RU", {
day: "numeric",
month: "long",
year: "numeric"
});
}
export function formatTime(timeStr: string): string {
if (!timeStr) {
return "";
}
return timeStr
// 1. Убираем абсолютно все пробелы (включая табы и неразрывные пробелы)
.replace(/\s+/g, "")
// 2. Заменяем все точки на двоеточия
.replace(/\./g, ":");
}
export function formatTimeRange(text: string): string {
if (!text) {
return "";
}
return text
// 1. Меняем точку на двоеточие ТОЛЬКО в форматах времени (например, 16.00 -> 16:00)
// (^|[^\d]) - проверяем, что перед цифрой нет других цифр
// ([0-2]?\d) - часы (от 0 до 29)
// \. - сама точка
// ([0-5]\d) - минуты (от 00 до 59)
// (?!\d) - после минут не должно быть других цифр
.replace(/(^|[^\d])([0-2]?\d)\.([0-5]\d)(?!\d)/g, "$1$2:$3")
// 2. Находим любые дефисы или тире и делаем красивые пробелы вокруг них ("-" -> " - ")
.replace(/\s*([-–—])\s*/g, " - ")
// 3. На всякий случай сжимаем двойные пробелы в один (если они случайно появились)
.replace(/\s{2,}/g, " ")
// 4. Убираем пробелы в самом начале и в самом конце строки
.trim();
}