Exemple #1
0
def periodCaption(dateFrom=None, dateTo=None, locale=None):
    """Convert two dates to a string in the specified locale that
    :meth:`decodeDatePeriod` will understand
    
    :param dateFrom: the beginning period date
    :param dateTo: the ending period date
    :param locale: the current locale (e.g: en, en_us, it)"""
    localNoPeriod = gnrlocale.getDateKeywords('no period', locale)[0]
    localTo = gnrlocale.getDateKeywords('to', locale)[0]
    localFrom = gnrlocale.getDateKeywords('from', locale)[0]
    textFrom = toText(dateFrom, locale=locale)
    textTo = toText(dateTo, locale=locale)
    if dateFrom and dateTo:
        return '%s %s %s %s' % (localFrom, textFrom, localTo, textTo)
    if dateFrom:
        return '%s %s' % (localFrom, textFrom)
    elif dateTo:
        return '%s %s' % (localTo, textTo)
    else:
        return localNoPeriod
Exemple #2
0
def periodCaption(dateFrom=None, dateTo=None, locale=None):
    """Convert two dates to a string in the specified locale that
    :meth:`decodeDatePeriod` will understand
    
    :param dateFrom: the beginning period date
    :param dateTo: the ending period date
    :param locale: the current locale (e.g: en, en_us, it)"""
    localNoPeriod = gnrlocale.getDateKeywords('no period', locale)[0]
    localTo = gnrlocale.getDateKeywords('to', locale)[0]
    localFrom = gnrlocale.getDateKeywords('from', locale)[0]
    textFrom = toText(dateFrom, locale=locale)
    textTo = toText(dateTo, locale=locale)
    if dateFrom and dateTo:
        return '%s %s %s %s' % (localFrom, textFrom, localTo, textTo)
    if dateFrom:
        return '%s %s' % (localFrom, textFrom)
    elif dateTo:
        return '%s %s' % (localTo, textTo)
    else:
        return localNoPeriod
Exemple #3
0
def periodCaption(dateFrom=None, dateTo=None, locale=None):
    """Convert two dates to a string in the specified locale that decodeDatePeriod will understand.
    
    :param dateFrom: add???. Default value is ``None``
    :param dateTo: add???. Default value is ``None``
    :param locale: add???. Default value is ``None``
    :returns: add???
    """
    localNoPeriod = gnrlocale.getDateKeywords('no period', locale)[0]
    localTo = gnrlocale.getDateKeywords('to', locale)[0]
    localFrom = gnrlocale.getDateKeywords('from', locale)[0]
    textFrom = toText(dateFrom, locale=locale)
    textTo = toText(dateTo, locale=locale)
    if dateFrom and dateTo:
        return '%s %s %s %s' % (localFrom, textFrom, localTo, textTo)
    if dateFrom:
        return '%s %s' % (localFrom, textFrom)
    elif dateTo:
        return '%s %s' % (localTo, textTo)
    else:
        return localNoPeriod
Exemple #4
0
def periodCaption(dateFrom=None, dateTo=None, locale=None):
    """Convert two dates to a string in the specified locale that decodeDatePeriod will understand.
    
    :param dateFrom: add???. Default value is ``None``
    :param dateTo: add???. Default value is ``None``
    :param locale: add???. Default value is ``None``
    :returns: add???
    """
    localNoPeriod = gnrlocale.getDateKeywords('no period', locale)[0]
    localTo = gnrlocale.getDateKeywords('to', locale)[0]
    localFrom = gnrlocale.getDateKeywords('from', locale)[0]
    textFrom = toText(dateFrom, locale=locale)
    textTo = toText(dateTo, locale=locale)
    if dateFrom and dateTo:
        return '%s %s %s %s' % (localFrom, textFrom, localTo, textTo)
    if dateFrom:
        return '%s %s' % (localFrom, textFrom)
    elif dateTo:
        return '%s %s' % (localTo, textTo)
    else:
        return localNoPeriod
Exemple #5
0
def checkDateKeywords(keywords,datestr,locale):
    return anyWordIn(gnrlocale.getDateKeywords(keywords, locale), datestr) or anyWordIn(
                gnrlocale.getDateKeywords(keywords, DEFAULT_LOCALE), datestr)
