changeset 6098:83b1bc99457c

upgrade parsedatetime from 0.8.7 to 2.1
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Tue, 06 Sep 2016 00:09:31 +0200
parents 815981fad7fd
children 1736bc375fc4
files MoinMoin/support/parsedatetime/__init__.py MoinMoin/support/parsedatetime/context.py MoinMoin/support/parsedatetime/parsedatetime.py MoinMoin/support/parsedatetime/parsedatetime_consts.py MoinMoin/support/parsedatetime/pdt_locales/__init__.py MoinMoin/support/parsedatetime/pdt_locales/base.py MoinMoin/support/parsedatetime/pdt_locales/de_DE.py MoinMoin/support/parsedatetime/pdt_locales/en_AU.py MoinMoin/support/parsedatetime/pdt_locales/en_US.py MoinMoin/support/parsedatetime/pdt_locales/es.py MoinMoin/support/parsedatetime/pdt_locales/icu.py MoinMoin/support/parsedatetime/pdt_locales/nl_NL.py MoinMoin/support/parsedatetime/pdt_locales/pt_BR.py MoinMoin/support/parsedatetime/pdt_locales/ru_RU.py MoinMoin/support/parsedatetime/warns.py
diffstat 15 files changed, 3891 insertions(+), 2686 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/support/parsedatetime/__init__.py	Mon Sep 05 23:55:33 2016 +0200
+++ b/MoinMoin/support/parsedatetime/__init__.py	Tue Sep 06 00:09:31 2016 +0200
@@ -1,28 +1,2782 @@
-"""
-parsedatetime.py contains the C{Calendar} class where the C{parse()}
-method can be found.
-
-parsedatetime_consts.py contains the C{Constants} class that builds the
-various regex values using locale information if available.
+# -*- coding: utf-8 -*-
+#
+# vim: sw=2 ts=2 sts=2
+#
+# Copyright 2004-2016 Mike Taylor
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""parsedatetime
+
+Parse human-readable date/time text.
+
+Requires Python 2.6 or later
 """
 
-version = '0.8.7'
-author  = 'Mike Taylor and Darshana Chhajed'
-license = """
-Copyright (c) 2004-2008 Mike Taylor
-Copyright (c) 2006-2008 Darshana Chhajed
-All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
+from __future__ import with_statement, absolute_import, unicode_literals
+
+import re
+import time
+import logging
+import warnings
+import datetime
+import calendar
+import contextlib
+import email.utils
+
+from .pdt_locales import (locales as _locales,
+                          get_icu, load_locale)
+from .context import pdtContext, pdtContextStack
+from .warns import pdt20DeprecationWarning
+
+
+__author__ = 'Mike Taylor'
+__email__ = 'bear@bear.im'
+__copyright__ = 'Copyright (c) 2016 Mike Taylor'
+__license__ = 'Apache License 2.0'
+__version__ = '2.1'
+__url__ = 'https://github.com/bear/parsedatetime'
+__download_url__ = 'https://pypi.python.org/pypi/parsedatetime'
+__description__ = 'Parse human-readable date/time text.'
+
+# as a library, do *not* setup logging
+# see docs.python.org/2/howto/logging.html#configuring-logging-for-a-library
+# Set default logging handler to avoid "No handler found" warnings.
+
+try:  # Python 2.7+
+    from logging import NullHandler
+except ImportError:
+    class NullHandler(logging.Handler):
+
+        def emit(self, record):
+            pass
+
+log = logging.getLogger(__name__)
+log.addHandler(NullHandler())
+
+debug = False
+
+pdtLocales = dict([(x, load_locale(x)) for x in _locales])
+
+
+# Copied from feedparser.py
+# Universal Feedparser
+# Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
+# Originally a def inside of _parse_date_w3dtf()
+def _extract_date(m):
+    year = int(m.group('year'))
+    if year < 100:
+        year = 100 * int(time.gmtime()[0] / 100) + int(year)
+    if year < 1000:
+        return 0, 0, 0
+    julian = m.group('julian')
+    if julian:
+        julian = int(julian)
+        month = julian / 30 + 1
+        day = julian % 30 + 1
+        jday = None
+        while jday != julian:
+            t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0))
+            jday = time.gmtime(t)[-2]
+            diff = abs(jday - julian)
+            if jday > julian:
+                if diff < day:
+                    day = day - diff
+                else:
+                    month = month - 1
+                    day = 31
+            elif jday < julian:
+                if day + diff < 28:
+                    day = day + diff
+                else:
+                    month = month + 1
+        return year, month, day
+    month = m.group('month')
+    day = 1
+    if month is None:
+        month = 1
+    else:
+        month = int(month)
+        day = m.group('day')
+        if day:
+            day = int(day)
+        else:
+            day = 1
+    return year, month, day
+
+
+# Copied from feedparser.py
+# Universal Feedparser
+# Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
+# Originally a def inside of _parse_date_w3dtf()
+def _extract_time(m):
+    if not m:
+        return 0, 0, 0
+    hours = m.group('hours')
+    if not hours:
+        return 0, 0, 0
+    hours = int(hours)
+    minutes = int(m.group('minutes'))
+    seconds = m.group('seconds')
+    if seconds:
+        seconds = seconds.replace(',', '.').split('.', 1)[0]
+        seconds = int(seconds)
+    else:
+        seconds = 0
+    return hours, minutes, seconds
+
+
+def _pop_time_accuracy(m, ctx):
+    if not m:
+        return
+    if m.group('hours'):
+        ctx.updateAccuracy(ctx.ACU_HOUR)
+    if m.group('minutes'):
+        ctx.updateAccuracy(ctx.ACU_MIN)
+    if m.group('seconds'):
+        ctx.updateAccuracy(ctx.ACU_SEC)
+
+
+# Copied from feedparser.py
+# Universal Feedparser
+# Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
+# Modified to return a tuple instead of mktime
+#
+# Original comment:
+#   W3DTF-style date parsing adapted from PyXML xml.utils.iso8601, written by
+#   Drake and licensed under the Python license.  Removed all range checking
+#   for month, day, hour, minute, and second, since mktime will normalize
+#   these later
+def __closure_parse_date_w3dtf():
+    # the __extract_date and __extract_time methods were
+    # copied-out so they could be used by my code --bear
+    def __extract_tzd(m):
+        '''Return the Time Zone Designator as an offset in seconds from UTC.'''
+        if not m:
+            return 0
+        tzd = m.group('tzd')
+        if not tzd:
+            return 0
+        if tzd == 'Z':
+            return 0
+        hours = int(m.group('tzdhours'))
+        minutes = m.group('tzdminutes')
+        if minutes:
+            minutes = int(minutes)
+        else:
+            minutes = 0
+        offset = (hours * 60 + minutes) * 60
+        if tzd[0] == '+':
+            return -offset
+        return offset
+
+    def _parse_date_w3dtf(dateString):
+        m = __datetime_rx.match(dateString)
+        if m is None or m.group() != dateString:
+            return
+        return _extract_date(m) + _extract_time(m) + (0, 0, 0)
+
+    __date_re = (r'(?P<year>\d\d\d\d)'
+                 r'(?:(?P<dsep>-|)'
+                 r'(?:(?P<julian>\d\d\d)'
+                 r'|(?P<month>\d\d)(?:(?P=dsep)(?P<day>\d\d))?))?')
+    __tzd_re = r'(?P<tzd>[-+](?P<tzdhours>\d\d)(?::?(?P<tzdminutes>\d\d))|Z)'
+    # __tzd_rx = re.compile(__tzd_re)
+    __time_re = (r'(?P<hours>\d\d)(?P<tsep>:|)(?P<minutes>\d\d)'
+                 r'(?:(?P=tsep)(?P<seconds>\d\d(?:[.,]\d+)?))?' +
+                 __tzd_re)
+    __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re)
+    __datetime_rx = re.compile(__datetime_re)
+
+    return _parse_date_w3dtf
+
+
+_parse_date_w3dtf = __closure_parse_date_w3dtf()
+del __closure_parse_date_w3dtf
+
+_monthnames = set([
+    'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul',
+    'aug', 'sep', 'oct', 'nov', 'dec',
+    'january', 'february', 'march', 'april', 'may', 'june', 'july',
+    'august', 'september', 'october', 'november', 'december'])
+_daynames = set(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'])
+
+
+# Copied from feedparser.py
+# Universal Feedparser
+# Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
+# Modified to return a tuple instead of mktime
+def _parse_date_rfc822(dateString):
+    '''Parse an RFC822, RFC1123, RFC2822, or asctime-style date'''
+    data = dateString.split()
+    if data[0][-1] in (',', '.') or data[0].lower() in _daynames:
+        del data[0]
+    if len(data) == 4:
+        s = data[3]
+        s = s.split('+', 1)
+        if len(s) == 2:
+            data[3:] = s
+        else:
+            data.append('')
+        dateString = " ".join(data)
+    if len(data) < 5:
+        dateString += ' 00:00:00 GMT'
+    return email.utils.parsedate_tz(dateString)
+
+
+# rfc822.py defines several time zones, but we define some extra ones.
+# 'ET' is equivalent to 'EST', etc.
+# _additional_timezones = {'AT': -400, 'ET': -500,
+#                          'CT': -600, 'MT': -700,
+#                          'PT': -800}
+# email.utils._timezones.update(_additional_timezones)
+
+VERSION_FLAG_STYLE = 1
+VERSION_CONTEXT_STYLE = 2
+
+
+class Calendar(object):
+
+    """
+    A collection of routines to input, parse and manipulate date and times.
+    The text can either be 'normal' date values or it can be human readable.
+    """
+
+    def __init__(self, constants=None, version=VERSION_FLAG_STYLE):
+        """
+        Default constructor for the L{Calendar} class.
+
+        @type  constants: object
+        @param constants: Instance of the class L{Constants}
+        @type  version:   integer
+        @param version:   Default style version of current Calendar instance.
+                          Valid value can be 1 (L{VERSION_FLAG_STYLE}) or
+                          2 (L{VERSION_CONTEXT_STYLE}). See L{parse()}.
+
+        @rtype:  object
+        @return: L{Calendar} instance
+        """
+        # if a constants reference is not included, use default
+        if constants is None:
+            self.ptc = Constants()
+        else:
+            self.ptc = constants
+
+        self.version = version
+        if version == VERSION_FLAG_STYLE:
+            warnings.warn(
+                'Flag style will be deprecated in parsedatetime 2.0. '
+                'Instead use the context style by instantiating `Calendar()` '
+                'with argument `version=parsedatetime.VERSION_CONTEXT_STYLE`.',
+                pdt20DeprecationWarning)
+        self._ctxStack = pdtContextStack()
+
+    @contextlib.contextmanager
+    def context(self):
+        ctx = pdtContext()
+        self._ctxStack.push(ctx)
+        yield ctx
+        ctx = self._ctxStack.pop()
+        if not self._ctxStack.isEmpty():
+            self.currentContext.update(ctx)
+
+    @property
+    def currentContext(self):
+        return self._ctxStack.last()
+
+    def _convertUnitAsWords(self, unitText):
+        """
+        Converts text units into their number value.
+
+        @type  unitText: string
+        @param unitText: number text to convert
+
+        @rtype:  integer
+        @return: numerical value of unitText
+        """
+        word_list, a, b = re.split(r"[,\s-]+", unitText), 0, 0
+        for word in word_list:
+            x = self.ptc.small.get(word)
+            if x is not None:
+                a += x
+            elif word == "hundred":
+                a *= 100
+            else:
+                x = self.ptc.magnitude.get(word)
+                if x is not None:
+                    b += a * x
+                    a = 0
+                elif word in self.ptc.ignore:
+                    pass
+                else:
+                    raise Exception("Unknown number: " + word)
+        return a + b
+
+    def _buildTime(self, source, quantity, modifier, units):
+        """
+        Take C{quantity}, C{modifier} and C{unit} strings and convert them
+        into values. After converting, calcuate the time and return the
+        adjusted sourceTime.
+
+        @type  source:   time
+        @param source:   time to use as the base (or source)
+        @type  quantity: string
+        @param quantity: quantity string
+        @type  modifier: string
+        @param modifier: how quantity and units modify the source time
+        @type  units:    string
+        @param units:    unit of the quantity (i.e. hours, days, months, etc)
+
+        @rtype:  struct_time
+        @return: C{struct_time} of the calculated time
+        """
+        ctx = self.currentContext
+        debug and log.debug('_buildTime: [%s][%s][%s]',
+                            quantity, modifier, units)
+
+        if source is None:
+            source = time.localtime()
+
+        if quantity is None:
+            quantity = ''
+        else:
+            quantity = quantity.strip()
+
+        qty = self._quantityToReal(quantity)
+
+        if modifier in self.ptc.Modifiers:
+            qty = qty * self.ptc.Modifiers[modifier]
+
+            if units is None or units == '':
+                units = 'dy'
+
+        # plurals are handled by regex's (could be a bug tho)
+
+        (yr, mth, dy, hr, mn, sec, _, _, _) = source
+
+        start = datetime.datetime(yr, mth, dy, hr, mn, sec)
+        target = start
+        # realunit = next((key for key, values in self.ptc.units.items()
+        #                  if any(imap(units.__contains__, values))), None)
+        realunit = units
+        for key, values in self.ptc.units.items():
+            if units in values:
+                realunit = key
+                break
+
+        debug and log.debug('units %s --> realunit %s (qty=%s)',
+                            units, realunit, qty)
+
+        try:
+            if realunit in ('years', 'months'):
+                target = self.inc(start, **{realunit[:-1]: qty})
+            elif realunit in ('days', 'hours', 'minutes', 'seconds', 'weeks'):
+                delta = datetime.timedelta(**{realunit: qty})
+                target = start + delta
+        except OverflowError:
+            # OverflowError is raise when target.year larger than 9999
+            pass
+        else:
+            ctx.updateAccuracy(realunit)
+
+        return target.timetuple()
+
+    def parseDate(self, dateString, sourceTime=None):
+        """
+        Parse short-form date strings::
+
+            '05/28/2006' or '04.21'
+
+        @type  dateString: string
+        @param dateString: text to convert to a C{datetime}
+        @type  sourceTime:     struct_time
+        @param sourceTime:     C{struct_time} value to use as the base
+
+        @rtype:  struct_time
+        @return: calculated C{struct_time} value of dateString
+        """
+        if sourceTime is None:
+            yr, mth, dy, hr, mn, sec, wd, yd, isdst = time.localtime()
+        else:
+            yr, mth, dy, hr, mn, sec, wd, yd, isdst = sourceTime
+
+        # values pulled from regex's will be stored here and later
+        # assigned to mth, dy, yr based on information from the locale
+        # -1 is used as the marker value because we want zero values
+        # to be passed thru so they can be flagged as errors later
+        v1 = -1
+        v2 = -1
+        v3 = -1
+        accuracy = []
+
+        s = dateString
+        m = self.ptc.CRE_DATE2.search(s)
+        if m is not None:
+            index = m.start()
+            v1 = int(s[:index])
+            s = s[index + 1:]
+
+        m = self.ptc.CRE_DATE2.search(s)
+        if m is not None:
+            index = m.start()
+            v2 = int(s[:index])
+            v3 = int(s[index + 1:])
+        else:
+            v2 = int(s.strip())
+
+        v = [v1, v2, v3]
+        d = {'m': mth, 'd': dy, 'y': yr}
+
+        # yyyy/mm/dd format
+        dp_order = self.ptc.dp_order if v1 <= 31 else ['y', 'm', 'd']
+
+        for i in range(0, 3):
+            n = v[i]
+            c = dp_order[i]
+            if n >= 0:
+                d[c] = n
+                accuracy.append({'m': pdtContext.ACU_MONTH,
+                                 'd': pdtContext.ACU_DAY,
+                                 'y': pdtContext.ACU_YEAR}[c])
+
+        # if the year is not specified and the date has already
+        # passed, increment the year
+        if v3 == -1 and ((mth > d['m']) or (mth == d['m'] and dy > d['d'])):
+            yr = d['y'] + self.ptc.YearParseStyle
+        else:
+            yr = d['y']
+
+        mth = d['m']
+        dy = d['d']
+
+        # birthday epoch constraint
+        if yr < self.ptc.BirthdayEpoch:
+            yr += 2000
+        elif yr < 100:
+            yr += 1900
+
+        daysInCurrentMonth = self.ptc.daysInMonth(mth, yr)
+        debug and log.debug('parseDate: %s %s %s %s',
+                            yr, mth, dy, daysInCurrentMonth)
+
+        with self.context() as ctx:
+            if mth > 0 and mth <= 12 and dy > 0 and \
+                    dy <= daysInCurrentMonth:
+                sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
+                ctx.updateAccuracy(*accuracy)
+            else:
+                # return current time if date string is invalid
+                sourceTime = time.localtime()
+
+        return sourceTime
+
+    def parseDateText(self, dateString, sourceTime=None):
+        """
+        Parse long-form date strings::
+
+            'May 31st, 2006'
+            'Jan 1st'
+            'July 2006'
+
+        @type  dateString: string
+        @param dateString: text to convert to a datetime
+        @type  sourceTime:     struct_time
+        @param sourceTime:     C{struct_time} value to use as the base
+
+        @rtype:  struct_time
+        @return: calculated C{struct_time} value of dateString
+        """
+        if sourceTime is None:
+            yr, mth, dy, hr, mn, sec, wd, yd, isdst = time.localtime()
+        else:
+            yr, mth, dy, hr, mn, sec, wd, yd, isdst = sourceTime
+
+        currentMth = mth
+        currentDy = dy
+        accuracy = []
+
+        debug and log.debug('parseDateText currentMth %s currentDy %s',
+                            mth, dy)
+
+        s = dateString.lower()
+        m = self.ptc.CRE_DATE3.search(s)
+        mth = m.group('mthname')
+        mth = self.ptc.MonthOffsets[mth]
+        accuracy.append('month')
+
+        if m.group('day') is not None:
+            dy = int(m.group('day'))
+            accuracy.append('day')
+        else:
+            dy = 1
+
+        if m.group('year') is not None:
+            yr = int(m.group('year'))
+            accuracy.append('year')
+
+            # birthday epoch constraint
+            if yr < self.ptc.BirthdayEpoch:
+                yr += 2000
+            elif yr < 100:
+                yr += 1900
+
+        elif (mth < currentMth) or (mth == currentMth and dy < currentDy):
+            # if that day and month have already passed in this year,
+            # then increment the year by 1
+            yr += self.ptc.YearParseStyle
+
+        with self.context() as ctx:
+            if dy > 0 and dy <= self.ptc.daysInMonth(mth, yr):
+                sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
+                ctx.updateAccuracy(*accuracy)
+            else:
+                # Return current time if date string is invalid
+                sourceTime = time.localtime()
+
+        debug and log.debug('parseDateText returned '
+                            'mth %d dy %d yr %d sourceTime %s',
+                            mth, dy, yr, sourceTime)
+
+        return sourceTime
+
+    def evalRanges(self, datetimeString, sourceTime=None):
+        """
+        Evaluate the C{datetimeString} text and determine if
+        it represents a date or time range.
+
+        @type  datetimeString: string
+        @param datetimeString: datetime text to evaluate
+        @type  sourceTime:     struct_time
+        @param sourceTime:     C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of: start datetime, end datetime and the invalid flag
+        """
+        rangeFlag = retFlag = 0
+        startStr = endStr = ''
+
+        s = datetimeString.strip().lower()
+
+        if self.ptc.rangeSep in s:
+            s = s.replace(self.ptc.rangeSep, ' %s ' % self.ptc.rangeSep)
+            s = s.replace('  ', ' ')
+
+        for cre, rflag in [(self.ptc.CRE_TIMERNG1, 1),
+                           (self.ptc.CRE_TIMERNG2, 2),
+                           (self.ptc.CRE_TIMERNG4, 7),
+                           (self.ptc.CRE_TIMERNG3, 3),
+                           (self.ptc.CRE_DATERNG1, 4),
+                           (self.ptc.CRE_DATERNG2, 5),
+                           (self.ptc.CRE_DATERNG3, 6)]:
+            m = cre.search(s)
+            if m is not None:
+                rangeFlag = rflag
+                break
+
+        debug and log.debug('evalRanges: rangeFlag = %s [%s]', rangeFlag, s)
+
+        if m is not None:
+            if (m.group() != s):
+                # capture remaining string
+                parseStr = m.group()
+                chunk1 = s[:m.start()]
+                chunk2 = s[m.end():]
+                s = '%s %s' % (chunk1, chunk2)
+
+                sourceTime, ctx = self.parse(s, sourceTime,
+                                             VERSION_CONTEXT_STYLE)
+
+                if not ctx.hasDateOrTime:
+                    sourceTime = None
+            else:
+                parseStr = s
+
+        if rangeFlag in (1, 2):
+            m = re.search(self.ptc.rangeSep, parseStr)
+            startStr = parseStr[:m.start()]
+            endStr = parseStr[m.start() + 1:]
+            retFlag = 2
+
+        elif rangeFlag in (3, 7):
+            m = re.search(self.ptc.rangeSep, parseStr)
+            # capturing the meridian from the end time
+            if self.ptc.usesMeridian:
+                ampm = re.search(self.ptc.am[0], parseStr)
+
+                # appending the meridian to the start time
+                if ampm is not None:
+                    startStr = parseStr[:m.start()] + self.ptc.meridian[0]
+                else:
+                    startStr = parseStr[:m.start()] + self.ptc.meridian[1]
+            else:
+                startStr = parseStr[:m.start()]
+
+            endStr = parseStr[m.start() + 1:]
+            retFlag = 2
+
+        elif rangeFlag == 4:
+            m = re.search(self.ptc.rangeSep, parseStr)
+            startStr = parseStr[:m.start()]
+            endStr = parseStr[m.start() + 1:]
+            retFlag = 1
+
+        elif rangeFlag == 5:
+            m = re.search(self.ptc.rangeSep, parseStr)
+            endStr = parseStr[m.start() + 1:]
+
+            # capturing the year from the end date
+            date = self.ptc.CRE_DATE3.search(endStr)
+            endYear = date.group('year')
+
+            # appending the year to the start date if the start date
+            # does not have year information and the end date does.
+            # eg : "Aug 21 - Sep 4, 2007"
+            if endYear is not None:
+                startStr = (parseStr[:m.start()]).strip()
+                date = self.ptc.CRE_DATE3.search(startStr)
+                startYear = date.group('year')
+
+                if startYear is None:
+                    startStr = startStr + ', ' + endYear
+            else:
+                startStr = parseStr[:m.start()]
+
+            retFlag = 1
+
+        elif rangeFlag == 6:
+            m = re.search(self.ptc.rangeSep, parseStr)
+
+            startStr = parseStr[:m.start()]
+
+            # capturing the month from the start date
+            mth = self.ptc.CRE_DATE3.search(startStr)
+            mth = mth.group('mthname')
+
+            # appending the month name to the end date
+            endStr = mth + parseStr[(m.start() + 1):]
+
+            retFlag = 1
+
+        else:
+            # if range is not found
+            startDT = endDT = time.localtime()
+
+        if retFlag:
+            startDT, sctx = self.parse(startStr, sourceTime,
+                                       VERSION_CONTEXT_STYLE)
+            endDT, ectx = self.parse(endStr, sourceTime,
+                                     VERSION_CONTEXT_STYLE)
+
+            if not sctx.hasDateOrTime or not ectx.hasDateOrTime:
+                retFlag = 0
+
+        return startDT, endDT, retFlag
+
+    def _CalculateDOWDelta(self, wd, wkdy, offset, style, currentDayStyle):
+        """
+        Based on the C{style} and C{currentDayStyle} determine what
+        day-of-week value is to be returned.
+
+        @type  wd:              integer
+        @param wd:              day-of-week value for the current day
+        @type  wkdy:            integer
+        @param wkdy:            day-of-week value for the parsed day
+        @type  offset:          integer
+        @param offset:          offset direction for any modifiers (-1, 0, 1)
+        @type  style:           integer
+        @param style:           normally the value
+                                set in C{Constants.DOWParseStyle}
+        @type  currentDayStyle: integer
+        @param currentDayStyle: normally the value
+                                set in C{Constants.CurrentDOWParseStyle}
+
+        @rtype:  integer
+        @return: calculated day-of-week
+        """
+        diffBase = wkdy - wd
+        origOffset = offset
+
+        if offset == 2:
+            # no modifier is present.
+            # i.e. string to be parsed is just DOW
+            if wkdy * style > wd * style or \
+                    currentDayStyle and wkdy == wd:
+                # wkdy located in current week
+                offset = 0
+            elif style in (-1, 1):
+                # wkdy located in last (-1) or next (1) week
+                offset = style
+            else:
+                # invalid style, or should raise error?
+                offset = 0
+
+        # offset = -1 means last week
+        # offset = 0 means current week
+        # offset = 1 means next week
+        diff = diffBase + 7 * offset
+        if style == 1 and diff < -7:
+            diff += 7
+        elif style == -1 and diff > 7:
+            diff -= 7
+
+        debug and log.debug("wd %s, wkdy %s, offset %d, "
+                            "style %d, currentDayStyle %d",
+                            wd, wkdy, origOffset, style, currentDayStyle)
+
+        return diff
+
+    def _quantityToReal(self, quantity):
+        """
+        Convert a quantity, either spelled-out or numeric, to a float
+
+        @type    quantity: string
+        @param   quantity: quantity to parse to float
+        @rtype:  int
+        @return: the quantity as an float, defaulting to 0.0
+        """
+        if not quantity:
+            return 1.0
+
+        try:
+            return float(quantity.replace(',', '.'))
+        except ValueError:
+            pass
+
+        try:
+            return float(self.ptc.numbers[quantity])
+        except KeyError:
+            pass
+
+        return 0.0
+
+    def _evalModifier(self, modifier, chunk1, chunk2, sourceTime):
+        """
+        Evaluate the C{modifier} string and following text (passed in
+        as C{chunk1} and C{chunk2}) and if they match any known modifiers
+        calculate the delta and apply it to C{sourceTime}.
+
+        @type  modifier:   string
+        @param modifier:   modifier text to apply to sourceTime
+        @type  chunk1:     string
+        @param chunk1:     text chunk that preceded modifier (if any)
+        @type  chunk2:     string
+        @param chunk2:     text chunk that followed modifier (if any)
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of: remaining text and the modified sourceTime
+        """
+        ctx = self.currentContext
+        offset = self.ptc.Modifiers[modifier]
+
+        if sourceTime is not None:
+            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime
+        else:
+            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = time.localtime()
+
+        if self.ptc.StartTimeFromSourceTime:
+            startHour = hr
+            startMinute = mn
+            startSecond = sec
+        else:
+            startHour = 9
+            startMinute = 0
+            startSecond = 0
+
+        # capture the units after the modifier and the remaining
+        # string after the unit
+        m = self.ptc.CRE_REMAINING.search(chunk2)
+        if m is not None:
+            index = m.start() + 1
+            unit = chunk2[:m.start()]
+            chunk2 = chunk2[index:]
+        else:
+            unit = chunk2
+            chunk2 = ''
+
+        debug and log.debug("modifier [%s] chunk1 [%s] "
+                            "chunk2 [%s] unit [%s]",
+                            modifier, chunk1, chunk2, unit)
+
+        if unit in self.ptc.units['months']:
+            currentDaysInMonth = self.ptc.daysInMonth(mth, yr)
+            if offset == 0:
+                dy = currentDaysInMonth
+                sourceTime = (yr, mth, dy, startHour, startMinute,
+                              startSecond, wd, yd, isdst)
+            elif offset == 2:
+                # if day is the last day of the month, calculate the last day
+                # of the next month
+                if dy == currentDaysInMonth:
+                    dy = self.ptc.daysInMonth(mth + 1, yr)
+
+                start = datetime.datetime(yr, mth, dy, startHour,
+                                          startMinute, startSecond)
+                target = self.inc(start, month=1)
+                sourceTime = target.timetuple()
+            else:
+                start = datetime.datetime(yr, mth, 1, startHour,
+                                          startMinute, startSecond)
+                target = self.inc(start, month=offset)
+                sourceTime = target.timetuple()
+            ctx.updateAccuracy(ctx.ACU_MONTH)
+
+        elif unit in self.ptc.units['weeks']:
+            if offset == 0:
+                start = datetime.datetime(yr, mth, dy, 17, 0, 0)
+                target = start + datetime.timedelta(days=(4 - wd))
+                sourceTime = target.timetuple()
+            elif offset == 2:
+                start = datetime.datetime(yr, mth, dy, startHour,
+                                          startMinute, startSecond)
+                target = start + datetime.timedelta(days=7)
+                sourceTime = target.timetuple()
+            else:
+                start = datetime.datetime(yr, mth, dy, startHour,
+                                          startMinute, startSecond)
+                target = start + offset * datetime.timedelta(weeks=1)
+                sourceTime = target.timetuple()
+            ctx.updateAccuracy(ctx.ACU_WEEK)
+
+        elif unit in self.ptc.units['days']:
+            if offset == 0:
+                sourceTime = (yr, mth, dy, 17, 0, 0, wd, yd, isdst)
+                ctx.updateAccuracy(ctx.ACU_HALFDAY)
+            elif offset == 2:
+                start = datetime.datetime(yr, mth, dy, hr, mn, sec)
+                target = start + datetime.timedelta(days=1)
+                sourceTime = target.timetuple()
+            else:
+                start = datetime.datetime(yr, mth, dy, startHour,
+                                          startMinute, startSecond)
+                target = start + datetime.timedelta(days=offset)
+                sourceTime = target.timetuple()
+            ctx.updateAccuracy(ctx.ACU_DAY)
+
+        elif unit in self.ptc.units['hours']:
+            if offset == 0:
+                sourceTime = (yr, mth, dy, hr, 0, 0, wd, yd, isdst)
+            else:
+                start = datetime.datetime(yr, mth, dy, hr, 0, 0)
+                target = start + datetime.timedelta(hours=offset)
+                sourceTime = target.timetuple()
+            ctx.updateAccuracy(ctx.ACU_HOUR)
+
+        elif unit in self.ptc.units['years']:
+            if offset == 0:
+                sourceTime = (yr, 12, 31, hr, mn, sec, wd, yd, isdst)
+            elif offset == 2:
+                sourceTime = (yr + 1, mth, dy, hr, mn, sec, wd, yd, isdst)
+            else:
+                sourceTime = (yr + offset, 1, 1, startHour, startMinute,
+                              startSecond, wd, yd, isdst)
+            ctx.updateAccuracy(ctx.ACU_YEAR)
+
+        elif modifier == 'eom':
+            dy = self.ptc.daysInMonth(mth, yr)
+            sourceTime = (yr, mth, dy, startHour, startMinute,
+                          startSecond, wd, yd, isdst)
+            ctx.updateAccuracy(ctx.ACU_DAY)
+
+        elif modifier == 'eoy':
+            mth = 12
+            dy = self.ptc.daysInMonth(mth, yr)
+            sourceTime = (yr, mth, dy, startHour, startMinute,
+                          startSecond, wd, yd, isdst)
+            ctx.updateAccuracy(ctx.ACU_MONTH)
+
+        elif self.ptc.CRE_WEEKDAY.match(unit):
+            m = self.ptc.CRE_WEEKDAY.match(unit)
+            debug and log.debug('CRE_WEEKDAY matched')
+            wkdy = m.group()
+
+            if modifier == 'eod':
+                ctx.updateAccuracy(ctx.ACU_HOUR)
+                # Calculate the upcoming weekday
+                sourceTime, subctx = self.parse(wkdy, sourceTime,
+                                                VERSION_CONTEXT_STYLE)
+                sTime = self.ptc.getSource(modifier, sourceTime)
+                if sTime is not None:
+                    sourceTime = sTime
+                    ctx.updateAccuracy(ctx.ACU_HALFDAY)
+            else:
+                # unless one of these modifiers is being applied to the
+                # day-of-week, we want to start with target as the day
+                # in the current week.
+                dowOffset = offset
+                if modifier not in ['next', 'last', 'prior', 'previous']:
+                    dowOffset = 0
+
+                wkdy = self.ptc.WeekdayOffsets[wkdy]
+                diff = self._CalculateDOWDelta(
+                    wd, wkdy, dowOffset, self.ptc.DOWParseStyle,
+                    self.ptc.CurrentDOWParseStyle)
+                start = datetime.datetime(yr, mth, dy, startHour,
+                                          startMinute, startSecond)
+                target = start + datetime.timedelta(days=diff)
+
+                if chunk1 != '':
+                    # consider "one day before thursday": we need to parse chunk1 ("one day")
+                    # and apply according to the offset ("before"), rather than allowing the
+                    # remaining parse step to apply "one day" without the offset direction.
+                    t, subctx = self.parse(chunk1, sourceTime, VERSION_CONTEXT_STYLE)
+                    if subctx.hasDateOrTime:
+                        delta = time.mktime(t) - time.mktime(sourceTime)
+                        target = start + datetime.timedelta(days=diff) + datetime.timedelta(seconds=delta * offset)
+                        chunk1 = ''
+
+                sourceTime = target.timetuple()
+            ctx.updateAccuracy(ctx.ACU_DAY)
+
+        elif self.ptc.CRE_TIME.match(unit):
+            m = self.ptc.CRE_TIME.match(unit)
+            debug and log.debug('CRE_TIME matched')
+            (yr, mth, dy, hr, mn, sec, wd, yd, isdst), subctx = \
+                self.parse(unit, None, VERSION_CONTEXT_STYLE)
+
+            start = datetime.datetime(yr, mth, dy, hr, mn, sec)
+            target = start + datetime.timedelta(days=offset)
+            sourceTime = target.timetuple()
+
+        else:
+            # check if the remaining text is parsable and if so,
+            # use it as the base time for the modifier source time
+
+            debug and log.debug('check for modifications '
+                                'to source time [%s] [%s]',
+                                chunk1, unit)
+
+            unit = unit.strip()
+            if unit:
+                s = '%s %s' % (unit, chunk2)
+                t, subctx = self.parse(s, sourceTime, VERSION_CONTEXT_STYLE)
+
+                if subctx.hasDate:  # working with dates
+                    u = unit.lower()
+                    if u in self.ptc.Months or \
+                            u in self.ptc.shortMonths:
+                        yr, mth, dy, hr, mn, sec, wd, yd, isdst = t
+                        start = datetime.datetime(
+                            yr, mth, dy, hr, mn, sec)
+                        t = self.inc(start, year=offset).timetuple()
+                    elif u in self.ptc.Weekdays:
+                        t = t + datetime.timedelta(weeks=offset)
+
+                if subctx.hasDateOrTime:
+                    sourceTime = t
+                    chunk2 = ''
+
+            chunk1 = chunk1.strip()
+
+            # if the word after next is a number, the string is more than
+            # likely to be "next 4 hrs" which we will have to combine the
+            # units with the rest of the string
+            if chunk1:
+                try:
+                    m = list(self.ptc.CRE_NUMBER.finditer(chunk1))[-1]
+                except IndexError:
+                    pass
+                else:
+                    qty = None
+                    debug and log.debug('CRE_NUMBER matched')
+                    qty = self._quantityToReal(m.group()) * offset
+                    chunk1 = '%s%s%s' % (chunk1[:m.start()],
+                                         qty, chunk1[m.end():])
+                t, subctx = self.parse(chunk1, sourceTime,
+                                       VERSION_CONTEXT_STYLE)
+
+                chunk1 = ''
+
+                if subctx.hasDateOrTime:
+                    sourceTime = t
+
+            debug and log.debug('looking for modifier %s', modifier)
+            sTime = self.ptc.getSource(modifier, sourceTime)
+            if sTime is not None:
+                debug and log.debug('modifier found in sources')
+                sourceTime = sTime
+                ctx.updateAccuracy(ctx.ACU_HALFDAY)
+
+        debug and log.debug('returning chunk = "%s %s" and sourceTime = %s',
+                            chunk1, chunk2, sourceTime)
+
+        return '%s %s' % (chunk1, chunk2), sourceTime
+
+    def _evalDT(self, datetimeString, sourceTime):
+        """
+        Calculate the datetime from known format like RFC822 or W3CDTF
+
+        Examples handled::
+            RFC822, W3CDTF formatted dates
+            HH:MM[:SS][ am/pm]
+            MM/DD/YYYY
+            DD MMMM YYYY
+
+        @type  datetimeString: string
+        @param datetimeString: text to try and parse as more "traditional"
+                               date/time text
+        @type  sourceTime:     struct_time
+        @param sourceTime:     C{struct_time} value to use as the base
+
+        @rtype:  datetime
+        @return: calculated C{struct_time} value or current C{struct_time}
+                 if not parsed
+        """
+        ctx = self.currentContext
+        s = datetimeString.strip()
+
+        # Given string date is a RFC822 date
+        if sourceTime is None:
+            sourceTime = _parse_date_rfc822(s)
+            debug and log.debug(
+                'attempt to parse as rfc822 - %s', str(sourceTime))
+
+            if sourceTime is not None:
+                (yr, mth, dy, hr, mn, sec, wd, yd, isdst, _) = sourceTime
+                ctx.updateAccuracy(ctx.ACU_YEAR, ctx.ACU_MONTH, ctx.ACU_DAY)
+
+                if hr != 0 and mn != 0 and sec != 0:
+                    ctx.updateAccuracy(ctx.ACU_HOUR, ctx.ACU_MIN, ctx.ACU_SEC)
+
+                sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
+
+        # Given string date is a W3CDTF date
+        if sourceTime is None:
+            sourceTime = _parse_date_w3dtf(s)
+
+            if sourceTime is not None:
+                ctx.updateAccuracy(ctx.ACU_YEAR, ctx.ACU_MONTH, ctx.ACU_DAY,
+                                   ctx.ACU_HOUR, ctx.ACU_MIN, ctx.ACU_SEC)
+
+        if sourceTime is None:
+            sourceTime = time.localtime()
+
+        return sourceTime
+
+    def _evalUnits(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseUnits()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        # Given string is a time string with units like "5 hrs 30 min"
+        modifier = ''  # TODO
+
+        m = self.ptc.CRE_UNITS.search(s)
+        if m is not None:
+            units = m.group('units')
+            quantity = s[:m.start('units')]
+
+        sourceTime = self._buildTime(sourceTime, quantity, modifier, units)
+        return sourceTime
+
+    def _evalQUnits(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseQUnits()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        # Given string is a time string with single char units like "5 h 30 m"
+        modifier = ''  # TODO
+
+        m = self.ptc.CRE_QUNITS.search(s)
+        if m is not None:
+            units = m.group('qunits')
+            quantity = s[:m.start('qunits')]
+
+        sourceTime = self._buildTime(sourceTime, quantity, modifier, units)
+        return sourceTime
+
+    def _evalDateStr(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseDateStr()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        # Given string is in the format  "May 23rd, 2005"
+        debug and log.debug('checking for MMM DD YYYY')
+        return self.parseDateText(s, sourceTime)
+
+    def _evalDateStd(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseDateStd()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        # Given string is in the format 07/21/2006
+        return self.parseDate(s, sourceTime)
+
+    def _evalDayStr(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseDaystr()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        # Given string is a natural language date string like today, tomorrow..
+        (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime
+
+        try:
+            offset = self.ptc.dayOffsets[s]
+        except KeyError:
+            offset = 0
+
+        if self.ptc.StartTimeFromSourceTime:
+            startHour = hr
+            startMinute = mn
+            startSecond = sec
+        else:
+            startHour = 9
+            startMinute = 0
+            startSecond = 0
+
+        self.currentContext.updateAccuracy(pdtContext.ACU_DAY)
+        start = datetime.datetime(yr, mth, dy, startHour,
+                                  startMinute, startSecond)
+        target = start + datetime.timedelta(days=offset)
+        return target.timetuple()
+
+    def _evalWeekday(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseWeekday()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        # Given string is a weekday
+        yr, mth, dy, hr, mn, sec, wd, yd, isdst = sourceTime
+
+        start = datetime.datetime(yr, mth, dy, hr, mn, sec)
+        wkdy = self.ptc.WeekdayOffsets[s]
+
+        if wkdy > wd:
+            qty = self._CalculateDOWDelta(wd, wkdy, 2,
+                                          self.ptc.DOWParseStyle,
+                                          self.ptc.CurrentDOWParseStyle)
+        else:
+            qty = self._CalculateDOWDelta(wd, wkdy, 2,
+                                          self.ptc.DOWParseStyle,
+                                          self.ptc.CurrentDOWParseStyle)
+
+        self.currentContext.updateAccuracy(pdtContext.ACU_DAY)
+        target = start + datetime.timedelta(days=qty)
+        return target.timetuple()
+
+    def _evalTimeStr(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseTimeStr()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        if s in self.ptc.re_values['now']:
+            self.currentContext.updateAccuracy(pdtContext.ACU_NOW)
+        else:
+            # Given string is a natural language time string like
+            # lunch, midnight, etc
+            sTime = self.ptc.getSource(s, sourceTime)
+            if sTime:
+                sourceTime = sTime
+            self.currentContext.updateAccuracy(pdtContext.ACU_HALFDAY)
+
+        return sourceTime
+
+    def _evalMeridian(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseMeridian()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        # Given string is in the format HH:MM(:SS)(am/pm)
+        yr, mth, dy, hr, mn, sec, wd, yd, isdst = sourceTime
+
+        m = self.ptc.CRE_TIMEHMS2.search(s)
+        if m is not None:
+            dt = s[:m.start('meridian')].strip()
+            if len(dt) <= 2:
+                hr = int(dt)
+                mn = 0
+                sec = 0
+            else:
+                hr, mn, sec = _extract_time(m)
+
+            if hr == 24:
+                hr = 0
+
+            meridian = m.group('meridian').lower()
+
+            # if 'am' found and hour is 12 - force hour to 0 (midnight)
+            if (meridian in self.ptc.am) and hr == 12:
+                hr = 0
+
+            # if 'pm' found and hour < 12, add 12 to shift to evening
+            if (meridian in self.ptc.pm) and hr < 12:
+                hr += 12
+
+        # time validation
+        if hr < 24 and mn < 60 and sec < 60:
+            sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
+            _pop_time_accuracy(m, self.currentContext)
+
+        return sourceTime
+
+    def _evalTimeStd(self, datetimeString, sourceTime):
+        """
+        Evaluate text passed by L{_partialParseTimeStd()}
+        """
+        s = datetimeString.strip()
+        sourceTime = self._evalDT(datetimeString, sourceTime)
+
+        # Given string is in the format HH:MM(:SS)
+        yr, mth, dy, hr, mn, sec, wd, yd, isdst = sourceTime
+
+        m = self.ptc.CRE_TIMEHMS.search(s)
+        if m is not None:
+            hr, mn, sec = _extract_time(m)
+        if hr == 24:
+            hr = 0
+
+        # time validation
+        if hr < 24 and mn < 60 and sec < 60:
+            sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
+            _pop_time_accuracy(m, self.currentContext)
+
+        return sourceTime
+
+    def _UnitsTrapped(self, s, m, key):
+        # check if a day suffix got trapped by a unit match
+        # for example Dec 31st would match for 31s (aka 31 seconds)
+        # Dec 31st
+        #     ^ ^
+        #     | +-- m.start('units')
+        #     |     and also m2.start('suffix')
+        #     +---- m.start('qty')
+        #           and also m2.start('day')
+        m2 = self.ptc.CRE_DAY2.search(s)
+        if m2 is not None:
+            t = '%s%s' % (m2.group('day'), m.group(key))
+            if m.start(key) == m2.start('suffix') and \
+                    m.start('qty') == m2.start('day') and \
+                    m.group('qty') == t:
+                return True
+            else:
+                return False
+        else:
+            return False
+
+    def _partialParseModifier(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_MODIFIER, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # Modifier like next/prev/from/after/prior..
+        m = self.ptc.CRE_MODIFIER.search(s)
+        if m is not None:
+            if m.group() != s:
+                # capture remaining string
+                parseStr = m.group()
+                chunk1 = s[:m.start()].strip()
+                chunk2 = s[m.end():].strip()
+            else:
+                parseStr = s
+
+        if parseStr:
+            debug and log.debug('found (modifier) [%s][%s][%s]',
+                                parseStr, chunk1, chunk2)
+            s, sourceTime = self._evalModifier(parseStr, chunk1,
+                                               chunk2, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseUnits(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_UNITS, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # Quantity + Units
+        m = self.ptc.CRE_UNITS.search(s)
+        if m is not None:
+            debug and log.debug('CRE_UNITS matched')
+            if self._UnitsTrapped(s, m, 'units'):
+                debug and log.debug('day suffix trapped by unit match')
+            else:
+                if (m.group('qty') != s):
+                    # capture remaining string
+                    parseStr = m.group('qty')
+                    chunk1 = s[:m.start('qty')].strip()
+                    chunk2 = s[m.end('qty'):].strip()
+
+                    if chunk1[-1:] == '-':
+                        parseStr = '-%s' % parseStr
+                        chunk1 = chunk1[:-1]
+
+                    s = '%s %s' % (chunk1, chunk2)
+                else:
+                    parseStr = s
+                    s = ''
+
+        if parseStr:
+            debug and log.debug('found (units) [%s][%s][%s]',
+                                parseStr, chunk1, chunk2)
+            sourceTime = self._evalUnits(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseQUnits(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_QUNITS, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # Quantity + Units
+        m = self.ptc.CRE_QUNITS.search(s)
+        if m is not None:
+            debug and log.debug('CRE_QUNITS matched')
+            if self._UnitsTrapped(s, m, 'qunits'):
+                debug and log.debug(
+                    'day suffix trapped by qunit match')
+            else:
+                if (m.group('qty') != s):
+                    # capture remaining string
+                    parseStr = m.group('qty')
+                    chunk1 = s[:m.start('qty')].strip()
+                    chunk2 = s[m.end('qty'):].strip()
+
+                    if chunk1[-1:] == '-':
+                        parseStr = '-%s' % parseStr
+                        chunk1 = chunk1[:-1]
+
+                    s = '%s %s' % (chunk1, chunk2)
+                else:
+                    parseStr = s
+                    s = ''
+
+        if parseStr:
+            debug and log.debug('found (qunits) [%s][%s][%s]',
+                                parseStr, chunk1, chunk2)
+            sourceTime = self._evalQUnits(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseDateStr(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_DATE3, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        m = self.ptc.CRE_DATE3.search(s)
+        # NO LONGER NEEDED, THE REGEXP HANDLED MTHNAME NOW
+        # for match in self.ptc.CRE_DATE3.finditer(s):
+        # to prevent "HH:MM(:SS) time strings" expressions from
+        # triggering this regex, we checks if the month field
+        # exists in the searched expression, if it doesn't exist,
+        # the date field is not valid
+        #     if match.group('mthname'):
+        #         m = self.ptc.CRE_DATE3.search(s, match.start())
+        #         valid_date = True
+        #         break
+
+        # String date format
+        if m is not None:
+
+            if (m.group('date') != s):
+                # capture remaining string
+                mStart = m.start('date')
+                mEnd = m.end('date')
+
+                # we need to check that anything following the parsed
+                # date is a time expression because it is often picked
+                # up as a valid year if the hour is 2 digits
+                fTime = False
+                mm = self.ptc.CRE_TIMEHMS2.search(s)
+                # "February 24th 1PM" doesn't get caught
+                # "February 24th 12PM" does
+                mYear = m.group('year')
+                if mm is not None and mYear is not None:
+                    fTime = True
+                else:
+                    # "February 24th 12:00"
+                    mm = self.ptc.CRE_TIMEHMS.search(s)
+                    if mm is not None and mYear is None:
+                        fTime = True
+                if fTime:
+                    hoursStart = mm.start('hours')
+
+                    if hoursStart < m.end('year'):
+                        mEnd = hoursStart
+
+                parseStr = s[mStart:mEnd]
+                chunk1 = s[:mStart]
+                chunk2 = s[mEnd:]
+
+                s = '%s %s' % (chunk1, chunk2)
+            else:
+                parseStr = s
+                s = ''
+
+        if parseStr:
+            debug and log.debug(
+                'found (date3) [%s][%s][%s]', parseStr, chunk1, chunk2)
+            sourceTime = self._evalDateStr(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseDateStd(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_DATE, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # Standard date format
+        m = self.ptc.CRE_DATE.search(s)
+        if m is not None:
+
+            if (m.group('date') != s):
+                # capture remaining string
+                parseStr = m.group('date')
+                chunk1 = s[:m.start('date')]
+                chunk2 = s[m.end('date'):]
+                s = '%s %s' % (chunk1, chunk2)
+            else:
+                parseStr = s
+                s = ''
+
+        if parseStr:
+            debug and log.debug(
+                'found (date) [%s][%s][%s]', parseStr, chunk1, chunk2)
+            sourceTime = self._evalDateStd(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseDayStr(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_DAY, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # Natural language day strings
+        m = self.ptc.CRE_DAY.search(s)
+        if m is not None:
+
+            if (m.group() != s):
+                # capture remaining string
+                parseStr = m.group()
+                chunk1 = s[:m.start()]
+                chunk2 = s[m.end():]
+                s = '%s %s' % (chunk1, chunk2)
+            else:
+                parseStr = s
+                s = ''
+
+        if parseStr:
+            debug and log.debug(
+                'found (day) [%s][%s][%s]', parseStr, chunk1, chunk2)
+            sourceTime = self._evalDayStr(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseWeekday(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_WEEKDAY, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # Weekday
+        m = self.ptc.CRE_WEEKDAY.search(s)
+        if m is not None:
+            gv = m.group()
+            if s not in self.ptc.dayOffsets:
+
+                if (gv != s):
+                    # capture remaining string
+                    parseStr = gv
+                    chunk1 = s[:m.start()]
+                    chunk2 = s[m.end():]
+                    s = '%s %s' % (chunk1, chunk2)
+                else:
+                    parseStr = s
+                    s = ''
+
+        if parseStr:
+            debug and log.debug(
+                'found (weekday) [%s][%s][%s]', parseStr, chunk1, chunk2)
+            sourceTime = self._evalWeekday(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseTimeStr(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_TIME, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # Natural language time strings
+        m = self.ptc.CRE_TIME.search(s)
+        if m is not None or s in self.ptc.re_values['now']:
+
+            if (m and m.group() != s):
+                # capture remaining string
+                parseStr = m.group()
+                chunk1 = s[:m.start()]
+                chunk2 = s[m.end():]
+                s = '%s %s' % (chunk1, chunk2)
+            else:
+                parseStr = s
+                s = ''
+
+        if parseStr:
+            debug and log.debug(
+                'found (time) [%s][%s][%s]', parseStr, chunk1, chunk2)
+            sourceTime = self._evalTimeStr(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseMeridian(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_TIMEHMS2, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # HH:MM(:SS) am/pm time strings
+        m = self.ptc.CRE_TIMEHMS2.search(s)
+        if m is not None:
+
+            if m.group('minutes') is not None:
+                if m.group('seconds') is not None:
+                    parseStr = '%s:%s:%s' % (m.group('hours'),
+                                             m.group('minutes'),
+                                             m.group('seconds'))
+                else:
+                    parseStr = '%s:%s' % (m.group('hours'),
+                                          m.group('minutes'))
+            else:
+                parseStr = m.group('hours')
+            parseStr += ' ' + m.group('meridian')
+
+            chunk1 = s[:m.start()]
+            chunk2 = s[m.end():]
+
+            s = '%s %s' % (chunk1, chunk2)
+
+        if parseStr:
+            debug and log.debug('found (meridian) [%s][%s][%s]',
+                                parseStr, chunk1, chunk2)
+            sourceTime = self._evalMeridian(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def _partialParseTimeStd(self, s, sourceTime):
+        """
+        test if giving C{s} matched CRE_TIMEHMS, used by L{parse()}
+
+        @type  s:          string
+        @param s:          date/time text to evaluate
+        @type  sourceTime: struct_time
+        @param sourceTime: C{struct_time} value to use as the base
+
+        @rtype:  tuple
+        @return: tuple of remained date/time text, datetime object and
+                 an boolean value to describ if matched or not
+
+        """
+        parseStr = None
+        chunk1 = chunk2 = ''
+
+        # HH:MM(:SS) time strings
+        m = self.ptc.CRE_TIMEHMS.search(s)
+        if m is not None:
+
+            if m.group('seconds') is not None:
+                parseStr = '%s:%s:%s' % (m.group('hours'),
+                                         m.group('minutes'),
+                                         m.group('seconds'))
+                chunk1 = s[:m.start('hours')]
+                chunk2 = s[m.end('seconds'):]
+            else:
+                parseStr = '%s:%s' % (m.group('hours'),
+                                      m.group('minutes'))
+                chunk1 = s[:m.start('hours')]
+                chunk2 = s[m.end('minutes'):]
+
+            s = '%s %s' % (chunk1, chunk2)
+
+        if parseStr:
+            debug and log.debug(
+                'found (hms) [%s][%s][%s]', parseStr, chunk1, chunk2)
+            sourceTime = self._evalTimeStd(parseStr, sourceTime)
+
+        return s, sourceTime, bool(parseStr)
+
+    def parseDT(self, datetimeString, sourceTime=None,
+                tzinfo=None, version=None):
+        """
+        C{datetimeString} is as C{.parse}, C{sourceTime} has the same semantic
+        meaning as C{.parse}, but now also accepts datetime objects.  C{tzinfo}
+        accepts a tzinfo object.  It is advisable to use pytz.
+
+
+        @type  datetimeString: string
+        @param datetimeString: date/time text to evaluate
+        @type  sourceTime:     struct_time, datetime, date, time
+        @param sourceTime:     time value to use as the base
+        @type  tzinfo:         tzinfo
+        @param tzinfo:         Timezone to apply to generated datetime objs.
+        @type  version:        integer
+        @param version:        style version, default will use L{Calendar}
+                               parameter version value
+
+        @rtype:  tuple
+        @return: tuple of: modified C{sourceTime} and the result flag/context
+
+        see .parse for return code details.
+        """
+        # if sourceTime has a timetuple method, use thet, else, just pass the
+        # entire thing to parse and prey the user knows what the hell they are
+        # doing.
+        sourceTime = getattr(sourceTime, 'timetuple', (lambda: sourceTime))()
+        # You REALLY SHOULD be using pytz.  Using localize if available,
+        # hacking if not.  Note, None is a valid tzinfo object in the case of
+        # the ugly hack.
+        localize = getattr(
+            tzinfo,
+            'localize',
+            (lambda dt: dt.replace(tzinfo=tzinfo)),  # ugly hack is ugly :(
+        )
+
+        # Punt
+        time_struct, ret_code = self.parse(
+            datetimeString,
+            sourceTime=sourceTime,
+            version=version)
+
+        # Comments from GHI indicate that it is desired to have the same return
+        # signature on this method as that one it punts to, with the exception
+        # of using datetime objects instead of time_structs.
+        dt = localize(datetime.datetime(*time_struct[:6]))
+        return dt, ret_code
+
+    def parse(self, datetimeString, sourceTime=None, version=None):
+        """
+        Splits the given C{datetimeString} into tokens, finds the regex
+        patterns that match and then calculates a C{struct_time} value from
+        the chunks.
+
+        If C{sourceTime} is given then the C{struct_time} value will be
+        calculated from that value, otherwise from the current date/time.
+
+        If the C{datetimeString} is parsed and date/time value found, then::
+
+            If C{version} equals to L{VERSION_FLAG_STYLE}, the second item of
+            the returned tuple will be a flag to let you know what kind of
+            C{struct_time} value is being returned::
+
+                0 = not parsed at all
+                1 = parsed as a C{date}
+                2 = parsed as a C{time}
+                3 = parsed as a C{datetime}
+
+            If C{version} equals to L{VERSION_CONTEXT_STYLE}, the second value
+            will be an instance of L{pdtContext}
+
+        @type  datetimeString: string
+        @param datetimeString: date/time text to evaluate
+        @type  sourceTime:     struct_time
+        @param sourceTime:     C{struct_time} value to use as the base
+        @type  version:        integer
+        @param version:        style version, default will use L{Calendar}
+                               parameter version value
+
+        @rtype:  tuple
+        @return: tuple of: modified C{sourceTime} and the result flag/context
+        """
+        debug and log.debug('parse()')
+
+        datetimeString = re.sub(r'(\w)\.(\s)', r'\1\2', datetimeString)
+        datetimeString = re.sub(r'(\w)[\'"](\s|$)', r'\1 \2', datetimeString)
+        datetimeString = re.sub(r'(\s|^)[\'"](\w)', r'\1 \2', datetimeString)
+
+        if sourceTime:
+            if isinstance(sourceTime, datetime.datetime):
+                debug and log.debug('coercing datetime to timetuple')
+                sourceTime = sourceTime.timetuple()
+            else:
+                if not isinstance(sourceTime, time.struct_time) and \
+                        not isinstance(sourceTime, tuple):
+                    raise ValueError('sourceTime is not a struct_time')
+        else:
+            sourceTime = time.localtime()
+
+        with self.context() as ctx:
+            s = datetimeString.lower().strip()
+            debug and log.debug('remainedString (before parsing): [%s]', s)
+
+            while s:
+                for parseMeth in (self._partialParseModifier,
+                                  self._partialParseUnits,
+                                  self._partialParseQUnits,
+                                  self._partialParseDateStr,
+                                  self._partialParseDateStd,
+                                  self._partialParseDayStr,
+                                  self._partialParseWeekday,
+                                  self._partialParseTimeStr,
+                                  self._partialParseMeridian,
+                                  self._partialParseTimeStd):
+                    retS, retTime, matched = parseMeth(s, sourceTime)
+                    if matched:
+                        s, sourceTime = retS.strip(), retTime
+                        break
+                else:
+                    # nothing matched
+                    s = ''
+
+                debug and log.debug('hasDate: [%s], hasTime: [%s]',
+                                    ctx.hasDate, ctx.hasTime)
+                debug and log.debug('remainedString: [%s]', s)
+
+            # String is not parsed at all
+            if sourceTime is None:
+                debug and log.debug('not parsed [%s]', str(sourceTime))
+                sourceTime = time.localtime()
+
+        if not isinstance(sourceTime, time.struct_time):
+            sourceTime = time.struct_time(sourceTime)
+
+        version = self.version if version is None else version
+        if version == VERSION_CONTEXT_STYLE:
+            return sourceTime, ctx
+        else:
+            return sourceTime, ctx.dateTimeFlag
+
+    def inc(self, source, month=None, year=None):
+        """
+        Takes the given C{source} date, or current date if none is
+        passed, and increments it according to the values passed in
+        by month and/or year.
+
+        This routine is needed because Python's C{timedelta()} function
+        does not allow for month or year increments.
+
+        @type  source: struct_time
+        @param source: C{struct_time} value to increment
+        @type  month:  float or integer
+        @param month:  optional number of months to increment
+        @type  year:   float or integer
+        @param year:   optional number of years to increment
+
+        @rtype:  datetime
+        @return: C{source} incremented by the number of months and/or years
+        """
+        yr = source.year
+        mth = source.month
+        dy = source.day
+
+        try:
+            month = float(month)
+        except (TypeError, ValueError):
+            month = 0
+
+        try:
+            year = float(year)
+        except (TypeError, ValueError):
+            year = 0
+        finally:
+            month += year * 12
+            year = 0
+
+        subMi = 0.0
+        maxDay = 0
+        if month:
+            mi = int(month)
+            subMi = month - mi
+
+            y = int(mi / 12.0)
+            m = mi - y * 12
+
+            mth = mth + m
+            if mth < 1:  # cross start-of-year?
+                y -= 1  # yes - decrement year
+                mth += 12  # and fix month
+            elif mth > 12:  # cross end-of-year?
+                y += 1  # yes - increment year
+                mth -= 12  # and fix month
+
+            yr += y
+
+            # if the day ends up past the last day of
+            # the new month, set it to the last day
+            maxDay = self.ptc.daysInMonth(mth, yr)
+            if dy > maxDay:
+                dy = maxDay
+
+        if yr > datetime.MAXYEAR or yr < datetime.MINYEAR:
+            raise OverflowError('year is out of range')
+
+        d = source.replace(year=yr, month=mth, day=dy)
+        if subMi:
+            d += datetime.timedelta(days=subMi * maxDay)
+        return source + (d - source)
+
+    def nlp(self, inputString, sourceTime=None, version=None):
+        """Utilizes parse() after making judgements about what datetime
+        information belongs together.
+
+        It makes logical groupings based on proximity and returns a parsed
+        datetime for each matched grouping of datetime text, along with
+        location info within the given inputString.
+
+        @type  inputString: string
+        @param inputString: natural language text to evaluate
+        @type  sourceTime:  struct_time
+        @param sourceTime:  C{struct_time} value to use as the base
+        @type  version:     integer
+        @param version:     style version, default will use L{Calendar}
+                            parameter version value
+
+        @rtype:  tuple or None
+        @return: tuple of tuples in the format (parsed_datetime as
+                 datetime.datetime, flags as int, start_pos as int,
+                 end_pos as int, matched_text as string) or None if there
+                 were no matches
+        """
+
+        orig_inputstring = inputString
+
+        # replace periods at the end of sentences w/ spaces
+        # opposed to removing them altogether in order to
+        # retain relative positions (identified by alpha, period, space).
+        # this is required for some of the regex patterns to match
+        inputString = re.sub(r'(\w)(\.)(\s)', r'\1 \3', inputString).lower()
+        inputString = re.sub(r'(\w)(\'|")(\s|$)', r'\1 \3', inputString)
+        inputString = re.sub(r'(\s|^)(\'|")(\w)', r'\1 \3', inputString)
+
+        startpos = 0  # the start position in the inputString during the loop
+
+        # list of lists in format:
+        # [startpos, endpos, matchedstring, flags, type]
+        matches = []
+
+        while startpos < len(inputString):
+
+            # empty match
+            leftmost_match = [0, 0, None, 0, None]
+
+            # Modifier like next\prev..
+            m = self.ptc.CRE_MODIFIER.search(inputString[startpos:])
+            if m is not None:
+                if leftmost_match[1] == 0 or \
+                        leftmost_match[0] > m.start() + startpos:
+                    leftmost_match[0] = m.start() + startpos
+                    leftmost_match[1] = m.end() + startpos
+                    leftmost_match[2] = m.group()
+                    leftmost_match[3] = 0
+                    leftmost_match[4] = 'modifier'
+
+            # Quantity + Units
+            m = self.ptc.CRE_UNITS.search(inputString[startpos:])
+            if m is not None:
+                debug and log.debug('CRE_UNITS matched')
+                if self._UnitsTrapped(inputString[startpos:], m, 'units'):
+                    debug and log.debug('day suffix trapped by unit match')
+                else:
+
+                    if leftmost_match[1] == 0 or \
+                            leftmost_match[0] > m.start('qty') + startpos:
+                        leftmost_match[0] = m.start('qty') + startpos
+                        leftmost_match[1] = m.end('qty') + startpos
+                        leftmost_match[2] = m.group('qty')
+                        leftmost_match[3] = 3
+                        leftmost_match[4] = 'units'
+
+                        if m.start('qty') > 0 and \
+                                inputString[m.start('qty') - 1] == '-':
+                            leftmost_match[0] = leftmost_match[0] - 1
+                            leftmost_match[2] = '-' + leftmost_match[2]
+
+            # Quantity + Units
+            m = self.ptc.CRE_QUNITS.search(inputString[startpos:])
+            if m is not None:
+                debug and log.debug('CRE_QUNITS matched')
+                if self._UnitsTrapped(inputString[startpos:], m, 'qunits'):
+                    debug and log.debug('day suffix trapped by qunit match')
+                else:
+                    if leftmost_match[1] == 0 or \
+                            leftmost_match[0] > m.start('qty') + startpos:
+                        leftmost_match[0] = m.start('qty') + startpos
+                        leftmost_match[1] = m.end('qty') + startpos
+                        leftmost_match[2] = m.group('qty')
+                        leftmost_match[3] = 3
+                        leftmost_match[4] = 'qunits'
+
+                        if m.start('qty') > 0 and \
+                                inputString[m.start('qty') - 1] == '-':
+                            leftmost_match[0] = leftmost_match[0] - 1
+                            leftmost_match[2] = '-' + leftmost_match[2]
+
+            m = self.ptc.CRE_DATE3.search(inputString[startpos:])
+            # NO LONGER NEEDED, THE REGEXP HANDLED MTHNAME NOW
+            # for match in self.ptc.CRE_DATE3.finditer(inputString[startpos:]):
+            # to prevent "HH:MM(:SS) time strings" expressions from
+            # triggering this regex, we checks if the month field exists
+            # in the searched expression, if it doesn't exist, the date
+            # field is not valid
+            #     if match.group('mthname'):
+            #         m = self.ptc.CRE_DATE3.search(inputString[startpos:],
+            #                                       match.start())
+            #         break
+
+            # String date format
+            if m is not None:
+                if leftmost_match[1] == 0 or \
+                        leftmost_match[0] > m.start('date') + startpos:
+                    leftmost_match[0] = m.start('date') + startpos
+                    leftmost_match[1] = m.end('date') + startpos
+                    leftmost_match[2] = m.group('date')
+                    leftmost_match[3] = 1
+                    leftmost_match[4] = 'dateStr'
+
+            # Standard date format
+            m = self.ptc.CRE_DATE.search(inputString[startpos:])
+            if m is not None:
+                if leftmost_match[1] == 0 or \
+                        leftmost_match[0] > m.start('date') + startpos:
+                    leftmost_match[0] = m.start('date') + startpos
+                    leftmost_match[1] = m.end('date') + startpos
+                    leftmost_match[2] = m.group('date')
+                    leftmost_match[3] = 1
+                    leftmost_match[4] = 'dateStd'
+
+            # Natural language day strings
+            m = self.ptc.CRE_DAY.search(inputString[startpos:])
+            if m is not None:
+                if leftmost_match[1] == 0 or \
+                        leftmost_match[0] > m.start() + startpos:
+                    leftmost_match[0] = m.start() + startpos
+                    leftmost_match[1] = m.end() + startpos
+                    leftmost_match[2] = m.group()
+                    leftmost_match[3] = 1
+                    leftmost_match[4] = 'dayStr'
+
+            # Weekday
+            m = self.ptc.CRE_WEEKDAY.search(inputString[startpos:])
+            if m is not None:
+                if inputString[startpos:] not in self.ptc.dayOffsets:
+                    if leftmost_match[1] == 0 or \
+                            leftmost_match[0] > m.start() + startpos:
+                        leftmost_match[0] = m.start() + startpos
+                        leftmost_match[1] = m.end() + startpos
+                        leftmost_match[2] = m.group()
+                        leftmost_match[3] = 1
+                        leftmost_match[4] = 'weekdy'
+
+            # Natural language time strings
+            m = self.ptc.CRE_TIME.search(inputString[startpos:])
+            if m is not None:
+                if leftmost_match[1] == 0 or \
+                        leftmost_match[0] > m.start() + startpos:
+                    leftmost_match[0] = m.start() + startpos
+                    leftmost_match[1] = m.end() + startpos
+                    leftmost_match[2] = m.group()
+                    leftmost_match[3] = 2
+                    leftmost_match[4] = 'timeStr'
+
+            # HH:MM(:SS) am/pm time strings
+            m = self.ptc.CRE_TIMEHMS2.search(inputString[startpos:])
+            if m is not None:
+                if leftmost_match[1] == 0 or \
+                        leftmost_match[0] > m.start('hours') + startpos:
+                    leftmost_match[0] = m.start('hours') + startpos
+                    leftmost_match[1] = m.end('meridian') + startpos
+                    leftmost_match[2] = inputString[leftmost_match[0]:
+                                                    leftmost_match[1]]
+                    leftmost_match[3] = 2
+                    leftmost_match[4] = 'meridian'
+
+            # HH:MM(:SS) time strings
+            m = self.ptc.CRE_TIMEHMS.search(inputString[startpos:])
+            if m is not None:
+                if leftmost_match[1] == 0 or \
+                        leftmost_match[0] > m.start('hours') + startpos:
+                    leftmost_match[0] = m.start('hours') + startpos
+                    if m.group('seconds') is not None:
+                        leftmost_match[1] = m.end('seconds') + startpos
+                    else:
+                        leftmost_match[1] = m.end('minutes') + startpos
+                    leftmost_match[2] = inputString[leftmost_match[0]:
+                                                    leftmost_match[1]]
+                    leftmost_match[3] = 2
+                    leftmost_match[4] = 'timeStd'
+
+            # Units only; must be preceded by a modifier
+            if len(matches) > 0 and matches[-1][3] == 0:
+                m = self.ptc.CRE_UNITS_ONLY.search(inputString[startpos:])
+                # Ensure that any match is immediately proceded by the
+                # modifier. "Next is the word 'month'" should not parse as a
+                # date while "next month" should
+                if m is not None and \
+                        inputString[startpos:startpos +
+                                    m.start()].strip() == '':
+                    debug and log.debug('CRE_UNITS_ONLY matched [%s]',
+                                        m.group())
+                    if leftmost_match[1] == 0 or \
+                            leftmost_match[0] > m.start() + startpos:
+                        leftmost_match[0] = m.start() + startpos
+                        leftmost_match[1] = m.end() + startpos
+                        leftmost_match[2] = m.group()
+                        leftmost_match[3] = 3
+                        leftmost_match[4] = 'unitsOnly'
+
+            # set the start position to the end pos of the leftmost match
+            startpos = leftmost_match[1]
+
+            # nothing was detected
+            # so break out of the loop
+            if startpos == 0:
+                startpos = len(inputString)
+            else:
+                if leftmost_match[3] > 0:
+                    m = self.ptc.CRE_NLP_PREFIX.search(
+                        inputString[:leftmost_match[0]] +
+                        ' ' + str(leftmost_match[3]))
+                    if m is not None:
+                        leftmost_match[0] = m.start('nlp_prefix')
+                        leftmost_match[2] = inputString[leftmost_match[0]:
+                                                        leftmost_match[1]]
+                matches.append(leftmost_match)
+
+        # find matches in proximity with one another and
+        # return all the parsed values
+        proximity_matches = []
+        if len(matches) > 1:
+            combined = ''
+            from_match_index = 0
+            date = matches[0][3] == 1
+            time = matches[0][3] == 2
+            units = matches[0][3] == 3
+            for i in range(1, len(matches)):
+
+                # test proximity (are there characters between matches?)
+                endofprevious = matches[i - 1][1]
+                begofcurrent = matches[i][0]
+                if orig_inputstring[endofprevious:
+                                    begofcurrent].lower().strip() != '':
+                    # this one isn't in proximity, but maybe
+                    # we have enough to make a datetime
+                    # TODO: make sure the combination of
+                    # formats (modifier, dateStd, etc) makes logical sense
+                    # before parsing together
+                    if date or time or units:
+                        combined = orig_inputstring[matches[from_match_index]
+                                                    [0]:matches[i - 1][1]]
+                        parsed_datetime, flags = self.parse(combined,
+                                                            sourceTime,
+                                                            version)
+                        proximity_matches.append((
+                            datetime.datetime(*parsed_datetime[:6]),
+                            flags,
+                            matches[from_match_index][0],
+                            matches[i - 1][1],
+                            combined))
+                    # not in proximity, reset starting from current
+                    from_match_index = i
+                    date = matches[i][3] == 1
+                    time = matches[i][3] == 2
+                    units = matches[i][3] == 3
+                    continue
+                else:
+                    if matches[i][3] == 1:
+                        date = True
+                    if matches[i][3] == 2:
+                        time = True
+                    if matches[i][3] == 3:
+                        units = True
+
+            # check last
+            # we have enough to make a datetime
+            if date or time or units:
+                combined = orig_inputstring[matches[from_match_index][0]:
+                                            matches[len(matches) - 1][1]]
+                parsed_datetime, flags = self.parse(combined, sourceTime,
+                                                    version)
+                proximity_matches.append((
+                    datetime.datetime(*parsed_datetime[:6]),
+                    flags,
+                    matches[from_match_index][0],
+                    matches[len(matches) - 1][1],
+                    combined))
+
+        elif len(matches) == 0:
+            return None
+        else:
+            if matches[0][3] == 0:  # not enough info to parse
+                return None
+            else:
+                combined = orig_inputstring[matches[0][0]:matches[0][1]]
+                parsed_datetime, flags = self.parse(matches[0][2], sourceTime,
+                                                    version)
+                proximity_matches.append((
+                    datetime.datetime(*parsed_datetime[:6]),
+                    flags,
+                    matches[0][0],
+                    matches[0][1],
+                    combined))
+
+        return tuple(proximity_matches)
+
+
+def _initSymbols(ptc):
+    """
+    Initialize symbols and single character constants.
+    """
+    # build am and pm lists to contain
+    # original case, lowercase, first-char and dotted
+    # versions of the meridian text
+    ptc.am = ['', '']
+    ptc.pm = ['', '']
+    for idx, xm in enumerate(ptc.locale.meridian[:2]):
+        # 0: am
+        # 1: pm
+        target = ['am', 'pm'][idx]
+        setattr(ptc, target, [xm])
+        target = getattr(ptc, target)
+        if xm:
+            lxm = xm.lower()
+            target.extend((xm[0], '{0}.{1}.'.format(*xm),
+                           lxm, lxm[0], '{0}.{1}.'.format(*lxm)))
+
+
+class Constants(object):
+
+    """
+    Default set of constants for parsedatetime.
+
+    If PyICU is present, then the class will first try to get PyICU
+    to return a locale specified by C{localeID}.  If either C{localeID} is
+    None or if the locale does not exist within PyICU, then each of the
+    locales defined in C{fallbackLocales} is tried in order.
+
+    If PyICU is not present or none of the specified locales can be used,
+    then the class will initialize itself to the en_US locale.
+
+    if PyICU is not present or not requested, only the locales defined by
+    C{pdtLocales} will be searched.
+    """
+
+    def __init__(self, localeID=None, usePyICU=True,
+                 fallbackLocales=['en_US']):
+        self.localeID = localeID
+        self.fallbackLocales = fallbackLocales[:]
+
+        if 'en_US' not in self.fallbackLocales:
+            self.fallbackLocales.append('en_US')
+
+        # define non-locale specific constants
+        self.locale = None
+        self.usePyICU = usePyICU
+
+        # starting cache of leap years
+        # daysInMonth will add to this if during
+        # runtime it gets a request for a year not found
+        self._leapYears = list(range(1904, 2097, 4))
+
+        self.Second = 1
+        self.Minute = 60  # 60 * self.Second
+        self.Hour = 3600  # 60 * self.Minute
+        self.Day = 86400  # 24 * self.Hour
+        self.Week = 604800  # 7   * self.Day
+        self.Month = 2592000  # 30  * self.Day
+        self.Year = 31536000  # 365 * self.Day
+
+        self._DaysInMonthList = (31, 28, 31, 30, 31, 30,
+                                 31, 31, 30, 31, 30, 31)
+        self.rangeSep = '-'
+        self.BirthdayEpoch = 50
+
+        # When True the starting time for all relative calculations will come
+        # from the given SourceTime, otherwise it will be 9am
+
+        self.StartTimeFromSourceTime = False
+
+        # YearParseStyle controls how we parse "Jun 12", i.e. dates that do
+        # not have a year present.  The default is to compare the date given
+        # to the current date, and if prior, then assume the next year.
+        # Setting this to 0 will prevent that.
+
+        self.YearParseStyle = 1
+
+        # DOWParseStyle controls how we parse "Tuesday"
+        # If the current day was Thursday and the text to parse is "Tuesday"
+        # then the following table shows how each style would be returned
+        # -1, 0, +1
+        #
+        # Current day marked as ***
+        #
+        #          Sun Mon Tue Wed Thu Fri Sat
+        # week -1
+        # current         -1,0     ***
+        # week +1          +1
+        #
+        # If the current day was Monday and the text to parse is "Tuesday"
+        # then the following table shows how each style would be returned
+        # -1, 0, +1
+        #
+        #          Sun Mon Tue Wed Thu Fri Sat
+        # week -1           -1
+        # current      *** 0,+1
+        # week +1
+
+        self.DOWParseStyle = 1
+
+        # CurrentDOWParseStyle controls how we parse "Friday"
+        # If the current day was Friday and the text to parse is "Friday"
+        # then the following table shows how each style would be returned
+        # True/False. This also depends on DOWParseStyle.
+        #
+        # Current day marked as ***
+        #
+        # DOWParseStyle = 0
+        #          Sun Mon Tue Wed Thu Fri Sat
+        # week -1
+        # current                      T,F
+        # week +1
+        #
+        # DOWParseStyle = -1
+        #          Sun Mon Tue Wed Thu Fri Sat
+        # week -1                       F
+        # current                       T
+        # week +1
+        #
+        # DOWParseStyle = +1
+        #
+        #          Sun Mon Tue Wed Thu Fri Sat
+        # week -1
+        # current                       T
+        # week +1                       F
+
+        self.CurrentDOWParseStyle = False
+
+        if self.usePyICU:
+            self.locale = get_icu(self.localeID)
+
+            if self.locale.icu is None:
+                self.usePyICU = False
+                self.locale = None
+
+        if self.locale is None:
+            if self.localeID not in pdtLocales:
+                for localeId in range(0, len(self.fallbackLocales)):
+                    self.localeID = self.fallbackLocales[localeId]
+                    if self.localeID in pdtLocales:
+                        break
+
+            self.locale = pdtLocales[self.localeID]
+
+        if self.locale is not None:
+
+            def _getLocaleDataAdjusted(localeData):
+                """
+                If localeData is defined as ["mon|mnd", 'tu|tues'...] then this
+                function splits those definitions on |
+                """
+                adjusted = []
+                for d in localeData:
+                    if '|' in d:
+                        adjusted += d.split("|")
+                    else:
+                        adjusted.append(d)
+                return adjusted
+
+            mths = _getLocaleDataAdjusted(self.locale.Months)
+            smths = _getLocaleDataAdjusted(self.locale.shortMonths)
+            swds = _getLocaleDataAdjusted(self.locale.shortWeekdays)
+            wds = _getLocaleDataAdjusted(self.locale.Weekdays)
+
+            re_join = lambda g: '|'.join(re.escape(i) for i in g)
+
+            # escape any regex special characters that may be found
+            self.locale.re_values['months'] = re_join(mths)
+            self.locale.re_values['shortmonths'] = re_join(smths)
+            self.locale.re_values['days'] = re_join(wds)
+            self.locale.re_values['shortdays'] = re_join(swds)
+            self.locale.re_values['dayoffsets'] = \
+                re_join(self.locale.dayOffsets)
+            self.locale.re_values['numbers'] = \
+                re_join(self.locale.numbers)
+            self.locale.re_values['decimal_mark'] = \
+                re.escape(self.locale.decimal_mark)
+
+            units = [unit for units in self.locale.units.values()
+                     for unit in units]  # flatten
+            units.sort(key=len, reverse=True)  # longest first
+            self.locale.re_values['units'] = re_join(units)
+            self.locale.re_values['modifiers'] = re_join(self.locale.Modifiers)
+            self.locale.re_values['sources'] = re_join(self.locale.re_sources)
+
+            # For distinguishing numeric dates from times, look for timeSep
+            # and meridian, if specified in the locale
+            self.locale.re_values['timecomponents'] = \
+                re_join(self.locale.timeSep + self.locale.meridian)
+
+            # build weekday offsets - yes, it assumes the Weekday and
+            # shortWeekday lists are in the same order and Mon..Sun
+            # (Python style)
+            def _buildOffsets(offsetDict, localeData, indexStart):
+                o = indexStart
+                for key in localeData:
+                    if '|' in key:
+                        for k in key.split('|'):
+                            offsetDict[k] = o
+                    else:
+                        offsetDict[key] = o
+                    o += 1
+
+            _buildOffsets(self.locale.WeekdayOffsets,
+                          self.locale.Weekdays, 0)
+            _buildOffsets(self.locale.WeekdayOffsets,
+                          self.locale.shortWeekdays, 0)
+
+            # build month offsets - yes, it assumes the Months and shortMonths
+            # lists are in the same order and Jan..Dec
+            _buildOffsets(self.locale.MonthOffsets,
+                          self.locale.Months, 1)
+            _buildOffsets(self.locale.MonthOffsets,
+                          self.locale.shortMonths, 1)
+
+        _initSymbols(self)
+
+        # TODO: add code to parse the date formats and build the regexes up
+        # from sub-parts, find all hard-coded uses of date/time separators
+
+        # not being used in code, but kept in case others are manually
+        # utilizing this regex for their own purposes
+        self.RE_DATE4 = r'''(?P<date>
+                                (
+                                    (
+                                        (?P<day>\d\d?)
+                                        (?P<suffix>{daysuffix})?
+                                        (,)?
+                                        (\s)?
+                                    )
+                                    (?P<mthname>
+                                        \b({months}|{shortmonths})\b
+                                    )\s?
+                                    (?P<year>\d\d
+                                        (\d\d)?
+                                    )?
+                                )
+                            )'''.format(**self.locale.re_values)
+
+        # still not completely sure of the behavior of the regex and
+        # whether it would be best to consume all possible irrelevant
+        # characters before the option groups (but within the {1,3} repetition
+        # group or inside of each option group, as it currently does
+        # however, right now, all tests are passing that were,
+        # including fixing the bug of matching a 4-digit year as ddyy
+        # when the day is absent from the string
+        self.RE_DATE3 = r'''(?P<date>
+                                (?:
+                                    (?:^|\s)
+                                    (?P<mthname>
+                                        {months}|{shortmonths}
+                                    )\b
+                                    |
+                                    (?:^|\s)
+                                    (?P<day>[1-9]|[012]\d|3[01])
+                                    (?P<suffix>{daysuffix}|)\b
+                                    (?!\s*(?:{timecomponents}))
+                                    |
+                                    ,?\s
+                                    (?P<year>\d\d(?:\d\d|))\b
+                                    (?!\s*(?:{timecomponents}))
+                                ){{1,3}}
+                                (?(mthname)|$-^)
+                            )'''.format(**self.locale.re_values)
+
+        # not being used in code, but kept in case others are manually
+        # utilizing this regex for their own purposes
+        self.RE_MONTH = r'''(\s|^)
+                            (?P<month>
+                                (
+                                    (?P<mthname>
+                                        \b({months}|{shortmonths})\b
+                                    )
+                                    (\s?
+                                        (?P<year>(\d{{4}}))
+                                    )?
+                                )
+                            )
+                            (?=\s|$|[^\w])'''.format(**self.locale.re_values)
+
+        self.RE_WEEKDAY = r'''\b
+                              (?:
+                                  {days}|{shortdays}
+                              )
+                              \b'''.format(**self.locale.re_values)
+
+        self.RE_NUMBER = (r'(\b(?:{numbers})\b|\d+(?:{decimal_mark}\d+|))'
+                          .format(**self.locale.re_values))
+
+        self.RE_SPECIAL = (r'(?P<special>^[{specials}]+)\s+'
+                           .format(**self.locale.re_values))
+
+        self.RE_UNITS_ONLY = (r'''\b({units})\b'''
+                              .format(**self.locale.re_values))
+
+        self.RE_UNITS = r'''\b(?P<qty>
+                                -?
+                                (?:\d+(?:{decimal_mark}\d+|)|(?:{numbers})\b)\s*
+                                (?P<units>{units})
+                            )\b'''.format(**self.locale.re_values)
+
+        self.RE_QUNITS = r'''\b(?P<qty>
+                                 -?
+                                 (?:\d+(?:{decimal_mark}\d+|)|(?:{numbers})s)\s?
+                                 (?P<qunits>{qunits})
+                             )\b'''.format(**self.locale.re_values)
+
+        self.RE_MODIFIER = r'''\b(?:
+                                   {modifiers}
+                               )\b'''.format(**self.locale.re_values)
+
+        self.RE_TIMEHMS = r'''([\s(\["'-]|^)
+                              (?P<hours>\d\d?)
+                              (?P<tsep>{timeseparator}|)
+                              (?P<minutes>\d\d)
+                              (?:(?P=tsep)
+                                  (?P<seconds>\d\d
+                                      (?:[\.,]\d+)?
+                                  )
+                              )?\b'''.format(**self.locale.re_values)
+
+        self.RE_TIMEHMS2 = r'''([\s(\["'-]|^)
+                               (?P<hours>\d\d?)
+                               (?:
+                                   (?P<tsep>{timeseparator}|)
+                                   (?P<minutes>\d\d?)
+                                   (?:(?P=tsep)
+                                       (?P<seconds>\d\d?
+                                           (?:[\.,]\d+)?
+                                       )
+                                   )?
+                               )?'''.format(**self.locale.re_values)
+
+        # 1, 2, and 3 here refer to the type of match date, time, or units
+        self.RE_NLP_PREFIX = r'''\b(?P<nlp_prefix>
+                                  (on)
+                                  (\s)+1
+                                  |
+                                  (at|in)
+                                  (\s)+2
+                                  |
+                                  (in)
+                                  (\s)+3
+                                 )'''
+
+        if 'meridian' in self.locale.re_values:
+            self.RE_TIMEHMS2 += (r'\s?(?P<meridian>{meridian})\b'
+                                 .format(**self.locale.re_values))
+        else:
+            self.RE_TIMEHMS2 += r'\b'
+
+        # Always support common . and - separators
+        dateSeps = ''.join(re.escape(s)
+                           for s in self.locale.dateSep + ['-', '.'])
+
+        self.RE_DATE = r'''([\s(\["'-]|^)
+                           (?P<date>
+                                \d\d?[{0}]\d\d?(?:[{0}]\d\d(?:\d\d)?)?
+                                |
+                                \d{{4}}[{0}]\d\d?[{0}]\d\d?
+                            )
+                           \b'''.format(dateSeps)
+
+        self.RE_DATE2 = r'[{0}]'.format(dateSeps)
+
+        assert 'dayoffsets' in self.locale.re_values
+
+        self.RE_DAY = r'''\b
+                          (?:
+                              {dayoffsets}
+                          )
+                          \b'''.format(**self.locale.re_values)
+
+        self.RE_DAY2 = r'''(?P<day>\d\d?)
+                           (?P<suffix>{daysuffix})?
+                       '''.format(**self.locale.re_values)
+
+        self.RE_TIME = r'''\b
+                           (?:
+                               {sources}
+                           )
+                           \b'''.format(**self.locale.re_values)
+
+        self.RE_REMAINING = r'\s+'
+
+        # Regex for date/time ranges
+        self.RE_RTIMEHMS = r'''(\s?|^)
+                               (\d\d?){timeseparator}
+                               (\d\d)
+                               ({timeseparator}(\d\d))?
+                               (\s?|$)'''.format(**self.locale.re_values)
+
+        self.RE_RTIMEHMS2 = (r'''(\s?|^)
+                                 (\d\d?)
+                                 ({timeseparator}(\d\d?))?
+                                 ({timeseparator}(\d\d?))?'''
+                             .format(**self.locale.re_values))
+
+        if 'meridian' in self.locale.re_values:
+            self.RE_RTIMEHMS2 += (r'\s?({meridian})'
+                                  .format(**self.locale.re_values))
+
+        self.RE_RDATE = r'(\d+([%s]\d+)+)' % dateSeps
+        self.RE_RDATE3 = r'''(
+                                (
+                                    (
+                                        \b({months})\b
+                                    )\s?
+                                    (
+                                        (\d\d?)
+                                        (\s?|{daysuffix}|$)+
+                                    )?
+                                    (,\s?\d{{4}})?
+                                )
+                            )'''.format(**self.locale.re_values)
+
+        # "06/07/06 - 08/09/06"
+        self.DATERNG1 = (r'{0}\s?{rangeseparator}\s?{0}'
+                         .format(self.RE_RDATE, **self.locale.re_values))
+
+        # "march 31 - june 1st, 2006"
+        self.DATERNG2 = (r'{0}\s?{rangeseparator}\s?{0}'
+                         .format(self.RE_RDATE3, **self.locale.re_values))
+
+        # "march 1rd -13th"
+        self.DATERNG3 = (r'{0}\s?{rangeseparator}\s?(\d\d?)\s?(rd|st|nd|th)?'
+                         .format(self.RE_RDATE3, **self.locale.re_values))
+
+        # "4:00:55 pm - 5:90:44 am", '4p-5p'
+        self.TIMERNG1 = (r'{0}\s?{rangeseparator}\s?{0}'
+                         .format(self.RE_RTIMEHMS2, **self.locale.re_values))
+
+        self.TIMERNG2 = (r'{0}\s?{rangeseparator}\s?{0}'
+                         .format(self.RE_RTIMEHMS, **self.locale.re_values))
+
+        # "4-5pm "
+        self.TIMERNG3 = (r'\d\d?\s?{rangeseparator}\s?{0}'
+                         .format(self.RE_RTIMEHMS2, **self.locale.re_values))
+
+        # "4:30-5pm "
+        self.TIMERNG4 = (r'{0}\s?{rangeseparator}\s?{1}'
+                         .format(self.RE_RTIMEHMS, self.RE_RTIMEHMS2,
+                                 **self.locale.re_values))
+
+        self.re_option = re.IGNORECASE + re.VERBOSE
+        self.cre_source = {'CRE_SPECIAL': self.RE_SPECIAL,
+                           'CRE_NUMBER': self.RE_NUMBER,
+                           'CRE_UNITS': self.RE_UNITS,
+                           'CRE_UNITS_ONLY': self.RE_UNITS_ONLY,
+                           'CRE_QUNITS': self.RE_QUNITS,
+                           'CRE_MODIFIER': self.RE_MODIFIER,
+                           'CRE_TIMEHMS': self.RE_TIMEHMS,
+                           'CRE_TIMEHMS2': self.RE_TIMEHMS2,
+                           'CRE_DATE': self.RE_DATE,
+                           'CRE_DATE2': self.RE_DATE2,
+                           'CRE_DATE3': self.RE_DATE3,
+                           'CRE_DATE4': self.RE_DATE4,
+                           'CRE_MONTH': self.RE_MONTH,
+                           'CRE_WEEKDAY': self.RE_WEEKDAY,
+                           'CRE_DAY': self.RE_DAY,
+                           'CRE_DAY2': self.RE_DAY2,
+                           'CRE_TIME': self.RE_TIME,
+                           'CRE_REMAINING': self.RE_REMAINING,
+                           'CRE_RTIMEHMS': self.RE_RTIMEHMS,
+                           'CRE_RTIMEHMS2': self.RE_RTIMEHMS2,
+                           'CRE_RDATE': self.RE_RDATE,
+                           'CRE_RDATE3': self.RE_RDATE3,
+                           'CRE_TIMERNG1': self.TIMERNG1,
+                           'CRE_TIMERNG2': self.TIMERNG2,
+                           'CRE_TIMERNG3': self.TIMERNG3,
+                           'CRE_TIMERNG4': self.TIMERNG4,
+                           'CRE_DATERNG1': self.DATERNG1,
+                           'CRE_DATERNG2': self.DATERNG2,
+                           'CRE_DATERNG3': self.DATERNG3,
+                           'CRE_NLP_PREFIX': self.RE_NLP_PREFIX}
+        self.cre_keys = set(self.cre_source.keys())
+
+    def __getattr__(self, name):
+        if name in self.cre_keys:
+            value = re.compile(self.cre_source[name], self.re_option)
+            setattr(self, name, value)
+            return value
+        elif name in self.locale.locale_keys:
+            return getattr(self.locale, name)
+        else:
+            raise AttributeError(name)
+
+    def daysInMonth(self, month, year):
+        """
+        Take the given month (1-12) and a given year (4 digit) return
+        the number of days in the month adjusting for leap year as needed
+        """
+        result = None
+        debug and log.debug('daysInMonth(%s, %s)', month, year)
+        if month > 0 and month <= 12:
+            result = self._DaysInMonthList[month - 1]
+
+            if month == 2:
+                if year in self._leapYears:
+                    result += 1
+                else:
+                    if calendar.isleap(year):
+                        self._leapYears.append(year)
+                        result += 1
+
+        return result
+
+    def getSource(self, sourceKey, sourceTime=None):
+        """
+        GetReturn a date/time tuple based on the giving source key
+        and the corresponding key found in self.re_sources.
+
+        The current time is used as the default and any specified
+        item found in self.re_sources is inserted into the value
+        and the generated dictionary is returned.
+        """
+        if sourceKey not in self.re_sources:
+            return None
+
+        if sourceTime is None:
+            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = time.localtime()
+        else:
+            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime
+
+        defaults = {'yr': yr, 'mth': mth, 'dy': dy,
+                    'hr': hr, 'mn': mn, 'sec': sec}
+
+        source = self.re_sources[sourceKey]
+
+        values = {}
+
+        for key, default in defaults.items():
+            values[key] = source.get(key, default)
+
+        return (values['yr'], values['mth'], values['dy'],
+                values['hr'], values['mn'], values['sec'],
+                wd, yd, isdst)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/context.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+"""
+parsedatetime/context.py
+
+Context related classes
+
+"""
+
+from threading import local
+
+
+class pdtContextStack(object):
+    """
+    A thread-safe stack to store context(s)
+
+    Internally used by L{Calendar} object
+    """
+
+    def __init__(self):
+        self.__local = local()
+
+    @property
+    def __stack(self):
+        if not hasattr(self.__local, 'stack'):
+            self.__local.stack = []
+        return self.__local.stack
+
+    def push(self, ctx):
+        self.__stack.append(ctx)
+
+    def pop(self):
+        try:
+            return self.__stack.pop()
+        except IndexError:
+            return None
+
+    def last(self):
+        try:
+            return self.__stack[-1]
+        except IndexError:
+            raise RuntimeError('context stack is empty')
+
+    def isEmpty(self):
+        return not self.__stack
+
+
+class pdtContext(object):
+    """
+    Context contains accuracy flag detected by L{Calendar.parse()}
+
+    Accuracy flag uses bitwise-OR operation and is combined by:
+
+        ACU_YEAR - "next year", "2014"
+        ACU_MONTH - "March", "July 2014"
+        ACU_WEEK - "last week", "next 3 weeks"
+        ACU_DAY - "tomorrow", "July 4th 2014"
+        ACU_HALFDAY - "morning", "tonight"
+        ACU_HOUR - "18:00", "next hour"
+        ACU_MIN - "18:32", "next 10 minutes"
+        ACU_SEC - "18:32:55"
+        ACU_NOW - "now"
+
+    """
+
+    __slots__ = ('accuracy',)
+
+    ACU_YEAR = 2 ** 0
+    ACU_MONTH = 2 ** 1
+    ACU_WEEK = 2 ** 2
+    ACU_DAY = 2 ** 3
+    ACU_HALFDAY = 2 ** 4
+    ACU_HOUR = 2 ** 5
+    ACU_MIN = 2 ** 6
+    ACU_SEC = 2 ** 7
+    ACU_NOW = 2 ** 8
+
+    ACU_DATE = ACU_YEAR | ACU_MONTH | ACU_WEEK | ACU_DAY
+    ACU_TIME = ACU_HALFDAY | ACU_HOUR | ACU_MIN | ACU_SEC | ACU_NOW
+
+    _ACCURACY_MAPPING = [
+        (ACU_YEAR, 'year'),
+        (ACU_MONTH, 'month'),
+        (ACU_WEEK, 'week'),
+        (ACU_DAY, 'day'),
+        (ACU_HALFDAY, 'halfday'),
+        (ACU_HOUR, 'hour'),
+        (ACU_MIN, 'min'),
+        (ACU_SEC, 'sec'),
+        (ACU_NOW, 'now')]
+
+    _ACCURACY_REVERSE_MAPPING = {
+        'year': ACU_YEAR,
+        'years': ACU_YEAR,
+        'month': ACU_MONTH,
+        'months': ACU_MONTH,
+        'week': ACU_WEEK,
+        'weeks': ACU_WEEK,
+        'day': ACU_DAY,
+        'days': ACU_DAY,
+        'halfday': ACU_HALFDAY,
+        'morning': ACU_HALFDAY,
+        'afternoon': ACU_HALFDAY,
+        'evening': ACU_HALFDAY,
+        'night': ACU_HALFDAY,
+        'tonight': ACU_HALFDAY,
+        'midnight': ACU_HALFDAY,
+        'hour': ACU_HOUR,
+        'hours': ACU_HOUR,
+        'min': ACU_MIN,
+        'minute': ACU_MIN,
+        'mins': ACU_MIN,
+        'minutes': ACU_MIN,
+        'sec': ACU_SEC,
+        'second': ACU_SEC,
+        'secs': ACU_SEC,
+        'seconds': ACU_SEC,
+        'now': ACU_NOW}
+
+    def __init__(self, accuracy=0):
+        """
+        Default constructor of L{pdtContext} class.
+
+        @type  accuracy: integer
+        @param accuracy: Accuracy flag
+
+        @rtype:  object
+        @return: L{pdtContext} instance
+        """
+        self.accuracy = accuracy
+
+    def updateAccuracy(self, *accuracy):
+        """
+        Updates current accuracy flag
+        """
+        for acc in accuracy:
+            if not isinstance(acc, int):
+                acc = self._ACCURACY_REVERSE_MAPPING[acc]
+            self.accuracy |= acc
+
+    def update(self, context):
+        """
+        Uses another L{pdtContext} instance to update current one
+        """
+        self.updateAccuracy(context.accuracy)
+
+    @property
+    def hasDate(self):
+        """
+        Returns True if current context is accurate to date
+        """
+        return bool(self.accuracy & self.ACU_DATE)
+
+    @property
+    def hasTime(self):
+        """
+        Returns True if current context is accurate to time
+        """
+        return bool(self.accuracy & self.ACU_TIME)
+
+    @property
+    def dateTimeFlag(self):
+        """
+        Returns the old date/time flag code
+        """
+        return int(self.hasDate and 1) | int(self.hasTime and 2)
+
+    @property
+    def hasDateOrTime(self):
+        """
+        Returns True if current context is accurate to date/time
+        """
+        return bool(self.accuracy)
+
+    def __repr__(self):
+        accuracy_repr = []
+        for acc, name in self._ACCURACY_MAPPING:
+            if acc & self.accuracy:
+                accuracy_repr.append('pdtContext.ACU_%s' % name.upper())
+        if accuracy_repr:
+            accuracy_repr = 'accuracy=' + ' | '.join(accuracy_repr)
+        else:
+            accuracy_repr = ''
+
+        return 'pdtContext(%s)' % accuracy_repr
+
+    def __eq__(self, ctx):
+        return self.accuracy == ctx.accuracy
--- a/MoinMoin/support/parsedatetime/parsedatetime.py	Mon Sep 05 23:55:33 2016 +0200
+++ b/MoinMoin/support/parsedatetime/parsedatetime.py	Tue Sep 06 00:09:31 2016 +0200
@@ -1,1541 +1,2 @@
-#!/usr/bin/env python
-
-"""
-Parse human-readable date/time text.
-"""
-
-__license__ = """
-Copyright (c) 2004-2008 Mike Taylor
-Copyright (c) 2006-2008 Darshana Chhajed
-All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-_debug = False
-
-
-import re
-import time
-import datetime
-import rfc822
-import parsedatetime_consts
-
-
-# Copied from feedparser.py
-# Universal Feedparser
-# Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
-# Originally a def inside of _parse_date_w3dtf()
-def _extract_date(m):
-    year = int(m.group('year'))
-    if year < 100:
-        year = 100 * int(time.gmtime()[0] / 100) + int(year)
-    if year < 1000:
-        return 0, 0, 0
-    julian = m.group('julian')
-    if julian:
-        julian = int(julian)
-        month = julian / 30 + 1
-        day = julian % 30 + 1
-        jday = None
-        while jday != julian:
-            t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0))
-            jday = time.gmtime(t)[-2]
-            diff = abs(jday - julian)
-            if jday > julian:
-                if diff < day:
-                    day = day - diff
-                else:
-                    month = month - 1
-                    day = 31
-            elif jday < julian:
-                if day + diff < 28:
-                    day = day + diff
-                else:
-                    month = month + 1
-        return year, month, day
-    month = m.group('month')
-    day = 1
-    if month is None:
-        month = 1
-    else:
-        month = int(month)
-        day = m.group('day')
-        if day:
-            day = int(day)
-        else:
-            day = 1
-    return year, month, day
-
-# Copied from feedparser.py
-# Universal Feedparser
-# Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
-# Originally a def inside of _parse_date_w3dtf()
-def _extract_time(m):
-    if not m:
-        return 0, 0, 0
-    hours = m.group('hours')
-    if not hours:
-        return 0, 0, 0
-    hours = int(hours)
-    minutes = int(m.group('minutes'))
-    seconds = m.group('seconds')
-    if seconds:
-        seconds = int(seconds)
-    else:
-        seconds = 0
-    return hours, minutes, seconds
-
-
-# Copied from feedparser.py
-# Universal Feedparser
-# Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
-# Modified to return a tuple instead of mktime
-#
-# Original comment:
-#   W3DTF-style date parsing adapted from PyXML xml.utils.iso8601, written by
-#   Drake and licensed under the Python license.  Removed all range checking
-#   for month, day, hour, minute, and second, since mktime will normalize
-#   these later
-def _parse_date_w3dtf(dateString):
-    # the __extract_date and __extract_time methods were
-    # copied-out so they could be used by my code --bear
-    def __extract_tzd(m):
-        '''Return the Time Zone Designator as an offset in seconds from UTC.'''
-        if not m:
-            return 0
-        tzd = m.group('tzd')
-        if not tzd:
-            return 0
-        if tzd == 'Z':
-            return 0
-        hours = int(m.group('tzdhours'))
-        minutes = m.group('tzdminutes')
-        if minutes:
-            minutes = int(minutes)
-        else:
-            minutes = 0
-        offset = (hours*60 + minutes) * 60
-        if tzd[0] == '+':
-            return -offset
-        return offset
-
-    __date_re = ('(?P<year>\d\d\d\d)'
-                 '(?:(?P<dsep>-|)'
-                 '(?:(?P<julian>\d\d\d)'
-                 '|(?P<month>\d\d)(?:(?P=dsep)(?P<day>\d\d))?))?')
-    __tzd_re = '(?P<tzd>[-+](?P<tzdhours>\d\d)(?::?(?P<tzdminutes>\d\d))|Z)'
-    __tzd_rx = re.compile(__tzd_re)
-    __time_re = ('(?P<hours>\d\d)(?P<tsep>:|)(?P<minutes>\d\d)'
-                 '(?:(?P=tsep)(?P<seconds>\d\d(?:[.,]\d+)?))?'
-                 + __tzd_re)
-    __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re)
-    __datetime_rx = re.compile(__datetime_re)
-    m = __datetime_rx.match(dateString)
-    if (m is None) or (m.group() != dateString): return
-    return _extract_date(m) + _extract_time(m) + (0, 0, 0)
-
-
-# Copied from feedparser.py
-# Universal Feedparser
-# Copyright (c) 2002-2006, Mark Pilgrim, All rights reserved.
-# Modified to return a tuple instead of mktime
-#
-def _parse_date_rfc822(dateString):
-    '''Parse an RFC822, RFC1123, RFC2822, or asctime-style date'''
-    data = dateString.split()
-    if data[0][-1] in (',', '.') or data[0].lower() in rfc822._daynames:
-        del data[0]
-    if len(data) == 4:
-        s = data[3]
-        i = s.find('+')
-        if i > 0:
-            data[3:] = [s[:i], s[i+1:]]
-        else:
-            data.append('')
-        dateString = " ".join(data)
-    if len(data) < 5:
-        dateString += ' 00:00:00 GMT'
-    return rfc822.parsedate_tz(dateString)
-
-# rfc822.py defines several time zones, but we define some extra ones.
-# 'ET' is equivalent to 'EST', etc.
-_additional_timezones = {'AT': -400, 'ET': -500,
-                         'CT': -600, 'MT': -700,
-                         'PT': -800}
-rfc822._timezones.update(_additional_timezones)
-
-
-class Calendar:
-    """
-    A collection of routines to input, parse and manipulate date and times.
-    The text can either be 'normal' date values or it can be human readable.
-    """
-
-    def __init__(self, constants=None):
-        """
-        Default constructor for the L{Calendar} class.
-
-        @type  constants: object
-        @param constants: Instance of the class L{parsedatetime_consts.Constants}
-
-        @rtype:  object
-        @return: L{Calendar} instance
-        """
-          # if a constants reference is not included, use default
-        if constants is None:
-            self.ptc = parsedatetime_consts.Constants()
-        else:
-            self.ptc = constants
-
-        self.weekdyFlag    = False  # monday/tuesday/...
-        self.dateStdFlag   = False  # 07/21/06
-        self.dateStrFlag   = False  # July 21st, 2006
-        self.timeStdFlag   = False  # 5:50 
-        self.meridianFlag  = False  # am/pm
-        self.dayStrFlag    = False  # tomorrow/yesterday/today/..
-        self.timeStrFlag   = False  # lunch/noon/breakfast/...
-        self.modifierFlag  = False  # after/before/prev/next/..
-        self.modifier2Flag = False  # after/before/prev/next/..
-        self.unitsFlag     = False  # hrs/weeks/yrs/min/..
-        self.qunitsFlag    = False  # h/m/t/d..
-
-        self.timeFlag      = 0
-        self.dateFlag      = 0
-
-
-    def _convertUnitAsWords(self, unitText):
-        """
-        Converts text units into their number value
-
-        Five = 5
-        Twenty Five = 25
-        Two hundred twenty five = 225
-        Two thousand and twenty five = 2025
-        Two thousand twenty five = 2025
-
-        @type  unitText: string
-        @param unitText: number text to convert
-
-        @rtype:  integer
-        @return: numerical value of unitText
-        """
-        # TODO: implement this
-        pass
-
-
-    def _buildTime(self, source, quantity, modifier, units):
-        """
-        Take C{quantity}, C{modifier} and C{unit} strings and convert them into values.
-        After converting, calcuate the time and return the adjusted sourceTime.
-
-        @type  source:   time
-        @param source:   time to use as the base (or source)
-        @type  quantity: string
-        @param quantity: quantity string
-        @type  modifier: string
-        @param modifier: how quantity and units modify the source time
-        @type  units:    string
-        @param units:    unit of the quantity (i.e. hours, days, months, etc)
-
-        @rtype:  struct_time
-        @return: C{struct_time} of the calculated time
-        """
-        if _debug:
-            print '_buildTime: [%s][%s][%s]' % (quantity, modifier, units)
-
-        if source is None:
-            source = time.localtime()
-
-        if quantity is None:
-            quantity = ''
-        else:
-            quantity = quantity.strip()
-
-        if len(quantity) == 0:
-            qty = 1
-        else:
-            try:
-                qty = int(quantity)
-            except ValueError:
-                qty = 0
-
-        if modifier in self.ptc.Modifiers:
-            qty = qty * self.ptc.Modifiers[modifier]
-
-            if units is None or units == '':
-                units = 'dy'
-
-        # plurals are handled by regex's (could be a bug tho)
-
-        (yr, mth, dy, hr, mn, sec, _, _, _) = source
-
-        start  = datetime.datetime(yr, mth, dy, hr, mn, sec)
-        target = start
-
-        if units.startswith('y'):
-            target        = self.inc(start, year=qty)
-            self.dateFlag = 1
-        elif units.endswith('th') or units.endswith('ths'):
-            target        = self.inc(start, month=qty)
-            self.dateFlag = 1
-        else:
-            if units.startswith('d'):
-                target        = start + datetime.timedelta(days=qty)
-                self.dateFlag = 1
-            elif units.startswith('h'):
-                target        = start + datetime.timedelta(hours=qty)
-                self.timeFlag = 2
-            elif units.startswith('m'):
-                target        = start + datetime.timedelta(minutes=qty)
-                self.timeFlag = 2
-            elif units.startswith('s'):
-                target        = start + datetime.timedelta(seconds=qty)
-                self.timeFlag = 2
-            elif units.startswith('w'):
-                target        = start + datetime.timedelta(weeks=qty)
-                self.dateFlag = 1
-
-        return target.timetuple()
-
-
-    def parseDate(self, dateString):
-        """
-        Parse short-form date strings::
-
-            '05/28/2006' or '04.21'
-
-        @type  dateString: string
-        @param dateString: text to convert to a C{datetime}
-
-        @rtype:  struct_time
-        @return: calculated C{struct_time} value of dateString
-        """
-        yr, mth, dy, hr, mn, sec, wd, yd, isdst = time.localtime()
-
-        # values pulled from regex's will be stored here and later
-        # assigned to mth, dy, yr based on information from the locale
-        # -1 is used as the marker value because we want zero values
-        # to be passed thru so they can be flagged as errors later
-        v1 = -1
-        v2 = -1
-        v3 = -1
-
-        s = dateString
-        m = self.ptc.CRE_DATE2.search(s)
-        if m is not None:
-            index = m.start()
-            v1    = int(s[:index])
-            s     = s[index + 1:]
-
-        m = self.ptc.CRE_DATE2.search(s)
-        if m is not None:
-            index = m.start()
-            v2    = int(s[:index])
-            v3    = int(s[index + 1:])
-        else:
-            v2 = int(s.strip())
-
-        v = [ v1, v2, v3 ]
-        d = { 'm': mth, 'd': dy, 'y': yr }
-
-        for i in range(0, 3):
-            n = v[i]
-            c = self.ptc.dp_order[i]
-            if n >= 0:
-                d[c] = n
-
-        # if the year is not specified and the date has already
-        # passed, increment the year
-        if v3 == -1 and ((mth > d['m']) or (mth == d['m'] and dy > d['d'])):
-            yr = d['y'] + 1
-        else:
-            yr  = d['y']
-
-        mth = d['m']
-        dy  = d['d']
-
-        # birthday epoch constraint
-        if yr < self.ptc.BirthdayEpoch:
-            yr += 2000
-        elif yr < 100:
-            yr += 1900
-
-        if _debug:
-            print 'parseDate: ', yr, mth, dy, self.ptc.daysInMonth(mth, yr)
-
-        if (mth > 0 and mth <= 12) and \
-           (dy > 0 and dy <= self.ptc.daysInMonth(mth, yr)):
-            sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
-        else:
-            self.dateFlag = 0
-            self.timeFlag = 0
-            sourceTime    = time.localtime() # return current time if date
-                                             # string is invalid
-
-        return sourceTime
-
-
-    def parseDateText(self, dateString):
-        """
-        Parse long-form date strings::
-
-            'May 31st, 2006'
-            'Jan 1st'
-            'July 2006'
-
-        @type  dateString: string
-        @param dateString: text to convert to a datetime
-
-        @rtype:  struct_time
-        @return: calculated C{struct_time} value of dateString
-        """
-        yr, mth, dy, hr, mn, sec, wd, yd, isdst = time.localtime()
-
-        currentMth = mth
-        currentDy  = dy
-
-        s   = dateString.lower()
-        m   = self.ptc.CRE_DATE3.search(s)
-        mth = m.group('mthname')
-        mth = self.ptc.MonthOffsets[mth]
-
-        if m.group('day') !=  None:
-            dy = int(m.group('day'))
-        else:
-            dy = 1
-
-        if m.group('year') !=  None:
-            yr = int(m.group('year'))
-
-            # birthday epoch constraint
-            if yr < self.ptc.BirthdayEpoch:
-                yr += 2000
-            elif yr < 100:
-                yr += 1900
-
-        elif (mth < currentMth) or (mth == currentMth and dy < currentDy):
-            # if that day and month have already passed in this year,
-            # then increment the year by 1
-            yr += 1
-
-        if dy > 0 and dy <= self.ptc.daysInMonth(mth, yr):
-            sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
-        else:
-            # Return current time if date string is invalid
-            self.dateFlag = 0
-            self.timeFlag = 0
-            sourceTime    = time.localtime()
-
-        return sourceTime
-
-
-    def evalRanges(self, datetimeString, sourceTime=None):
-        """
-        Evaluate the C{datetimeString} text and determine if
-        it represents a date or time range.
-
-        @type  datetimeString: string
-        @param datetimeString: datetime text to evaluate
-        @type  sourceTime:     struct_time
-        @param sourceTime:     C{struct_time} value to use as the base
-
-        @rtype:  tuple
-        @return: tuple of: start datetime, end datetime and the invalid flag
-        """
-        startTime = ''
-        endTime   = ''
-        startDate = ''
-        endDate   = ''
-        rangeFlag = 0
-
-        s = datetimeString.strip().lower()
-
-        if self.ptc.rangeSep in s:
-            s = s.replace(self.ptc.rangeSep, ' %s ' % self.ptc.rangeSep)
-            s = s.replace('  ', ' ')
-
-        m = self.ptc.CRE_TIMERNG1.search(s)
-        if m is not None:
-            rangeFlag = 1
-        else:
-            m = self.ptc.CRE_TIMERNG2.search(s)
-            if m is not None:
-                rangeFlag = 2
-            else:
-                m = self.ptc.CRE_TIMERNG4.search(s)
-                if m is not None:
-                    rangeFlag = 7
-                else:
-                    m = self.ptc.CRE_TIMERNG3.search(s)
-                    if m is not None:
-                        rangeFlag = 3
-                    else:
-                        m = self.ptc.CRE_DATERNG1.search(s)
-                        if m is not None:
-                            rangeFlag = 4
-                        else:
-                            m = self.ptc.CRE_DATERNG2.search(s)
-                            if m is not None:
-                                rangeFlag = 5
-                            else:
-                                m = self.ptc.CRE_DATERNG3.search(s)
-                                if m is not None:
-                                    rangeFlag = 6
-
-        if _debug:
-            print 'evalRanges: rangeFlag =', rangeFlag, '[%s]' % s
-
-        if m is not None:
-            if (m.group() != s):
-                # capture remaining string
-                parseStr = m.group()
-                chunk1   = s[:m.start()]
-                chunk2   = s[m.end():]
-                s        = '%s %s' % (chunk1, chunk2)
-                flag     = 1
-
-                sourceTime, flag = self.parse(s, sourceTime)
-
-                if flag == 0:
-                    sourceTime = None
-            else:
-                parseStr = s
-
-        if rangeFlag == 1:
-            m                = re.search(self.ptc.rangeSep, parseStr)
-            startTime, sflag = self.parse((parseStr[:m.start()]),       sourceTime)
-            endTime, eflag   = self.parse((parseStr[(m.start() + 1):]), sourceTime)
-
-            if (eflag != 0)  and (sflag != 0):
-                return (startTime, endTime, 2)
-
-        elif rangeFlag == 2:
-            m                = re.search(self.ptc.rangeSep, parseStr)
-            startTime, sflag = self.parse((parseStr[:m.start()]),       sourceTime)
-            endTime, eflag   = self.parse((parseStr[(m.start() + 1):]), sourceTime)
-
-            if (eflag != 0)  and (sflag != 0):
-                return (startTime, endTime, 2)
-
-        elif rangeFlag == 3 or rangeFlag == 7:
-            m = re.search(self.ptc.rangeSep, parseStr)
-            # capturing the meridian from the end time
-            if self.ptc.usesMeridian:
-                ampm = re.search(self.ptc.am[0], parseStr)
-
-                # appending the meridian to the start time
-                if ampm is not None:
-                    startTime, sflag = self.parse((parseStr[:m.start()] + self.ptc.meridian[0]), sourceTime)
-                else:
-                    startTime, sflag = self.parse((parseStr[:m.start()] + self.ptc.meridian[1]), sourceTime)
-            else:
-                startTime, sflag = self.parse((parseStr[:m.start()]), sourceTime)
-
-            endTime, eflag = self.parse(parseStr[(m.start() + 1):], sourceTime)
-
-            if (eflag != 0)  and (sflag != 0):
-                return (startTime, endTime, 2)
-
-        elif rangeFlag == 4:
-            m                = re.search(self.ptc.rangeSep, parseStr)
-            startDate, sflag = self.parse((parseStr[:m.start()]),       sourceTime)
-            endDate, eflag   = self.parse((parseStr[(m.start() + 1):]), sourceTime)
-
-            if (eflag != 0)  and (sflag != 0):
-                return (startDate, endDate, 1)
-
-        elif rangeFlag == 5:
-            m       = re.search(self.ptc.rangeSep, parseStr)
-            endDate = parseStr[(m.start() + 1):]
-
-            # capturing the year from the end date
-            date    = self.ptc.CRE_DATE3.search(endDate)
-            endYear = date.group('year')
-
-            # appending the year to the start date if the start date
-            # does not have year information and the end date does.
-            # eg : "Aug 21 - Sep 4, 2007"
-            if endYear is not None:
-                startDate = (parseStr[:m.start()]).strip()
-                date      = self.ptc.CRE_DATE3.search(startDate)
-                startYear = date.group('year')
-
-                if startYear is None:
-                    startDate = startDate + ', ' + endYear
-            else:
-                startDate = parseStr[:m.start()]
-
-            startDate, sflag = self.parse(startDate, sourceTime)
-            endDate, eflag   = self.parse(endDate, sourceTime)
-
-            if (eflag != 0)  and (sflag != 0):
-                return (startDate, endDate, 1)
-
-        elif rangeFlag == 6:
-            m = re.search(self.ptc.rangeSep, parseStr)
-
-            startDate = parseStr[:m.start()]
-
-            # capturing the month from the start date
-            mth = self.ptc.CRE_DATE3.search(startDate)
-            mth = mth.group('mthname')
-
-            # appending the month name to the end date
-            endDate = mth + parseStr[(m.start() + 1):]
-
-            startDate, sflag = self.parse(startDate, sourceTime)
-            endDate, eflag   = self.parse(endDate, sourceTime)
-
-            if (eflag != 0)  and (sflag != 0):
-                return (startDate, endDate, 1)
-        else:
-            # if range is not found
-            sourceTime = time.localtime()
-
-            return (sourceTime, sourceTime, 0)
-
-
-    def _CalculateDOWDelta(self, wd, wkdy, offset, style, currentDayStyle):
-        """
-        Based on the C{style} and C{currentDayStyle} determine what
-        day-of-week value is to be returned.
-
-        @type  wd:              integer
-        @param wd:              day-of-week value for the current day
-        @type  wkdy:            integer
-        @param wkdy:            day-of-week value for the parsed day
-        @type  offset:          integer
-        @param offset:          offset direction for any modifiers (-1, 0, 1)
-        @type  style:           integer
-        @param style:           normally the value set in C{Constants.DOWParseStyle}
-        @type  currentDayStyle: integer
-        @param currentDayStyle: normally the value set in C{Constants.CurrentDOWParseStyle}
-
-        @rtype:  integer
-        @return: calculated day-of-week
-        """
-        if offset == 1:
-            # modifier is indicating future week eg: "next".
-            # DOW is calculated as DOW of next week
-            diff = 7 - wd + wkdy
-
-        elif offset == -1:
-            # modifier is indicating past week eg: "last","previous"
-            # DOW is calculated as DOW of previous week
-            diff = wkdy - wd - 7
-
-        elif offset == 0:
-            # modifier is indiacting current week eg: "this"
-            # DOW is calculated as DOW of this week
-            diff = wkdy - wd
-
-        elif offset == 2:
-            # no modifier is present.
-            # i.e. string to be parsed is just DOW
-            if style == 1:
-                # next occurance of the DOW is calculated
-                if currentDayStyle == True:
-                    if wkdy >= wd:
-                        diff = wkdy - wd
-                    else:
-                        diff = 7 - wd + wkdy
-                else:
-                    if wkdy > wd:
-                        diff = wkdy - wd
-                    else:
-                        diff = 7 - wd + wkdy
-
-            elif style == -1:
-                # last occurance of the DOW is calculated
-                if currentDayStyle == True:
-                    if wkdy <= wd:
-                        diff = wkdy - wd
-                    else:
-                        diff = wkdy - wd - 7
-                else:
-                    if wkdy < wd:
-                        diff = wkdy - wd
-                    else:
-                        diff = wkdy - wd - 7
-            else:
-                # occurance of the DOW in the current week is calculated
-                diff = wkdy - wd
-
-        if _debug:
-            print "wd %s, wkdy %s, offset %d, style %d\n" % (wd, wkdy, offset, style)
-
-        return diff
-
-
-    def _evalModifier(self, modifier, chunk1, chunk2, sourceTime):
-        """
-        Evaluate the C{modifier} string and following text (passed in
-        as C{chunk1} and C{chunk2}) and if they match any known modifiers
-        calculate the delta and apply it to C{sourceTime}.
-
-        @type  modifier:   string
-        @param modifier:   modifier text to apply to sourceTime
-        @type  chunk1:     string
-        @param chunk1:     first text chunk that followed modifier (if any)
-        @type  chunk2:     string
-        @param chunk2:     second text chunk that followed modifier (if any)
-        @type  sourceTime: struct_time
-        @param sourceTime: C{struct_time} value to use as the base
-
-        @rtype:  tuple
-        @return: tuple of: remaining text and the modified sourceTime
-        """
-        offset = self.ptc.Modifiers[modifier]
-
-        if sourceTime is not None:
-            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime
-        else:
-            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = time.localtime()
-
-        # capture the units after the modifier and the remaining
-        # string after the unit
-        m = self.ptc.CRE_REMAINING.search(chunk2)
-        if m is not None:
-            index  = m.start() + 1
-            unit   = chunk2[:m.start()]
-            chunk2 = chunk2[index:]
-        else:
-            unit   = chunk2
-            chunk2 = ''
-
-        flag = False
-
-        if unit == 'month' or \
-           unit == 'mth' or \
-           unit == 'm':
-            if offset == 0:
-                dy         = self.ptc.daysInMonth(mth, yr)
-                sourceTime = (yr, mth, dy, 9, 0, 0, wd, yd, isdst)
-            elif offset == 2:
-                # if day is the last day of the month, calculate the last day
-                # of the next month
-                if dy == self.ptc.daysInMonth(mth, yr):
-                    dy = self.ptc.daysInMonth(mth + 1, yr)
-
-                start      = datetime.datetime(yr, mth, dy, 9, 0, 0)
-                target     = self.inc(start, month=1)
-                sourceTime = target.timetuple()
-            else:
-                start      = datetime.datetime(yr, mth, 1, 9, 0, 0)
-                target     = self.inc(start, month=offset)
-                sourceTime = target.timetuple()
-
-            flag = True
-            self.dateFlag = 1
-
-        if unit == 'week' or \
-             unit == 'wk' or \
-             unit == 'w':
-            if offset == 0:
-                start      = datetime.datetime(yr, mth, dy, 17, 0, 0)
-                target     = start + datetime.timedelta(days=(4 - wd))
-                sourceTime = target.timetuple()
-            elif offset == 2:
-                start      = datetime.datetime(yr, mth, dy, 9, 0, 0)
-                target     = start + datetime.timedelta(days=7)
-                sourceTime = target.timetuple()
-            else:
-                return self._evalModifier(modifier, chunk1, "monday " + chunk2, sourceTime)
-
-            flag          = True
-            self.dateFlag = 1
-
-        if unit == 'day' or \
-            unit == 'dy' or \
-            unit == 'd':
-            if offset == 0:
-                sourceTime    = (yr, mth, dy, 17, 0, 0, wd, yd, isdst)
-                self.timeFlag = 2
-            elif offset == 2:
-                start      = datetime.datetime(yr, mth, dy, hr, mn, sec)
-                target     = start + datetime.timedelta(days=1)
-                sourceTime = target.timetuple()
-            else:
-                start      = datetime.datetime(yr, mth, dy, 9, 0, 0)
-                target     = start + datetime.timedelta(days=offset)
-                sourceTime = target.timetuple()
-
-            flag          = True
-            self.dateFlag = 1
-
-        if unit == 'hour' or \
-           unit == 'hr':
-            if offset == 0:
-                sourceTime = (yr, mth, dy, hr, 0, 0, wd, yd, isdst)
-            else:
-                start      = datetime.datetime(yr, mth, dy, hr, 0, 0)
-                target     = start + datetime.timedelta(hours=offset)
-                sourceTime = target.timetuple()
-
-            flag          = True
-            self.timeFlag = 2
-
-        if unit == 'year' or \
-             unit == 'yr' or \
-             unit == 'y':
-            if offset == 0:
-                sourceTime = (yr, 12, 31, hr, mn, sec, wd, yd, isdst)
-            elif offset == 2:
-                sourceTime = (yr + 1, mth, dy, hr, mn, sec, wd, yd, isdst)
-            else:
-                sourceTime = (yr + offset, 1, 1, 9, 0, 0, wd, yd, isdst)
-
-            flag          = True
-            self.dateFlag = 1
-
-        if flag == False:
-            m = self.ptc.CRE_WEEKDAY.match(unit)
-            if m is not None:
-                wkdy          = m.group()
-                self.dateFlag = 1
-
-                if modifier == 'eod':
-                    # Calculate the  upcoming weekday
-                    self.modifierFlag = False
-                    (sourceTime, _)   = self.parse(wkdy, sourceTime)
-                    sources           = self.ptc.buildSources(sourceTime)
-                    self.timeFlag     = 2
-
-                    if modifier in sources:
-                        sourceTime = sources[modifier]
-
-                else:
-                    wkdy       = self.ptc.WeekdayOffsets[wkdy]
-                    diff       = self._CalculateDOWDelta(wd, wkdy, offset,
-                                                         self.ptc.DOWParseStyle,
-                                                         self.ptc.CurrentDOWParseStyle)
-                    start      = datetime.datetime(yr, mth, dy, 9, 0, 0)
-                    target     = start + datetime.timedelta(days=diff)
-                    sourceTime = target.timetuple()
-
-                flag          = True
-                self.dateFlag = 1
-
-        if not flag:
-            m = self.ptc.CRE_TIME.match(unit)
-            if m is not None:
-                self.modifierFlag = False
-                (yr, mth, dy, hr, mn, sec, wd, yd, isdst), _ = self.parse(unit)
-
-                start      = datetime.datetime(yr, mth, dy, hr, mn, sec)
-                target     = start + datetime.timedelta(days=offset)
-                sourceTime = target.timetuple()
-                flag       = True
-            else:
-                self.modifierFlag = False
-
-                # check if the remaining text is parsable and if so,
-                # use it as the base time for the modifier source time
-                t, flag2 = self.parse('%s %s' % (chunk1, unit), sourceTime)
-
-                if flag2 != 0:
-                    sourceTime = t
-
-                sources = self.ptc.buildSources(sourceTime)
-
-                if modifier in sources:
-                    sourceTime    = sources[modifier]
-                    flag          = True
-                    self.timeFlag = 2
-
-        # if the word after next is a number, the string is more than likely
-        # to be "next 4 hrs" which we will have to combine the units with the
-        # rest of the string
-        if not flag:
-            if offset < 0:
-                # if offset is negative, the unit has to be made negative
-                unit = '-%s' % unit
-
-            chunk2 = '%s %s' % (unit, chunk2)
-
-        self.modifierFlag = False
-
-        #return '%s %s' % (chunk1, chunk2), sourceTime
-        return '%s' % chunk2, sourceTime
-
-    def _evalModifier2(self, modifier, chunk1 , chunk2, sourceTime):
-        """
-        Evaluate the C{modifier} string and following text (passed in
-        as C{chunk1} and C{chunk2}) and if they match any known modifiers
-        calculate the delta and apply it to C{sourceTime}.
-
-        @type  modifier:   string
-        @param modifier:   modifier text to apply to C{sourceTime}
-        @type  chunk1:     string
-        @param chunk1:     first text chunk that followed modifier (if any)
-        @type  chunk2:     string
-        @param chunk2:     second text chunk that followed modifier (if any)
-        @type  sourceTime: struct_time
-        @param sourceTime: C{struct_time} value to use as the base
-
-        @rtype:  tuple
-        @return: tuple of: remaining text and the modified sourceTime
-        """
-        offset = self.ptc.Modifiers[modifier]
-        digit  = r'\d+'
-
-        self.modifier2Flag = False
-
-        # If the string after the negative modifier starts with digits,
-        # then it is likely that the string is similar to ' before 3 days'
-        # or 'evening prior to 3 days'.
-        # In this case, the total time is calculated by subtracting '3 days'
-        # from the current date.
-        # So, we have to identify the quantity and negate it before parsing
-        # the string.
-        # This is not required for strings not starting with digits since the
-        # string is enough to calculate the sourceTime
-        if chunk2 != '':
-            if offset < 0:
-                m = re.match(digit, chunk2.strip())
-                if m is not None:
-                    qty    = int(m.group()) * -1
-                    chunk2 = chunk2[m.end():]
-                    chunk2 = '%d%s' % (qty, chunk2)
-
-            sourceTime, flag1 = self.parse(chunk2, sourceTime)
-            if flag1 == 0:
-                flag1 = True
-            else:
-                flag1 = False
-            flag2 = False
-        else:
-            flag1 = False
-
-        if chunk1 != '':
-            if offset < 0:
-                m = re.search(digit, chunk1.strip())
-                if m is not None:
-                    qty    = int(m.group()) * -1
-                    chunk1 = chunk1[m.end():]
-                    chunk1 = '%d%s' % (qty, chunk1)
-
-            tempDateFlag       = self.dateFlag
-            tempTimeFlag       = self.timeFlag
-            sourceTime2, flag2 = self.parse(chunk1, sourceTime)
-        else:
-            return sourceTime, (flag1 and flag2)
-
-        # if chunk1 is not a datetime and chunk2 is then do not use datetime
-        # value returned by parsing chunk1
-        if not (flag1 == False and flag2 == 0):
-            sourceTime = sourceTime2
-        else:
-            self.timeFlag = tempTimeFlag
-            self.dateFlag = tempDateFlag
-
-        return sourceTime, (flag1 and flag2)
-
-
-    def _evalString(self, datetimeString, sourceTime=None):
-        """
-        Calculate the datetime based on flags set by the L{parse()} routine
-
-        Examples handled::
-            RFC822, W3CDTF formatted dates
-            HH:MM[:SS][ am/pm]
-            MM/DD/YYYY
-            DD MMMM YYYY
-
-        @type  datetimeString: string
-        @param datetimeString: text to try and parse as more "traditional"
-                               date/time text
-        @type  sourceTime:     struct_time
-        @param sourceTime:     C{struct_time} value to use as the base
-
-        @rtype:  datetime
-        @return: calculated C{struct_time} value or current C{struct_time}
-                 if not parsed
-        """
-        s   = datetimeString.strip()
-        now = time.localtime()
-
-        # Given string date is a RFC822 date
-        if sourceTime is None:
-            sourceTime = _parse_date_rfc822(s)
-
-            if sourceTime is not None:
-                (yr, mth, dy, hr, mn, sec, wd, yd, isdst, _) = sourceTime
-                self.dateFlag = 1
-
-                if (hr != 0) and (mn != 0) and (sec != 0):
-                    self.timeFlag = 2
-
-                sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
-
-        # Given string date is a W3CDTF date
-        if sourceTime is None:
-            sourceTime = _parse_date_w3dtf(s)
-
-            if sourceTime is not None:
-                self.dateFlag = 1
-                self.timeFlag = 2
-
-        if sourceTime is None:
-            s = s.lower()
-
-        # Given string is in the format HH:MM(:SS)(am/pm)
-        if self.meridianFlag:
-            if sourceTime is None:
-                (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now
-            else:
-                (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime
-
-            m = self.ptc.CRE_TIMEHMS2.search(s)
-            if m is not None:
-                dt = s[:m.start('meridian')].strip()
-                if len(dt) <= 2:
-                    hr  = int(dt)
-                    mn  = 0
-                    sec = 0
-                else:
-                    hr, mn, sec = _extract_time(m)
-
-                if hr == 24:
-                    hr = 0
-
-                sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
-                meridian   = m.group('meridian').lower()
-
-                  # if 'am' found and hour is 12 - force hour to 0 (midnight)
-                if (meridian in self.ptc.am) and hr == 12:
-                    sourceTime = (yr, mth, dy, 0, mn, sec, wd, yd, isdst)
-
-                  # if 'pm' found and hour < 12, add 12 to shift to evening
-                if (meridian in self.ptc.pm) and hr < 12:
-                    sourceTime = (yr, mth, dy, hr + 12, mn, sec, wd, yd, isdst)
-
-              # invalid time
-            if hr > 24 or mn > 59 or sec > 59:
-                sourceTime    = now
-                self.dateFlag = 0
-                self.timeFlag = 0
-
-            self.meridianFlag = False
-
-          # Given string is in the format HH:MM(:SS)
-        if self.timeStdFlag:
-            if sourceTime is None:
-                (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now
-            else:
-                (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime
-
-            m = self.ptc.CRE_TIMEHMS.search(s)
-            if m is not None:
-                hr, mn, sec = _extract_time(m)
-            if hr == 24:
-                hr = 0
-
-            if hr > 24 or mn > 59 or sec > 59:
-                # invalid time
-                sourceTime    = now
-                self.dateFlag = 0
-                self.timeFlag = 0
-            else:
-                sourceTime = (yr, mth, dy, hr, mn, sec, wd, yd, isdst)
-
-            self.timeStdFlag = False
-
-        # Given string is in the format 07/21/2006
-        if self.dateStdFlag:
-            sourceTime       = self.parseDate(s)
-            self.dateStdFlag = False
-
-        # Given string is in the format  "May 23rd, 2005"
-        if self.dateStrFlag:
-            sourceTime       = self.parseDateText(s)
-            self.dateStrFlag = False
-
-        # Given string is a weekday
-        if self.weekdyFlag:
-            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = now
-
-            start = datetime.datetime(yr, mth, dy, hr, mn, sec)
-            wkdy  = self.ptc.WeekdayOffsets[s]
-
-            if wkdy > wd:
-                qty = self._CalculateDOWDelta(wd, wkdy, 2,
-                                              self.ptc.DOWParseStyle,
-                                              self.ptc.CurrentDOWParseStyle)
-            else:
-                qty = self._CalculateDOWDelta(wd, wkdy, 2,
-                                              self.ptc.DOWParseStyle,
-                                              self.ptc.CurrentDOWParseStyle)
-
-            target = start + datetime.timedelta(days=qty)
-            wd     = wkdy
-
-            sourceTime      = target.timetuple()
-            self.weekdyFlag = False
-
-        # Given string is a natural language time string like
-        # lunch, midnight, etc
-        if self.timeStrFlag:
-            if s in self.ptc.re_values['now']:
-                sourceTime = now
-            else:
-                sources = self.ptc.buildSources(sourceTime)
-
-                if s in sources:
-                    sourceTime = sources[s]
-                else:
-                    sourceTime    = now
-                    self.dateFlag = 0
-                    self.timeFlag = 0
-
-            self.timeStrFlag = False
-
-        # Given string is a natural language date string like today, tomorrow..
-        if self.dayStrFlag:
-            if sourceTime is None:
-                sourceTime = now
-
-            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime
-
-            if s in self.ptc.dayOffsets:
-                offset = self.ptc.dayOffsets[s]
-            else:
-                offset = 0
-
-            start      = datetime.datetime(yr, mth, dy, 9, 0, 0)
-            target     = start + datetime.timedelta(days=offset)
-            sourceTime = target.timetuple()
-
-            self.dayStrFlag = False
-
-        # Given string is a time string with units like "5 hrs 30 min"
-        if self.unitsFlag:
-            modifier = ''  # TODO
-
-            if sourceTime is None:
-                sourceTime = now
-
-            m = self.ptc.CRE_UNITS.search(s)
-            if m is not None:
-                units    = m.group('units')
-                quantity = s[:m.start('units')]
-
-            sourceTime     = self._buildTime(sourceTime, quantity, modifier, units)
-            self.unitsFlag = False
-
-        # Given string is a time string with single char units like "5 h 30 m"
-        if self.qunitsFlag:
-            modifier = ''  # TODO
-
-            if sourceTime is None:
-                sourceTime = now
-
-            m = self.ptc.CRE_QUNITS.search(s)
-            if m is not None:
-                units    = m.group('qunits')
-                quantity = s[:m.start('qunits')]
-
-            sourceTime      = self._buildTime(sourceTime, quantity, modifier, units)
-            self.qunitsFlag = False
-
-          # Given string does not match anything
-        if sourceTime is None:
-            sourceTime    = now
-            self.dateFlag = 0
-            self.timeFlag = 0
-
-        return sourceTime
-
-
-    def parse(self, datetimeString, sourceTime=None):
-        """
-        Splits the given C{datetimeString} into tokens, finds the regex
-        patterns that match and then calculates a C{struct_time} value from
-        the chunks.
-
-        If C{sourceTime} is given then the C{struct_time} value will be
-        calculated from that value, otherwise from the current date/time.
-
-        If the C{datetimeString} is parsed and date/time value found then
-        the second item of the returned tuple will be a flag to let you know
-        what kind of C{struct_time} value is being returned::
-
-            0 = not parsed at all
-            1 = parsed as a C{date}
-            2 = parsed as a C{time}
-            3 = parsed as a C{datetime}
-
-        @type  datetimeString: string
-        @param datetimeString: date/time text to evaluate
-        @type  sourceTime:     struct_time
-        @param sourceTime:     C{struct_time} value to use as the base
-
-        @rtype:  tuple
-        @return: tuple of: modified C{sourceTime} and the result flag
-        """
-
-        if sourceTime:
-            if isinstance(sourceTime, datetime.datetime):
-                if _debug:
-                    print 'coercing datetime to timetuple'
-                sourceTime = sourceTime.timetuple()
-            else:
-                if not isinstance(sourceTime, time.struct_time) and \
-                   not isinstance(sourceTime, tuple):
-                    raise Exception('sourceTime is not a struct_time')
-
-        s         = datetimeString.strip().lower()
-        parseStr  = ''
-        totalTime = sourceTime
-
-        if s == '' :
-            if sourceTime is not None:
-                return (sourceTime, self.dateFlag + self.timeFlag)
-            else:
-                return (time.localtime(), 0)
-
-        self.timeFlag = 0
-        self.dateFlag = 0
-
-        while len(s) > 0:
-            flag   = False
-            chunk1 = ''
-            chunk2 = ''
-
-            if _debug:
-                print 'parse (top of loop): [%s][%s]' % (s, parseStr)
-
-            if parseStr == '':
-                # Modifier like next\prev..
-                m = self.ptc.CRE_MODIFIER.search(s)
-                if m is not None:
-                    self.modifierFlag = True
-                    if (m.group('modifier') != s):
-                        # capture remaining string
-                        parseStr = m.group('modifier')
-                        chunk1   = s[:m.start('modifier')].strip()
-                        chunk2   = s[m.end('modifier'):].strip()
-                        flag     = True
-                    else:
-                        parseStr = s
-
-            if parseStr == '':
-                # Modifier like from\after\prior..
-                m = self.ptc.CRE_MODIFIER2.search(s)
-                if m is not None:
-                    self.modifier2Flag = True
-                    if (m.group('modifier') != s):
-                        # capture remaining string
-                        parseStr = m.group('modifier')
-                        chunk1   = s[:m.start('modifier')].strip()
-                        chunk2   = s[m.end('modifier'):].strip()
-                        flag     = True
-                    else:
-                        parseStr = s
-
-            if parseStr == '':
-                valid_date = False
-                for match in self.ptc.CRE_DATE3.finditer(s):
-                    # to prevent "HH:MM(:SS) time strings" expressions from triggering
-                    # this regex, we checks if the month field exists in the searched 
-                    # expression, if it doesn't exist, the date field is not valid
-                    if match.group('mthname'):
-                        m = self.ptc.CRE_DATE3.search(s, match.start())
-                        valid_date = True
-                        break
-
-                # String date format
-                if valid_date:
-                    self.dateStrFlag = True
-                    self.dateFlag    = 1
-                    if (m.group('date') != s):
-                        # capture remaining string
-                        parseStr = m.group('date')
-                        chunk1   = s[:m.start('date')]
-                        chunk2   = s[m.end('date'):]
-                        s        = '%s %s' % (chunk1, chunk2)
-                        flag     = True
-                    else:
-                        parseStr = s
-
-            if parseStr == '':
-                # Standard date format
-                m = self.ptc.CRE_DATE.search(s)
-                if m is not None:
-                    self.dateStdFlag = True
-                    self.dateFlag    = 1
-                    if (m.group('date') != s):
-                        # capture remaining string
-                        parseStr = m.group('date')
-                        chunk1   = s[:m.start('date')]
-                        chunk2   = s[m.end('date'):]
-                        s        = '%s %s' % (chunk1, chunk2)
-                        flag     = True
-                    else:
-                        parseStr = s
-
-            if parseStr == '':
-                # Natural language day strings
-                m = self.ptc.CRE_DAY.search(s)
-                if m is not None:
-                    self.dayStrFlag = True
-                    self.dateFlag   = 1
-                    if (m.group('day') != s):
-                        # capture remaining string
-                        parseStr = m.group('day')
-                        chunk1   = s[:m.start('day')]
-                        chunk2   = s[m.end('day'):]
-                        s        = '%s %s' % (chunk1, chunk2)
-                        flag     = True
-                    else:
-                        parseStr = s
-
-            if parseStr == '':
-                # Quantity + Units
-                m = self.ptc.CRE_UNITS.search(s)
-                if m is not None:
-                    self.unitsFlag = True
-                    if (m.group('qty') != s):
-                        # capture remaining string
-                        parseStr = m.group('qty')
-                        chunk1   = s[:m.start('qty')].strip()
-                        chunk2   = s[m.end('qty'):].strip()
-
-                        if chunk1[-1:] == '-':
-                            parseStr = '-%s' % parseStr
-                            chunk1   = chunk1[:-1]
-
-                        s    = '%s %s' % (chunk1, chunk2)
-                        flag = True
-                    else:
-                        parseStr = s
-
-            if parseStr == '':
-                # Quantity + Units
-                m = self.ptc.CRE_QUNITS.search(s)
-                if m is not None:
-                    self.qunitsFlag = True
-
-                    if (m.group('qty') != s):
-                        # capture remaining string
-                        parseStr = m.group('qty')
-                        chunk1   = s[:m.start('qty')].strip()
-                        chunk2   = s[m.end('qty'):].strip()
-
-                        if chunk1[-1:] == '-':
-                            parseStr = '-%s' % parseStr
-                            chunk1   = chunk1[:-1]
-
-                        s    = '%s %s' % (chunk1, chunk2)
-                        flag = True
-                    else:
-                        parseStr = s 
-
-            if parseStr == '':
-                # Weekday
-                m = self.ptc.CRE_WEEKDAY.search(s)
-                if m is not None:
-                    gv = m.group('weekday')
-                    if s not in self.ptc.dayOffsets:
-                        self.weekdyFlag = True
-                        self.dateFlag   = 1
-                        if (gv != s):
-                            # capture remaining string
-                            parseStr = gv
-                            chunk1   = s[:m.start('weekday')]
-                            chunk2   = s[m.end('weekday'):]
-                            s        = '%s %s' % (chunk1, chunk2)
-                            flag     = True
-                        else:
-                            parseStr = s
-
-            if parseStr == '':
-                # Natural language time strings
-                m = self.ptc.CRE_TIME.search(s)
-                if m is not None:
-                    self.timeStrFlag = True
-                    self.timeFlag    = 2
-                    if (m.group('time') != s):
-                        # capture remaining string
-                        parseStr = m.group('time')
-                        chunk1   = s[:m.start('time')]
-                        chunk2   = s[m.end('time'):]
-                        s        = '%s %s' % (chunk1, chunk2)
-                        flag     = True
-                    else:
-                        parseStr = s
-
-            if parseStr == '':
-                # HH:MM(:SS) am/pm time strings
-                m = self.ptc.CRE_TIMEHMS2.search(s)
-                if m is not None:
-                    self.meridianFlag = True
-                    self.timeFlag     = 2
-                    if m.group('minutes') is not None:
-                        if m.group('seconds') is not None:
-                            parseStr = '%s:%s:%s %s' % (m.group('hours'),
-                                                        m.group('minutes'),
-                                                        m.group('seconds'),
-                                                        m.group('meridian'))
-                        else:
-                            parseStr = '%s:%s %s' % (m.group('hours'),
-                                                     m.group('minutes'),
-                                                     m.group('meridian'))
-                    else:
-                        parseStr = '%s %s' % (m.group('hours'),
-                                              m.group('meridian'))
-
-                    chunk1 = s[:m.start('hours')]
-                    chunk2 = s[m.end('meridian'):]
-
-                    s    = '%s %s' % (chunk1, chunk2)
-                    flag = True
-
-            if parseStr == '':
-                # HH:MM(:SS) time strings
-                m = self.ptc.CRE_TIMEHMS.search(s)
-                if m is not None:
-                    self.timeStdFlag = True
-                    self.timeFlag    = 2
-                    if m.group('seconds') is not None:
-                        parseStr = '%s:%s:%s' % (m.group('hours'),
-                                                 m.group('minutes'),
-                                                 m.group('seconds'))
-                        chunk1   = s[:m.start('hours')]
-                        chunk2   = s[m.end('seconds'):]
-                    else:
-                        parseStr = '%s:%s' % (m.group('hours'),
-                                              m.group('minutes'))
-                        chunk1   = s[:m.start('hours')]
-                        chunk2   = s[m.end('minutes'):]
-
-                    s    = '%s %s' % (chunk1, chunk2)
-                    flag = True
-
-            # if string does not match any regex, empty string to
-            # come out of the while loop
-            if not flag:
-                s = ''
-
-            if _debug:
-                print 'parse (bottom) [%s][%s][%s][%s]' % (s, parseStr, chunk1, chunk2)
-                print 'weekday %s, dateStd %s, dateStr %s, time %s, timeStr %s, meridian %s' % \
-                       (self.weekdyFlag, self.dateStdFlag, self.dateStrFlag, self.timeStdFlag, self.timeStrFlag, self.meridianFlag)
-                print 'dayStr %s, modifier %s, modifier2 %s, units %s, qunits %s' % \
-                       (self.dayStrFlag, self.modifierFlag, self.modifier2Flag, self.unitsFlag, self.qunitsFlag)
-
-            # evaluate the matched string
-            if parseStr != '':
-                if self.modifierFlag == True:
-                    t, totalTime = self._evalModifier(parseStr, chunk1, chunk2, totalTime)
-                    # t is the unparsed part of the chunks.
-                    # If it is not date/time, return current
-                    # totalTime as it is; else return the output
-                    # after parsing t.
-                    if (t != '') and (t != None):
-                        tempDateFlag       = self.dateFlag
-                        tempTimeFlag       = self.timeFlag
-                        (totalTime2, flag) = self.parse(t, totalTime)
-
-                        if flag == 0 and totalTime is not None:
-                            self.timeFlag = tempTimeFlag
-                            self.dateFlag = tempDateFlag
-
-                            return (totalTime, self.dateFlag + self.timeFlag)
-                        else:
-                            return (totalTime2, self.dateFlag + self.timeFlag)
-
-                elif self.modifier2Flag == True:
-                    totalTime, invalidFlag = self._evalModifier2(parseStr, chunk1, chunk2, totalTime)
-
-                    if invalidFlag == True:
-                        self.dateFlag = 0
-                        self.timeFlag = 0
-
-                else:
-                    totalTime = self._evalString(parseStr, totalTime)
-                    parseStr  = ''
-
-        # String is not parsed at all
-        if totalTime is None or totalTime == sourceTime:
-            totalTime     = time.localtime()
-            self.dateFlag = 0
-            self.timeFlag = 0
-
-        return (totalTime, self.dateFlag + self.timeFlag)
-
-
-    def inc(self, source, month=None, year=None):
-        """
-        Takes the given C{source} date, or current date if none is
-        passed, and increments it according to the values passed in
-        by month and/or year.
-
-        This routine is needed because Python's C{timedelta()} function
-        does not allow for month or year increments.
-
-        @type  source: struct_time
-        @param source: C{struct_time} value to increment
-        @type  month:  integer
-        @param month:  optional number of months to increment
-        @type  year:   integer
-        @param year:   optional number of years to increment
-
-        @rtype:  datetime
-        @return: C{source} incremented by the number of months and/or years
-        """
-        yr  = source.year
-        mth = source.month
-        dy  = source.day
-
-        if year:
-            try:
-                yi = int(year)
-            except ValueError:
-                yi = 0
-
-            yr += yi
-
-        if month:
-            try:
-                mi = int(month)
-            except ValueError:
-                mi = 0
-
-            m = abs(mi)
-            y = m / 12      # how many years are in month increment
-            m = m % 12      # get remaining months
-
-            if mi < 0:
-                mth = mth - m           # sub months from start month
-                if mth < 1:             # cross start-of-year?
-                    y   -= 1            #   yes - decrement year
-                    mth += 12           #         and fix month
-            else:
-                mth = mth + m           # add months to start month
-                if mth > 12:            # cross end-of-year?
-                    y   += 1            #   yes - increment year
-                    mth -= 12           #         and fix month
-
-            yr += y
-
-            # if the day ends up past the last day of
-            # the new month, set it to the last day
-            if dy > self.ptc.daysInMonth(mth, yr):
-                dy = self.ptc.daysInMonth(mth, yr)
-
-        d = source.replace(year=yr, month=mth, day=dy)
-
-        return source + (d - source)
-
+# Backward compatibility fix.
+from . import *
--- a/MoinMoin/support/parsedatetime/parsedatetime_consts.py	Mon Sep 05 23:55:33 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1119 +0,0 @@
-#!/usr/bin/env python
-
-"""
-parsedatetime constants and helper functions to determine
-regex values from Locale information if present.
-
-Also contains the internal Locale classes to give some sane
-defaults if PyICU is not found.
-"""
-
-__license__ = """
-Copyright (c) 2004-2008 Mike Taylor
-Copyright (c) 2006-2008 Darshana Chhajed
-Copyright (c)      2007 Bernd Zeimetz <bzed@debian.org>
-All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-"""
-
-try:
-    import PyICU as pyicu
-except:
-    pyicu = None
-
-
-import datetime
-import calendar
-import time
-import re
-
-
-class pdtLocale_en:
-    """
-    en_US Locale constants
-
-    This class will be used to initialize L{Constants} if PyICU is not located.
-
-    Defined as class variables are the lists and strings needed by parsedatetime
-    to evaluate strings for USA
-    """
-
-    localeID      = 'en_US'   # don't use a unicode string
-    dateSep       = [ u'/', u'.' ]
-    timeSep       = [ u':' ]
-    meridian      = [ u'AM', u'PM' ]
-    usesMeridian  = True
-    uses24        = False
-
-    Weekdays      = [ u'monday', u'tuesday', u'wednesday',
-                      u'thursday', u'friday', u'saturday', u'sunday',
-                    ]
-    shortWeekdays = [ u'mon', u'tues', u'wed',
-                      u'thu', u'fri', u'sat', u'sun',
-                    ]
-    Months        = [ u'january', u'february', u'march',
-                      u'april',   u'may',      u'june',
-                      u'july',    u'august',   u'september',
-                      u'october', u'november', u'december',
-                    ]
-    shortMonths   = [ u'jan', u'feb', u'mar',
-                      u'apr', u'may', u'jun',
-                      u'jul', u'aug', u'sep',
-                      u'oct', u'nov', u'dec',
-                    ]
-    dateFormats   = { 'full':   'EEEE, MMMM d, yyyy',
-                      'long':   'MMMM d, yyyy',
-                      'medium': 'MMM d, yyyy',
-                      'short':  'M/d/yy',
-                    }
-    timeFormats   = { 'full':   'h:mm:ss a z',
-                      'long':   'h:mm:ss a z',
-                      'medium': 'h:mm:ss a',
-                      'short':  'h:mm a',
-                    }
-
-    dp_order = [ u'm', u'd', u'y' ]
-
-      # this will be added to re_consts later
-    units = { 'seconds': [ 'second', 'sec' ],
-              'minutes': [ 'minute', 'min' ],
-              'hours':   [ 'hour',   'hr'  ],
-              'days':    [ 'day',    'dy'  ],
-              'weeks':   [ 'week',   'wk'  ],
-              'months':  [ 'month',  'mth' ],
-              'years':   [ 'year',   'yr'  ],
-            }
-
-      # text constants to be used by regex's later
-    re_consts     = { 'specials':       'in|on|of|at',
-                      'timeseperator':  ':',
-                      'rangeseperator': '-',
-                      'daysuffix':      'rd|st|nd|th',
-                      'meridian':       'am|pm|a.m.|p.m.|a|p',
-                      'qunits':         'h|m|s|d|w|m|y',
-                      'now':            [ 'now' ],
-                    }
-
-      # Used to adjust the returned date before/after the source
-    modifiers = { 'from':       1,
-                  'before':    -1,
-                  'after':      1,
-                  'ago':       -1,
-                  'prior':     -1,
-                  'prev':      -1,
-                  'last':      -1,
-                  'next':       1,
-                  'previous':  -1,
-                  'in a':       2,
-                  'end of':     0,
-                  'eod':        0,
-                  'eo':         0
-                }
-
-    dayoffsets = { 'tomorrow':   1,
-                   'today':      0,
-                   'yesterday': -1,
-                 }
-
-      # special day and/or times, i.e. lunch, noon, evening
-      # each element in the dictionary is a dictionary that is used
-      # to fill in any value to be replace - the current date/time will
-      # already have been populated by the method buildSources
-    re_sources    = { 'noon':      { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      'lunch':     { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      'morning':   { 'hr':  6, 'mn': 0, 'sec': 0 },
-                      'breakfast': { 'hr':  8, 'mn': 0, 'sec': 0 },
-                      'dinner':    { 'hr': 19, 'mn': 0, 'sec': 0 },
-                      'evening':   { 'hr': 18, 'mn': 0, 'sec': 0 },
-                      'midnight':  { 'hr':  0, 'mn': 0, 'sec': 0 },
-                      'night':     { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      'tonight':   { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      'eod':       { 'hr': 17, 'mn': 0, 'sec': 0 },
-                    }
-
-
-class pdtLocale_au:
-    """
-    en_AU Locale constants
-
-    This class will be used to initialize L{Constants} if PyICU is not located.
-
-    Defined as class variables are the lists and strings needed by parsedatetime
-    to evaluate strings for Australia
-    """
-
-    localeID      = 'en_AU'   # don't use a unicode string
-    dateSep       = [ u'-', u'/' ]
-    timeSep       = [ u':' ]
-    meridian      = [ u'AM', u'PM' ]
-    usesMeridian  = True
-    uses24        = False
-
-    Weekdays      = [ u'monday', u'tuesday', u'wednesday',
-                      u'thursday', u'friday', u'saturday', u'sunday',
-                    ]
-    shortWeekdays = [ u'mon', u'tues', u'wed',
-                      u'thu', u'fri', u'sat', u'sun',
-                    ]
-    Months        = [ u'january', u'february', u'march',
-                      u'april',   u'may',      u'june',
-                      u'july',    u'august',   u'september',
-                      u'october', u'november', u'december',
-                    ]
-    shortMonths   = [ u'jan', u'feb', u'mar',
-                      u'apr', u'may', u'jun',
-                      u'jul', u'aug', u'sep',
-                      u'oct', u'nov', u'dec',
-                    ]
-    dateFormats   = { 'full':   'EEEE, d MMMM yyyy',
-                      'long':   'd MMMM yyyy',
-                      'medium': 'dd/MM/yyyy',
-                      'short':  'd/MM/yy',
-                    }
-    timeFormats   = { 'full':   'h:mm:ss a z',
-                      'long':   'h:mm:ss a',
-                      'medium': 'h:mm:ss a',
-                      'short':  'h:mm a',
-                    }
-
-    dp_order = [ u'd', u'm', u'y' ]
-
-      # this will be added to re_consts later
-    units = { 'seconds': [ 'second', 'sec' ],
-              'minutes': [ 'minute', 'min' ],
-              'hours':   [ 'hour',   'hr'  ],
-              'days':    [ 'day',    'dy'  ],
-              'weeks':   [ 'week',   'wk'  ],
-              'months':  [ 'month',  'mth' ],
-              'years':   [ 'year',   'yr'  ],
-            }
-
-      # text constants to be used by regex's later
-    re_consts     = { 'specials':       'in|on|of|at',
-                      'timeseperator':  ':',
-                      'rangeseperator': '-',
-                      'daysuffix':      'rd|st|nd|th',
-                      'meridian':       'am|pm|a.m.|p.m.|a|p',
-                      'qunits':         'h|m|s|d|w|m|y',
-                      'now':            [ 'now' ],
-                    }
-
-      # Used to adjust the returned date before/after the source
-    modifiers = { 'from':       1,
-                  'before':    -1,
-                  'after':      1,
-                  'ago':        1,
-                  'prior':     -1,
-                  'prev':      -1,
-                  'last':      -1,
-                  'next':       1,
-                  'previous':  -1,
-                  'in a':       2,
-                  'end of':     0,
-                  'eo':         0,
-                }
-
-    dayoffsets = { 'tomorrow':   1,
-                   'today':      0,
-                   'yesterday': -1,
-                 }
-
-      # special day and/or times, i.e. lunch, noon, evening
-      # each element in the dictionary is a dictionary that is used
-      # to fill in any value to be replace - the current date/time will
-      # already have been populated by the method buildSources
-    re_sources    = { 'noon':      { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      'lunch':     { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      'morning':   { 'hr':  6, 'mn': 0, 'sec': 0 },
-                      'breakfast': { 'hr':  8, 'mn': 0, 'sec': 0 },
-                      'dinner':    { 'hr': 19, 'mn': 0, 'sec': 0 },
-                      'evening':   { 'hr': 18, 'mn': 0, 'sec': 0 },
-                      'midnight':  { 'hr':  0, 'mn': 0, 'sec': 0 },
-                      'night':     { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      'tonight':   { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      'eod':       { 'hr': 17, 'mn': 0, 'sec': 0 },
-                    }
-
-
-class pdtLocale_es:
-    """
-    es Locale constants
-
-    This class will be used to initialize L{Constants} if PyICU is not located.
-
-    Defined as class variables are the lists and strings needed by parsedatetime
-    to evaluate strings in Spanish
-
-    Note that I don't speak Spanish so many of the items below are still in English
-    """
-
-    localeID      = 'es'   # don't use a unicode string
-    dateSep       = [ u'/' ]
-    timeSep       = [ u':' ]
-    meridian      = []
-    usesMeridian  = False
-    uses24        = True
-
-    Weekdays      = [ u'lunes', u'martes', u'mi\xe9rcoles',
-                      u'jueves', u'viernes', u's\xe1bado', u'domingo',
-                    ]
-    shortWeekdays = [ u'lun', u'mar', u'mi\xe9',
-                      u'jue', u'vie', u's\xe1b', u'dom',
-                    ]
-    Months        = [ u'enero', u'febrero', u'marzo',
-                      u'abril', u'mayo', u'junio',
-                      u'julio', u'agosto', u'septiembre',
-                      u'octubre', u'noviembre', u'diciembre'
-                    ]
-    shortMonths   = [ u'ene', u'feb', u'mar',
-                      u'abr', u'may', u'jun',
-                      u'jul', u'ago', u'sep',
-                      u'oct', u'nov', u'dic'
-                    ]
-    dateFormats   = { 'full':   "EEEE d' de 'MMMM' de 'yyyy",
-                      'long':   "d' de 'MMMM' de 'yyyy",
-                      'medium': "dd-MMM-yy",
-                      'short':  "d/MM/yy",
-                    }
-    timeFormats   = { 'full':   "HH'H'mm' 'ss z",
-                      'long':   "HH:mm:ss z",
-                      'medium': "HH:mm:ss",
-                      'short':  "HH:mm",
-                    }
-
-    dp_order = [ u'd', u'm', u'y' ]
-
-      # this will be added to re_consts later
-    units = { 'seconds': [ 'second', 'sec' ],
-              'minutes': [ 'minute', 'min' ],
-              'hours':   [ 'hour',   'hr'  ],
-              'days':    [ 'day',    'dy'  ],
-              'weeks':   [ 'week',   'wk'  ],
-              'months':  [ 'month',  'mth' ],
-              'years':   [ 'year',   'yr'  ],
-            }
-
-      # text constants to be used by regex's later
-    re_consts     = { 'specials':       'in|on|of|at',
-                      'timeseperator':  timeSep,
-                      'dateseperator':  dateSep,
-                      'rangeseperator': '-',
-                      'daysuffix':      'rd|st|nd|th',
-                      'qunits':         'h|m|s|d|w|m|y',
-                      'now':            [ 'now' ],
-                    }
-
-      # Used to adjust the returned date before/after the source
-    modifiers = { 'from':      1,
-                  'before':   -1,
-                  'after':     1,
-                  'ago':       1,
-                  'prior':    -1,
-                  'prev':     -1,
-                  'last':     -1,
-                  'next':      1,
-                  'previous': -1,
-                  'in a':      2,
-                  'end of':    0,
-                  'eo':        0,
-                }
-
-    dayoffsets = { 'tomorrow':   1,
-                   'today':      0,
-                   'yesterday': -1,
-                 }
-
-      # special day and/or times, i.e. lunch, noon, evening
-      # each element in the dictionary is a dictionary that is used
-      # to fill in any value to be replace - the current date/time will
-      # already have been populated by the method buildSources
-    re_sources    = { 'noon':      { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      'lunch':     { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      'morning':   { 'hr':  6, 'mn': 0, 'sec': 0 },
-                      'breakfast': { 'hr':  8, 'mn': 0, 'sec': 0 },
-                      'dinner':    { 'hr': 19, 'mn': 0, 'sec': 0 },
-                      'evening':   { 'hr': 18, 'mn': 0, 'sec': 0 },
-                      'midnight':  { 'hr':  0, 'mn': 0, 'sec': 0 },
-                      'night':     { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      'tonight':   { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      'eod':       { 'hr': 17, 'mn': 0, 'sec': 0 },
-                    }
-
-
-class pdtLocale_de:
-    """
-    de_DE Locale constants
-
-    This class will be used to initialize L{Constants} if PyICU is not located.
-
-    Contributed by Debian parsedatetime package maintainer Bernd Zeimetz <bzed@debian.org>
-
-    Defined as class variables are the lists and strings needed by parsedatetime
-    to evaluate strings for German
-    """
-
-    localeID      = 'de_DE'   # don't use a unicode string
-    dateSep       = [ u'.' ]
-    timeSep       = [ u':' ]
-    meridian      = [ ]
-    usesMeridian  = False
-    uses24        = True
-
-    Weekdays      = [ u'montag', u'dienstag', u'mittwoch',
-                      u'donnerstag', u'freitag', u'samstag', u'sonntag',
-                    ]
-    shortWeekdays = [ u'mo', u'di', u'mi',
-                      u'do', u'fr', u'sa', u'so',
-                    ]
-    Months        = [ u'januar',  u'februar',  u'm\xe4rz',
-                      u'april',   u'mai',      u'juni',
-                      u'juli',    u'august',   u'september',
-                      u'oktober', u'november', u'dezember',
-                    ]
-    shortMonths   = [ u'jan', u'feb', u'mrz',
-                      u'apr', u'mai', u'jun',
-                      u'jul', u'aug', u'sep',
-                      u'okt', u'nov', u'dez',
-                    ]
-    dateFormats   = { 'full':   u'EEEE, d. MMMM yyyy',
-                      'long':   u'd. MMMM yyyy',
-                      'medium': u'dd.MM.yyyy',
-                      'short':  u'dd.MM.yy'
-                    }
-
-    timeFormats   = { 'full':   u'HH:mm:ss v',
-                      'long':   u'HH:mm:ss z',
-                      'medium': u'HH:mm:ss',
-                      'short':  u'HH:mm'
-                    }
-
-    dp_order = [ u'd', u'm', u'y' ]
-
-      # this will be added to re_consts later
-    units = { 'seconds': [ 'sekunden', 'sek',  's' ],
-              'minutes': [ 'minuten',  'min' , 'm' ],
-              'hours':   [ 'stunden',  'std',  'h' ],
-              'days':    [ 'tage',     't' ],
-              'weeks':   [ 'wochen',   'w' ],
-              'months':  [ 'monate' ], #the short version would be a capital M,
-                                       #as I understand it we can't distinguis
-                                       #between m for minutes and M for months.
-              'years':   [ 'jahre',    'j' ],
-            }
-
-      # text constants to be used by regex's later
-    re_consts     = { 'specials':       'am|dem|der|im|in|den|zum',
-                      'timeseperator':  ':',
-                      'rangeseperator': '-',
-                      'daysuffix':      '',
-                      'qunits':         'h|m|s|t|w|m|j',
-                      'now':            [ 'jetzt' ],
-                    }
-
-      # Used to adjust the returned date before/after the source
-      #still looking for insight on how to translate all of them to german.
-    modifiers = { u'from':         1,
-                  u'before':      -1,
-                  u'after':        1,
-                  u'vergangener': -1,
-                  u'vorheriger':  -1,
-                  u'prev':        -1,
-                  u'letzter':     -1,
-                  u'n\xe4chster':  1,
-                  u'dieser':       0,
-                  u'previous':    -1,
-                  u'in a':         2,
-                  u'end of':       0,
-                  u'eod':          0,
-                  u'eo':           0,
-                }
-
-     #morgen/abermorgen does not work, see http://code.google.com/p/parsedatetime/issues/detail?id=19
-    dayoffsets = { u'morgen':        1,
-                   u'heute':         0,
-                   u'gestern':      -1,
-                   u'vorgestern':   -2,
-                   u'\xfcbermorgen': 2,
-                 }
-
-      # special day and/or times, i.e. lunch, noon, evening
-      # each element in the dictionary is a dictionary that is used
-      # to fill in any value to be replace - the current date/time will
-      # already have been populated by the method buildSources
-    re_sources    = { u'mittag':      { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      u'mittags':     { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      u'mittagessen': { 'hr': 12, 'mn': 0, 'sec': 0 },
-                      u'morgen':      { 'hr':  6, 'mn': 0, 'sec': 0 },
-                      u'morgens':     { 'hr':  6, 'mn': 0, 'sec': 0 },
-                      u'fr\e4hst\xe4ck': { 'hr':  8, 'mn': 0, 'sec': 0 },
-                      u'abendessen':  { 'hr': 19, 'mn': 0, 'sec': 0 },
-                      u'abend':       { 'hr': 18, 'mn': 0, 'sec': 0 },
-                      u'abends':      { 'hr': 18, 'mn': 0, 'sec': 0 },
-                      u'mitternacht': { 'hr':  0, 'mn': 0, 'sec': 0 },
-                      u'nacht':       { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      u'nachts':      { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      u'heute abend': { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      u'heute nacht': { 'hr': 21, 'mn': 0, 'sec': 0 },
-                      u'feierabend':  { 'hr': 17, 'mn': 0, 'sec': 0 },
-                    }
-
-
-pdtLocales = { 'en_US': pdtLocale_en,
-               'en_AU': pdtLocale_au,
-               'es_ES': pdtLocale_es,
-               'de_DE': pdtLocale_de,
-             }
-
-
-def _initLocale(ptc):
-    """
-    Helper function to initialize the different lists and strings
-    from either PyICU or one of the internal pdt Locales and store
-    them into ptc.
-    """
-
-    def lcase(x):
-        return x.lower()
-
-    if pyicu and ptc.usePyICU:
-        ptc.icuLocale = None
-
-        if ptc.localeID is not None:
-            ptc.icuLocale = pyicu.Locale(ptc.localeID)
-
-        if ptc.icuLocale is None:
-            for id in range(0, len(ptc.fallbackLocales)):
-                ptc.localeID  = ptc.fallbackLocales[id]
-                ptc.icuLocale = pyicu.Locale(ptc.localeID)
-
-                if ptc.icuLocale is not None:
-                    break
-
-        ptc.icuSymbols = pyicu.DateFormatSymbols(ptc.icuLocale)
-
-          # grab ICU list of weekdays, skipping first entry which
-          # is always blank
-        wd  = map(lcase, ptc.icuSymbols.getWeekdays()[1:])
-        swd = map(lcase, ptc.icuSymbols.getShortWeekdays()[1:])
-
-          # store them in our list with Monday first (ICU puts Sunday first)
-        ptc.Weekdays      = wd[1:] + wd[0:1]
-        ptc.shortWeekdays = swd[1:] + swd[0:1]
-        ptc.Months        = map(lcase, ptc.icuSymbols.getMonths())
-        ptc.shortMonths   = map(lcase, ptc.icuSymbols.getShortMonths())
-
-          # not quite sure how to init this so for now
-          # set it to none so it will be set to the en_US defaults for now
-        ptc.re_consts   = None
-        ptc.icu_df      = { 'full':   pyicu.DateFormat.createDateInstance(pyicu.DateFormat.kFull,   ptc.icuLocale),
-                            'long':   pyicu.DateFormat.createDateInstance(pyicu.DateFormat.kLong,   ptc.icuLocale),
-                            'medium': pyicu.DateFormat.createDateInstance(pyicu.DateFormat.kMedium, ptc.icuLocale),
-                            'short':  pyicu.DateFormat.createDateInstance(pyicu.DateFormat.kShort,  ptc.icuLocale),
-                          }
-        ptc.icu_tf      = { 'full':   pyicu.DateFormat.createTimeInstance(pyicu.DateFormat.kFull,   ptc.icuLocale),
-                            'long':   pyicu.DateFormat.createTimeInstance(pyicu.DateFormat.kLong,   ptc.icuLocale),
-                            'medium': pyicu.DateFormat.createTimeInstance(pyicu.DateFormat.kMedium, ptc.icuLocale),
-                            'short':  pyicu.DateFormat.createTimeInstance(pyicu.DateFormat.kShort,  ptc.icuLocale),
-                          }
-        ptc.dateFormats = { 'full':   ptc.icu_df['full'].toPattern(),
-                            'long':   ptc.icu_df['long'].toPattern(),
-                            'medium': ptc.icu_df['medium'].toPattern(),
-                            'short':  ptc.icu_df['short'].toPattern(),
-                          }
-        ptc.timeFormats = { 'full':   ptc.icu_tf['full'].toPattern(),
-                            'long':   ptc.icu_tf['long'].toPattern(),
-                            'medium': ptc.icu_tf['medium'].toPattern(),
-                            'short':  ptc.icu_tf['short'].toPattern(),
-                          }
-    else:
-        if not ptc.localeID in pdtLocales:
-            for id in range(0, len(ptc.fallbackLocales)):
-                ptc.localeID  = ptc.fallbackLocales[id]
-
-                if ptc.localeID in pdtLocales:
-                    break
-
-        ptc.locale   = pdtLocales[ptc.localeID]
-        ptc.usePyICU = False
-
-        ptc.Weekdays      = ptc.locale.Weekdays
-        ptc.shortWeekdays = ptc.locale.shortWeekdays
-        ptc.Months        = ptc.locale.Months
-        ptc.shortMonths   = ptc.locale.shortMonths
-        ptc.dateFormats   = ptc.locale.dateFormats
-        ptc.timeFormats   = ptc.locale.timeFormats
-
-      # these values are used to setup the various bits 
-      # of the regex values used to parse
-      #
-      # check if a local set of constants has been
-      # provided, if not use en_US as the default
-    if ptc.localeID in pdtLocales:
-        ptc.re_sources = pdtLocales[ptc.localeID].re_sources
-        ptc.re_values  = pdtLocales[ptc.localeID].re_consts
-
-        units = pdtLocales[ptc.localeID].units
-
-        ptc.Modifiers  = pdtLocales[ptc.localeID].modifiers
-        ptc.dayOffsets = pdtLocales[ptc.localeID].dayoffsets
-
-          # for now, pull over any missing keys from the US set
-        for key in pdtLocales['en_US'].re_consts:
-            if not key in ptc.re_values:
-                ptc.re_values[key] = pdtLocales['en_US'].re_consts[key]
-    else:
-        ptc.re_sources = pdtLocales['en_US'].re_sources
-        ptc.re_values  = pdtLocales['en_US'].re_consts
-        ptc.Modifiers  = pdtLocales['en_US'].modifiers
-        ptc.dayOffsets = pdtLocales['en_US'].dayoffsets
-        units          = pdtLocales['en_US'].units
-
-      # escape any regex special characters that may be found
-    wd   = tuple(map(re.escape, ptc.Weekdays))
-    swd  = tuple(map(re.escape, ptc.shortWeekdays))
-    mth  = tuple(map(re.escape, ptc.Months))
-    smth = tuple(map(re.escape, ptc.shortMonths))
-
-    ptc.re_values['months']      = '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' % mth
-    ptc.re_values['shortmonths'] = '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' % smth
-    ptc.re_values['days']        = '%s|%s|%s|%s|%s|%s|%s' % wd
-    ptc.re_values['shortdays']   = '%s|%s|%s|%s|%s|%s|%s' % swd
-
-    l = []
-    for unit in units:
-        l.append('|'.join(units[unit]))
-
-    ptc.re_values['units'] = '|'.join(l)
-    ptc.Units              = ptc.re_values['units'].split('|')
-
-
-def _initSymbols(ptc):
-    """
-    Helper function to initialize the single character constants
-    and other symbols needed.
-    """
-    ptc.timeSep  = [ u':' ]
-    ptc.dateSep  = [ u'/' ]
-    ptc.meridian = [ u'AM', u'PM' ]
-
-    ptc.usesMeridian = True
-    ptc.uses24       = False
-
-    if pyicu and ptc.usePyICU:
-        am = u''
-        pm = u''
-        ts = ''
-
-        # ICU doesn't seem to provide directly the
-        # date or time seperator - so we have to
-        # figure it out
-        o = ptc.icu_tf['short']
-        s = ptc.timeFormats['short']
-
-        ptc.usesMeridian = u'a' in s
-        ptc.uses24       = u'H' in s
-
-        # '11:45 AM' or '11:45'
-        s = o.format(datetime.datetime(2003, 10, 30, 11, 45))
-
-        # ': AM' or ':'
-        s = s.replace('11', '').replace('45', '')
-
-        if len(s) > 0:
-            ts = s[0]
-
-        if ptc.usesMeridian:
-            # '23:45 AM' or '23:45'
-            am = s[1:].strip()
-            s  = o.format(datetime.datetime(2003, 10, 30, 23, 45))
-
-            if ptc.uses24:
-                s = s.replace('23', '')
-            else:
-                s = s.replace('11', '')
-
-            # 'PM' or ''
-            pm = s.replace('45', '').replace(ts, '').strip()
-
-        ptc.timeSep  = [ ts ]
-        ptc.meridian = [ am, pm ]
-
-        o = ptc.icu_df['short']
-        s = o.format(datetime.datetime(2003, 10, 30, 11, 45))
-        s = s.replace('10', '').replace('30', '').replace('03', '').replace('2003', '')
-
-        if len(s) > 0:
-            ds = s[0]
-        else:
-            ds = '/'
-
-        ptc.dateSep = [ ds ]
-        s           = ptc.dateFormats['short']
-        l           = s.lower().split(ds)
-        dp_order    = []
-
-        for s in l:
-            if len(s) > 0:
-                dp_order.append(s[:1])
-
-        ptc.dp_order = dp_order
-    else:
-        ptc.timeSep      = ptc.locale.timeSep
-        ptc.dateSep      = ptc.locale.dateSep
-        ptc.meridian     = ptc.locale.meridian
-        ptc.usesMeridian = ptc.locale.usesMeridian
-        ptc.uses24       = ptc.locale.uses24
-        ptc.dp_order     = ptc.locale.dp_order
-
-      # build am and pm lists to contain
-      # original case, lowercase and first-char
-      # versions of the meridian text
-
-    if len(ptc.meridian) > 0:
-        am     = ptc.meridian[0]
-        ptc.am = [ am ]
-
-        if len(am) > 0:
-            ptc.am.append(am[0])
-            am = am.lower()
-            ptc.am.append(am)
-            ptc.am.append(am[0])
-    else:
-        am     = ''
-        ptc.am = [ '', '' ]
-
-    if len(ptc.meridian) > 1:
-        pm     = ptc.meridian[1]
-        ptc.pm = [ pm ]
-
-        if len(pm) > 0:
-            ptc.pm.append(pm[0])
-            pm = pm.lower()
-            ptc.pm.append(pm)
-            ptc.pm.append(pm[0])
-    else:
-        pm     = ''
-        ptc.pm = [ '', '' ]
-
-
-def _initPatterns(ptc):
-    """
-    Helper function to take the different localized bits from ptc and
-    create the regex strings.
-    """
-    # TODO add code to parse the date formats and build the regexes up from sub-parts
-    # TODO find all hard-coded uses of date/time seperators
-
-    ptc.RE_DATE4     = r'''(?P<date>(((?P<day>\d\d?)(?P<suffix>%(daysuffix)s)?(,)?(\s)?)
-                                      (?P<mthname>(%(months)s|%(shortmonths)s))\s?
-                                      (?P<year>\d\d(\d\d)?)?
-                                    )
-                           )''' % ptc.re_values
-
-    # I refactored DATE3 to fix Issue 16 http://code.google.com/p/parsedatetime/issues/detail?id=16
-    # I suspect the final line was for a trailing time - but testing shows it's not needed
-    # ptc.RE_DATE3     = r'''(?P<date>((?P<mthname>(%(months)s|%(shortmonths)s))\s?
-    #                                  ((?P<day>\d\d?)(\s?|%(daysuffix)s|$)+)?
-    #                                  (,\s?(?P<year>\d\d(\d\d)?))?))
-    #                        (\s?|$|[^0-9a-zA-Z])''' % ptc.re_values
-    ptc.RE_DATE3     = r'''(?P<date>(
-                                     (((?P<mthname>(%(months)s|%(shortmonths)s))|
-                                     ((?P<day>\d\d?)(?P<suffix>%(daysuffix)s)?))(\s)?){1,2}
-                                     ((,)?(\s)?(?P<year>\d\d(\d\d)?))?
-                                    )
-                           )''' % ptc.re_values
-    ptc.RE_MONTH     = r'''(\s?|^)
-                           (?P<month>(
-                                      (?P<mthname>(%(months)s|%(shortmonths)s))
-                                      (\s?(?P<year>(\d\d\d\d)))?
-                                     ))
-                           (\s?|$|[^0-9a-zA-Z])''' % ptc.re_values
-    ptc.RE_WEEKDAY   = r'''(\s?|^)
-                           (?P<weekday>(%(days)s|%(shortdays)s))
-                           (\s?|$|[^0-9a-zA-Z])''' % ptc.re_values
-
-    ptc.RE_SPECIAL   = r'(?P<special>^[%(specials)s]+)\s+' % ptc.re_values
-    ptc.RE_UNITS     = r'''(?P<qty>(-?\d+\s*
-                                    (?P<units>((%(units)s)s?))
-                                   ))''' % ptc.re_values
-    ptc.RE_QUNITS    = r'''(?P<qty>(-?\d+\s?
-                                    (?P<qunits>%(qunits)s)
-                                    (\s?|,|$)
-                                   ))''' % ptc.re_values
-    ptc.RE_MODIFIER  = r'''(\s?|^)
-                           (?P<modifier>
-                            (previous|prev|last|next|eod|eo|(end\sof)|(in\sa)))''' % ptc.re_values
-    ptc.RE_MODIFIER2 = r'''(\s?|^)
-                           (?P<modifier>
-                            (from|before|after|ago|prior))
-                           (\s?|$|[^0-9a-zA-Z])''' % ptc.re_values
-    ptc.RE_TIMEHMS   = r'''(\s?|^)
-                           (?P<hours>\d\d?)
-                           (?P<tsep>%(timeseperator)s|)
-                           (?P<minutes>\d\d)
-                           (?:(?P=tsep)(?P<seconds>\d\d(?:[.,]\d+)?))?''' % ptc.re_values
-    ptc.RE_TIMEHMS2  = r'''(?P<hours>(\d\d?))
-                           ((?P<tsep>%(timeseperator)s|)
-                            (?P<minutes>(\d\d?))
-                            (?:(?P=tsep)
-                               (?P<seconds>\d\d?
-                                (?:[.,]\d+)?))?)?''' % ptc.re_values
-
-    if 'meridian' in ptc.re_values:
-        ptc.RE_TIMEHMS2 += r'\s?(?P<meridian>(%(meridian)s))' % ptc.re_values
-
-    dateSeps = ''.join(ptc.dateSep) + '.'
-
-    ptc.RE_DATE      = r'''(\s?|^)
-                           (?P<date>(\d\d?[%s]\d\d?([%s]\d\d(\d\d)?)?))
-                           (\s?|$|[^0-9a-zA-Z])''' % (dateSeps, dateSeps)
-    ptc.RE_DATE2     = r'[%s]' % dateSeps
-    ptc.RE_DAY       = r'''(\s?|^)
-                           (?P<day>(today|tomorrow|yesterday))
-                           (\s?|$|[^0-9a-zA-Z])''' % ptc.re_values
-    ptc.RE_DAY2      = r'''(?P<day>\d\d?)|(?P<suffix>%(daysuffix)s)
-                        ''' % ptc.re_values
-    ptc.RE_TIME      = r'''(\s?|^)
-                           (?P<time>(morning|breakfast|noon|lunch|evening|midnight|tonight|dinner|night|now))
-                           (\s?|$|[^0-9a-zA-Z])''' % ptc.re_values
-    ptc.RE_REMAINING = r'\s+'
-
-    # Regex for date/time ranges
-    ptc.RE_RTIMEHMS  = r'''(\s?|^)
-                           (\d\d?)%(timeseperator)s
-                           (\d\d)
-                           (%(timeseperator)s(\d\d))?
-                           (\s?|$)''' % ptc.re_values
-    ptc.RE_RTIMEHMS2 = r'''(\s?|^)
-                           (\d\d?)
-                           (%(timeseperator)s(\d\d?))?
-                           (%(timeseperator)s(\d\d?))?''' % ptc.re_values
-
-    if 'meridian' in ptc.re_values:
-        ptc.RE_RTIMEHMS2 += r'\s?(%(meridian)s)' % ptc.re_values
-
-    ptc.RE_RDATE  = r'(\d+([%s]\d+)+)' % dateSeps
-    ptc.RE_RDATE3 = r'''((((%(months)s))\s?
-                         ((\d\d?)
-                          (\s?|%(daysuffix)s|$)+)?
-                         (,\s?\d\d\d\d)?))''' % ptc.re_values
-
-    # "06/07/06 - 08/09/06"
-    ptc.DATERNG1 = ptc.RE_RDATE + r'\s?%(rangeseperator)s\s?' + ptc.RE_RDATE
-    ptc.DATERNG1 = ptc.DATERNG1 % ptc.re_values
-
-    # "march 31 - june 1st, 2006"
-    ptc.DATERNG2 = ptc.RE_RDATE3 + r'\s?%(rangeseperator)s\s?' + ptc.RE_RDATE3
-    ptc.DATERNG2 = ptc.DATERNG2 % ptc.re_values
-
-    # "march 1rd -13th"
-    ptc.DATERNG3 = ptc.RE_RDATE3 + r'\s?%(rangeseperator)s\s?(\d\d?)\s?(rd|st|nd|th)?'
-    ptc.DATERNG3 = ptc.DATERNG3 % ptc.re_values
-
-    # "4:00:55 pm - 5:90:44 am", '4p-5p'
-    ptc.TIMERNG1 = ptc.RE_RTIMEHMS2 + r'\s?%(rangeseperator)s\s?' + ptc.RE_RTIMEHMS2
-    ptc.TIMERNG1 = ptc.TIMERNG1 % ptc.re_values
-
-    # "4:00 - 5:90 ", "4:55:55-3:44:55"
-    ptc.TIMERNG2 = ptc.RE_RTIMEHMS + r'\s?%(rangeseperator)s\s?' + ptc.RE_RTIMEHMS
-    ptc.TIMERNG2 = ptc.TIMERNG2 % ptc.re_values
-
-    # "4-5pm "
-    ptc.TIMERNG3 = r'\d\d?\s?%(rangeseperator)s\s?' + ptc.RE_RTIMEHMS2
-    ptc.TIMERNG3 = ptc.TIMERNG3 % ptc.re_values
-
-    # "4:30-5pm "
-    ptc.TIMERNG4 = ptc.RE_RTIMEHMS + r'\s?%(rangeseperator)s\s?' + ptc.RE_RTIMEHMS2
-    ptc.TIMERNG4 = ptc.TIMERNG4 % ptc.re_values
-
-
-def _initConstants(ptc):
-    """
-    Create localized versions of the units, week and month names
-    """
-      # build weekday offsets - yes, it assumes the Weekday and shortWeekday
-      # lists are in the same order and Mon..Sun (Python style)
-    ptc.WeekdayOffsets = {}
-
-    o = 0
-    for key in ptc.Weekdays:
-        ptc.WeekdayOffsets[key] = o
-        o += 1
-    o = 0
-    for key in ptc.shortWeekdays:
-        ptc.WeekdayOffsets[key] = o
-        o += 1
-
-      # build month offsets - yes, it assumes the Months and shortMonths
-      # lists are in the same order and Jan..Dec
-    ptc.MonthOffsets = {}
-
-    o = 1
-    for key in ptc.Months:
-        ptc.MonthOffsets[key] = o
-        o += 1
-    o = 1
-    for key in ptc.shortMonths:
-        ptc.MonthOffsets[key] = o
-        o += 1
-
-    # ptc.DaySuffixes = ptc.re_consts['daysuffix'].split('|')
-
-
-class Constants:
-    """
-    Default set of constants for parsedatetime.
-
-    If PyICU is present, then the class will first try to get PyICU
-    to return a locale specified by C{localeID}.  If either C{localeID} is
-    None or if the locale does not exist within PyICU, then each of the
-    locales defined in C{fallbackLocales} is tried in order.
-
-    If PyICU is not present or none of the specified locales can be used,
-    then the class will initialize itself to the en_US locale.
-
-    if PyICU is not present or not requested, only the locales defined by
-    C{pdtLocales} will be searched.
-    """
-    def __init__(self, localeID=None, usePyICU=True, fallbackLocales=['en_US']):
-        self.localeID        = localeID
-        self.fallbackLocales = fallbackLocales
-
-        if 'en_US' not in self.fallbackLocales:
-            self.fallbackLocales.append('en_US')
-
-          # define non-locale specific constants
-
-        self.locale   = None
-        self.usePyICU = usePyICU
-
-        # starting cache of leap years
-        # daysInMonth will add to this if during
-        # runtime it gets a request for a year not found
-        self._leapYears = [ 1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 1940, 1944,
-                            1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988,
-                            1992, 1996, 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032,
-                            2036, 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076,
-                            2080, 2084, 2088, 2092, 2096 ]
-
-        self.Second =   1
-        self.Minute =  60 * self.Second
-        self.Hour   =  60 * self.Minute
-        self.Day    =  24 * self.Hour
-        self.Week   =   7 * self.Day
-        self.Month  =  30 * self.Day
-        self.Year   = 365 * self.Day
-
-        self.rangeSep = u'-'
-
-        self._DaysInMonthList = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
-
-        self.BirthdayEpoch = 50
-
-        # DOWParseStyle controls how we parse "Tuesday"
-        # If the current day was Thursday and the text to parse is "Tuesday"
-        # then the following table shows how each style would be returned
-        # -1, 0, +1
-        #
-        # Current day marked as ***
-        #
-        #          Sun Mon Tue Wed Thu Fri Sat
-        # week -1
-        # current         -1,0     ***
-        # week +1          +1
-        #
-        # If the current day was Monday and the text to parse is "Tuesday"
-        # then the following table shows how each style would be returned
-        # -1, 0, +1
-        #
-        #          Sun Mon Tue Wed Thu Fri Sat
-        # week -1           -1
-        # current      *** 0,+1
-        # week +1
-
-        self.DOWParseStyle = 1
-
-        # CurrentDOWParseStyle controls how we parse "Friday"
-        # If the current day was Friday and the text to parse is "Friday"
-        # then the following table shows how each style would be returned
-        # True/False. This also depends on DOWParseStyle.
-        #
-        # Current day marked as ***
-        #
-        # DOWParseStyle = 0
-        #          Sun Mon Tue Wed Thu Fri Sat
-        # week -1
-        # current                      T,F
-        # week +1
-        #
-        # DOWParseStyle = -1
-        #          Sun Mon Tue Wed Thu Fri Sat
-        # week -1                       F
-        # current                       T
-        # week +1
-        #
-        # DOWParseStyle = +1
-        #
-        #          Sun Mon Tue Wed Thu Fri Sat
-        # week -1
-        # current                       T
-        # week +1                       F
-
-        self.CurrentDOWParseStyle = False
-
-        # initalize attributes to empty values to ensure
-        # they are defined
-        self.re_sources     = None
-        self.re_values      = None
-        self.Modifiers      = None
-        self.dayOffsets     = None
-        self.WeekdayOffsets = None
-        self.MonthOffsets   = None
-        self.dateSep        = None
-        self.timeSep        = None
-        self.am             = None
-        self.pm             = None
-        self.meridian       = None
-        self.usesMeridian   = None
-        self.uses24         = None
-        self.dp_order       = None
-
-        self.RE_DATE4     = r''
-        self.RE_DATE3     = r''
-        self.RE_MONTH     = r''
-        self.RE_WEEKDAY   = r''
-        self.RE_SPECIAL   = r''
-        self.RE_UNITS     = r''
-        self.RE_QUNITS    = r''
-        self.RE_MODIFIER  = r''
-        self.RE_MODIFIER2 = r''
-        self.RE_TIMEHMS   = r''
-        self.RE_TIMEHMS2  = r''
-        self.RE_DATE      = r''
-        self.RE_DATE2     = r''
-        self.RE_DAY       = r''
-        self.RE_DAY2      = r''
-        self.RE_TIME      = r''
-        self.RE_REMAINING = r''
-        self.RE_RTIMEHMS  = r''
-        self.RE_RTIMEHMS2 = r''
-        self.RE_RDATE     = r''
-        self.RE_RDATE3    = r''
-        self.DATERNG1     = r''
-        self.DATERNG2     = r''
-        self.DATERNG3     = r''
-        self.TIMERNG1     = r''
-        self.TIMERNG2     = r''
-        self.TIMERNG3     = r''
-        self.TIMERNG4     = r''
-
-        _initLocale(self)
-        _initConstants(self)
-        _initSymbols(self)
-        _initPatterns(self)
-
-        self.re_option = re.IGNORECASE + re.VERBOSE
-        self.cre_source = { 'CRE_SPECIAL':   self.RE_SPECIAL,
-                            'CRE_UNITS':     self.RE_UNITS,
-                            'CRE_QUNITS':    self.RE_QUNITS,
-                            'CRE_MODIFIER':  self.RE_MODIFIER,
-                            'CRE_MODIFIER2': self.RE_MODIFIER2,
-                            'CRE_TIMEHMS':   self.RE_TIMEHMS,
-                            'CRE_TIMEHMS2':  self.RE_TIMEHMS2,
-                            'CRE_DATE':      self.RE_DATE,
-                            'CRE_DATE2':     self.RE_DATE2,
-                            'CRE_DATE3':     self.RE_DATE3,
-                            'CRE_DATE4':     self.RE_DATE4,
-                            'CRE_MONTH':     self.RE_MONTH,
-                            'CRE_WEEKDAY':   self.RE_WEEKDAY,
-                            'CRE_DAY':       self.RE_DAY,
-                            'CRE_DAY2':      self.RE_DAY2,
-                            'CRE_TIME':      self.RE_TIME,
-                            'CRE_REMAINING': self.RE_REMAINING,
-                            'CRE_RTIMEHMS':  self.RE_RTIMEHMS,
-                            'CRE_RTIMEHMS2': self.RE_RTIMEHMS2,
-                            'CRE_RDATE':     self.RE_RDATE,
-                            'CRE_RDATE3':    self.RE_RDATE3,
-                            'CRE_TIMERNG1':  self.TIMERNG1,
-                            'CRE_TIMERNG2':  self.TIMERNG2,
-                            'CRE_TIMERNG3':  self.TIMERNG3,
-                            'CRE_TIMERNG4':  self.TIMERNG4,
-                            'CRE_DATERNG1':  self.DATERNG1,
-                            'CRE_DATERNG2':  self.DATERNG2,
-                            'CRE_DATERNG3':  self.DATERNG3,
-                          }
-        self.cre_keys = self.cre_source.keys()
-
-
-    def __getattr__(self, name):
-        if name in self.cre_keys:
-            value = re.compile(self.cre_source[name], self.re_option)
-            setattr(self, name, value)
-            return value
-        else:
-            raise AttributeError, name
-
-    def daysInMonth(self, month, year):
-        """
-        Take the given month (1-12) and a given year (4 digit) return
-        the number of days in the month adjusting for leap year as needed
-        """
-        result = None
-
-        if month > 0 and month <= 12:
-            result = self._DaysInMonthList[month - 1]
-
-            if month == 2:
-                if year in self._leapYears:
-                    result += 1
-                else:
-                    if calendar.isleap(year):
-                        self._leapYears.append(year)
-                        result += 1
-
-        return result
-
-    def buildSources(self, sourceTime=None):
-        """
-        Return a dictionary of date/time tuples based on the keys
-        found in self.re_sources.
-
-        The current time is used as the default and any specified
-        item found in self.re_sources is inserted into the value
-        and the generated dictionary is returned.
-        """
-        if sourceTime is None:
-            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = time.localtime()
-        else:
-            (yr, mth, dy, hr, mn, sec, wd, yd, isdst) = sourceTime
-
-        sources  = {}
-        defaults = { 'yr': yr, 'mth': mth, 'dy':  dy,
-                     'hr': hr, 'mn':  mn,  'sec': sec, }
-
-        for item in self.re_sources:
-            values = {}
-            source = self.re_sources[item]
-
-            for key in defaults.keys():
-                if key in source:
-                    values[key] = source[key]
-                else:
-                    values[key] = defaults[key]
-
-            sources[item] = ( values['yr'], values['mth'], values['dy'],
-                              values['hr'], values['mn'], values['sec'], wd, yd, isdst )
-
-        return sources
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/__init__.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,30 @@
+# -*- encoding: utf-8 -*-
+
+"""
+pdt_locales
+
+All of the included locale classes shipped with pdt.
+"""
+
+from __future__ import absolute_import
+from .icu import get_icu
+
+locales = ['de_DE', 'en_AU', 'en_US', 'es', 'nl_NL', 'pt_BR', 'ru_RU']
+
+__locale_caches = {}
+
+__all__ = ['get_icu', 'load_locale']
+
+
+def load_locale(locale, icu=False):
+    """
+    Return data of locale
+    :param locale:
+    :return:
+    """
+    if locale not in locales:
+        raise NotImplementedError("The locale '%s' is not supported" % locale)
+    if locale not in __locale_caches:
+        mod = __import__(__name__, fromlist=[locale], level=0)
+        __locale_caches[locale] = getattr(mod, locale)
+    return __locale_caches[locale]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/base.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,199 @@
+from __future__ import unicode_literals
+
+locale_keys = set([
+    'MonthOffsets', 'Months', 'WeekdayOffsets', 'Weekdays',
+    'dateFormats', 'dateSep', 'dayOffsets', 'dp_order',
+    'localeID', 'meridian', 'Modifiers', 're_sources', 're_values',
+    'shortMonths', 'shortWeekdays', 'timeFormats', 'timeSep', 'units',
+    'uses24', 'usesMeridian', 'numbers', 'decimal_mark', 'small',
+    'magnitude', 'ignore'])
+
+localeID = None
+
+dateSep = ['/', '.']
+timeSep = [':']
+meridian = ['AM', 'PM']
+usesMeridian = True
+uses24 = True
+WeekdayOffsets = {}
+MonthOffsets = {}
+
+# always lowercase any lookup values - helper code expects that
+Weekdays = [
+    'monday', 'tuesday', 'wednesday', 'thursday',
+    'friday', 'saturday', 'sunday',
+]
+
+shortWeekdays = [
+    'mon', 'tues|tue', 'wed', 'thu', 'fri', 'sat', 'sun',
+]
+
+Months = [
+    'january', 'february', 'march', 'april', 'may', 'june', 'july',
+    'august', 'september', 'october', 'november', 'december',
+]
+
+shortMonths = [
+    'jan', 'feb', 'mar', 'apr', 'may', 'jun',
+    'jul', 'aug', 'sep', 'oct', 'nov', 'dec',
+]
+
+# use the same formats as ICU by default
+dateFormats = {
+    'full': 'EEEE, MMMM d, yyyy',
+    'long': 'MMMM d, yyyy',
+    'medium': 'MMM d, yyyy',
+    'short': 'M/d/yy'
+}
+
+timeFormats = {
+    'full': 'h:mm:ss a z',
+    'long': 'h:mm:ss a z',
+    'medium': 'h:mm:ss a',
+    'short': 'h:mm a',
+}
+
+dp_order = ['m', 'd', 'y']
+
+# Used to parse expressions like "in 5 hours"
+numbers = {
+    'zero': 0,
+    'one': 1,
+    'a': 1,
+    'an': 1,
+    'two': 2,
+    'three': 3,
+    'four': 4,
+    'five': 5,
+    'six': 6,
+    'seven': 7,
+    'eight': 8,
+    'nine': 9,
+    'ten': 10,
+    'eleven': 11,
+    'thirteen': 13,
+    'fourteen': 14,
+    'fifteen': 15,
+    'sixteen': 16,
+    'seventeen': 17,
+    'eighteen': 18,
+    'nineteen': 19,
+    'twenty': 20,
+}
+
+decimal_mark = '.'
+
+
+# this will be added to re_values later
+units = {
+    'seconds': ['second', 'seconds', 'sec', 's'],
+    'minutes': ['minute', 'minutes', 'min', 'm'],
+    'hours': ['hour', 'hours', 'hr', 'h'],
+    'days': ['day', 'days', 'dy', 'd'],
+    'weeks': ['week', 'weeks', 'wk', 'w'],
+    'months': ['month', 'months', 'mth'],
+    'years': ['year', 'years', 'yr', 'y'],
+}
+
+
+# text constants to be used by later regular expressions
+re_values = {
+    'specials': 'in|on|of|at',
+    'timeseparator': ':',
+    'rangeseparator': '-',
+    'daysuffix': 'rd|st|nd|th',
+    'meridian': 'am|pm|a.m.|p.m.|a|p',
+    'qunits': 'h|m|s|d|w|y',
+    'now': ['now'],
+}
+
+# Used to adjust the returned date before/after the source
+Modifiers = {
+    'from': 1,
+    'before': -1,
+    'after': 1,
+    'ago': -1,
+    'prior': -1,
+    'prev': -1,
+    'last': -1,
+    'next': 1,
+    'previous': -1,
+    'end of': 0,
+    'this': 0,
+    'eod': 1,
+    'eom': 1,
+    'eoy': 1,
+}
+
+dayOffsets = {
+    'tomorrow': 1,
+    'today': 0,
+    'yesterday': -1,
+}
+
+# special day and/or times, i.e. lunch, noon, evening
+# each element in the dictionary is a dictionary that is used
+# to fill in any value to be replace - the current date/time will
+# already have been populated by the method buildSources
+re_sources = {
+    'noon': {'hr': 12, 'mn': 0, 'sec': 0},
+    'afternoon': {'hr': 13, 'mn': 0, 'sec': 0},
+    'lunch': {'hr': 12, 'mn': 0, 'sec': 0},
+    'morning': {'hr': 6, 'mn': 0, 'sec': 0},
+    'breakfast': {'hr': 8, 'mn': 0, 'sec': 0},
+    'dinner': {'hr': 19, 'mn': 0, 'sec': 0},
+    'evening': {'hr': 18, 'mn': 0, 'sec': 0},
+    'midnight': {'hr': 0, 'mn': 0, 'sec': 0},
+    'night': {'hr': 21, 'mn': 0, 'sec': 0},
+    'tonight': {'hr': 21, 'mn': 0, 'sec': 0},
+    'eod': {'hr': 17, 'mn': 0, 'sec': 0},
+}
+
+small = {
+    'zero': 0,
+    'one': 1,
+    'a': 1,
+    'an': 1,
+    'two': 2,
+    'three': 3,
+    'four': 4,
+    'five': 5,
+    'six': 6,
+    'seven': 7,
+    'eight': 8,
+    'nine': 9,
+    'ten': 10,
+    'eleven': 11,
+    'twelve': 12,
+    'thirteen': 13,
+    'fourteen': 14,
+    'fifteen': 15,
+    'sixteen': 16,
+    'seventeen': 17,
+    'eighteen': 18,
+    'nineteen': 19,
+    'twenty': 20,
+    'thirty': 30,
+    'forty': 40,
+    'fifty': 50,
+    'sixty': 60,
+    'seventy': 70,
+    'eighty': 80,
+    'ninety': 90
+}
+
+magnitude = {
+    'thousand': 1000,
+    'million': 1000000,
+    'billion': 1000000000,
+    'trillion': 1000000000000,
+    'quadrillion': 1000000000000000,
+    'quintillion': 1000000000000000000,
+    'sextillion': 1000000000000000000000,
+    'septillion': 1000000000000000000000000,
+    'octillion': 1000000000000000000000000000,
+    'nonillion': 1000000000000000000000000000000,
+    'decillion': 1000000000000000000000000000000000,
+}
+
+ignore = ('and', ',')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/de_DE.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from .base import *  # noqa
+
+# don't use an unicode string
+localeID = 'de_DE'
+dateSep = ['.']
+timeSep = [':']
+meridian = []
+usesMeridian = False
+uses24 = True
+decimal_mark = ','
+
+Weekdays = [
+    'montag', 'dienstag', 'mittwoch',
+    'donnerstag', 'freitag', 'samstag', 'sonntag',
+]
+shortWeekdays = ['mo', 'di', 'mi', 'do', 'fr', 'sa', 'so']
+Months = [
+    'januar', 'februar', 'märz',
+    'april', 'mai', 'juni',
+    'juli', 'august', 'september',
+    'oktober', 'november', 'dezember',
+]
+shortMonths = [
+    'jan', 'feb', 'mrz', 'apr', 'mai', 'jun',
+    'jul', 'aug', 'sep', 'okt', 'nov', 'dez',
+]
+
+dateFormats = {
+    'full': 'EEEE, d. MMMM yyyy',
+    'long': 'd. MMMM yyyy',
+    'medium': 'dd.MM.yyyy',
+    'short': 'dd.MM.yy',
+}
+
+timeFormats = {
+    'full': 'HH:mm:ss v',
+    'long': 'HH:mm:ss z',
+    'medium': 'HH:mm:ss',
+    'short': 'HH:mm',
+}
+
+dp_order = ['d', 'm', 'y']
+
+# the short version would be a capital M,
+# as I understand it we can't distinguish
+# between m for minutes and M for months.
+units = {
+    'seconds': ['sekunden', 'sek', 's'],
+    'minutes': ['minuten', 'min', 'm'],
+    'hours': ['stunden', 'std', 'h'],
+    'days': ['tag', 'tage', 't'],
+    'weeks': ['wochen', 'w'],
+    'months': ['monat', 'monate'],
+    'years': ['jahr', 'jahre', 'j'],
+}
+
+re_values = re_values.copy()
+re_values.update({
+    'specials': 'am|dem|der|im|in|den|zum',
+    'timeseparator': ':',
+    'rangeseparator': '-',
+    'daysuffix': '',
+    'qunits': 'h|m|s|t|w|m|j',
+    'now': ['jetzt'],
+})
+
+# Used to adjust the returned date before/after the source
+# still looking for insight on how to translate all of them to german.
+Modifiers = {
+    'from': 1,
+    'before': -1,
+    'after': 1,
+    'vergangener': -1,
+    'vorheriger': -1,
+    'prev': -1,
+    'letzter': -1,
+    'nächster': 1,
+    'dieser': 0,
+    'previous': -1,
+    'in a': 2,
+    'end of': 0,
+    'eod': 0,
+    'eo': 0,
+}
+
+# morgen/abermorgen does not work, see
+# http://code.google.com/p/parsedatetime/issues/detail?id=19
+dayOffsets = {
+    'morgen': 1,
+    'heute': 0,
+    'gestern': -1,
+    'vorgestern': -2,
+    'übermorgen': 2,
+}
+
+# special day and/or times, i.e. lunch, noon, evening
+# each element in the dictionary is a dictionary that is used
+# to fill in any value to be replace - the current date/time will
+# already have been populated by the method buildSources
+re_sources = {
+    'mittag': {'hr': 12, 'mn': 0, 'sec': 0},
+    'mittags': {'hr': 12, 'mn': 0, 'sec': 0},
+    'mittagessen': {'hr': 12, 'mn': 0, 'sec': 0},
+    'morgen': {'hr': 6, 'mn': 0, 'sec': 0},
+    'morgens': {'hr': 6, 'mn': 0, 'sec': 0},
+    'frühstück': {'hr': 8, 'mn': 0, 'sec': 0},
+    'abendessen': {'hr': 19, 'mn': 0, 'sec': 0},
+    'abend': {'hr': 18, 'mn': 0, 'sec': 0},
+    'abends': {'hr': 18, 'mn': 0, 'sec': 0},
+    'mitternacht': {'hr': 0, 'mn': 0, 'sec': 0},
+    'nacht': {'hr': 21, 'mn': 0, 'sec': 0},
+    'nachts': {'hr': 21, 'mn': 0, 'sec': 0},
+    'heute abend': {'hr': 21, 'mn': 0, 'sec': 0},
+    'heute nacht': {'hr': 21, 'mn': 0, 'sec': 0},
+    'feierabend': {'hr': 17, 'mn': 0, 'sec': 0},
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/en_AU.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from .base import *  # noqa
+
+# don't use an unicode string
+localeID = 'en_AU'
+dateSep = ['-', '/']
+uses24 = False
+
+dateFormats = {
+    'full': 'EEEE, d MMMM yyyy',
+    'long': 'd MMMM yyyy',
+    'medium': 'dd/MM/yyyy',
+    'short': 'd/MM/yy',
+}
+
+timeFormats = {
+    'long': timeFormats['full'],
+}
+
+dp_order = ['d', 'm', 'y']
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/en_US.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,7 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from .base import *  # noqa
+
+# don't use an unicode string
+localeID = 'en_US'
+uses24 = False
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/es.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from .base import *  # noqa
+
+# don't use an unicode string
+localeID = 'es'
+dateSep = ['/']
+usesMeridian = False
+uses24 = True
+decimal_mark = ','
+
+Weekdays = [
+    'lunes', 'martes', 'miércoles',
+    'jueves', 'viernes', 'sábado', 'domingo',
+]
+shortWeekdays = [
+    'lun', 'mar', 'mié',
+    'jue', 'vie', 'sáb', 'dom',
+]
+Months = [
+    'enero', 'febrero', 'marzo',
+    'abril', 'mayo', 'junio',
+    'julio', 'agosto', 'septiembre',
+    'octubre', 'noviembre', 'diciembre',
+]
+shortMonths = [
+    'ene', 'feb', 'mar',
+    'abr', 'may', 'jun',
+    'jul', 'ago', 'sep',
+    'oct', 'nov', 'dic',
+]
+dateFormats = {
+    'full': "EEEE d' de 'MMMM' de 'yyyy",
+    'long': "d' de 'MMMM' de 'yyyy",
+    'medium': "dd-MMM-yy",
+    'short': "d/MM/yy",
+}
+
+timeFormats = {
+    'full': "HH'H'mm' 'ss z",
+    'long': "HH:mm:ss z",
+    'medium': "HH:mm:ss",
+    'short': "HH:mm",
+}
+
+dp_order = ['d', 'm', 'y']
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/icu.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,149 @@
+# -*- encoding: utf-8 -*-
+
+"""
+pdt_locales
+
+All of the included locale classes shipped with pdt.
+"""
+import datetime
+
+try:
+    range = xrange
+except NameError:
+    pass
+
+try:
+    import PyICU as pyicu
+except:
+    pyicu = None
+
+
+def icu_object(mapping):
+    return type('_icu', (object,), mapping)
+
+
+def merge_weekdays(base_wd, icu_wd):
+    result = []
+    for left, right in zip(base_wd, icu_wd):
+        if left == right:
+            result.append(left)
+            continue
+        left = set(left.split('|'))
+        right = set(right.split('|'))
+        result.append('|'.join(left | right))
+    return result
+
+
+def get_icu(locale):
+    from . import base
+    result = dict([(key, getattr(base, key))
+                   for key in dir(base) if not key.startswith('_')])
+    result['icu'] = None
+
+    if pyicu is None:
+        return icu_object(result)
+
+    if locale is None:
+        locale = 'en_US'
+    result['icu'] = icu = pyicu.Locale(locale)
+
+    if icu is None:
+        return icu_object(result)
+
+    # grab spelled out format of all numbers from 0 to 100
+    rbnf = pyicu.RuleBasedNumberFormat(pyicu.URBNFRuleSetTag.SPELLOUT, icu)
+    result['numbers'].update([(rbnf.format(i), i) for i in range(0, 100)])
+
+    symbols = result['symbols'] = pyicu.DateFormatSymbols(icu)
+
+    # grab ICU list of weekdays, skipping first entry which
+    # is always blank
+    wd = [w.lower() for w in symbols.getWeekdays()[1:]]
+    swd = [sw.lower() for sw in symbols.getShortWeekdays()[1:]]
+
+    # store them in our list with Monday first (ICU puts Sunday first)
+    result['Weekdays'] = merge_weekdays(result['Weekdays'],
+                                        wd[1:] + wd[0:1])
+    result['shortWeekdays'] = merge_weekdays(result['shortWeekdays'],
+                                             swd[1:] + swd[0:1])
+    result['Months'] = [m.lower() for m in symbols.getMonths()]
+    result['shortMonths'] = [sm.lower() for sm in symbols.getShortMonths()]
+    keys = ['full', 'long', 'medium', 'short']
+
+    createDateInstance = pyicu.DateFormat.createDateInstance
+    createTimeInstance = pyicu.DateFormat.createTimeInstance
+    icu_df = result['icu_df'] = {
+        'full': createDateInstance(pyicu.DateFormat.kFull, icu),
+        'long': createDateInstance(pyicu.DateFormat.kLong, icu),
+        'medium': createDateInstance(pyicu.DateFormat.kMedium, icu),
+        'short': createDateInstance(pyicu.DateFormat.kShort, icu),
+    }
+    icu_tf = result['icu_tf'] = {
+        'full': createTimeInstance(pyicu.DateFormat.kFull, icu),
+        'long': createTimeInstance(pyicu.DateFormat.kLong, icu),
+        'medium': createTimeInstance(pyicu.DateFormat.kMedium, icu),
+        'short': createTimeInstance(pyicu.DateFormat.kShort, icu),
+    }
+
+    result['dateFormats'] = {}
+    result['timeFormats'] = {}
+    for x in keys:
+        result['dateFormats'][x] = icu_df[x].toPattern()
+        result['timeFormats'][x] = icu_tf[x].toPattern()
+
+    am = pm = ts = ''
+
+    # ICU doesn't seem to provide directly the date or time separator
+    # so we have to figure it out
+    o = result['icu_tf']['short']
+    s = result['timeFormats']['short']
+
+    result['usesMeridian'] = 'a' in s
+    result['uses24'] = 'H' in s
+
+    # '11:45 AM' or '11:45'
+    s = o.format(datetime.datetime(2003, 10, 30, 11, 45))
+
+    # ': AM' or ':'
+    s = s.replace('11', '').replace('45', '')
+
+    if len(s) > 0:
+        ts = s[0]
+
+    if result['usesMeridian']:
+        # '23:45 AM' or '23:45'
+        am = s[1:].strip()
+        s = o.format(datetime.datetime(2003, 10, 30, 23, 45))
+
+        if result['uses24']:
+            s = s.replace('23', '')
+        else:
+            s = s.replace('11', '')
+
+            # 'PM' or ''
+        pm = s.replace('45', '').replace(ts, '').strip()
+
+    result['timeSep'] = [ts]
+    result['meridian'] = [am, pm] if am and pm else []
+
+    o = result['icu_df']['short']
+    s = o.format(datetime.datetime(2003, 10, 30, 11, 45))
+    s = s.replace('10', '').replace('30', '').replace(
+        '03', '').replace('2003', '')
+
+    if len(s) > 0:
+        ds = s[0]
+    else:
+        ds = '/'
+
+    result['dateSep'] = [ds]
+    s = result['dateFormats']['short']
+    l = s.lower().split(ds)
+    dp_order = []
+
+    for s in l:
+        if len(s) > 0:
+            dp_order.append(s[:1])
+
+    result['dp_order'] = dp_order
+    return icu_object(result)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/nl_NL.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from .base import *  # noqa
+
+# don't use an unicode string
+localeID = 'nl_NL'
+dateSep = ['-', '/']
+timeSep = [':']
+meridian = []
+usesMeridian = False
+uses24 = True
+decimal_mark = ','
+
+Weekdays = [
+    'maandag', 'dinsdag', 'woensdag', 'donderdag',
+    'vrijdag', 'zaterdag', 'zondag',
+]
+shortWeekdays = [
+    'ma', 'di', 'wo', 'do', 'vr', 'za', 'zo',
+]
+Months = [
+    'januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli',
+    'augustus', 'september', 'oktober', 'november', 'december',
+]
+shortMonths = [
+    'jan', 'feb', 'mar', 'apr', 'mei', 'jun',
+    'jul', 'aug', 'sep', 'okt', 'nov', 'dec',
+]
+dateFormats = {
+    'full': 'EEEE, dd MMMM yyyy',
+    'long': 'dd MMMM yyyy',
+    'medium': 'dd-MM-yyyy',
+    'short': 'dd-MM-yy',
+}
+
+timeFormats = {
+    'full': 'HH:mm:ss v',
+    'long': 'HH:mm:ss z',
+    'medium': 'HH:mm:ss',
+    'short': 'HH:mm',
+}
+
+dp_order = ['d', 'm', 'y']
+
+# the short version would be a capital M,
+# as I understand it we can't distinguish
+# between m for minutes and M for months.
+units = {
+    'seconds': ['secunden', 'sec', 's'],
+    'minutes': ['minuten', 'min', 'm'],
+    'hours': ['uren', 'uur', 'h'],
+    'days': ['dagen', 'dag', 'd'],
+    'weeks': ['weken', 'w'],
+    'months': ['maanden', 'maand'],
+    'years': ['jaar', 'jaren', 'j'],
+}
+
+re_values = re_values.copy()
+re_values.update({
+    'specials': 'om',
+    'timeseparator': ':',
+    'rangeseparator': '-',
+    'daysuffix': ' |de',
+    'qunits': 'h|m|s|d|w|m|j',
+    'now': ['nu'],
+})
+
+# Used to adjust the returned date before/after the source
+# still looking for insight on how to translate all of them to german.
+Modifiers = {
+    'vanaf': 1,
+    'voor': -1,
+    'na': 1,
+    'vorige': -1,
+    'eervorige': -1,
+    'prev': -1,
+    'laastste': -1,
+    'volgende': 1,
+    'deze': 0,
+    'vorige': -1,
+    'over': 2,
+    'eind van': 0,
+}
+
+# morgen/abermorgen does not work, see
+# http://code.google.com/p/parsedatetime/issues/detail?id=19
+dayOffsets = {
+    'morgen': 1,
+    'vandaag': 0,
+    'gisteren': -1,
+    'eergisteren': -2,
+    'overmorgen': 2,
+}
+
+# special day and/or times, i.e. lunch, noon, evening
+# each element in the dictionary is a dictionary that is used
+# to fill in any value to be replace - the current date/time will
+# already have been populated by the method buildSources
+re_sources = {
+    'middag': {'hr': 12, 'mn': 0, 'sec': 0},
+    'vanmiddag': {'hr': 12, 'mn': 0, 'sec': 0},
+    'lunch': {'hr': 12, 'mn': 0, 'sec': 0},
+    'morgen': {'hr': 6, 'mn': 0, 'sec': 0},
+    "'s morgens": {'hr': 6, 'mn': 0, 'sec': 0},
+    'ontbijt': {'hr': 8, 'mn': 0, 'sec': 0},
+    'avondeten': {'hr': 19, 'mn': 0, 'sec': 0},
+    'avond': {'hr': 18, 'mn': 0, 'sec': 0},
+    'avonds': {'hr': 18, 'mn': 0, 'sec': 0},
+    'middernacht': {'hr': 0, 'mn': 0, 'sec': 0},
+    'nacht': {'hr': 21, 'mn': 0, 'sec': 0},
+    'nachts': {'hr': 21, 'mn': 0, 'sec': 0},
+    'vanavond': {'hr': 21, 'mn': 0, 'sec': 0},
+    'vannacht': {'hr': 21, 'mn': 0, 'sec': 0},
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/pt_BR.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from .base import *  # noqa
+
+# don't use an unicode string
+localeID = 'pt_BR'
+dateSep = ['/']
+usesMeridian = False
+uses24 = True
+decimal_mark = ','
+
+Weekdays = [
+    'segunda-feira', 'terça-feira', 'quarta-feira',
+    'quinta-feira', 'sexta-feira', 'sábado', 'domingo',
+]
+shortWeekdays = [
+    'seg', 'ter', 'qua', 'qui', 'sex', 'sáb', 'dom',
+]
+Months = [
+    'janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho',
+    'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'
+]
+shortMonths = [
+    'jan', 'fev', 'mar', 'abr', 'mai', 'jun',
+    'jul', 'ago', 'set', 'out', 'nov', 'dez'
+]
+dateFormats = {
+    'full': "EEEE, d' de 'MMMM' de 'yyyy",
+    'long': "d' de 'MMMM' de 'yyyy",
+    'medium': "dd-MM-yy",
+    'short': "dd/MM/yyyy",
+}
+
+timeFormats = {
+    'full': "HH'H'mm' 'ss z",
+    'long': "HH:mm:ss z",
+    'medium': "HH:mm:ss",
+    'short': "HH:mm",
+}
+
+dp_order = ['d', 'm', 'y']
+
+units = {
+    'seconds': ['segundo', 'seg', 's'],
+    'minutes': ['minuto', 'min', 'm'],
+    'days': ['dia', 'dias', 'd'],
+    'months': ['mês', 'meses'],
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/pdt_locales/ru_RU.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,164 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from .base import *  # noqa
+
+# don't use an unicode string
+localeID = 'ru_RU'
+dateSep = ['-', '.']
+timeSep = [':']
+meridian = []
+usesMeridian = False
+uses24 = True
+
+Weekdays = [
+    'понедельник', 'вторник', 'среда', 'четверг',
+    'пятница', 'суббота', 'воскресенье',
+]
+shortWeekdays = [
+    'пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс',
+]
+# library does not know how to conjugate words
+# библиотека не умеет спрягать слова
+Months = [
+    'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля',
+    'августа', 'сентября', 'октября', 'ноября', 'декабря',
+]
+shortMonths = [
+    'явн', 'фев', 'мрт', 'апр', 'май', 'июн',
+    'июл', 'авг', 'сен', 'окт', 'нбр', 'дек',
+]
+dateFormats = {
+    'full': 'EEEE, dd MMMM yyyy',
+    'long': 'dd MMMM yyyy',
+    'medium': 'dd-MM-yyyy',
+    'short': 'dd-MM-yy',
+}
+
+timeFormats = {
+    'full': 'HH:mm:ss v',
+    'long': 'HH:mm:ss z',
+    'medium': 'HH:mm:ss',
+    'short': 'HH:mm',
+}
+
+dp_order = ['d', 'm', 'y']
+
+decimal_mark = '.'
+
+units = {
+    'seconds': ['секунда', 'секунды', 'секунд', 'сек', 'с'],
+    'minutes': ['минута', 'минуты', 'минут', 'мин', 'м'],
+    'hours': ['час', 'часов', 'часа', 'ч'],
+    'days': ['день', 'дней', 'д'],
+    'weeks': ['неделя', 'недели', 'н'],
+    'months': ['месяц', 'месяца', 'мес'],
+    'years': ['год', 'года', 'годы', 'г'],
+}
+
+re_values = re_values.copy()
+re_values.update({
+    'specials': 'om',
+    'timeseparator': ':',
+    'rangeseparator': '-',
+    'daysuffix': 'ого|ой|ий|тье',
+    'qunits': 'д|мес|г|ч|н|м|с',
+    'now': ['сейчас'],
+})
+
+Modifiers = {
+    'после': 1,
+    'назад': -1,
+    'предыдущий': -1,
+    'последний': -1,
+    'далее': 1,
+    'ранее': -1,
+}
+
+dayOffsets = {
+    'завтра': 1,
+    'сегодня': 0,
+    'вчера': -1,
+    'позавчера': -2,
+    'послезавтра': 2,
+}
+
+re_sources = {
+    'полдень': {'hr': 12, 'mn': 0, 'sec': 0},
+    'день': {'hr': 13, 'mn': 0, 'sec': 0},
+    'обед': {'hr': 12, 'mn': 0, 'sec': 0},
+    'утро': {'hr': 6, 'mn': 0, 'sec': 0},
+    'завтрак': {'hr': 8, 'mn': 0, 'sec': 0},
+    'ужин': {'hr': 19, 'mn': 0, 'sec': 0},
+    'вечер': {'hr': 18, 'mn': 0, 'sec': 0},
+    'полночь': {'hr': 0, 'mn': 0, 'sec': 0},
+    'ночь': {'hr': 21, 'mn': 0, 'sec': 0},
+}
+
+small = {
+    'ноль': 0,
+    'один': 1,
+    'два': 2,
+    'три': 3,
+    'четыре': 4,
+    'пять': 5,
+    'шесть': 6,
+    'семь': 7,
+    'восемь': 8,
+    'девять': 9,
+    'десять': 10,
+    'одиннадцать': 11,
+    'двенадцать': 12,
+    'тринадцать': 13,
+    'четырнадцать': 14,
+    'пятнадцать': 15,
+    'шестнадцать': 16,
+    'семнадцать': 17,
+    'восемнадцать': 18,
+    'девятнадцать': 19,
+    'двадцать': 20,
+    'тридцать': 30,
+    'сорок': 40,
+    'пятьдесят': 50,
+    'шестьдесят': 60,
+    'семьдесят': 70,
+    'восемьдесят': 80,
+    'девяносто': 90,
+}
+
+numbers = {
+    'ноль': 0,
+    'один': 1,
+    'два': 2,
+    'три': 3,
+    'четыре': 4,
+    'пять': 5,
+    'шесть': 6,
+    'семь': 7,
+    'восемь': 8,
+    'девять': 9,
+    'десять': 10,
+    'одиннадцать': 11,
+    'двенадцать': 12,
+    'тринадцать': 13,
+    'четырнадцать': 14,
+    'пятнадцать': 15,
+    'шестнадцать': 16,
+    'семнадцать': 17,
+    'восемнадцать': 18,
+    'девятнадцать': 19,
+    'двадцать': 20,
+}
+
+magnitude = {
+    'тысяча': 1000,
+    'миллион': 1000000,
+    'миллиард': 1000000000,
+    'триллион': 1000000000000,
+    'квадриллион': 1000000000000000,
+    'квинтиллион': 1000000000000000000,
+    'секстиллион': 1000000000000000000000,
+    'септиллион': 1000000000000000000000000,
+    'октиллион': 1000000000000000000000000000,
+    'нониллион': 1000000000000000000000000000000,
+    'дециллион': 1000000000000000000000000000000000,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/parsedatetime/warns.py	Tue Sep 06 00:09:31 2016 +0200
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+"""
+parsedatetime/warns.py
+
+All subclasses inherited from `Warning` class
+
+"""
+from __future__ import absolute_import
+
+import warnings
+
+
+class pdtDeprecationWarning(DeprecationWarning):
+    pass
+
+
+class pdtPendingDeprecationWarning(PendingDeprecationWarning):
+    pass
+
+
+class pdt20DeprecationWarning(pdtPendingDeprecationWarning):
+    pass
+
+
+warnings.simplefilter('default', pdtDeprecationWarning)
+warnings.simplefilter('ignore', pdtPendingDeprecationWarning)