datelist.py

'''\
Date List.

classes:
    datelist
    datelistiter

Developer@Sonnack.com
April 2012
'''
####################################################################################################
from sys import stdinstdoutstderrargv
from datetime import datedatetimetimedelta
from re import compile as RegEx
from logger import logger
####################################################################################################
FLog = logger('datelist')


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
## DATE PARSER CLASS
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class DateParser (object):
    '''Date Parser class. Converts various date strings to datetime objects.'''
    date_pattern = r'\s\d?\d:\d\d(:\d\d)?'  # finds time at end: ?#:##(:##)?
    ptime_fmts = [
        '%m/%d/%Y'                  ,   # 11/11/2011
        '%m-%d-%Y'                  ,   # 11-11-2011
        '%m.%d.%Y'                  ,   # 11.11.2011
        '%m/%d/%Y %H:%M:%S'         ,   # 11/11/2011 23:59:59
        '%m/%d/%Y %H:%M'            ,   # 11/11/2011 23:59
        '%m-%d-%Y %H:%M:%S'         ,   # 11-11-2011 23:59:59
        '%m-%d-%Y %H:%M'            ,   # 11-11-2011 23:59
        '%d %b %Y %H:%M:%S'         ,   # 11 Nov 2011 23:59:59
        '%d %b %Y %H:%M'            ,   # 11 Nov 2011 23:59
        '%a, %d %b %Y %H:%M:%S'     ,   # Mon, 11 Nov 2011 23:59:59
        '%a, %d %b %Y %H:%M'        ,   # Wed, 14 Oct 2009 16:08
        '%m/%d/%y %H:%M:%S'         ,   # 11/11/11 23:59:59
        '%m/%d/%y %H:%M'            ,   # 11/11/11 23:59
        '%m-%d-%y %H:%M:%S'         ,   # 11-11-11 23:59:59
        '%m-%d-%y %H:%M'            ,   # 11-11-11 23:59
        '%d %b %y %H:%M:%S'         ,   # 11 Nov 11 23:59:59
        '%d %b %y %H:%M'            ,   # 11 Nov 11 23:59
        '%a, %d %b %y %H:%M:%S'     ,   # Mon, 11 Nov 11 23:59:59
        '%a, %d %b %y %H:%M'        ]   # Wed, 14 Oct 09 16:08

    def __init__ (self):
        '''Create new DateParser instance.'''
        self.matcher = RegEx(self.date_pattern)

    def parse (selfdate_string):
        '''Parse a date string; return a datetime, if possible, else None.'''
        # Find the time sub-string (we'll assume it indicates (1) a date and (2) the end of the date)...
        m = self.matcher.search(date_string)
        if m:
            # Got something, so extract up to (and including) the time sub-string...
            tm = date_string[:m.end()]
            # Try parse formats until we find one that works...
            for ix,fmt in enumerate(self.ptime_fmts):
                try:
                    dt = datetime.strptime(tmfmt)
                    return dt # HAPPY!!
                except:
                    # (That one didn't work!)
                    FLog.trace('###Parse Date### "%s" (attempt #%d: "%s")' % (date_string1+ixfmt))

            # Didn't find one, so...
            FLog.error('###Unable to Parse Date### "%s"' % date_string)
        else:
            # No match, so not a date format we understood...
            FLog.error('###Unknown Date Format### "%s"' % date_string)

        # If we end up here, parsing failed; return None...
        return None


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
## DATE LIST CLASS
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class datelist (object):
    '''\
Date List Class.

properties:
    .dt0            - first date
    .dt1            - last date
    .td             - date difference

methods:
    as_list()       - get a List of Dates
    as_tuple()      - get a Tuple of Dates
    obj[n]          - get nth date in range
    iter(obj)       - get a date range iterator
    len(obj)        - date difference in days
    str(obj)        - printable date range string
    repr(obj)       - JSON-like string

'''
    def __init__ (selfdate0date1=date.today()):
        '''Create a new datelist instance.'''
        if date1 < date0:
            self.dt0 = date1
            self.dt1 = date0
        else:
            self.dt0 = date0
            self.dt1 = date1
        self.td = timedelta(1) + (self.dt1 - self.dt0)

    def as_list (self):
        return list(self)

    def as_tuple (self):
        return tuple(self)

    def __cmp__ (selfother):
        return cmp(self.dt0other.dt0or cmp(self.dt1other.dt1)

    def __len__ (self):
        '''Get number of days in range.'''
        return self.td.days

    def __getitem__ (selfix):
        '''Get Nth Date in range.'''
        if (ix < 0or (len(self) <= ix):
            raise IndexError'Index out of range: %d' % ix
        if ix == 0:
            return self.dt0
        if ix == (len(self) - 1):
            return self.dt1
        return self.dt0 + timedelta(ix)

    def __iter__ (self):
        '''Get a datelist iterator '''
        return datelistiter(self)

    def __str__ (self):
        '''Return a printable date range string.'''
        y0 = self.dt0.year
        m0 = self.dt0.month
        d0 = self.dt0.day
        y1 = self.dt1.year
        m1 = self.dt1.month
        d1 = self.dt1.day
        ss = '' if len(self) == 1 else 's'
        return '[%d/%d/%d to %d/%d/%d (%d day%s)]' % (m0,d0,y0m1,d1,y1len(self), ss)

    def __repr__ (self):
        '''Return a serializeable JSON object.'''
        return '{datelist:{dt0:"%s", dt1:"%s"}}' % (self.dt0self.dt1)



##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
## DATE LIST ITERATOR CLASS
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class datelistiter (object):
    '''\
Date List Iterator Class.

properties:
    .dt0            - first date (increases towards .dt1)
    .dt1            - last date
    .dtd            - date increment (one day)

methods:
    next(obj)       - get next item
    iter(obj)       - get self

'''
    def __init__ (self_datelist):
        self.dt0 = _datelist.dt0
        self.dt1 = _datelist.dt1
        self.dtd = timedelta(1)

    def next (self):
        if  self.dt1 < self.dt0:
            raise StopIteration
        dt = self.dt0
        self.dt0 = self.dt0 + self.dtd
        return dt

    def __iter__ (self):
        return self



####################################################################################################
'''eof'''