def _evalModifier()

in hgext/pushlog/parsedatetime/__init__.py [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
                relativeModifier = modifier not in ['this', 'next', 'last', 'prior', 'previous']
                if relativeModifier:
                    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 != '' and relativeModifier:
                    # 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 chunk1 == '' and chunk2 == '' and 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