'Parse a date and return a `Period` object or `None` if the
string is empty, or raise an InvalidQueryArgumentValueError if
the string cannot be parsed to a date.
The date may be absolute or relative. Absolute dates look like
`YYYY`, or `YYYY-MM-DD`, or `YYYY-MM-DD HH:MM:SS`, etc. Relative
dates have three parts:
- Optionally, a ``+`` or ``-`` sign indicating the future or the
past. The default is the future.
- A number: how much to add or subtract.
- A letter indicating the unit: days, weeks, months or years
(``d``, ``w``, ``m`` or ``y``). A "month" is exactly 30 days
and a "year" is exactly 365 days.'

@classmethod
def parse(cls, string):

    def find_date_and_format(string):
        for (ord, format) in enumerate(cls.date_formats):
            for format_option in format:
                try:
                    date = datetime.strptime(string, format_option)
                    return (date, ord)
                except ValueError:
                    pass
        return (None, None)
    if (not string):
        return None
    match_dq = re.match(cls.relative_re, string)
    if match_dq:
        sign = match_dq.group('sign')
        quantity = match_dq.group('quantity')
        timespan = match_dq.group('timespan')
        multiplier = ((-1) if (sign == '-') else 1)
        days = cls.relative_units[timespan]
        date = (datetime.now() + (timedelta(days=(int(quantity) * days)) * multiplier))
        return cls(date, cls.precisions[5])
    (date, ordinal) = find_date_and_format(string)
    if (date is None):
        raise InvalidQueryArgumentValueError(string, 'a    valid    date/time    string')
    precision = cls.precisions[ordinal]
    return cls(date, precision)
