Date string to date object, seconds to readable string

TrustyTony 1 Tallied Votes 962 Views Share

Here tool functions, which came up during discussions earlier. Expanded slightly, added elimination of traceback prints by try..except.

Added dd.mm.yy routine as that is format in my home country Finland.

Optimized docstring beginnings to be informative in editor tooltip (at least IDLE shows, but code must be run or imported first).

If you like to use put your library path and add mention of origin in your comments/documentation. (Looking for job, you know)

Gribouillis commented: useful constructors for date objects. +3
""" Time and date related routines, Tony Veijalainen 2010 """
import datetime

def todate_m(datestring): ## month first
    """ To date month first. Input string in month, day, year order
Returns date object for date string with any separator between the parts.
"""
    try:
        sep=[s for s in datestring if not s.isdigit()]

        if len(sep)!=2 or sep[0] != sep [1]:
            print "Mixup in separators",sep
        else:
            datesep=sep[0]
            mm,dd,yy = (int(part) for part in datestring.split(datesep))
            return datetime.date(yy,mm,dd)
    except ValueError as e:
        ## stop traceback for ValueError
        print 'todate_m("'+datestring+'"): '+str(e) 
    
def todate_d(datestring): ## day first
    """ To date day first. Input string in day, month,year order
It returns date object for date string with any separator between the parts.
"""
    try:
        sep=[s for s in datestring if not s.isdigit()]

        if len(sep)!=2 or sep[0] != sep [1]:
            print "Mixup in separators",sep
        else:
            datesep=sep[0]
            dd,mm,yy = (int(part) for part in datestring.split(datesep))
            return datetime.date(yy,mm,dd)
    except ValueError as e:
        ## stop traceback for ValueError
        print 'todate_d("'+datestring+'"): '+str(e) 

    """ Use exemles for todate functions
>>> todate_m('01/23/2010')
datetime.date(2010, 1, 23)
##before:
##>>> todate_m('31.12.2020')
##
##Traceback (most recent call last):
##  File "<pyshell#17>", line 1, in <module>
##    todate_m('31.12.2020')
##  File "D:/Python26/Lib/site-packages/timestring.py", line 17, in todate_m
##    return datetime.date(yy,mm,dd)
##ValueError: month must be in 1..12
##After putting: try..except error becomes
>>> todate_m('31.12.2001')
todate_m("31.12.2001"): month must be in 1..12
>>> todate_m('12.31.2020')
datetime.date(2020, 12, 31)
>>> todate_m('12/31/2020')
datetime.date(2020, 12, 31)
>>> todate_d('31.12.2020')
datetime.date(2020, 12, 31)
>>> todate_d('31m12d2020')
Mixup in separators ['m', 'd']
>>> """
    
def timestr(t):
    """ verbalizes times to more understandable units.

        <-- takes time t as seconds,
        ---> returns string, which can be printed or added to other strings,

        seconds in time is return in millisecond accuracy, if not needed, use
        timestr(mytime)[:-6], if time is known to be more than 1 second or adapt the function
        
        longest implemented unit days
    """
    if t>=24*60*60:
        days,t=divmod(t,24*60*60)
        return "%i days %s" % (days,timestr(t))        
    elif t>=60*60:
        hours,t=divmod(t,60*60)
        return "%i hours %s" % (hours,timestr(t))
    elif t>=60: return "%i min %.3f s" % divmod(t,60)
    elif t>=1: return "%.3f s" % t
    else: return "%i ms" % int(t*1000)
    """ Examples:
>>> timestr(24*60*60+.34543)
'1 days 345 ms'
>>> timestr(24*60*60+67+.34543)
'1 days 1 min 7.345 s'
>>> timestr(24*60*60+456++67+.34543)
'1 days 8 min 43.345 s'
>>> timestr(24*60*60+6543+456++67+.34543)
'1 days 1 hours 57 min 46.345 s'
>>> timestr(24*60*60+16543+456++67+.34543)
'1 days 4 hours 44 min 26.345 s'
>>> timestr(24*60*90+16543+456++67+.34543)
'1 days 16 hours 44 min 26.345 s'
>>> timestr(34*60*90+16543+456++67+.34543)
'2 days 7 hours 44 min 26.345 s'
>>> """
TrustyTony 888 pyMod Team Colleague Featured Poster

As I see now from my test cases here would be obvious place to use the other snippet for plurals.

