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
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
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 '')
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 '')