Exemple #6
0
def decodeDatePeriod(datestr, workdate=None, locale=None, returnDate=False, dtype='D'):
    """Parse a string representing a date or a period and returns a string of one or two dates in iso format separated by ``;``.
    See doc of :meth:`decodeOneDate()` for details on possible formats of a single date
    
    :param datestr: the string representing a date or a period
    :param workdate: the :ref:`workdate`
    :param locale: the current locale (e.g: en, en_us, it)
    :param returnDate: boolean. If ``True``, return the following tuple: ``(dateStart, dateEnd)``
    :param dtype: the :ref:`datatype`
    
    The input *datestr* can be:
    
    * two dates separated by ``;``: e.g. ``22 4, 2008;28 4, 2008``
    * two dates separated by `` to ``
    * two dates in form ``from date to date`` (can be translated and supports synonimous, e.g. ``between date and date``)
    
        * if a period is given as starting date, the start date of period is kept
        * if a period is given as end date, the end date of period is kept
        * if no year is specified, the year is relative to :ref:`workdate`, keeping all periods in the past
          (e.g. if working date is 2008-04-28, december is interpreted as december 2007)
        * if a year is specified for the end date (or period) a relative year is calculated for the starting period
          (e.g. from december to march 06: returns ``'2005-12-01;2006-03-31'``)
    * a starting date in form ``from date``, if date is a period starting date is keep: e.g. april returns ``'2008-04-01;'``
    * an end date in form ``to date``, if date is a period end date is keep: e.g. april returns ``';2008-04-30'``
    * a single expression representing a period: e.g. 2007 returns ``'2007-01-01;2007-12-31'``
    * a single expression representing a single date: e.g. today returns ``'2008-04-28'``
    """
    workdate = workdate or datetime.date.today()
    months = gnrlocale.getMonthNames(locale)
    days = gnrlocale.getDayNames(locale)
    datestr = datestr or ''
    datestr = datestr.lower().strip().replace(',', ';').replace(':', ';')
    exercise = False  # TODO

    dateStart = None
    dateEnd = None

    localNoPeriod = gnrlocale.getDateKeywords('no period', locale)
    localTo = gnrlocale.getDateKeywords('to', locale)
    localFrom = gnrlocale.getDateKeywords('from', locale)

    # check if the period is given with two separate date infos
    if ';' in datestr:
        # two dates or keywords separated by ";":   today;today+5
        dateStart, dateEnd = datestr.split(';')
    elif [k for k in localNoPeriod if k in datestr]:
        dateStart = dateEnd = ''
    elif [k for k in localTo if datestr.startswith('%s ' % k)]:
        # only end date: to december
        dateStart = ''
        dateEnd = datestr.split(' ', 1)[1]
    elif [k for k in localTo if (' %s ' % k) in datestr]:
        # two dates or keywords separated by ' to ' and optionally starting with 'from ': from october to december
        if [k for k in localFrom if datestr.startswith('%s ' % k)]:
            datestr = datestr.split(' ', 1)[1]
        dateStart, dateEnd = datestr.split(' %s ' % [k for k in localTo if (' %s ' % k) in datestr][0])
    elif [k for k in localFrom if datestr.startswith('%s ' % k)]:
        # only start date: from december
        dateStart = datestr.split(' ', 1)[1]
        dateEnd = ''

    if dateStart is None and dateEnd is None:
        # the period is given as an unique string info
        dateStart = dateEnd = datestr

    dateStart = decodeOneDate(dateStart, workdate, months=months, days=days, locale=locale)
    dateEnd = decodeOneDate(dateEnd, workdate, months=months, days=days, locale=locale, isEndPeriod=True)

    if isinstance(dateStart, tuple): # is a month
        year, month = dateStart
        if year is None:                      # no year info is given, try to calculate
            year = workdate.year              # default year is this year
            #if month > workdate.month:        # if the starting month is in the future, change to last year
            #    year = year - 1
        endyear = None
        endmonth = None
        if isinstance(dateEnd, datetime.date):# dateend is yet decoded
            endyear = dateEnd.year
            endmonth = dateEnd.month
        elif isinstance(dateEnd, tuple):
            endyear, endmonth = dateEnd
        if endyear:
            if year > endyear:           # if start year (maybe automatic from workdate) is greater than dateEnd.year 
                year = endyear
            if year == endyear:
                if month > endmonth:     # if startmonth is greater than end month in the same year
                    year = year - 1
        dateStart = monthStart(year, month)

    if isinstance(dateEnd, tuple): # is a month
        year, month = dateEnd
        if year is None:                      # no year info is given, try to calculate
            year = workdate.year              # default year is this year
            if month > workdate.month:        # if the end month is in the future, change to last year
                year = year - 1
        if isinstance(dateStart, datetime.date): # if there is a starting date
            if year < dateStart.year:            # if end year (maybe automatic from workdate) is lesser than dateStart.year 
                year = dateStart.year
            if year == dateStart.year:
                if dateStart.month > month:      # if endmonth is lower than start month
                    year = year + 1              # end is in next year
        dateEnd = monthEnd(year, month)
    if dtype == 'DH':
        dateStart = datetime.datetime(dateStart.year, dateStart.month, dateStart.day)
        dateEnd = datetime.datetime(dateEnd.year, dateEnd.month, dateEnd.day)

    if returnDate:
        return (dateStart, dateEnd)

    if dateStart == dateEnd:
        return str(dateStart or '')
    else:
        return '%s;%s' % (dateStart or '', dateEnd or '')
