""" @author Thomas Lehmann @file dateBack.py @brief provides a human readable format for a time delta """ from datetime import datetime , timedelta def dateBack(theDateAndTime, precise=False, fromDate=None): """ provides a human readable format for a time delta @param theDateAndTime this is time equal or older than now or the date in 'fromDate' @param precise when true then milliseconds and microseconds are included @param fromDate when None the 'now' is used otherwise a concrete date is expected @return the time delta as text @note I don't calculate months and years because those varies (28,29,30 or 31 days a month and 365 or 366 days the year depending on leap years). In addition please refer to the documentation for timedelta limitations. """ if not fromDate: fromDate = datetime.now() if theDateAndTime > fromDate: return None elif theDateAndTime == fromDate: return "now" delta = fromDate - theDateAndTime # the timedelta structure does not have all units; bigger units are converted # into given smaller ones (hours -> seconds, minutes -> seconds, weeks > days, ...) # but we need all units: deltaMinutes = delta.seconds // 60 deltaHours = delta.seconds // 3600 deltaMinutes -= deltaHours * 60 deltaWeeks = delta.days // 7 deltaSeconds = delta.seconds - deltaMinutes * 60 - deltaHours * 3600 deltaDays = delta.days - deltaWeeks * 7 deltaMilliSeconds = delta.microseconds // 1000 deltaMicroSeconds = delta.microseconds - deltaMilliSeconds * 1000 valuesAndNames =[ (deltaWeeks ,"week" ), (deltaDays ,"day" ), (deltaHours ,"hour" ), (deltaMinutes,"minute"), (deltaSeconds,"second") ] if precise: valuesAndNames.append((deltaMilliSeconds, "millisecond")) valuesAndNames.append((deltaMicroSeconds, "microsecond")) text ="" for value, name in valuesAndNames: if value > 0: text += len(text) and ", " or "" text += "%d %s" % (value, name) text += (value > 1) and "s" or "" # replacing last occurrence of a comma by an 'and' if text.find(",") > 0: text = " and ".join(text.rsplit(", ",1)) return text def test(): """ testing function "dateBack" """ # we need a date to rely on for testing concrete deltas fromDate = datetime(year=2012, month=4, day=26, hour=8, minute=40, second=45) testCases = [ ("1 second" , fromDate-timedelta(seconds=1), False), ("5 seconds" , fromDate-timedelta(seconds=5), False), ("1 minute" , fromDate-timedelta(minutes=1), False), ("5 minutes" , fromDate-timedelta(minutes=5), False), ("1 minute and 10 seconds" , fromDate-timedelta(minutes=1, seconds=10), False), ("1 hour" , fromDate-timedelta(hours= 1), False), ("1 hour and 1 second" , fromDate-timedelta(hours=1, seconds=1), False), ("1 hour and 1 minute" , fromDate-timedelta(hours=1, minutes=1), False), ("1 hour, 1 minute and 1 second" , fromDate-timedelta(hours=1, minutes=1, seconds=1), False), ("1 week" , fromDate-timedelta(weeks=1), False), ("2 weeks" , fromDate-timedelta(weeks=2), False), ("1 week and 1 second" , fromDate-timedelta(weeks=1, seconds=1), False), ("1 week and 1 minute" , fromDate-timedelta(weeks=1, minutes=1), False), ("1 week and 1 hour" , fromDate-timedelta(weeks=1, hours=1), False), ("1 week, 1 hour, 1 minute and 1 second", fromDate-timedelta(weeks=1, hours=1, minutes=1, seconds=1), False), ("1 millisecond" , fromDate-timedelta(milliseconds=1),True), ("2 milliseconds" , fromDate-timedelta(milliseconds=2),True), ("1 microsecond" , fromDate-timedelta(microseconds=1),True), ("2 microseconds" , fromDate-timedelta(microseconds=2),True), ("1 millisecond and 1 microsecond" , fromDate-timedelta(milliseconds=1, microseconds=1),True) ] for expectedResult, testDate, precise in testCases: print("test case for '%s'" % expectedResult) calculatedResult = dateBack(testDate, precise=precise, fromDate=fromDate) try: assert expectedResult == calculatedResult except: print(" -> error: wrong value: '%s'" % calculatedResult) # future date in relation to 'fromDate' (1 hour) futureDate = fromDate + timedelta(hours=1) expectedResult = None assert expectedResult == dateBack(futureDate, precise=False, fromDate=fromDate) if __name__ == "__main__": test()