【问题标题】:How to annotate a difference of datetime in days如何以天为单位注释日期时间的差异
【发布时间】:2016-11-23 11:01:43
【问题描述】:

我有一个 Booking 模型,它具有 startend 日期时间字段。我想知道预订涵盖多少天。我可以在 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 就足以解决这个问题?

【问题讨论】:

标签: django datetime django-annotate django-database-functions


【解决方案1】:

我编写了几个数据库函数来转换和截断日期,以解决 PostgreSQL 下的这两个问题。我使用的DATE_PARTDATE_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

【讨论】:

    【解决方案2】:

    这个问题还有另一个简单的解决方案。您可以使用:

    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
    

    【讨论】:

    • 这仅适用于月份和年份具有相同值的情况。否则会失败
    【解决方案3】:

    如果您使用的是 MYSQL 数据库,您可以使用 Custom DB Function 作为,

    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(
        days=TimeStampDiff(F('start'), F('end'), output_field=IntegerField()))
    if booking_queryset.exist():
        print(booking_queryset[0].__dict__)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-15
      • 1970-01-01
      • 2011-04-19
      • 1970-01-01
      • 2018-04-02
      • 2015-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多