feat: update alert timestamps to use Minsk timezone for accurate display and input handling

This commit is contained in:
2026-09-17 18:57:55 +03:00
parent d32c15c85e
commit d284c6e940

View File

@@ -380,16 +380,16 @@ function AlertCard({ alert, index, onEdit, onDelete }) {
<div className="text-xs text-gray-500">
{alert.start_time && (
<div>
Начало: {new Date(alert.start_time).toLocaleString('ru-RU')}
Начало: {new Date(alert.start_time).toLocaleString('ru-RU', { timeZone: MINSK_TIME_ZONE })}
</div>
)}
{alert.end_time && (
<div>
Конец: {new Date(alert.end_time).toLocaleString('ru-RU')}
Конец: {new Date(alert.end_time).toLocaleString('ru-RU', { timeZone: MINSK_TIME_ZONE })}
</div>
)}
{status === 'scheduled' && <div className="mt-1 text-blue-700">Push будет отправлен в момент начала</div>}
{alert.notification_sent_at && <div className="mt-1 text-green-700">Push отправлен: {new Date(alert.notification_sent_at).toLocaleString('ru-RU')}</div>}
{alert.notification_sent_at && <div className="mt-1 text-green-700">Push отправлен: {new Date(alert.notification_sent_at).toLocaleString('ru-RU', { timeZone: MINSK_TIME_ZONE })}</div>}
</div>
<div className="flex gap-2">
@@ -416,11 +416,30 @@ function createAlertIdempotencyKey() {
return `alert-${Date.now()}-${Math.random().toString(36).slice(2)}`;
}
const MINSK_TIME_ZONE = 'Europe/Minsk';
const MINSK_OFFSET = '+03:00';
function toDateTimeInput(value = new Date()) {
const date = new Date(value);
if (Number.isNaN(date.getTime())) return '';
const local = new Date(date.getTime() - date.getTimezoneOffset() * 60_000);
return local.toISOString().slice(0, 16);
const parts = new Intl.DateTimeFormat('en-CA', {
timeZone: MINSK_TIME_ZONE,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', hourCycle: 'h23'
}).formatToParts(date).reduce((result, part) => ({ ...result, [part.type]: part.value }), {});
return `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}`;
}
// PostgreSQL currently stores alert timestamps without a timezone. Keep UTC
// values there, while the operator always sees and enters Polotsk time.
function toApiDateTime(value) {
if (!value) return null;
const date = new Date(`${value}:00${MINSK_OFFSET}`);
return Number.isNaN(date.getTime()) ? value : date.toISOString().slice(0, 19);
}
function toMinskDate(value) {
return new Date(`${value}:00${MINSK_OFFSET}`);
}
function AlertForm({ alert, routes, templates, onSubmit, onCancel }) {
@@ -443,7 +462,8 @@ function AlertForm({ alert, routes, templates, onSubmit, onCancel }) {
const data = {
...formData,
route_id: formData.route_id || null,
end_time: formData.end_time || null
start_time: toApiDateTime(formData.start_time),
end_time: toApiDateTime(formData.end_time)
};
setSubmitting(true);
@@ -472,7 +492,7 @@ function AlertForm({ alert, routes, templates, onSubmit, onCancel }) {
}));
};
const startDate = new Date(formData.start_time);
const startDate = toMinskDate(formData.start_time);
const isScheduled = formData.is_active && !Number.isNaN(startDate.getTime()) && startDate > new Date();
const saveLabel = alert ? 'Сохранить' : !formData.is_active ? 'Сохранить черновик' : isScheduled ? 'Запланировать' : 'Опубликовать';