feat: update alert timestamps to use Minsk timezone for accurate display and input handling
This commit is contained in:
@@ -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 ? 'Запланировать' : 'Опубликовать';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user