Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
162 lines (130 sloc) 4.87 KB
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import datetime
import re
from django.core import exceptions
from django.db import models
from django.utils import six
from django.utils.translation import ugettext_lazy as _
partial_date_re = re.compile(
class PartialDate(object):
YEAR = 0
DAY = 2
_date = None
_precision = None
YEAR: '%Y',
MONTH: '%Y-%m',
DAY: '%Y-%m-%d'
def __init__(self, date, precision=DAY):
if isinstance(date, six.text_type):
date, precision = PartialDate.parseDate(date) = date
self.precision = precision
def __repr__(self):
return "" if not self._date else self._date.strftime(self.DATE_FORMATS[self._precision])
def format(self, precision_year=None, precision_month=None, precision_day=None):
if self.precisionYear():
format = precision_year
elif self.precisionMonth():
format = precision_month
format = precision_day
return "" if not self._date else self._date.strftime(format)
def date(self):
return self._date
def date(self, value):
if not isinstance(value,
raise exceptions.ValidationError(
_("%(value)s is not instance"),
params={'value': value},
self._date = value
def precision(self):
return self._precision
def precision(self, value):
self._precision = value if value in (self.YEAR, self.MONTH, self.DAY) else self.DAY
if self._precision == self.MONTH:
if self._precision == self.YEAR:
self._date.replace(month=1, day=1)
def precisionYear(self):
return self.precision == self.YEAR
def precisionMonth(self):
return self.precision == self.MONTH
def precisionDay(self):
return self.precision == self.DAY
def parseDate(value):
Returns a tuple (, precision) from a string formatted as YYYY, YYYY-MM, YYYY-MM-DD.
match = partial_date_re.match(value)
match_dict = match.groupdict()
kw = {k: int(v) if v else 1 for k, v in six.iteritems(match_dict)}
precision = PartialDate.DAY if match_dict["day"] else \
PartialDate.MONTH if match_dict["month"] else \
return (**kw), precision)
except (AttributeError, ValueError):
raise exceptions.ValidationError(
_("'%(value)s' is not a valid date string (YYYY, YYYY-MM, YYYY-MM-DD)"),
params={'value': value}
def __eq__(self, other):
if isinstance(other, PartialDate):
return == and self.precision == other.precision
return NotImplemented
def __gt__(self, other):
if isinstance(other, PartialDate):
return self.__ge__(other) and not self.__eq__(other)
return NotImplemented
def __ge__(self, other):
if isinstance(other, PartialDate):
return >= and self.precision >= other.precision
return NotImplemented
class PartialDateField(models.Field):
A django model field for storing partial dates.
Accepts None, a partial_date.PartialDate object,
or a formatted string such as YYYY, YYYY-MM, YYYY-MM-DD.
In the database it saves the date in a column of type DateTimeField
and uses the seconds to save the level of precision.
def get_internal_type(self):
return "DateTimeField"
def from_db_value(self, value, expression, connection, context):
if value is None:
return value
return PartialDate(, value.second)
def to_python(self, value):
if value is None:
return value
if isinstance(value, PartialDate):
return value
if isinstance(value, six.text_type):
return PartialDate(value)
raise exceptions.ValidationError(
_("'%(name)s' value must be a PartialDate instance, "
"a valid partial date string (YYYY, YYYY-MM, YYYY-MM-DD) "
"or None, not '%(value)s'"),
params={'name':, 'value': value},
def get_prep_value(self, value):
if value in (None, ''):
return None
partial_date = self.to_python(value)
date =
return datetime.datetime(date.year, date.month,, second=partial_date.precision)
You can’t perform that action at this time.