Django ORM Запрос, агрегирующий datetime + timeoffset и фильтр по новому значению
У меня следующая структура таблицы (обратите внимание на локализованное время начала):
id | start_time | offset | score |
---|---|---|---|
1 | 2024-06-14 02:03:00.000 +0200 |
+1000 |
15 |
2 | 2024-06-14 02:04:00.000 +0200 |
+1000 |
15 |
3 | 2024-06-14 02:05:00.000 +0200 |
+1000 |
12 |
4 | 2024-06-14 02:06:00.000 +0200 |
+1000 |
10 |
Я пытаюсь придумать запрос, который извлекает все записи, в которых время начала, локализованное в смещении столбца, больше заданной даты.
До сих пор я пробовал что-то вроде:
from django.db.models import F, ExpressionWrapper
from django.db import models
from django.db.models.functions import TruncDate
from datetime import datetime
import pytz
utc = pytz.timezone('UTC')
localized_start_time = ExpressionWrapper(
TruncDate('start_time', tzinfo=utc) + F('offset'), output_field=models.DateTimeField()
)
result = MyModel.objects.annotate(
localized_start_time=localized_start_time
).filter(
localized_start_time=datetime.now()
)
Однако при выполнении запроса на выходе я получаю:
operator does not exist: date + character varying
Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
Подскажите, как добавить локализованное время начала, используя значение в столбце смещения?
К сожалению, я не могу изменить способ хранения данных, поскольку это производственная база данных, и последствия будут слишком большими.
Закончилось все тем, что я выполнил кучу операций кастинга:
Создайте выражение для преобразования смещения varchar в соответствующее смещение часов, примеры:
+0700 -> (substring) -> +07 -> (cast integer) -> +7
-0200 -> (substring) -> -02 -> (cast integer) -> -2
Внутренняя аннотация:
- Приведите
start_time
к временной метке, чтобы удалить часовой пояс - Добавьте смещение из выражения выше к приведенному
start_time
- Приведите
Фильтр по аннотированному значению
# extract substring +0700 -> +07 and convert to int +7 adjusted_offset_expr = ExpressionWrapper( Cast(Substr("offset", 1, 3), IntegerField()) * Cast(timedelta(hours=1), TimeField()), output_field=DurationField(), ) # cast start_time to timestamp to remove timezone # add offset to start_time activity_log = ( MyModel.objects.annotate( adjusted_start_time=ExpressionWrapper( Cast("start_time", DateTimeField()) + adjusted_offset_expr, output_field=DateTimeField(), ) ) .filter( adjusted_start_time__gte=start_time.replace(tzinfo=pytz.UTC), ).values() )