Gribouillis 1,391 Programming Explorer Team Colleague

I like the idea of providing simple constructors for the datetime module classes. I think you should not however catch the ValueErrors which may arise. A python function is more usable when it raises an exception for wrong input, instead of printing an error message. An error message in output doesn't say where it comes from and your function's client code will never know that the call failed, which is worse than having an exception.

TrustyTony 888 pyMod Team Colleague Featured Poster

It does say the function and the argument, when it was called. I did however rewrite my code many times for the error case. I do not like those function and line number messages popping up to users eyes. Now the message tells the function with actual value of parameter, instead of listing of program code. The value from function will be None, which should identify enough the calling place, usually complaint "NoneType can not add to string" or printing None in certain place of output.

I do not know how much this worsen or improve the situation. However the information of the actual value of faulty parameter is what I usually want to do to debug the situation. Basically I tried to allready give that information in the adapted error message.

Really those plurals are missing here (I added just today those days and hours as my programs don't usually run that long).

I do however to change the implementation to list of (number_of_seconds, singular, plural) style and loop instead of case-like if.

Have to consider if to put that handling to next version or not.

TrustyTony 888 pyMod Team Colleague Featured Poster

Error catching removed, added plural forms for big units using the plural snippet (here again as it is so small)

## You can add to this number to words converter if you like
def plural(n,sing,plur):
    if n-1: return str(n)+' '+plur
    return str(n)+' '+sing
""" Time and date related routines, Tony Veijalainen 2010 """
import datetime
from plural import plural ## plural snippet for practice (even oneliner)

## try..except removed by request
def todate_m(datestring): ## month first
    """ To date month first. Input string in month, day, year order
Returns date object for date string with any separator between the parts.
"""
    sep=[s for s in datestring if not s.isdigit()]

    if len(sep)!=2 or sep[0] != sep [1]:
        print "Mixup in separators",sep
    else:
        datesep=sep[0]
        mm,dd,yy = (int(part) for part in datestring.split(datesep))
        return datetime.date(yy,mm,dd)
    
def todate_d(datestring): ## day first
    """ To date day first. Input string in day, month,year order
It returns date object for date string with any separator between the parts.
"""
    sep=[s for s in datestring if not s.isdigit()]

    if len(sep)!=2 or sep[0] != sep [1]:
        print "Mixup in separators",sep
    else:
        datesep=sep[0]
        dd,mm,yy = (int(part) for part in datestring.split(datesep))
        return datetime.date(yy,mm,dd)

    """
Use examples for todate functions
>>> todate_m('01/23/2010')
datetime.date(2010, 1, 23)
>>> todate_m('12.31.2020')
datetime.date(2020, 12, 31)
>>> todate_m('12/31/2020')
datetime.date(2020, 12, 31)
>>> todate_d('31.12.2020')
datetime.date(2020, 12, 31)
>>> todate_d('31m12d2020')
Mixup in separators ['m', 'd']
>>> 
"""
    
## plural snipet use added and format string use removed for big units
##   -> simpler and more correct
def timestr(t):
    """ verbalizes times to more understandable units.

        <-- takes time t as seconds,
        ---> returns string, which can be printed or added to other strings,

        seconds in time is return in millisecond accuracy, if not needed, use
        timestr(mytime)[:-6], if time is known to be more than 1 second or adapt the function
        
        longest implemented unit days
    """
    if t>=24*60*60:
        days,t=divmod(t,24*60*60)
        days=int(days)
        return plural(days,"day ","days ")+timestr(t)  ## space after each unit
    elif t>=60*60:
        hours,t=divmod(t,60*60)
        hours=int(hours)
        return plural(hours,'hour ','hours ')+timestr(t)
    elif t>=60: return "%i min %.3f s" % divmod(t,60) ## let's keep min and sec as abreviation
    elif t>=1: return "%.3f s" % t
    else: return "%i ms" % int(t*1000)
    """
Examples:
>>> timestr(2*60*60+16543+456+41+.34543)
'6 hours 44 min 0.345 s'
>>> timestr(24*60*60+16543+456++21+.34543)
'1 day 4 hours 43 min 40.345 s'
>>> timestr(24*60*60+.34543)
'1 day 345 ms'
>>> timestr(24*60*60+6543+456++67+.34543)
'1 day 1 hour 57 min 46.345 s'
>>> timestr(123456789)
'1428 days 21 hours 33 min 9.000 s'
>>> 
"""
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.