Example #1
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. Return ``datetime.date``
    or ``tuple(year,month)`` or ``None``
    
    :param datestr: the string to be interpreted
    :param workdate: the :ref:`workdate`
    :param months: names of months according to locale (just for caching)
    :param days: names of weekdays according to locale (just for caching)
    :param quarters: names of quarters according to locale (just for caching)
    :param locale: the current locale (e.g: en, en_us, it)
    :param isEndPeriod: if the string represents a period, return the end date
                        (default return the start date)
    
    Special keywords like ``today`` or the name of a month can be translated in all languages
    and support synonimous. (e.g: this month; e.g: 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):
        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):#l'errore è nel chiamate che passa addmonth sbagliato
        delta=0
        if '+' in datestr:
            delta =  int(datestr.split('+')[1].strip())
        if '-' in datestr:
            delta = -int(datestr.split('-')[1].strip())
        month = date.month + addmonth+ delta
        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 checkDateKeywords('today', datestr, locale):     # today
            dateStart = addToDay(datestr, workdate)
        elif checkDateKeywords('yesterday', datestr, locale): # yesterday
            dateStart = addToDay(datestr, workdate - datetime.timedelta(1))
        elif checkDateKeywords('tomorrow', datestr, locale):  # tomorrow
            dateStart = addToDay(datestr, workdate + datetime.timedelta(1))

        elif checkDateKeywords(('this week', 'next week', 'last week'), datestr, locale): # relative week
            j = workdate.weekday()
            dateStart = workdate - datetime.timedelta(j)
            if checkDateKeywords('last week', datestr, locale):
                dateStart = dateStart - datetime.timedelta(7)
            elif checkDateKeywords('next week', datestr, locale):
                dateStart = dateStart + datetime.timedelta(7)
            if '+' in datestr:
                 dateStart = dateStart + datetime.timedelta(7*int(datestr.split('+')[1]))
            if '-' in datestr:
                 dateStart = dateStart - datetime.timedelta(7*int(datestr.split('-')[1]))
            if isEndPeriod:
                dateEnd = dateStart + datetime.timedelta(6)
        elif checkDateKeywords(('this month', 'next month', 'last month'), datestr, locale): # relative month
            if checkDateKeywords('last month', datestr, locale):  
                dateStart = addToMonth(datestr, workdate, -1)
            elif checkDateKeywords('next month', datestr, 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
Example #2
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. Return ``datetime.date``
    or ``tuple(year,month)`` or ``None``
    
    :param datestr: the string to be interpreted
    :param workdate: the :ref:`workdate`
    :param months: names of months according to locale (just for caching)
    :param days: names of weekdays according to locale (just for caching)
    :param quarters: names of quarters according to locale (just for caching)
    :param locale: the current locale (e.g: en, en_us, it)
    :param isEndPeriod: if the string represents a period, return the end date
                        (default return the start date)
    
    Special keywords like ``today`` or the name of a month can be translated in all languages
    and support synonimous. (e.g: this month; e.g: 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):
        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):#l'errore è nel chiamate che passa addmonth sbagliato
        delta=0
        if '+' in datestr:
            delta =  int(datestr.split('+')[1].strip())
        if '-' in datestr:
            delta = -int(datestr.split('-')[1].strip())
        month = date.month + addmonth+ delta
        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 checkDateKeywords('today', datestr, locale):     # today
            dateStart = addToDay(datestr, workdate)
        elif checkDateKeywords('yesterday', datestr, locale): # yesterday
            dateStart = addToDay(datestr, workdate - datetime.timedelta(1))
        elif checkDateKeywords('tomorrow', datestr, locale):  # tomorrow
            dateStart = addToDay(datestr, workdate + datetime.timedelta(1))

        elif checkDateKeywords(('this week', 'next week', 'last week'), datestr, locale): # relative week
            j = workdate.weekday()
            dateStart = workdate - datetime.timedelta(j)
            if checkDateKeywords('last week', datestr, locale):
                dateStart = dateStart - datetime.timedelta(7)
            elif checkDateKeywords('next week', datestr, locale):
                dateStart = dateStart + datetime.timedelta(7)
            if '+' in datestr:
                 dateStart = dateStart + datetime.timedelta(7*int(datestr.split('+')[1]))
            if '-' in datestr:
                 dateStart = dateStart - datetime.timedelta(7*int(datestr.split('-')[1]))
            if isEndPeriod:
                dateEnd = dateStart + datetime.timedelta(6)
        elif checkDateKeywords(('this month', 'next month', 'last month'), datestr, locale): # relative month
            if checkDateKeywords('last month', datestr, locale):  
                dateStart = addToMonth(datestr, workdate, -1)
            elif checkDateKeywords('next month', datestr, 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[qt]
            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
Example #3
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 '')
Example #4
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 '')