Exemple #7
0
def checkDateKeywords(keywords,datestr,locale):
    return anyWordIn(gnrlocale.getDateKeywords(keywords, locale), datestr) or anyWordIn(
                gnrlocale.getDateKeywords(keywords, DEFAULT_LOCALE), datestr)
Exemple #8
0
def decodeDatePeriod(datestr, workdate=None, locale=None, returnDate=False, dtype='D',min_date=None,max_date=None):
    """Parse a string representing a date or a period and returns a string of one or two dates in iso format separated by ``;``.
    See doc of :meth:`decodeOneDate()` for details on possible formats of a single date
    
    :param datestr: the string representing a date or a period
    :param workdate: the :ref:`workdate`
    :param locale: the current locale (e.g: en, en_us, it)
    :param returnDate: boolean. If ``True``, return the following tuple: ``(dateStart, dateEnd)``
    :param dtype: the :ref:`datatype`
    
    The input *datestr* can be:
    
    * two dates separated by ``;``: e.g. ``22 4, 2008;28 4, 2008``
    * two dates separated by `` to ``
    * two dates in form ``from date to date`` (can be translated and supports synonimous, e.g. ``between date and date``)
    
        * if a period is given as starting date, the start date of period is kept
        * if a period is given as end date, the end date of period is kept
        * if no year is specified, the year is relative to :ref:`workdate`, keeping all periods in the past
          (e.g. if working date is 2008-04-28, december is interpreted as december 2007)
        * if a year is specified for the end date (or period) a relative year is calculated for the starting period
          (e.g. from december to march 06: returns ``'2005-12-01;2006-03-31'``)
    * a starting date in form ``from date``, if date is a period starting date is keep: e.g. april returns ``'2008-04-01;'``
    * an end date in form ``to date``, if date is a period end date is keep: e.g. april returns ``';2008-04-30'``
    * a single expression representing a period: e.g. 2007 returns ``'2007-01-01;2007-12-31'``
    * a single expression representing a single date: e.g. today returns ``'2008-04-28'``
    """
    workdate = workdate or datetime.date.today()
    months = gnrlocale.getMonthNames(locale)
    days = gnrlocale.getDayNames(locale)
    datestr = datestr or ''
    datestr = datestr.lower().strip().replace(',', ';').replace(':', ';')
    exercise = False  # TODO

    dateStart = None
    dateEnd = None

    localNoPeriod = gnrlocale.getDateKeywords('no period', locale)
    localTo = gnrlocale.getDateKeywords('to', locale)
    localFrom = gnrlocale.getDateKeywords('from', locale)

    # check if the period is given with two separate date infos
    if ';' in datestr:
        # two dates or keywords separated by ";":   today;today+5
        dateStart, dateEnd = datestr.split(';')
    elif [k for k in localNoPeriod if k == datestr]:
        dateStart = dateEnd = ''
    elif [k for k in localTo if datestr.startswith('%s ' % k)]:
        # only end date: to december
        dateStart = ''
        dateEnd = datestr.split(' ', 1)[1]
    elif [k for k in localTo if (' %s ' % k) in datestr]:
        # two dates or keywords separated by ' to ' and optionally starting with 'from ': from october to december
        if [k for k in localFrom if datestr.startswith('%s ' % k)]:
            datestr = datestr.split(' ', 1)[1]
        dateStart, dateEnd = datestr.split(' %s ' % [k for k in localTo if (' %s ' % k) in datestr][0])
    elif [k for k in localFrom if datestr.startswith('%s ' % k)]:
        # only start date: from december
        dateStart = datestr.split(' ', 1)[1]
        dateEnd = ''

    if dateStart is None and dateEnd is None:
        # the period is given as an unique string info
        dateStart = dateEnd = datestr

    dateStart = decodeOneDate(dateStart, workdate, months=months, days=days, locale=locale)
    dateEnd = decodeOneDate(dateEnd, workdate, months=months, days=days, locale=locale, isEndPeriod=True)

    if isinstance(dateStart, tuple): # is a month
        year, month = dateStart
        if year is None:                      # no year info is given, try to calculate
            year = workdate.year              # default year is this year
            #if month > workdate.month:        # if the starting month is in the future, change to last year
            #    year = year - 1
        endyear = None
        endmonth = None
        if isinstance(dateEnd, datetime.date):# dateend is yet decoded
            endyear = dateEnd.year
            endmonth = dateEnd.month
        elif isinstance(dateEnd, tuple):
            endyear, endmonth = dateEnd
        if endyear:
            if year > endyear:           # if start year (maybe automatic from workdate) is greater than dateEnd.year 
                year = endyear
            if year == endyear:
                if month > endmonth:     # if startmonth is greater than end month in the same year
                    year = year - 1
        dateStart = monthStart(year, month)

    if isinstance(dateEnd, tuple): # is a month
        year, month = dateEnd
        if year is None:                      # no year info is given, try to calculate
            year = workdate.year              # default year is this year
            if month > workdate.month:        # if the end month is in the future, change to last year
                year = year - 1
        if isinstance(dateStart, datetime.date): # if there is a starting date
            if year < dateStart.year:            # if end year (maybe automatic from workdate) is lesser than dateStart.year 
                year = dateStart.year
            if year == dateStart.year:
                if dateStart.month > month:      # if endmonth is lower than start month
                    year = year + 1              # end is in next year
        dateEnd = monthEnd(year, month)
    if min_date and (dateStart is None or dateStart<min_date):
        dateStart = min_date
    if max_date and (dateEnd is None or dateEnd>max_date):
        dateEnd = max_date

    if dtype == 'DH':
        dateStart = datetime.datetime(dateStart.year, dateStart.month, dateStart.day)
        dateEnd = datetime.datetime(dateEnd.year, dateEnd.month, dateEnd.day)

    if returnDate:
        return (dateStart, dateEnd)

    if dateStart == dateEnd:
        return str(dateStart or '')
    else:
        return '%s;%s' % (dateStart or '', dateEnd or '')
