Next / Previous / Contents / Shipman's homepage

20.4. BirdNoteTree._timeFilter(): Check date and seasonal ranges

birdnotes.py
# - - -   B i r d N o t e S e t . _ t i m e F i l t e r

    def _timeFilter(self, monthKey, startDate, endDate,
                       startSeason, endSeason):
        '''Does this month contain records of interest?

          [ (monthKey is a month name as "YYYY-MM") and
            (startDate is an inclusive starting date as a
            datetime.date, or None for no starting cutoff) and
            (endDate is an inclusive ending date as a
            datetime.date or None for no ending cutoff) and
            (startSeason is an inclusive starting month and day
            as a datetime.date or None for no starting cutoff) and
            (endSeason is an inclusive ending month and day
            as a datetime.date or None for no ending cutoff) ->
              if monthKey's month might contain records in
              those date and day-of-year ranges ->
                return True
              else -> return False ]
        '''

First we must set up two datetime.date instances representing the limits of monthKey's month. Rather than have to worry about the date of the last day of each month, we use a half-open interval [firstOfThis, firstOfNext) where the interval includes the first day of the month, but does not include the first day of the following month (which is easier to compute).

birdnotes.py
        #-- 1 --
        # [ firstOfThis  :=  a datetime.date instance representing
        #       the first day of monthKey's month
        #   firstOfNext  :=  a datetime.date instance representing
        #       the first day of the month following monthKey ]
        #--
        #   NB: Positions within a YYYY-MM string:
        #       0 1 2 3 4 5 6 7
        #        Y Y Y Y - M M
        #--
        yyyy = int(monthKey[:4])
        mm = int(monthKey[5:])
        firstOfThis = datetime.date(yyyy, mm, 1)
        if mm==12:
            firstOfNext = datetime.date(yyyy+1, 1, 1)
        else:
            firstOfNext = datetime.date(yyyy, mm+1, 1)

The selection logic is straightforward. When one of the limit values is None, there is no cutoff at that limit. Where there is a cutoff, we can use the handy property that the normal comparison operators work on datetime.date instances.

birdnotes.py
        #-- 2 --
        if((startDate is not None) and
             (startDate >= firstOfNext)):
            return False

        #-- 3 --
        if((endDate is not None) and
             (endDate < firstOfThis)):
            return False

To test for the correct month without regard to the year, we use the datetime.date.replace() method to create copies of the supplied startSeason and endSeason with the year changed to yyyy. Each copy is then compared to the half-open interval [firstOfThis, firstOfNext) as above.

birdnotes.py
        #-- 4 --
        if startSeason is not None:
            start = startSeason.replace(yyyy)
            if start >= firstOfNext:
                return False

        #-- 5 --
        if endSeason is not None:
            end = endSeason.replace(yyyy)
            if end < firstOfThis:
                return False

        #-- 6 --
        return True