Beregning av avstand i tid
Steg1
Vi begynner med å lage en kodestrategi. Jeg vil gjøre det slik at jeg lager en funksjon for until-beregninger, en for since-beregninger og en for between-beregninger. Disse vil jeg kalle på grunnlag av de parametrne modulen mottar. Tolking av disse parametrene vil jeg teste senere. Nå konsentrerer jeg meg om de tre funksjonene. Skjelettet for modulen min blir slik:
""" This module will calculate the difference in days between today and any other date, or between two dates """ import datetime def daysUntil(thisDay,datstr): """ thisDay is reference date, datstr is target date in string format""" return '?' def daysSince(thisDay,datstr): """ thisDay is reference date, dat is target date in string format""" return '?' def daysBetween(dat1str,dat2str): """ the two involved dates in string format""" return '?'
Så etablerer jeg testen slik:
""" Testing the module daydiff """ # import the module we want to test: import daydiff # import library for making dates import datetime # import the PyUnit class we want to subclass: import unittest class testdatediff(unittest.TestCase): def setUp(self): # set up a refrence date that will be used # in stead of datetime.date.today() self.refdate=datetime.date(2007,9,15) def testUntil(self): """ testing the until-case """ for d in [['2007-09-15','0'], ['2007-09-16','1'], ['2007-08-15','-31'], ['xxxx-09-25','10'], ['xxxx-09-05','356'], ['09-05','?'], ['2006:09-05','?']]: print d self.assert_(daydiff.daysUntil(self.refdate,d[0])==d[1]) def testSince(self): """ testing the since-case """ for d in [['2007-09-15','0'], ['2007-09-16','-1'], ['xxxx-09-16','364'], ['xxxx-09-14','1'] ]: print d self.assert_(daydiff.daysSince(self.refdate,d[0])==d[1]) def testBetween(self): """ testing the between-case """ for d in [['2007-09-16','2007-09-17','1'], ['2007-09-16','2007-09-11','-5'], ['2007-09-16','xxxx-09-17','1'], ['2007-09-16','xxxx-09-15','365'], ['xxxx-09-15','xxxx-09-14','365'] ]: print d self.assert_(daydiff.daysBetween(d[0],d[1])==d[2]) def makeTestSuite(): suite=unittest.TestSuite() suite.addTest(testdatediff('testUntil')) suite.addTest(testdatediff('testSince')) suite.addTest(testdatediff('testBetween')) return suite suite = makeTestSuite() unittest.TextTestRunner(verbosity=2).run(suite)
Første forsøk på å teste går naturlig nok galt, alle de første testsettene for hver funksjon feiler:
testing the until-case ... ['2007-09-15', '0'] FAIL testing the since-case ... ['2007-09-15', '0'] FAIL testing the between-case ... ['2007-09-16', '2007-09-17', '1'] FAIL .......
Steg2
Jeg bearbeider funksjonene og får noe slikt.
""" This module will calculate the difference in days between today and any other date, or between two dates """ import datetime import os def makeLegalDateParts(datstr): """ make legal y,m,d from the string datstr. y=xxxx is legal""" prts=datstr.split('-') if len(prts) != 3: return [-1,-1,-1] (y,m,d)=prts m=int(m.lstrip('0')) d=int(d.lstrip('0')) if y.isdigit(): y=y.lstrip('0') return [y,m,d] if y=='xxxx': return [y,m,d] return [-1,-1,-1] def daysUntil(thisDay,datstr): """ thisDay is reference date, datstr is target date in string format""" (y,m,d)=makeLegalDateParts(datstr) if y == -1: return '?' if y.isdigit(): try: dat=datetime.date(int(y),m,d) return str((dat-thisDay).days) except: return '?' # we have an xxxx situation for y in range(thisDay.year,thisDay.year+2): try: dat=datetime.date(y,m,d) if dat > thisDay: return str((dat-thisDay).days) except: return '?' return '?' def daysSince(thisDay,datstr): """ thisDay is reference date, dat is target date in string format""" (y,m,d)=makeLegalDateParts(datstr) if y == -1: return '?' if y.isdigit(): try: dat=datetime.date(int(y),m,d) return str((thisDay-dat).days) except: return '?' # we have an xxxx situation for y in range(thisDay.year,thisDay.year-2,-1): try: dat=datetime.date(y,m,d) if dat < thisDay: return str((thisDay-dat).days) except: return '?' return '?' def daysBetween(dat1str,dat2str): """ the two involved dates in string format""" (y1,m1,d1)=makeLegalDateParts(dat1str) (y2,m2,d2)=makeLegalDateParts(dat2str) if y1==-1 or y2==-1: return '?' if y1.isdigit(): try: return daysUntil(datetime.date(int(y1),m1,d1),dat2str) except: return '?' elif y2.isdigit(): try: return daysSince(datetime.date(int(y2),m2,d2),dat1str) except: return '?' else: # we have two xxxx cases, we make a choice # go for a positive value try: dat1=datetime.date(datetime.date.today().year,m1,d1) dat2=datetime.date(datetime.date.today().year,m2,d2) if dat1 > dat2: dat1=datetime.date(datetime.date.today().year-1,m1,d1) return daysUntil(dat1,dat2str) except: return '?' return '?'
Testkoden er som før og jeg får:
testing the until-case ... ['2007-09-15', '0'] ['2007-09-16', '1'] ['2007-08-15', '-31'] ['xxxx-09-25', '10'] ['xxxx-09-05', '356'] ['09-05', '?'] ['2006:09-05', '?'] ok testing the since-case ... ['2007-09-15', '0'] ['2007-09-16', '-1'] ['xxxx-09-16', '364'] ['xxxx-09-14', '1'] ok testing the between-case ... ['2007-09-16', '2007-09-17', '1'] ['2007-09-16', '2007-09-11', '-5'] ['2007-09-16', 'xxxx-09-17', '1'] ['2007-09-16', 'xxxx-09-15', '365'] ['xxxx-09-15', 'xxxx-09-14', '365'] ok ---------------------------------------------------------------------- Ran 3 tests in 0.063s OK
Vi slår oss til ro med at vi har skrevet tre testede funksjoner.
Steg3
Nå skal vi bruke de tre funksjonene i en modul som ligger på nettet og besvarer spørsmål. Vi har nå flere veier å gå når vi skal fortsette og teste. Jeg velger å lage meg en vevside som tester modulen. Igjen skriver jeg testen før jeg går videre med koden.
Det som testes er nå den ferdige modulen:
#! /usr/bin/python """ Calculating the difference between days since: yyyy.mm.dd xxxx.mm.dd ( days since last time the date (mm.dd) occured) until: yyy.mm.dd xxx.mm.dd ( days until next time the date (mm.dd) will occur) between:yyyy.mm.dd yyyy.mm.dd yyyy.mm.dd xxxx.mm.dd ( days from from first date until next time the date (mm.dd) will occur) xxxx.mm.dd yyyy.mm.dd ( days since the date (mm.dd) occured, as seen from second date) xxxx.mm.dd xxxx.mm.dd ( days between dates in same year, this year is used. possibly a leapyear) Returns: a single integer, possibly negative Returns: ? if parameters does not make sence, or are in bad format """ import datetime import os """ pack up a datestr and find integers """ def makeLegalDateParts(datstr): """ make legal y,m,d from the string datstr. y=xxxx is legal""" prts=datstr.split('-') if len(prts) != 3: return [-1,-1,-1] (y,m,d)=prts m=int(m.lstrip('0')) d=int(d.lstrip('0')) if y.isdigit(): y=y.lstrip('0') return [y,m,d] if y=='xxxx': return [y,m,d] return [-1,-1,-1] """ until is ordered """ def daysUntil(thisDay,datstr): """ thisDay is reference date, datstr is target date in string format""" (y,m,d)=makeLegalDateParts(datstr) if y == -1: return '?' if y.isdigit(): try: dat=datetime.date(int(y),m,d) return str((dat-thisDay).days) except: return '?' # we have an xxxx situation for y in range(thisDay.year,thisDay.year+2): try: dat=datetime.date(y,m,d) if dat > thisDay: return str((dat-thisDay).days) except: return '?' return '?' """ since is ordered """ def daysSince(thisDay,datstr): """ thisDay is reference date, dat is target date in string format""" (y,m,d)=makeLegalDateParts(datstr) if y == -1: return '?' if y.isdigit(): try: dat=datetime.date(int(y),m,d) return str((thisDay-dat).days) except: return '?' # we have an xxxx situation for y in range(thisDay.year,thisDay.year-2,-1): try: dat=datetime.date(y,m,d) if dat < thisDay: return str((thisDay-dat).days) except: return '?' return '?' """ between is ordered """ def daysBetween(dat1str,dat2str): """ the two involved dates in string format""" (y1,m1,d1)=makeLegalDateParts(dat1str) (y2,m2,d2)=makeLegalDateParts(dat2str) if y1==-1 or y2==-1: return '?' if y1.isdigit(): try: return daysUntil(datetime.date(int(y1),m1,d1),dat2str) except: return '?' elif y2.isdigit(): try: return daysSince(datetime.date(int(y2),m2,d2),dat1str) except: return '?' else: # we have two xxxx cases, we make a choice # go for a positive value try: dat1=datetime.date(datetime.date.today().year,m1,d1) dat2=datetime.date(datetime.date.today().year,m2,d2) if dat1 > dat2: dat1=datetime.date(datetime.date.today().year-1,m1,d1) return daysUntil(dat1,dat2str) except: return '?' return '?' if __name__=="__main__": import cgi form=cgi.FieldStorage() #--------------------------- # expect to find parameters: # diff= until | since | between # diff=until | since -> date # dif=between -> date and date2 #---------------------------- thisDay=datetime.date.today() # if requested from testpage we set reference date if os.environ.has_key('HTTP_REFERER')\ and str(os.environ['HTTP_REFERER']).find('_full_test.html') != -1: thisDay=datetime.date(2007,9,15) print 'Content-type: text/plain\n' diff='' date1='' date2='' if form.has_key('diff'): diff=form['diff'].value if form.has_key('date'): date1=form['date'].value if form.has_key('date2'): date2=form['date2'].value #print(diff+' '+date) if diff=='until': print daysUntil(thisDay,date1) elif diff=='since': print daysSince(thisDay,date1) elif diff=='between': print daysBetween(date1,date2) else: print '?'
Vi har da en modul daydiff.py, vi har en testmodul, daytest.py og vi har en testside, fulltest,html. Hele testbatteriet er intakt og vi kan test senere når vi oppdager feil, eller dersom vi ønsker å skrive om koden. (det forekommer meg at den kan forenkles noe ?)
Bruk
Den ferdige modulen kan brukes herfra: http://www.it.hiof.no/~borres/cgi-bin/ajax/days/daydiff.py. Dokumentasjonen kan være omtrent slik:
url:http://www.it.hiof.no/~borres/cgi-bin/ajax/days/daydiff.py Calculating and returning the day difference between dates Parameters: diff since | until | between since date date yyyy.mm.dd date xxxx.mm.dd ( days since last time the date (mm.dd) occured) until date yyyy.mm.dd date xxxx.mm.dd ( days until next time the date (mm.dd) will occur) between: date date 2 date=yyyy.mm.dd date2=yyyy.mm.dd ( days between the two dates) date=yyyy.mm.dd date2=xxxx.mm.dd ( days from from first date until next time the date2 will occur) date=xxxx.mm.dd date2=yyyy.mm.dd ( days since the date (mm.dd) occured, as seen from second date) date=xxxx.mm.dd date2=xxxx.mm.dd ( days between dates, baed on date, always positive) Returns: a single integer, possibly negative Returns: ? if parameters does not make sence, or are in bad format Samples: url?diff=until&date=2011-12-24 url?diff=since&date=2011-12-24 url?diff=between&date=2011-12-24&date2=2013-12-24
Eksempel på bruk: Så mange dager er det til jul:
<p> Eksempel på bruk: Så mange dager er det til jul: <span id="jul" style="color:red;fontweight:bold"></span> </p> <script> $('#jul').load('http://www.it.hiof.no/~borres/cgi-bin/ajax/days/daydiff.py?diff=until&date=xxxx-12-24'); </script>
Det er laget en forklaringsside: http://www.it.hiof.no/~borres/cgi-bin/ajax/days/explained.html.