Exemple #9
0
def decodeOneDate(datestr,
                  workdate=None,
                  months=None,
                  days=None,
                  quarters=None,
                  locale=None,
                  isEndPeriod=False):
    """Parse a string representing a date or a period.

    :param datestr: the string to be interpreted
    :param workdate: a date of reference for calculate relative periods (e.g: tomorrow or this week).
                     Default value is ``None``
    :param months: names of months according to locale (just for caching). Default value is ``None``
    :param days: names of weekdays according to locale (just for caching). Default value is ``None``
    :param quarters: names of quarters according to locale (just for caching). Default value is ``None``
    :param locale: the current locale string (e.g: en, en_us, it). Default value is ``None``
    :param isEndPeriod: if the string represents a period, return the end date (default return the start date)
    :returns: datetime.date or tuple(year,month) or None

    Special keywords like ``today`` or the name of a month can be translated in all languages and support synonimous,
    e.g: ``this month`` or ``month``.

    The input string can be:
    
    * a year: e.g. 2007 or 07
    * today, yesterday, tomorrow (can be translated in all languages)
        * you can specify a number of days to add to today: e.g. 'today + 3' or 'today - 15'
    * this week, next week, last week (can be translated in all languages)
    * this month, next month, last month (can be translated in all languages )
        * can be specified a number of months to add to current month: e.g. 'this month + 3' or 'this month - 24'
    * the name of a quarter: e.g. Q1 or 1st quarter
    * the name of a month: e.g. april or apr
        * can be specified a year after the month: e.g. apr 07 or april 2007
        * returns a tuple (year, month): if year is not specified in datestr, year is returned None
    * the name of a weekday: e.g. monday or mon
        * the date returned is the date of the given weekday in this week (relative to workdate)
    * an iso date: e.g. 2008-04-28
    * a date formatted according to locale (see babel doc): e.g. 4 28, 2008 (en_us) or 28-4-08 (it)
                           various separators are admitted: 28-4-08, 28/4/08, 28 4 08
    """
    def addToDay(datestr, date):
        """add???
        
        :param datestr: add???
        :param date: add???
        :returns: add???
        """
        if '+' in datestr:
            days = int(datestr.split('+')[1].strip())
            return date + datetime.timedelta(days)
        if '-' in datestr:
            days = int(datestr.split('-')[1].strip())
            return date - datetime.timedelta(days)
        return date

    def addToMonth(datestr, date, addmonth=0):
        """add???
        
        :param datestr: add???
        :param date: add???
        :param addmonth: add???. Default value is ``0``
        :returns: add???
        """
        if '+' in datestr:
            addmonth = int(datestr.split('+')[1].strip())
        if '-' in datestr:
            addmonth = -int(datestr.split('-')[1].strip())
        month = date.month + addmonth
        year = date.year
        while month <= 0:
            month = month + 12
            year = year - 1
        while month > 12:
            month = month - 12
            year = year + 1
        return datetime.date(year, month, 1)

    datestr = datestr or ''
    datestr = datestr.strip()
    if datestr:
        months = months or gnrlocale.getMonthNames(locale)
        def_months = gnrlocale.getMonthNames(DEFAULT_LOCALE)
        days = days or gnrlocale.getDayNames(locale)
        def_days = gnrlocale.getDayNames(DEFAULT_LOCALE)
        quarters = quarters or gnrlocale.getQuarterNames(locale)
        def_quarters = gnrlocale.getQuarterNames(DEFAULT_LOCALE)
        dateStart = None
        dateEnd = None
        workdate = workdate or datetime.date.today()

        if datestr.isdigit() and len(datestr) in (2, 4):  # a full year
            year = yearDecode(datestr)
            dateStart = datetime.date(year, 1, 1)
            if isEndPeriod:
                dateEnd = datetime.date(year, 12, 31)

        elif anyWordIn(gnrlocale.getDateKeywords('today', locale),
                       datestr) or anyWordIn(
                           gnrlocale.getDateKeywords('today', DEFAULT_LOCALE),
                           datestr):  # today
            dateStart = addToDay(datestr, workdate)
        elif anyWordIn(gnrlocale.getDateKeywords(
                'yesterday', locale), datestr) or anyWordIn(
                    gnrlocale.getDateKeywords('yesterday', DEFAULT_LOCALE),
                    datestr):  # yesterday
            dateStart = addToDay(datestr, workdate - datetime.timedelta(1))
        elif anyWordIn(gnrlocale.getDateKeywords('tomorrow', locale),
                       datestr) or anyWordIn(
                           gnrlocale.getDateKeywords('tomorrow', locale),
                           DEFAULT_LOCALE):  # tomorrow
            dateStart = addToDay(datestr, workdate + datetime.timedelta(1))

        elif (datestr in gnrlocale.getDateKeywords(
            ('this week', 'next week', 'last week'),
                locale)) or (datestr in gnrlocale.getDateKeywords(
                    ('this week', 'next week', 'last week'),
                    DEFAULT_LOCALE)):  # relative week
            j = workdate.weekday()
            dateStart = workdate - datetime.timedelta(j)
            if (datestr in gnrlocale.getDateKeywords(
                    'last week',
                    locale)) or (datestr in gnrlocale.getDateKeywords(
                        'last week', DEFAULT_LOCALE)):
                dateStart = dateStart - datetime.timedelta(7)
            elif (datestr in gnrlocale.getDateKeywords(
                    'next week',
                    locale)) or (datestr in gnrlocale.getDateKeywords(
                        'next week', DEFAULT_LOCALE)):
                dateStart = dateStart + datetime.timedelta(7)
            if isEndPeriod:
                dateEnd = dateStart + datetime.timedelta(6)
        elif anyWordIn(
                gnrlocale.getDateKeywords(
                    ('this month', 'next month', 'last month'), locale),
                datestr) or anyWordIn(
                    gnrlocale.getDateKeywords(
                        ('this month', 'next month', 'last month'),
                        DEFAULT_LOCALE), datestr):  # relative month
            if (datestr in gnrlocale.getDateKeywords(
                    'last month',
                    locale)) or (datestr in gnrlocale.getDateKeywords(
                        'last month', DEFAULT_LOCALE)):
                dateStart = addToMonth(datestr, workdate, -1)
            elif (datestr in gnrlocale.getDateKeywords(
                    'next month',
                    locale)) or (datestr in gnrlocale.getDateKeywords(
                        'next month', DEFAULT_LOCALE)):
                dateStart = addToMonth(datestr, workdate, 1)
            else:
                dateStart = addToMonth(datestr, workdate)
            if isEndPeriod:
                dateEnd = monthEnd(date=dateStart)
        elif anyWordIn(quarters.keys(), datestr):  # quarter
            qt, year = splitAndStrip(datestr, sep=' ', n=1, fixed=2)
            year = yearDecode(year)
            qt = quarters[datestr]
            dateStart = (year, qt * 3 - 2)
            if isEndPeriod:
                dateEnd = (year, qt * 3)
        elif anyWordIn(def_quarters.keys(), datestr):  # quarter
            qt, year = splitAndStrip(datestr, sep=' ', n=1, fixed=2)
            year = yearDecode(year)
            qt = def_quarters[datestr]
            dateStart = (year, qt * 3 - 2)
            if isEndPeriod:
                dateEnd = (year, qt * 3)
        elif anyWordIn(months.keys(), datestr):  # month name
            month, year = splitAndStrip(datestr, sep=' ', n=1, fixed=2)
            year = yearDecode(year)
            month = months[month]
            dateStart = (year, month)
        elif anyWordIn(def_months.keys(), datestr):  # month name
            month, year = splitAndStrip(datestr, sep=' ', n=1, fixed=2)
            year = yearDecode(year)
            month = def_months[month]
            dateStart = (year, month)
        elif datestr in days:  # weekday name
            dateStart = workdate + datetime.timedelta(days[datestr] -
                                                      workdate.weekday())
        elif datestr in def_days:  # weekday name
            dateStart = workdate + datetime.timedelta(def_days[datestr] -
                                                      workdate.weekday())
        elif re.match('\d{4}-\d{2}-\d{2}', datestr):  # ISO date
            date_items = [int(el) for el in wordSplit(datestr)[0:3]]
            dateStart = datetime.date(
                *[int(el) for el in wordSplit(datestr)[0:3]])
        else:  # a date in local format
            dateStart = gnrlocale.parselocal(datestr, datetime.date, locale)

        if isEndPeriod and dateEnd:
            return dateEnd
        else:
            return dateStart
