Skip to content

Instantly share code, notes, and snippets.

@tarkatronic
Created March 25, 2016 16:47
Show Gist options
  • Save tarkatronic/e777e1330d84c40aa5f0 to your computer and use it in GitHub Desktop.
Save tarkatronic/e777e1330d84c40aa5f0 to your computer and use it in GitHub Desktop.
django-filters TimeSpanFilter
import re
import time
from datetime import datetime, timedelta
import django_filters
from django.utils import timezone
from .lookups import Now
class TimeSpanFilter(django_filters.Filter):
def filter(self, qs, value):
value = value.lower().strip()
now = timezone.localtime(timezone.now())
if value == 'today':
# FIXME: https://code.djangoproject.com/ticket/9596
# Fixed in Django 1.9: https://github.com/django/django/commit/44f3ee77166bd5c0e8a4604f2d96015268dce100
this_morning = now.replace(hour=0, minute=0, second=0)
tonight = now.replace(hour=23, minute=59, second=59)
return qs.filter(**{'%s__range' % self.name: (this_morning, tonight)})
elif value == 'tomorrow':
tomorrow = now + timedelta(days=1)
tomorrow_morning = tomorrow.replace(hour=0, minute=0, second=0)
tomorrow_night = tomorrow.replace(hour=23, minute=59, second=59)
return qs.filter(**{'%s__range' % self.name: (tomorrow_morning, tomorrow_night)})
elif value == 'yesterday':
yesterday = now - timedelta(days=1)
yesterday_morning = yesterday.replace(hour=0, minute=0, second=0)
yesterday_night = yesterday.replace(hour=23, minute=59, second=59)
return qs.filter(**{'%s__range' % self.name: (yesterday_morning, yesterday_night)})
elif value in ('week', 'this_week'):
return qs.filter(**{'%s__year' % self.name: now.year,
'%s__week' % self.name: Now()}) # FIXME: https://code.djangoproject.com/ticket/25240
elif value == 'next_week':
last_sunday = now - timedelta(days=(now.weekday() + 1))
next_sunday = last_sunday + timedelta(weeks=1)
begin_next_week = next_sunday.replace(hour=0, minute=0, second=0)
end_next_week = (next_sunday + timedelta(days=6)).replace(hour=23, minute=59, second=59)
return qs.filter(**{'%s__range' % self.name: (begin_next_week, end_next_week)})
elif value == 'last_week':
last_sunday = now - timedelta(days=(now.weekday() + 1))
begin_last_week = (last_sunday - timedelta(weeks=1)).replace(hour=0, minute=0, second=0)
end_last_week = (begin_last_week + timedelta(days=6)).replace(hour=23, minute=59, second=59)
return qs.filter(**{'%s__range' % self.name: (begin_last_week, end_last_week)})
elif value in ('month', 'this_month'):
return qs.filter(**{'%s__year' % self.name: now.year,
'%s__month' % self.name: now.month})
elif value in ('year', 'this_year'):
return qs.filter(**{'%s__year' % self.name: now.year})
elif value == 'past':
return qs.filter(**{'%s__lte' % self.name: now})
elif value == 'future':
return qs.filter(**{'%s__gte' % self.name: now})
else:
try: # Specific month filtering
ts = time.strptime(value, '%Y-%m')
return qs.filter(**{'%s__year' % self.name: ts.tm_year,
'%s__month' % self.name: ts.tm_mon})
except ValueError:
pass
try: # Specific day filtering
ts = time.strptime(value, '%Y-%m-%d')
that_day = datetime.datetime(ts.tm_year, ts.tm_mon, ts.tm_mday)
that_morning = that_day.replace(hour=0, minute=0, second=0)
that_night = that_day.replace(hour=23, minute=59, second=59)
return qs.filter(**{'%s__range' % self.name: (that_morning, that_night)})
except ValueError:
pass
# Specific week matching
ts = re.match('(?P<year>\d{4})-week-(?P<week>\d{1,2})', value)
if ts:
match_year = datetime(int(ts.group('year')), 1, 1)
match_week = match_year + timedelta(weeks=int(ts.group('week')))
begin_match_week = (match_week - timedelta(days=(match_week.weekday() + 1))).replace(hour=0, minute=0,
second=0)
end_match_week = (begin_match_week + timedelta(days=6)).replace(hour=23, minute=59, second=59)
return qs.filter(**{'%s__range' % self.name: (begin_match_week, end_match_week)})
return qs # Invalid value; ignore it
from django.db.models.functions import Func
# FIXME: https://code.djangoproject.com/ticket/25240
class Now(Func):
template = 'CURRENT_TIMESTAMP'
def __init__(self, output_field=None, **extra):
if output_field is None:
output_field = DateTimeField()
super(Now, self).__init__(output_field=output_field, **extra)
def as_postgresql(self, compiler, connection):
# Postgres' CURRENT_TIMESTAMP means "the time at the start of the
# transaction". We use STATEMENT_TIMESTAMP to be cross-compatible with
# other databases.
self.template = 'STATEMENT_TIMESTAMP()'
return self.as_sql(compiler, connection)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment