Нахождение курса валюты, ближайшего к заданной дате

Найдите ближайшую запись не позже даты и ближайшую не раньше, затем сравните две разницы. Такой подход использует индекс по datetime и не требует вычислять расстояние до каждой строки таблицы.

Короткий ответ

Что ответить на собеседовании

Найдите ближайшую запись не позже даты и ближайшую не раньше, затем сравните две разницы. Такой подход использует индекс по datetime и не требует вычислять расстояние до каждой строки таблицы.

Подробный разбор

Ответ с пояснениями

Условие

Необходимо реализовать функцию, которая по переданной дате возвращает курс валюты, записанный в базе в момент, наиболее близкий к этой дате. В качестве примера используется Django‑модель CurrencyRate, содержащая поля rate (Decimal) и datetime (DateTime). В таблице могут быть записи за разные годы, а запрос должен подобрать значение, чей timestamp минимально отклоняется от запрошенного.

# currency service
class CurrencyRate(models.Model):
    rate = models.DecimalField(...)
    datetime = models.DatetimeField(...)

# Пример данных таблицы
| rate | datetime                      |
|------|-------------------------------|
| ...  | ...                           |
| 34.9 | 1999-05-21 15:44:12.983411    |
| 70.3 | 2022-12-20 08:30:16.123351    |
| 68.1 | 2023-01-09 12:30:13.431559    |
| 68.2 | 2023-01-10 10:00:12.123471    |

# Пример преобразования
| 2020-12-20 00:00:00.000000 -> 70.3 |
| 2000-12-20 00:00:00.000000 -> 34.9 |

def handler(dt: datetime) -> Decimal:
    ...

Ответ

Найдите ближайшую запись не позже даты и ближайшую не раньше, затем сравните две разницы. Такой подход использует индекс по datetime и не требует вычислять расстояние до каждой строки таблицы.

При равном расстоянии выберем более раннюю запись; при пустой таблице — LookupError:

def handler(dt):
    before = CurrencyRate.objects.filter(
        datetime__lte=dt,
    ).order_by('-datetime', 'pk').first()
    after = CurrencyRate.objects.filter(
        datetime__gte=dt,
    ).order_by('datetime', 'pk').first()
    candidates = [row for row in (before, after) if row is not None]
    if not candidates:
        raise LookupError('No currency rates')
    best = min(candidates, key=lambda row: (
        abs(row.datetime - dt), row.datetime, row.pk,
    ))
    return best.rate

Уточнение модели: правильное имя поля Django — DateTimeField. Используйте согласованные timezone-aware даты. При конкурентном изменении набора курсов два запроса требуют общего снимка, если нужна строгая согласованность.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее