[英]Django F expression on datetime objects
我的模型是:
class Test():
date1 = models.DateTimeField()
date2 = models.DateTimeField()
我可以使用以下查询找出date2
大于date1
对象:
Test.obejcts.filter(date2__gt=F('date1'))
我想找到date2
比date1
一年的所有对象。
如何根据date1
和date2
之间的差异找出对象?
一般解决方案:
您可以annotate
日期差异,然后根据timedelta(days=365)
(与@Anonymous 在他的评论中建议的非常接近):
Test.objects.annotate(
duration=F('date2') - F('date1')
).filter(duration__gt=timedelta(days=365))
如果您使用的是PostgreSQL
,则此答案还有另一个选项:
from django.db.models import F, Func Test.objects.annotate( duration = Func(F('date2'), F('date1'), function='age') ).filter(duration__gt=timedelta(days=365))
您可以同时使用__date
查找和TruncDate
函数:
from django.db.models import DateField, ExpressionWrapper, F
from django.db.models.functions import TruncDate
Test.obejcts.filter(
date2__date__gt=ExpressionWrapper(
TruncDate(F('date1')) + datetime.timedelta(days=365),
output_field=DateField(),
),
)
如果您真正需要的是date1 = 2019-05-14
, date2 > 2020-05-14
。 那么这种方法并不总是正确的,因为闰年有 366 天。 这个问题可以结合使用Trunc
和Extract
函数来解决。 不同的方法是可能的......例如:
from django.db.models import DateField, ExpressionWrapper, F
from django.db.models.functions import TruncDate, ExtractDay
date_field = DateField()
YEAR = timedelta(days=365)
LEAP_YEAR = timedelta(days=366)
shifted_date1 = ExpressionWrapper(
TruncDate(F('date1')) + YEAR,
output_field=date_field,
)
leap_shifted_date1 = ExpressionWrapper(
TruncDate(F('date1')) + LEAP_YEAR,
output_field=date_field,
)
qs = Test.objects.filter(
(
# It's ok to add 365 days if...
Q(date2__date__gt=shifted_date1)
&
(
# If day of month after 365 days is the same...
Q(date1__day=ExtractDay(shifted_date1))
|
# Or it's 29-th of February
Q(
date1__month=2,
date1__day=29,
)
)
)
|
Q(
# Use 366 days for other cases
date2__date__gt=leap_shifted_date1,
)
)
PS 如果您有USE_TZ = True
并在特定时区执行查询(例如在执行查询集之前使用timezone.activate(...)
),那么在添加timedelta
之前执行TruncDate
很重要,因为执行TruncDate(F('date1')+timedelta(...))
在每年在不同日期执行切换到“夏令时”的国家/地区可能会给出不正确的结果。 例如:
2019-03-31
切换到 DST 时间, 2019-03-31
在 2020 年切换到2020-03-29
。2019-03-30 23:30
还没有使用夏令时。2020-03-30 23:30 "non-DST"
,所以在“标准化”之后,这个日期时间将变成2020-03-31 00:30 "DST"
TruncDate
之前使用TruncDate
解决了这个问题,因为TruncDate
将值转换为 date 。额外信息:一些国家/地区在固定日期(例如每年 2 月 1 日)切换到 DST,其他国家/地区可能会在“3 月的最后一个星期日”切换,这可能是每年不同的日期。
import pytz
import datetime
kyiv.localize(datetime.datetime(2011, 3, 28, 0, 1)) - kyiv.localize(datetime.datetime(2010, 3, 28, 0, 1))
# `datetime.timedelta(364, 82800)` is less than 365 days
“闰秒年”的 PPS 最后几秒 ( 2016-12-31 23:59:60.999
) 也可能受到 TruncDate/timedelta-shift 排序的影响,但“幸运的是”大多数数据库不支持闰秒,而 python 的datetime.datetime
也没有这个功能
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.