Exemple #10
0
def decodeOneDate(datestr, workdate=None, months=None, days=None, quarters=None, locale=None, isEndPeriod=False):
    """Parse a string representing a date or a period.

    :param datestr: the string to be interpreted
    :param workdate: a date of reference for calculate relative periods (e.g: tomorrow or this week).
                     Default value is ``None``
    :param months: names of months according to locale (just for caching). Default value is ``None``
    :param days: names of weekdays according to locale (just for caching). Default value is ``None``
    :param quarters: names of quarters according to locale (just for caching). Default value is ``None``
    :param locale: the current locale string (e.g: en, en_us, it). Default value is ``None``
    :param isEndPeriod: if the string represents a period, return the end date (default return the start date)
    :returns: datetime.date or tuple(year,month) or None

    Special keywords like ``today`` or the name of a month can be translated in all languages and support synonimous,
    e.g: ``this month`` or ``month``.

    The input string can be:
    
    * a year: e.g. 2007 or 07
    * today, yesterday, tomorrow (can be translated in all languages)
        * you can specify a number of days to add to today: e.g. 'today + 3' or 'today - 15'
    * this week, next week, last week (can be translated in all languages)
    * this month, next month, last month (can be translated in all languages )
        * can be specified a number of months to add to current month: e.g. 'this month + 3' or 'this month - 24'
    * the name of a quarter: e.g. Q1 or 1st quarter
    * the name of a month: e.g. april or apr
        * can be specified a year after the month: e.g. apr 07 or april 2007
        * returns a tuple (year, month): if year is not specified in datestr, year is returned None
    * the name of a weekday: e.g. monday or mon
        * the date returned is the date of the given weekday in this week (relative to workdate)
    * an iso date: e.g. 2008-04-28
    * a date formatted according to locale (see babel doc): e.g. 4 28, 2008 (en_us) or 28-4-08 (it)
                           various separators are admitted: 28-4-08, 28/4/08, 28 4 08
    """
    
    def addToDay(datestr, date):
        """add???
        
        :param datestr: add???
        :param date: add???
        :returns: add???
        """
        if '+' in datestr:
            days = int(datestr.split('+')[1].strip())
            return date + datetime.timedelta(days)
        if '-' in datestr:
            days = int(datestr.split('-')[1].strip())
            return date - datetime.timedelta(days)
        return date

    def addToMonth(datestr, date, addmonth=0):
        """add???
        
        :param datestr: add???
        :param date: add???
        :param addmonth: add???. Default value is ``0``
        :returns: add???
        """
        if '+' in datestr:
            addmonth = int(datestr.split('+')[1].strip())
        if '-' in datestr:
            addmonth = -int(datestr.split('-')[1].strip())
        month = date.month + addmonth
        year = date.year
        while month <= 0:
            month = month + 12
            year = year - 1
        while month > 12:
            month = month - 12
            year = year + 1
        return datetime.date(year, month, 1)
        
    datestr = datestr or ''
    datestr = datestr.strip()
    if datestr:
        months = months or gnrlocale.getMonthNames(locale)
        def_months = gnrlocale.getMonthNames(DEFAULT_LOCALE)
        days = days or gnrlocale.getDayNames(locale)
        def_days = gnrlocale.getDayNames(DEFAULT_LOCALE)
        quarters = quarters or gnrlocale.getQuarterNames(locale)
        def_quarters = gnrlocale.getQuarterNames(DEFAULT_LOCALE)
        dateStart = None
        dateEnd = None
        workdate = workdate or datetime.date.today()
        
        if datestr.isdigit() and len(datestr) in (2, 4):                          # a full year
            year = yearDecode(datestr)
            dateStart = datetime.date(year, 1, 1)
            if isEndPeriod:
                dateEnd = datetime.date(year, 12, 31)
                
        elif anyWordIn(gnrlocale.getDateKeywords('today', locale), datestr) or anyWordIn(
                gnrlocale.getDateKeywords('today', DEFAULT_LOCALE), datestr):     # today
            dateStart = addToDay(datestr, workdate)
        elif anyWordIn(gnrlocale.getDateKeywords('yesterday', locale), datestr) or anyWordIn(
                gnrlocale.getDateKeywords('yesterday', DEFAULT_LOCALE), datestr): # yesterday
            dateStart = addToDay(datestr, workdate - datetime.timedelta(1))
        elif anyWordIn(gnrlocale.getDateKeywords('tomorrow', locale), datestr) or anyWordIn(
                gnrlocale.getDateKeywords('tomorrow', locale), DEFAULT_LOCALE):  # tomorrow
            dateStart = addToDay(datestr, workdate + datetime.timedelta(1))
            
        elif (datestr in gnrlocale.getDateKeywords(('this week', 'next week', 'last week'), locale)) or  (
        datestr in gnrlocale.getDateKeywords(('this week', 'next week', 'last week'), DEFAULT_LOCALE)): # relative week
            j = workdate.weekday()
            dateStart = workdate - datetime.timedelta(j)
            if (datestr in gnrlocale.getDateKeywords('last week', locale)) or (
            datestr in gnrlocale.getDateKeywords('last week', DEFAULT_LOCALE)):
                dateStart = dateStart - datetime.timedelta(7)
            elif (datestr in gnrlocale.getDateKeywords('next week', locale)) or (
            datestr in gnrlocale.getDateKeywords('next week', DEFAULT_LOCALE)):
                dateStart = dateStart + datetime.timedelta(7)
            if isEndPeriod:
                dateEnd = dateStart + datetime.timedelta(6)
        elif anyWordIn(gnrlocale.getDateKeywords(('this month', 'next month', 'last month'), locale),
                       datestr) or anyWordIn(
                gnrlocale.getDateKeywords(('this month', 'next month', 'last month'), DEFAULT_LOCALE),
                datestr): # relative month
            if (datestr in gnrlocale.getDateKeywords('last month', locale)) or (
            datestr in gnrlocale.getDateKeywords('last month', DEFAULT_LOCALE)):
                dateStart = addToMonth(datestr, workdate, -1)
            elif (datestr in gnrlocale.getDateKeywords('next month', locale)) or (
            datestr in gnrlocale.getDateKeywords('next month', DEFAULT_LOCALE)):
                dateStart = addToMonth(datestr, workdate, 1)
            else:
                dateStart = addToMonth(datestr, workdate)
            if isEndPeriod:
                dateEnd = monthEnd(date=dateStart)
        elif anyWordIn(quarters.keys(), datestr): # quarter
            qt, year = splitAndStrip(datestr, sep=' ', n=1, fixed=2)
            year = yearDecode(year)
            qt = quarters[datestr]
            dateStart = (year, qt * 3 - 2)
            if isEndPeriod:
                dateEnd = (year, qt * 3)
        elif anyWordIn(def_quarters.keys(), datestr): # quarter
            qt, year = splitAndStrip(datestr, sep=' ', n=1, fixed=2)
            year = yearDecode(year)
            qt = def_quarters[datestr]
            dateStart = (year, qt * 3 - 2)
            if isEndPeriod:
                dateEnd = (year, qt * 3)
        elif anyWordIn(months.keys(), datestr):                                 # month name
            month, year = splitAndStrip(datestr, sep=' ', n=1, fixed=2)
            year = yearDecode(year)
            month = months[month]
            dateStart = (year, month)
        elif anyWordIn(def_months.keys(), datestr):                                 # month name
            month, year = splitAndStrip(datestr, sep=' ', n=1, fixed=2)
            year = yearDecode(year)
            month = def_months[month]
            dateStart = (year, month)
        elif datestr in days:                                                   # weekday name
            dateStart = workdate + datetime.timedelta(days[datestr] - workdate.weekday())
        elif datestr in def_days:                                                   # weekday name
            dateStart = workdate + datetime.timedelta(def_days[datestr] - workdate.weekday())
        elif re.match('\d{4}-\d{2}-\d{2}', datestr):                            # ISO date
            date_items = [int(el) for el in wordSplit(datestr)[0:3]]
            dateStart = datetime.date(*[int(el) for el in wordSplit(datestr)[0:3]])
        else:                                                                   # a date in local format
            dateStart = gnrlocale.parselocal(datestr, datetime.date, locale)
            
        if isEndPeriod and dateEnd:
            return dateEnd
        else:
            return dateStart