Django filter by date range but exclude the year

Viewed 437

I know that if I want to filter results by date range, I just use something like

Sample.objects.filter(birthday__range=(start_date, end_date))

But how would I filter by a date range that excludes the year?

2 Answers

You can use both filter and exclude. Filter as you done, and then exclude the year, like 2021

Sample.objects.filter(birthday__range=(start_date, end_date)).exclude(birthday_date__year=2021)

We can work with a range of datetimes with:

from datetime import timedelta
from django.db.models import Q

def filter_query(start_date, end_date):
    a = am, ad = start_date.month, start_date.day
    b = bm, bd = end_date.month, end_date.day
    end_date2 = end_date + timedelta(days=1)
    c = end_date2.month, end_date2.day
    if c == (2, 29) and a == (3, 1):
        return ~Q(birthday__month=2, birthday__day__gt=28)
    if a == c:
        return Q()
    if a <= b:
        if am == bm:
            return Q(birthday__month=am, birthday__day__range=(ad, bd))
        else:
            return (Q(birthday__month=am, birthday__day__gte=ad) |
                    Q(month__range=(am+1, bm-1)) |
                    Q(birthday__month=bm, birthday__day__lte=bd))
    else:
        return ~filter_query(end_date+timedelta(days=1), start_date-timedelta(days=1))

This will return a Q object that then can be used by filtering the queryset, for example:

Sample.objects.filter(filter_query(date(2020, 12, 15), date(2021, 1, 16)))

There are basically five cases:

  • if the end_date is February 28th, and the start date is March 1st, then we exclude February 29th.
  • if the end_date plus one day is the same as the start date, then we do not have to filter;
  • if the start_date is less than the end_date and the months match, then we filter on that month, and a range check on the days;
  • if the start_date is less than the end_date and the months do not match, then we look for the ranges:
    • items with the same month start_date and with a day greater than the month of the start_date;
    • items with a month between the next month of the start_date and less than the previous month of the end_date; and
    • items with the same month as the end_date and a day less than or equal to the day of the end_date.
  • if the end date is less than the start date, then we return a negation of the end_date plus one day, and the start_date minus one day.
Related