Django - Возвращает месяцы, которые охватывают сообщения пользователя с заданным временным смещением от UTC
Я работаю над приложением для социальных сетей и хочу, чтобы пользователь A мог получить все месяцы, которые опубликовал пользователь B в часовом поясе пользователя A. Внешний интерфейс выполнен на Javascript, и наиболее разумным было бы, если бы пользователь отправлял смещение своего часового пояса от UTC (это можно сделать с помощью new Date().getTimezoneOffset()
в JS), поскольку именно так в Django сохраняются даты часовых поясов. Так, например, пользователь A будет пинговать url, который выглядит примерно как /<user B's username>/<year>/<user A's time zone offset from UTC>
.
Допустим, пользователь B находится в PST и создал сообщение 15 ноября 2021 года в 12:00 вечера и 31 декабря 2021 года в 11:00 вечера. Тогда, допустим, пользователь A, который находится в EST, вызывает этот url, тогда он должен вернуть [November, January] (Или эквивалентное числовое отображение), потому что 31 декабря в 11:00pm PST - это 1 января в 2:00am EST.
Как вернуть все месяцы, которые пользователь B опубликовал в часовом поясе пользователя A, задав смещение часового пояса пользователя A относительно UTC?
models.py
class Post(models.Model):
uuid = models.UUIDField(primary_key=True)
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="post_creator")
body = models.CharField(max_length=POST_MAX_LEN, validators=[MinLengthValidator(POST_MIN_LEN)])
Нет возможности написать запрос для отбора объектов с определенным часовым поясом. Поэтому, если вы хотите сделать такой запрос, вы либо запрашиваете все посты за этот год, а затем фильтруете набор запросов с помощью чистых методов python (например, filter()
); либо вы добавляете новое поле в пост, где вы сохраняете часовой пояс, чтобы вы могли написать свой запрос. Это было бы более эффективным решением
Тем не менее, я хотел бы отметить, что такой запрос немного странный, поскольку любое время даты указывает на точный момент времени, и похоже, что вас не интересует временной диапазон, а только часовой пояс... Возможно, вас интересует местоположение, или, по крайней мере, более крупная область.
Если вы включите поддержку часовых поясов в Django (см. Документацию по часовым поясам в Django), вы сможете делать запросы, используя объекты datetime с учетом часовых поясов.
Чтобы включить его, необходимо установить USE_TZ = True
в файле настроек.
Вам необходимо вызвать метод timezone.activate
, чтобы установить часовой пояс текущего пользователя.
Допустим, вы собираетесь передать смещение UTC в URL, как указано выше (/<user B username>/<new Date().getTimezoneOffset() in JS>
), и это смещение будет доступно в переменной utc_offset
. Затем вы можете установить "текущий часовой пояс" (т.е. часовой пояс текущего пользователя) следующим образом:
import datetime
from django.utils import timezone
timezone.activate(datetime.timezone(datetime.timedelta(hours=utc_offset)))
Теперь при выполнении timezone.now()
дата будет возвращена в часовом поясе пользователя.
И когда вы отображаете любые значения DateTimeField
в любых шаблонах, они будут отображаться в часовом поясе пользователя.
Если вы используете Django Rest Framework (DRF), DateTimeField
s также будет возвращаться в часовом поясе пользователя.
Если вы не используете DRF или не выводите даты в шаблонах, вы можете получить значение времени в часовом поясе пользователя, используя astimezone
и timezone.get_current_timezone()
:
post.created.astimezone(timezone.get_current_timezone())
Похоже, что вам нужно сделать хранение данных пользователя через datetime(). Лучший способ, который я нашел для решения той же проблемы - хранить временные зоны каждого пользователя и отбрасывать все дубликаты. Затем, когда любой пользователь публикует данные, он записывает дату и время каждого часового пояса в этот момент и сохраняет их вместе с сообщением. Затем при отображении данных вы определяете, в каком часовом поясе находится пользователь, и отображаете дату и время конкретного часового пояса в отображаемом сообщении.
Прежде всего, вы должны решить проблемы с часовым поясом пользователей B и часовым поясом сервера! Как вы сказали,
Допустим, пользователь B находится в PST и создал сообщение в... и 31 декабря 2021 года в 11:00 вечера.
это зависит от часового пояса вашего сервера, как это будет записано в вашей базе данных! Если часовой пояс вашего сервера UTC, то у вас будет запись: 2021-01-01 07:00. Какая дата была на самом деле, а? Хорошо, предположим, что у вас часовой пояс сервера UTC, и в БД будет запись, соответствующая 2021-01-01 07:00. Итак, все, что вам нужно в этом случае, это добавить смещение к datetime и получить месяц, вот так:
def get_month_from_user_side(dt: datetime, offset: int) -> str:
result_dt = dt - timedelta(minutes=offset)
return result_dt.strftime('%B, %Y')
Я протестировал его:
from datetime import date, datetime, timedelta
if __name__ == '__main__':
user_b_post_dt = datetime(2022, 1, 1, 7, 00) # This time – from DB
offset = -120 # This should be got from JS from user A
print(
get_month_from_user_side(user_b_post_dt, offset)
)
>>January, 2022
"Магия" здесь в том, что если пользователь B попросит свои собственные посты, он получит декабрьские, как и должно быть!
if __name__ == '__main__':
user_b_post_dt = datetime(2022, 1, 1, 7, 00) # This time – from DB
offset = 480 # PST time offset from UTC (for user B)
print(
get_month_from_user_side(user_b_post_dt, offset)
)
>>December, 2021
Примечание: Этот код нуждается в тестах для определения знака +- timedelta, может быть ошибка.
В руководстве Django рекомендуется использовать set USE_TZ=True https://docs.djangoproject.com/en/4.0/topics/i18n/timezones/#setup, что я и делаю. Я также рекомендую установить TIME_ZONE = 'UTC'