Допустим, платёж прошёл 10 ноября 2026 года в 23:30 UTC. В отчёте по UTC его сумма попадёт в 10 ноября. В часовом поясе UTC+7 уже 11 ноября, 06:30. Поэтому два отчёта могут показать разные суммы по дням и одинаковую общую сумму, если в обоих выбран один и тот же набор платежей.

С этим легко столкнуться в PostgreSQL, если группировать платежи через paid_at::date. Когда paid_at имеет тип timestamptz, результат зависит от часового пояса текущей сессии. Один запрос, запущенный из двух подключений с разными настройками, может разнести платежи по разным дням.

Проверим на трёх условных успешных платежах. Все суммы в рублях. Для примера понадобится PostgreSQL и отдельное тестовое подключение. SET TIME ZONE меняет настройку этого подключения, а временная таблица существует до его закрытия. Все команды выполняйте в нём же.

CREATE TEMP TABLE payments (
    id integer PRIMARY KEY,
    paid_at timestamptz NOT NULL,
    amount numeric(12, 2) NOT NULL
);

INSERT INTO payments VALUES
    (1, '2026-11-10 16:59:00+00', 100),
    (2, '2026-11-10 17:00:00+00', 200),
    (3, '2026-11-10 23:30:00+00', 300);

SET TIME ZONE 'UTC';

SELECT paid_at::date AS report_day, sum(amount) AS paid_total
FROM payments
GROUP BY 1
ORDER BY 1;

Получится одна строка за 10 ноября с суммой 600 ₽. Теперь в том же подключении выполните SET TIME ZONE 'Asia/Krasnoyarsk' и повторите SELECT. За 10 ноября останется 100 ₽, за 11 ноября появится 500 ₽. Все три платежа остались в таблице, поменялось правило определения даты.

Для рабочего отчёта я бы явно задавал часовой пояс до группировки.

SELECT
    (paid_at AT TIME ZONE 'Asia/Krasnoyarsk')::date AS report_day,
    sum(amount) AS paid_total
FROM payments
GROUP BY 1
ORDER BY 1;

Этот запрос даст одинаковые даты при обеих настройках сессии. AT TIME ZONE переводит момент платежа в местное время выбранного пояса, а приведение к date берёт его календарную дату.

Платёж

Время UTC

Время UTC+7

День отчёта UTC+7

1

10 ноября, 16:59

10 ноября, 23:59

10 ноября

2

10 ноября, 17:00

11 ноября, 00:00

11 ноября

3

10 ноября, 23:30

11 ноября, 06:30

11 ноября

То же правило нужно использовать в фильтре. Для 11 ноября по времени Красноярска границы можно задать так.

SELECT sum(amount) AS paid_total
FROM payments
WHERE paid_at >=
      timestamp '2026-11-11 00:00:00' AT TIME ZONE 'Asia/Krasnoyarsk'
  AND paid_at <
      timestamp '2026-11-12 00:00:00' AT TIME ZONE 'Asia/Krasnoyarsk';

Результат — 500 ₽. Нижняя граница включена, верхняя исключена, поэтому платёж ровно в полночь попадёт в один день. Обе полуночи заданы в часовом поясе отчёта. Это пригодится и для поясов с переводом часов, где календарные сутки могут отличаться от 24 часов.

В примере paid_at — время успешного платежа. Если в одном отчёте взять время платежа, а в другом время получения webhook, расхождения останутся и после настройки часового пояса. Для сверки нужны одинаковые событие, часовой пояс и границы периода. Ну и желательно вывести эти условия рядом с датой выгрузки. Как-то так…