如何以天为单位注释日期时间差异
How to annotate a difference of datetime in days
我有一个 Booking
模型,它有 start
和 end
日期时间字段。我想知道预订涵盖多少天。我可以在 Python 中执行此操作,但我需要此值以进行进一步注释。
这是我尝试过的方法:
In [1]: Booking.objects.annotate(days=F('end')-F('start'))[0].days
Out[1]: datetime.timedelta(16, 50400)
这里有几个问题:
- 我想要天数的整数(或我可以在计算中使用的其他数字类型)作为输出,而不是 timedelta。设置 output_field 在这里没有任何意义。
- 我的总和是基于日期次。像这样的减法,如果不删除时间,可能会导致整个天数被关闭。
在 Python 我会做 (end.date() - start.date()).days + 1
。我怎样才能在数据库中做到这一点,最好是通过 ORM(例如数据库函数),但是 RawSQL
就足以把它拿出来?
我已经编写了几个数据库函数来转换和截断日期以解决 PostgreSQL 下的这两个问题。我使用的 DATE_PART
和 DATE_TRUNC
内部函数是特定于数据库的 ☹
from django.db.models import Func
class DiffDays(Func):
function = 'DATE_PART'
template = "%(function)s('day', %(expressions)s)"
class CastDate(Func):
function = 'date_trunc'
template = "%(function)s('day', %(expressions)s)"
那我可以:
In [25]: Booking.objects.annotate(days=DiffDays(CastDate(F('end'))-CastDate(F('start'))) + 1)[0].days
Out[25]: 18.0
这个问题还有另一个简单的解决方案。您可以使用:
from django.db.models import F
from django.db.models.functions import ExtractDay
然后:
Booking.objects.annotate(days=(ExtractDay(F('end')-F('start'))+1))[0].days
如果您正在使用 MYSQL 数据库,您可以使用 自定义数据库函数 作为,
from django.db.models.functions import Func
class TimeStampDiff(Func):
class PrettyStringFormatting(dict):
def __missing__(self, key):
return '%(' + key + ')s'
def __init__(self, *expressions, **extra):
unit = extra.pop('unit', 'day')
self.template = self.template % self.PrettyStringFormatting({"unit": unit})
super().__init__(*expressions, **extra)
function = 'TIMESTAMPDIFF'
template = "%(function)s(%(unit)s, %(expressions)s)"
用法
from django.db.models import F, IntegerField
booking_queryset = Booking.objects.annotate(
<b>days=TimeStampDiff(F('start'), F('end'), output_field=IntegerField())</b>)
if booking_queryset.exist():
print(booking_queryset[0].__dict__)
我有一个 Booking
模型,它有 start
和 end
日期时间字段。我想知道预订涵盖多少天。我可以在 Python 中执行此操作,但我需要此值以进行进一步注释。
这是我尝试过的方法:
In [1]: Booking.objects.annotate(days=F('end')-F('start'))[0].days
Out[1]: datetime.timedelta(16, 50400)
这里有几个问题:
- 我想要天数的整数(或我可以在计算中使用的其他数字类型)作为输出,而不是 timedelta。设置 output_field 在这里没有任何意义。
- 我的总和是基于日期次。像这样的减法,如果不删除时间,可能会导致整个天数被关闭。
在 Python 我会做 (end.date() - start.date()).days + 1
。我怎样才能在数据库中做到这一点,最好是通过 ORM(例如数据库函数),但是 RawSQL
就足以把它拿出来?
我已经编写了几个数据库函数来转换和截断日期以解决 PostgreSQL 下的这两个问题。我使用的 DATE_PART
和 DATE_TRUNC
内部函数是特定于数据库的 ☹
from django.db.models import Func
class DiffDays(Func):
function = 'DATE_PART'
template = "%(function)s('day', %(expressions)s)"
class CastDate(Func):
function = 'date_trunc'
template = "%(function)s('day', %(expressions)s)"
那我可以:
In [25]: Booking.objects.annotate(days=DiffDays(CastDate(F('end'))-CastDate(F('start'))) + 1)[0].days
Out[25]: 18.0
这个问题还有另一个简单的解决方案。您可以使用:
from django.db.models import F
from django.db.models.functions import ExtractDay
然后:
Booking.objects.annotate(days=(ExtractDay(F('end')-F('start'))+1))[0].days
如果您正在使用 MYSQL 数据库,您可以使用 自定义数据库函数 作为,
from django.db.models.functions import Func
class TimeStampDiff(Func):
class PrettyStringFormatting(dict):
def __missing__(self, key):
return '%(' + key + ')s'
def __init__(self, *expressions, **extra):
unit = extra.pop('unit', 'day')
self.template = self.template % self.PrettyStringFormatting({"unit": unit})
super().__init__(*expressions, **extra)
function = 'TIMESTAMPDIFF'
template = "%(function)s(%(unit)s, %(expressions)s)"
用法
from django.db.models import F, IntegerField
booking_queryset = Booking.objects.annotate(
<b>days=TimeStampDiff(F('start'), F('end'), output_field=IntegerField())</b>)
if booking_queryset.exist():
print(booking_queryset[0].__dict__)