Refactored RotatingFileHandler to create a base class  for rotating handlers. Added TimedRotatingFileHandler.
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
index 36225b9..97632ea 100644
--- a/Lib/logging/handlers.py
+++ b/Lib/logging/handlers.py
@@ -27,7 +27,7 @@
 To use, simply 'import logging' and log away!
 """
 
-import sys, logging, socket, types, os, string, cPickle, struct, time
+import sys, logging, socket, types, os, string, cPickle, struct, time, glob
 
 #
 # Some constants...
@@ -39,8 +39,34 @@
 DEFAULT_SOAP_LOGGING_PORT   = 9023
 SYSLOG_UDP_PORT             = 514
 
+class BaseRotatingHandler(logging.FileHandler):
+    """
+    Base class for handlers that rotate log files at a certain point.
+    Not meant to be instantiated directly.  Instead, use RotatingFileHandler
+    or TimedRotatingFileHandler.
+    """
+    def __init__(self, filename, mode):
+        """
+        Use the specified filename for streamed logging
+        """
+        logging.FileHandler.__init__(self, filename, mode)
 
-class RotatingFileHandler(logging.FileHandler):
+    def emit(self, record):
+        """
+        Emit a record.
+
+        Output the record to the file, catering for rollover as described
+        in doRollover().
+        """
+        if self.shouldRollover(record):
+            self.doRollover()
+        logging.FileHandler.emit(self, record)
+
+class RotatingFileHandler(BaseRotatingHandler):
+    """
+    Handler for logging to a set of files, which switches from one file
+    to the next when the current file reaches a certain size.
+    """
     def __init__(self, filename, mode="a", maxBytes=0, backupCount=0):
         """
         Open the specified file and use it as the stream for logging.
@@ -62,11 +88,12 @@
 
         If maxBytes is zero, rollover never occurs.
         """
-        logging.FileHandler.__init__(self, filename, mode)
+        self.mode = mode
+        if maxBytes > 0:
+            self.mode = "a" # doesn't make sense otherwise!
+        BaseRotatingHandler.__init__(self, filename, self.mode)
         self.maxBytes = maxBytes
         self.backupCount = backupCount
-        if maxBytes > 0:
-            self.mode = "a"
 
     def doRollover(self):
         """
@@ -90,20 +117,149 @@
             #print "%s -> %s" % (self.baseFilename, dfn)
         self.stream = open(self.baseFilename, "w")
 
-    def emit(self, record):
+    def shouldRollover(self, record):
         """
-        Emit a record.
+        Determine if rollover should occur.
 
-        Output the record to the file, catering for rollover as described
-        in doRollover().
+        Basically, see if the supplied record would cause the file to exceed
+        the size limit we have.
         """
         if self.maxBytes > 0:                   # are we rolling over?
             msg = "%s\n" % self.format(record)
             self.stream.seek(0, 2)  #due to non-posix-compliant Windows feature
             if self.stream.tell() + len(msg) >= self.maxBytes:
-                self.doRollover()
-        logging.FileHandler.emit(self, record)
+                return 1
+        return 0
 
+class TimedRotatingFileHandler(BaseRotatingHandler):
+    """
+    Handler for logging to a file, rotating the log file at certain timed
+    intervals.
+
+    If backupCount is > 0, when rollover is done, no more than backupCount
+    files are kept - the oldest ones are deleted.
+    """
+    def __init__(self, filename, when='h', interval=1, backupCount=0):
+        BaseRotatingHandler.__init__(self, filename, 'a')
+        self.when = string.upper(when)
+        self.backupCount = backupCount
+        # Calculate the real rollover interval, which is just the number of
+        # seconds between rollovers.  Also set the filename suffix used when
+        # a rollover occurs.  Current 'when' events supported:
+        # S - Seconds
+        # M - Minutes
+        # H - Hours
+        # D - Days
+        # midnight - roll over at midnight
+        # W{0-6} - roll over on a certain day; 0 - Monday
+        #
+        # Case of the 'when' specifier is not important; lower or upper case
+        # will work.
+        currentTime = int(time.time())
+        if self.when == 'S':
+            self.interval = 1 # one second
+            self.suffix = "%Y-%m-%d_%H-%M-%S"
+        elif self.when == 'M':
+            self.interval = 60 # one minute
+            self.suffix = "%Y-%m-%d_%H-%M"
+        elif self.when == 'H':
+            self.interval = 60 * 60 # one hour
+            self.suffix = "%Y-%m-%d_%H"
+        elif self.when == 'D' or self.when == 'MIDNIGHT':
+            self.interval = 60 * 60 * 24 # one day
+            self.suffix = "%Y-%m-%d"
+        elif self.when.startswith('W'):
+            self.interval = 60 * 60 * 24 * 7 # one week
+            if len(self.when) != 2:
+                raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
+            if self.when[1] < '0' or self.when[1] > '6':
+                raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
+            self.dayOfWeek = int(self.when[1])
+            self.suffix = "%Y-%m-%d"
+        else:
+            raise ValueError("Invalid rollover interval specified: %s" % self.when)
+
+        self.interval *= interval # multiply by units requested
+        self.rolloverAt = currentTime + self.interval
+
+        # If we are rolling over at midnight or weekly, then the interval is already known.
+        # What we need to figure out is WHEN the next interval is.  In other words,
+        # if you are rolling over at midnight, then your base interval is 1 day,
+        # but you want to start that one day clock at midnight, not now.  So, we
+        # have to fudge the rolloverAt value in order to trigger the first rollover
+        # at the right time.  After that, the regular interval will take care of
+        # the rest.  Note that this code doesn't care about leap seconds. :)
+        if self.when == 'MIDNIGHT' or self.when.startswith('W'):
+            # This could be done with less code, but I wanted it to be clear
+            t = time.localtime(currentTime)
+            currentHour = t[3]
+            currentMinute = t[4]
+            currentSecond = t[5]
+            # r is the number of seconds left between now and midnight
+            r = (24 - currentHour) * 60 * 60 # number of hours in seconds
+            r += (59 - currentMinute) * 60 # plus the number of minutes (in secs)
+            r += (59 - currentSecond) # plus the number of seconds
+            self.rolloverAt = currentTime + r
+            # If we are rolling over on a certain day, add in the number of days until
+            # the next rollover, but offset by 1 since we just calculated the time
+            # until the next day starts.  There are three cases:
+            # Case 1) The day to rollover is today; in this case, do nothing
+            # Case 2) The day to rollover is further in the interval (i.e., today is
+            #         day 2 (Wednesday) and rollover is on day 6 (Sunday).  Days to
+            #         next rollover is simply 6 - 2 - 1, or 3.
+            # Case 3) The day to rollover is behind us in the interval (i.e., today
+            #         is day 5 (Saturday) and rollover is on day 3 (Thursday).
+            #         Days to rollover is 6 - 5 + 3, or 4.  In this case, it's the
+            #         number of days left in the current week (1) plus the number
+            #         of days in the next week until the rollover day (3).
+            if when.startswith('W'):
+                day = t[6] # 0 is Monday
+                if day > self.dayOfWeek:
+                    daysToWait = (day - self.dayOfWeek) - 1
+                    self.rolloverAt += (daysToWait * (60 * 60 * 24))
+                if day < self.dayOfWeek:
+                    daysToWait = (6 - self.dayOfWeek) + day
+                    self.rolloverAt += (daysToWait * (60 * 60 * 24))
+
+        print "Will rollover at %d, %d seconds from now" % (self.rolloverAt, self.rolloverAt - currentTime)
+
+    def shouldRollover(self, record):
+        """
+        Determine if rollover should occur
+
+        record is not used, as we are just comparing times, but it is needed so
+        the method siguratures are the same
+        """
+        t = int(time.time())
+        if t >= self.rolloverAt:
+            return 1
+        print "No need to rollover: %d, %d" % (t, self.rolloverAt)
+        return 0
+
+    def doRollover(self):
+        """
+        do a rollover; in this case, a date/time stamp is appended to the filename
+        when the rollover happens.  However, you want the file to be named for the
+        start of the interval, not the current time.  If there is a backup count,
+        then we have to get a list of matching filenames, sort them and remove
+        the one with the oldest suffix.
+        """
+        self.stream.close()
+        # get the time that this sequence started at and make it a TimeTuple
+        t = self.rolloverAt - self.interval
+        timeTuple = time.localtime(t)
+        dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
+        if os.path.exists(dfn):
+            os.remove(dfn)
+        os.rename(self.baseFilename, dfn)
+        if self.backupCount > 0:
+            # find the oldest log file and delete it
+            s = glob.glob(self.baseFilename + ".20*")
+            if len(s) > self.backupCount:
+                os.remove(s[0])
+        print "%s -> %s" % (self.baseFilename, dfn)
+        self.stream = open(self.baseFilename, "w")
+        self.rolloverAt = int(time.time()) + self.interval
 
 class SocketHandler(logging.Handler):
     """