studio/src/utils/kubikonTime.js
МИН 31adbf151b Initial public release: Студия Рублокса v1.0
Open-source веб-студия для создания игр Рублокса, двойная лицензия
AGPL-3.0 + Коммерческая.

Главное:
- Vite 5 + React 18 + Babylon 7.54.3 + Monaco Editor + Colyseus 0.16
- Самодостаточный движок ~28к строк (66 файлов): BlockManager,
  TerrainVoxelBuilder, ModelManager, DecoManager, PlayerController,
  ScriptSandboxWorker, MultiplayerSync, 30+ GD-гейммодов
- Главный редактор KubikonEditor (~37к строк) + панели, ScriptEditor (Monaco)
- Витрина игр (KubikonFeed, KubikonStudio, KubikonDocs, KubikonLearn)
- Geometry Dash sub-app (GdMenu, GdShop, GdRules, GdCoverArt)
- 10 admin-preview каталогов для дизайнеров (скины, музыка, порталы и т.д.)
- Конфигурируемый бэкенд через VITE_API_BASE — работает со staging
  (dev-api.rublox.pro) без настройки
- Standalone-режим (VITE_STANDALONE=true) — открыть пустой редактор без бэка
- Полная документация (на русском): README, ARCHITECTURE, CONTRIBUTING,
  SECURITY, CHANGELOG
- ESLint + Prettier + EditorConfig
- Legal: LICENSE (AGPL-3.0), LICENSE-COMMERCIAL.md, CLA.md, COPYRIGHT.md
- Issue templates: bug_report, feature_request, security_disclosure

Перед публикацией:
- Все импорты из minecraftia заменены на локальные
- Все хардкоды URL (minecraftia-school.ru) и внутренних IP убраны → env
- Admin-эндпоинты Kubikon3DService вырезаны (остаются в приватном репо)
- AdminKubikonModeration не публикуется (модерация — в team.rublox.pro)
- 93 МБ ассетов public/kubikon-assets вынесены в .gitignore
  (раздаются через release artifact)
2026-05-27 23:41:10 +03:00

72 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Утилиты форматирования времени для Рублокс 3D.
*
* Сервер сохраняет время в формате 'YYYY-MM-DD HH:MM:SS' БЕЗ маркера TZ.
* Это UTC (контейнер запущен в UTC), но строка ISO-парсера JS воспринимает
* её как local time → разница с реальным локальным.
*
* Эти функции трактуют сырое время как UTC и приводят к локальному.
*/
/**
* Парсит 'YYYY-MM-DD HH:MM:SS' (как UTC) или ISO ('...Z' / '+03:00') в Date.
*/
export function parseServerTime(s) {
if (!s) return null;
if (typeof s !== 'string') return null;
// Если уже ISO с TZ — Date справится
if (/Z$|[+-]\d{2}:?\d{2}$/.test(s)) {
const d = new Date(s);
return isNaN(d.getTime()) ? null : d;
}
// 'YYYY-MM-DD HH:MM:SS' — добавляем 'Z' чтобы интерпретировать как UTC
const iso = s.replace(' ', 'T') + 'Z';
const d = new Date(iso);
return isNaN(d.getTime()) ? null : d;
}
/** HH:MM в локальном поясе пользователя. */
export function formatTimeShort(s) {
const d = parseServerTime(s);
if (!d) return '';
return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
}
/** Полное «01.05.2026 11:33» в локальном поясе. */
export function formatDateTime(s) {
const d = parseServerTime(s);
if (!d) return '';
return d.toLocaleString(undefined, {
day: '2-digit', month: '2-digit', year: 'numeric',
hour: '2-digit', minute: '2-digit',
});
}
/** «5 минут назад» / «вчера» / «01.05.2026 11:33». */
export function formatRelative(s) {
const d = parseServerTime(s);
if (!d) return '';
const diffSec = (Date.now() - d.getTime()) / 1000;
if (diffSec < 0) return formatTimeShort(s); // в будущем — fallback
if (diffSec < 60) return 'только что';
if (diffSec < 3600) return `${Math.floor(diffSec / 60)} мин назад`;
if (diffSec < 86400) {
const h = Math.floor(diffSec / 3600);
return `${h} ${pluralRu(h, ['час', 'часа', 'часов'])} назад`;
}
if (diffSec < 86400 * 2) return 'вчера в ' + formatTimeShort(s);
if (diffSec < 86400 * 7) {
const days = Math.floor(diffSec / 86400);
return `${days} ${pluralRu(days, ['день', 'дня', 'дней'])} назад`;
}
return formatDateTime(s);
}
function pluralRu(n, forms) {
const mod10 = n % 10;
const mod100 = n % 100;
if (mod10 === 1 && mod100 !== 11) return forms[0];
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14)) return forms[1];
return forms[2];
}