Пришёл в Ruby из Java, работаю джуном второй месяц. Делаю в админке отчёт по заказам за день, Rails 7.2. Менеджеры говорят, что ночные заказы, с 00:00 до 03:00, оказываются во вчерашнем дне. Сначала не поверил, выгрузил заказы за 14 ноября и сверил руками: из 312 в отчёт попали 287, а 25 ночных уехали в отчёт за 13-е. В config стоит config.time_zone = "Moscow", в базе время вроде правильное. Код такой:
day = Date.today
@orders = Order.where("DATE(created_at) = ?", day)
@total = @orders.sum(:amount)
Это пояс, да. config.time_zone влияет на то, как Rails показывает и разбирает время, но в базе created_at всё равно лежит в UTC. DATE(created_at) считает PostgreSQL, и для него заказ в 01:30 по Петербургу это 22:30 предыдущего дня. А Date.today берёт пояс сервера, а не приложения, его в Rails лучше не использовать совсем.
Задавай диапазон времени, а не сравнивай даты:
day = Date.current # дата в поясе приложения
Order.where(created_at: day.all_day)
# WHERE created_at BETWEEN '2024-11-14 21:00:00' AND '2024-11-15 20:59:59.999999'
all_day от Date строит границы в Time.zone, то есть с 00:00 до 23:59 по Москве, и в SQL уходят уже правильные значения в UTC. Бонус: такой запрос может использовать индекс по created_at, а DATE(created_at) без отдельного индекса по выражению не может. То же правило для Time.now: в коде приложения всегда Time.current.
И проверь расписание фоновых задач: у планировщика пояс свой, ночная выгрузка может запускаться не тогда, когда вы думаете
У меня такое было ещё в бухгалтерии, до программирования: банк присылал операции в UTC, и ночные платежи уезжали в прошлый день, а 1-го числа ещё и в прошлый месяц. Проверь заодно отчёт за месяц, на границе месяца эффект тот же, только заметнее.
Заменил на Date.current и all_day, ночные заказы встали на место. Нашёл ещё четыре Date.today в экспорте и фильтрах, поправил все, месячный отчёт тоже сошёлся с тем, что менеджеры считают руками. Добавил тест с travel_to на 01:30 ночи, чтобы такое не вернулось.