templatetags: add date filters (#36943)

This commit is contained in:
Valentin Deniaud 2020-01-15 11:57:46 +01:00
parent 7bc26d0365
commit baab48cd97
3 changed files with 489 additions and 6 deletions

View File

@ -26,13 +26,15 @@ from django.core import signing
from django.core.exceptions import PermissionDenied
from django.template import VariableDoesNotExist
from django.template.base import TOKEN_BLOCK, TOKEN_VAR, TOKEN_COMMENT
from django.template import defaultfilters
from django.template.defaultfilters import stringfilter
from django.utils import dateparse
from django.utils import dateparse, six
from django.utils.encoding import force_text
from combo.data.models import Page, Placeholder
from combo.public.menu import get_menu_context
from combo.utils import NothingInCacheException, flatten_context
from combo.utils.date import make_date, make_datetime
from combo.apps.dashboard.models import DashboardCell, Tile
register = template.Library()
@ -176,27 +178,68 @@ def strptime(date_string, date_format):
@register.filter
def parse_date(date_string):
try:
return make_date(date_string)
except ValueError:
pass
# fallback to Django function
try:
return dateparse.parse_date(date_string)
except (ValueError, TypeError):
return None
@register.filter(expects_localtime=True, is_safe=False)
def date(value, arg=None):
if arg is None:
return parse_date(value) or ''
if not isinstance(value, (datetime.datetime, datetime.date, datetime.time)):
value = parse_datetime(value) or parse_date(value)
return defaultfilters.date(value, arg=arg)
@register.filter
def parse_datetime(date_string):
if isinstance(date_string, time.struct_time):
return datetime.datetime.fromtimestamp(time.mktime(date_string))
def parse_datetime(datetime_string):
try:
return dateparse.parse_datetime(date_string)
return make_datetime(datetime_string)
except ValueError:
pass
# fallback to Django function
try:
return dateparse.parse_datetime(datetime_string)
except (ValueError, TypeError):
return None
@register.filter(name='datetime', expects_localtime=True, is_safe=False)
def datetime_(value, arg=None):
if arg is None:
return parse_datetime(value) or ''
if not isinstance(value, (datetime.datetime, datetime.date, datetime.time)):
value = parse_datetime(value)
return defaultfilters.date(value, arg=arg)
@register.filter
def parse_time(time_string):
# if input is a datetime, extract its time
try:
dt = parse_datetime(time_string)
if dt:
return dt.time()
except (ValueError, TypeError):
pass
# fallback to Django function
try:
return dateparse.parse_time(time_string)
except (ValueError, TypeError):
return None
@register.filter(expects_localtime=True, is_safe=False)
def time(value, arg=None):
if arg is None:
parsed = parse_time(value)
return parsed if parsed is not None else '' # because bool(midnight) == False
if not isinstance(value, (datetime.datetime, datetime.date, datetime.time)):
value = parse_time(value)
return defaultfilters.date(value, arg=arg)
@register.filter
def shown_because_admin(cell, request):
if not (request.user and request.user.is_superuser):
@ -269,3 +312,104 @@ def get_page(page_slug):
@register.filter
def startswith(string, substring):
return string and force_text(string).startswith(force_text(substring))
def parse_float(value):
if isinstance(value, six.string_types):
# replace , by . for French users comfort
value = value.replace(',', '.')
try:
return float(value)
except (ValueError, TypeError):
return ''
def get_as_datetime(s):
result = parse_datetime(s)
if not result:
result = parse_date(s)
if result:
result = datetime.datetime(year=result.year, month=result.month, day=result.day)
return result
@register.filter(expects_localtime=True, is_safe=False)
def add_days(value, arg):
value = parse_date(value) # consider only date, not hours
if not value:
return ''
arg = parse_float(arg)
if not arg:
return value
result = value + datetime.timedelta(days=float(arg))
return result
@register.filter(expects_localtime=True, is_safe=False)
def add_hours(value, arg):
value = parse_datetime(value)
if not value:
return ''
arg = parse_float(arg)
if not arg:
return value
return value + datetime.timedelta(hours=float(arg))
@register.filter(expects_localtime=True, is_safe=False)
def age_in_days(value, today=None):
value = parse_date(value)
if not value:
return ''
if today is not None:
today = parse_date(today)
if not today:
return ''
else:
today = datetime.date.today()
return (today - value).days
@register.filter(expects_localtime=True, is_safe=False)
def age_in_hours(value, now=None):
# consider value and now as datetimes (and not dates)
value = parse_datetime(value)
if not value:
return ''
if now is not None:
now = parse_datetime(now)
if not now:
return ''
else:
now = datetime.datetime.now()
return int((now - value).total_seconds() / 3600)
def age_in_years_and_months(born, today=None):
'''Compute age since today as the number of years and months elapsed'''
born = make_date(born)
if not born:
return ''
if today is not None:
today = make_date(today)
if not today:
return ''
else:
today = datetime.date.today()
before = (today.month, today.day) < (born.month, born.day)
years = today.year - born.year
months = today.month - born.month
if before:
years -= 1
months += 12
if today.day < born.day:
months -= 1
return years, months
@register.filter(expects_localtime=True, is_safe=False)
def age_in_years(value, today=None):
try:
return age_in_years_and_months(value, today)[0]
except ValueError:
return ''
@register.filter(expects_localtime=True, is_safe=False)
def age_in_months(value, today=None):
try:
years, months = age_in_years_and_months(value, today)
except ValueError:
return ''
return years * 12 + months

77
combo/utils/date.py Normal file
View File

@ -0,0 +1,77 @@
# combo - content management system
# Copyright (C) 2015-2018 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Code is borrowed from w.c.s. https://dev.entrouvert.org/projects/wcs
import datetime
import time
DATE_FORMATS = {
'C': ['%Y-%m-%d', '%y-%m-%d'],
'fr': ['%d/%m/%Y', '%d/%m/%y'],
}
DATETIME_FORMATS = {
'C': ['%Y-%m-%d %H:%M', '%Y-%m-%d %H:%M:%S', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%dT%H:%M:%SZ',
'%y-%m-%d %H:%M', '%y-%m-%d %H:%M:%S'],
'fr': ['%d/%m/%Y %H:%M', '%d/%m/%Y %H:%M:%S', '%d/%m/%Y %Hh%M',
'%d/%m/%y %H:%M', '%d/%m/%y %H:%M:%S', '%d/%m/%y %Hh%M'],
}
def get_as_datetime(s):
formats = []
for value in DATETIME_FORMATS.values():
formats.extend(value)
for value in DATE_FORMATS.values():
formats.extend(value)
for format_string in formats:
try:
return datetime.datetime.strptime(s, format_string)
except ValueError:
pass
raise ValueError()
def make_date(date_var):
'''Extract a date from a datetime, a date, a struct_time or a string'''
if isinstance(date_var, datetime.datetime):
return date_var.date()
if isinstance(date_var, datetime.date):
return date_var
if isinstance(date_var, time.struct_time) or (
isinstance(date_var, tuple) and len(date_var) == 9):
return datetime.date(*date_var[:3])
try:
return get_as_datetime(str(date_var)).date()
except ValueError:
raise ValueError('invalid date value: %s' % date_var)
def make_datetime(datetime_var):
'''Extract a date from a datetime, a date, a struct_time or a string'''
if isinstance(datetime_var, datetime.datetime):
return datetime_var
if isinstance(datetime_var, datetime.date):
return datetime.datetime(year=datetime_var.year, month=datetime_var.month,
day=datetime_var.day)
if isinstance(datetime_var, time.struct_time) or (
isinstance(datetime_var, tuple) and len(datetime_var) == 9):
return datetime.datetime(*datetime_var[:6])
try:
return get_as_datetime(str(datetime_var))
except ValueError:
raise ValueError('invalid datetime value: %s' % datetime_var)

View File

@ -1,3 +1,4 @@
import datetime
import os
import shutil
import time
@ -48,7 +49,7 @@ def test_parse_datetime():
t = Template('{{ someday|parse_date|date:"Y m d" }}')
expected = '2015 04 15'
assert t.render(Context({'someday': '2015-04-15'})) == expected
assert t.render(Context({'someday': '2015-04-15T13:11:12Z'})) == ''
assert t.render(Context({'someday': '2015-04-15T13:11:12Z'})) == expected
assert t.render(Context({'someday': 'foobar'})) == ''
assert t.render(Context({'someday': ''})) == ''
assert t.render(Context({'someday': None})) == ''
@ -201,3 +202,264 @@ def test_startswith():
assert t.render(context) == ''
context = Context({'foo': 'bar'})
assert t.render(context) == 'ok'
def test_datetime_templatetags():
tmpl = Template('{{ plop|datetime }}')
assert tmpl.render(Context({'plop': '2017-12-21 10:32'})) == 'Dec. 21, 2017, 10:32 a.m.'
assert tmpl.render(Context({'plop': '21/12/2017 10h32'})) == 'Dec. 21, 2017, 10:32 a.m.'
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 21, 2017, midnight'
assert tmpl.render(Context({'plop': '21/12/2017'})) == 'Dec. 21, 2017, midnight'
assert tmpl.render(Context({'plop': '10h32'})) == ''
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
tmpl = Template('{{ plop|datetime:"d i" }}')
assert tmpl.render(Context({'plop': '2017-12-21 10:32'})) == '21 32'
assert tmpl.render(Context({'plop': '2017-12-21 10:32:42'})) == '21 32'
assert tmpl.render(Context({'plop': '21/12/2017 10:32'})) == '21 32'
assert tmpl.render(Context({'plop': '21/12/2017 10:32:42'})) == '21 32'
assert tmpl.render(Context({'plop': '21/12/2017 10h32'})) == '21 32'
assert tmpl.render(Context({'plop': '21/12/2017'})) == '21 00'
assert tmpl.render(Context({'plop': '10h32'})) == ''
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
tmpl = Template('{% if d1|datetime > d2|datetime %}d1>d2{% else %}d1<=d2{% endif %}')
assert tmpl.render(Context({'d1': '2017-12-22', 'd2': '2017-12-21'})) == 'd1>d2'
assert tmpl.render(Context({'d1': '2017-12-21', 'd2': '2017-12-21'})) == 'd1<=d2'
assert tmpl.render(Context({'d1': '2017-12-21 10:30', 'd2': '2017-12-21 09:00'})) == 'd1>d2'
assert tmpl.render(Context({'d1': '2017-12-21 10:30', 'd2': '2017-12-21'})) == 'd1>d2'
assert tmpl.render(Context({'d1': '2017-12-22'})) == 'd1<=d2'
assert tmpl.render(Context({'d2': '2017-12-22'})) == 'd1<=d2'
tmpl = Template('{{ plop|date }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 21, 2017'
assert tmpl.render(Context({'plop': '21/12/2017'})) == 'Dec. 21, 2017'
assert tmpl.render(Context({'plop': '2017-12-21 10:32'})) == 'Dec. 21, 2017'
assert tmpl.render(Context({'plop': '21/12/2017 10:32'})) == 'Dec. 21, 2017'
assert tmpl.render(Context({'plop': '21/12/2017 10h32'})) == 'Dec. 21, 2017'
assert tmpl.render(Context({'plop': '21/12/2017 10:32:42'})) == 'Dec. 21, 2017'
assert tmpl.render(Context({'plop': '10:32'})) == ''
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
tmpl = Template('{{ plop|date:"d" }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == '21'
assert tmpl.render(Context({'plop': '21/12/2017'})) == '21'
assert tmpl.render(Context({'plop': '2017-12-21 10:32'})) == '21'
assert tmpl.render(Context({'plop': '10:32'})) == ''
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
tmpl = Template('{% if d1|date > d2|date %}d1>d2{% else %}d1<=d2{% endif %}')
assert tmpl.render(Context({'d1': '2017-12-22', 'd2': '2017-12-21'})) == 'd1>d2'
assert tmpl.render(Context({'d1': '2017-12-21', 'd2': '2017-12-21'})) == 'd1<=d2'
assert tmpl.render(Context({'d1': '2017-12-22'})) == 'd1<=d2'
assert tmpl.render(Context({'d2': '2017-12-22'})) == 'd1<=d2'
tmpl = Template('{{ plop|time }}')
assert tmpl.render(Context({'plop': '10:32'})) == '10:32 a.m.'
assert tmpl.render(Context({'plop': '2017-12-21 10:32'})) == '10:32 a.m.'
assert tmpl.render(Context({'plop': '21/12/2017 10h32'})) == '10:32 a.m.'
assert tmpl.render(Context({'plop': '21/12/2017'})) == 'midnight'
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
tmpl = Template('{{ plop|time:"H i" }}')
assert tmpl.render(Context({'plop': '10:32'})) == '10 32'
assert tmpl.render(Context({'plop': '2017-12-21 10:32'})) == '10 32'
assert tmpl.render(Context({'plop': '21/12/2017 10h32'})) == '10 32'
assert tmpl.render(Context({'plop': '21/12/2017'})) == '00 00'
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
# old fashion, with parse_*
tmpl = Template('{{ plop|parse_datetime|date:"d i" }}')
assert tmpl.render(Context({'plop': '2017-12-21 10:32'})) == '21 32'
assert tmpl.render(Context({'plop': '2017-12-21 10:32:42'})) == '21 32'
assert tmpl.render(Context({'plop': '21/12/2017 10:32'})) == '21 32'
assert tmpl.render(Context({'plop': '21/12/2017 10:32:42'})) == '21 32'
assert tmpl.render(Context({'plop': '21/12/2017 10h32'})) == '21 32'
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
tmpl = Template('{{ plop|parse_date|date:"d" }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == '21'
assert tmpl.render(Context({'plop': '21/12/2017'})) == '21'
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
tmpl = Template('{{ plop|parse_time|date:"H i" }}')
assert tmpl.render(Context({'plop': '10:32'})) == '10 32'
assert tmpl.render(Context({'plop': 'x'})) == ''
assert tmpl.render(Context({'plop': None})) == ''
assert tmpl.render(Context({'plop': 3})) == ''
assert tmpl.render(Context({'plop': {'foo': 'bar'}})) == ''
assert tmpl.render(Context()) == ''
def test_date_maths():
tmpl = Template('{{ plop|add_days:4 }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 25, 2017'
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 25, 2017'
tmpl = Template('{{ plop|add_days:"-1" }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 20, 2017'
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 20, 2017'
tmpl = Template('{{ plop|add_days:1.5 }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 22, 2017'
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 22, 2017'
tmpl = Template('{{ plop|add_days:"1.5" }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 22, 2017'
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 22, 2017'
tmpl = Template('{{ plop|add_hours:24 }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 22, 2017, midnight'
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 22, 2017, 6 p.m.'
tmpl = Template('{{ plop|add_hours:"12.5" }}')
assert tmpl.render(Context({'plop': '2017-12-21'})) == 'Dec. 21, 2017, 12:30 p.m.'
assert tmpl.render(Context({'plop': '2017-12-21 18:00'})) == 'Dec. 22, 2017, 6:30 a.m.'
def test_age_in():
context = {
'form_var_datefield': time.strptime('2018-07-31', '%Y-%m-%d'),
'form_var_datefield2': time.strptime('2018-08-31', '%Y-%m-%d'),
'form_var_datestring': '2018-07-31',
'today': datetime.date.today(),
'now': datetime.datetime.now(),
}
for condition_value in ( # hope date is > 2018
# age_in_days
'"1970-01-01"|age_in_days > 0',
'"01/01/1970"|age_in_days > 0',
'"2500-01-01"|age_in_days < 0',
'"01/01/2500"|age_in_days < 0',
'form_var_datefield|age_in_days > 50',
'form_var_datefield|age_in_days:form_var_datestring == 0',
'form_var_datefield|age_in_days:form_var_datefield2 == 31',
'form_var_datefield2|age_in_days:form_var_datefield == -31',
'form_var_datefield|age_in_days:form_var_datefield == 0',
'form_var_datestring|age_in_days:form_var_datefield == 0',
'form_var_datestring|age_in_days:form_var_datestring == 0',
'today|add_days:-5|age_in_days == 5',
'today|add_days:5|age_in_days == -5',
'today|age_in_days == 0',
# with datetimes
'"1970-01-01 02:03"|age_in_days > 0',
'"01/01/1970 02h03"|age_in_days > 0',
'"2500-01-01 02:03"|age_in_days < 0',
'"01/01/2500 02h03"|age_in_days < 0',
'now|age_in_days == 0',
'now|add_hours:-24|age_in_days == 1',
'now|add_hours:24|age_in_days == -1',
'"2010-11-12 13:14"|age_in_days:"2010-11-12 13:14" == 0',
'"2010-11-12 13:14"|age_in_days:"2010-11-12 12:14" == 0',
'"2010-11-12 13:14"|age_in_days:"2010-11-12 14:14" == 0',
'"2010-11-12 13:14"|age_in_days:"2010-11-13 13:13" == 1',
'"2010-11-12 13:14"|age_in_days:"2010-11-13 13:15" == 1',
# age_in_hours
'now|add_hours:-5|age_in_hours == 5',
'now|add_hours:25|age_in_hours == -24',
'now|age_in_hours == 0',
'"2010-11-12 13:14"|age_in_hours:"2010-11-12 13:14" == 0',
'"2010-11-12 13:14"|age_in_hours:"2010-11-12 12:14" == -1',
'"2010-11-12 13:14"|age_in_hours:"2010-11-12 14:14" == 1',
'"2010-11-12 13:14"|age_in_hours:"2010-11-13 13:13" == 23',
'"2010-11-12 13:14"|age_in_hours:"2010-11-13 13:15" == 24',
'"1970-01-01 02:03"|age_in_hours > 0',
'"01/01/1970 02h03"|age_in_hours > 0',
'"2500-01-01 02:03"|age_in_hours < 0',
'"01/01/2500 02h03"|age_in_hours < 0',
# with dates
'"1970-01-01"|age_in_hours > 0',
'"01/01/1970"|age_in_hours > 0',
'"2500-01-01"|age_in_hours < 0',
'"01/01/2500"|age_in_hours < 0',
'form_var_datefield|age_in_hours > 1200',
'form_var_datefield|age_in_hours:form_var_datestring == 0',
'form_var_datefield|age_in_hours:form_var_datefield2 == 744', # 31*24
'form_var_datefield2|age_in_hours:form_var_datefield == -744',
'form_var_datefield|age_in_hours:form_var_datefield == 0',
'form_var_datestring|age_in_hours:form_var_datefield == 0',
'form_var_datestring|age_in_hours:form_var_datestring == 0',
'today|add_days:-1|age_in_hours >= 24',
'today|add_days:1|age_in_hours <= -0',
'today|add_days:1|age_in_hours >= -24',
'today|age_in_hours >= 0',
# age_in_years
'"1970-01-01"|age_in_years > 0',
'"01/01/1970"|age_in_years > 0',
'"2500-01-01"|age_in_years < 0',
'"01/01/2500"|age_in_years < 0',
'form_var_datefield|age_in_years:"2019-07-31" == 1',
'form_var_datefield|age_in_years:"2019-09-20" == 1',
'form_var_datefield|age_in_years:"2020-07-30" == 1',
'form_var_datefield|age_in_years:"2020-07-31" == 2',
'form_var_datestring|age_in_years:"2019-07-31" == 1',
'today|age_in_years == 0',
'today|add_days:-500|age_in_years == 1',
'today|add_days:-300|age_in_years == 0',
'today|add_days:300|age_in_years == -1',
'now|age_in_years == 0',
'now|add_days:-500|age_in_years == 1',
'now|add_days:-300|age_in_years == 0',
'now|add_days:300|age_in_years == -1',
'"1970-01-01 02:03"|age_in_years > 0',
'"2500-01-01 02:03"|age_in_years < 0',
# age_in_months
'form_var_datefield|age_in_months:form_var_datefield2 == 1',
'form_var_datefield2|age_in_months:form_var_datefield == -1',
'form_var_datefield|age_in_months:"2019-07-31" == 12',
'form_var_datefield|age_in_months:"2019-08-20" == 12',
'form_var_datefield|age_in_months:"2019-09-20" == 13',
'form_var_datestring|age_in_months:"2019-09-20" == 13',
'"1970-01-01"|age_in_months > 0',
'"01/01/1970"|age_in_months > 0',
'"2500-01-01"|age_in_months < 0',
'"01/01/2500"|age_in_months < 0',
'"1970-01-01 02:03"|age_in_months > 0',
'"2500-01-01 02:03"|age_in_months < 0',
# fail produce empty string
'foobar|age_in_days == ""',
'"foobar"|age_in_days == ""',
'"1970-01-01"|age_in_days:"foobar" == ""',
'foobar|age_in_hours == ""',
'"foobar"|age_in_hours == ""',
'"1970-01-01"|age_in_hours:"foobar" == ""',
'foobar|age_in_years == ""',
'"foobar"|age_in_years == ""',
'"1970-01-01"|age_in_years:"foobar" == ""',
'foobar|age_in_months == ""',
'"foobar"|age_in_months == ""',
'"1970-01-01"|age_in_months:"foobar" == ""',
):
tmpl = Template('{%% if %s %%}Good{%% endif %%}' % condition_value)
assert tmpl.render(Context(context)) == 'Good'