Search code examples
pythontype-conversionstring-formattingtimedelta

Python timedelta object - strfdelta and deltafstr functions for conversion of timedelta 🠞 string 🠞 timedelta


Is there an implementation of strfdelta() and deltafstr() functions for Python similar to the way that strftime() works on a datetime object?

There are similar questions on this...

... but no consistent way of being able to convert back and forth between the two formats.

I want to be able to convert from timedelta to string, and then back to timedelta.

The intended use is for a Hadoop mapper/reducer process (the intermediate delta time output from the mapper script, for input into the reducer script).


Solution

  • After searching for such functions, and not being able to find one that converts back and forth, I wrote the following two functions and include them in a script. This is compatible with Python v2.6.6, which doesn't support some newer features such as timedelta.total_seconds():

    #!/usr/bin/python
    
    import re
    import sys
    import datetime
    
    # String from Date/Time Delta:
    #  Takes a datetime.timedelta object, and converts the internal values
    #  to a dd:HH:mm:ss:ffffff string, prefixed with "-" if the delta is
    #  negative
    def strfdelta(tdelta):
    
        # Handle Negative time deltas
        negativeSymbol = ""
        if tdelta < datetime.timedelta(0):
            negativeSymbol = "-"
    
        # Convert days to seconds, as individual components could
        # possibly both be negative
        tdSeconds = (tdelta.seconds) + (tdelta.days * 86400)
    
        # Capture +/- state of seconds for later user with milliseonds calculation
        secsNegMultiplier = 1
        if tdSeconds < 0:
            secsNegMultiplier = -1
    
        # Extract minutes from seconds
        tdMinutes, tdSeconds = divmod(abs(tdSeconds), 60)
    
        # Extract hours from minutes
        tdHours, tdMinutes = divmod(tdMinutes, 60)
        # Extract days from hours
        tdDays, tdHours = divmod(tdHours, 24)
    
        # Convert seconds to microseconds, as individual components 
        # could possibly both be negative
        tdMicroseconds = (tdelta.microseconds) + (tdSeconds * 1000000 * secsNegMultiplier)
    
        # Get seconds and microsecond components
        tdSeconds, tdMicroseconds = divmod( abs(tdMicroseconds), 1000000)
    
        return "{negSymbol}{days}:{hours:02d}:{minutes:02d}:{seconds:02d}:{microseconds:06d}".format(
            negSymbol=negativeSymbol,
            days=tdDays,
            hours=tdHours,
            minutes=tdMinutes,
            seconds=tdSeconds,
            microseconds=tdMicroseconds)
    
    
    # Date/Time delta from string
    # Example: -1:23:32:59:020030 (negative sign optional)
    def deltafstr(stringDelta):
    
        # Regular expression to capture status change events, with groups for date/time, 
        #  instrument ID and state
        regex = re.compile("^(-?)(\d{1,6}):([01]?\d|2[0-3]):([0-5][0-9]):([0-5][0-9]):(\d{6})$",re.UNICODE)
        matchObj = regex.search(stringDelta)
    
        # If this line doesn't match, return None
        if(matchObj is None):
            return None;
    
        # Debug - Capture date-time from regular expression 
        # for g in range(0, 7):
        #     print "Grp {grp}: ".format(grp=g) + str(matchObj.group(g)) 
    
        # Get Seconds multiplier (-ve sign at start)
        secsNegMultiplier = 1
        if matchObj.group(1):
            secsNegMultiplier = -1
    
        # Get time components
        tdDays = int(matchObj.group(2)) * secsNegMultiplier
        tdHours = int(matchObj.group(3)) * secsNegMultiplier
        tdMinutes = int(matchObj.group(4)) * secsNegMultiplier
        tdSeconds = int(matchObj.group(5)) * secsNegMultiplier
        tdMicroseconds = int(matchObj.group(6)) * secsNegMultiplier
    
        # Prepare return timedelta
        retTimedelta = datetime.timedelta(
            days=tdDays,
            hours=tdHours,
            minutes=tdMinutes,
            seconds=tdSeconds,
            microseconds=tdMicroseconds)
    
        return retTimedelta;
    

    Here is some code that tests going back and forward between the two formats. The constructor arguments for the timedelta object may be changed to test different scenarios:

    # Testing (change the constructor for timedelta to test other cases)
    firstDelta = datetime.timedelta(seconds=-1,microseconds=999999, days=-1)
    print "--------"
    print firstDelta
    firstDeltaStr = strfdelta(firstDelta)
    print "--------"
    print firstDeltaStr;
    secondDelta = deltafstr(firstDeltaStr)
    print "--------"
    print secondDelta
    secondDeltaStr = strfdelta(secondDelta)
    print "--------"
    print secondDelta
    print "--------"