Scripting Data Source - Seconds to hh:mm:ss

I have a data source using a Tag Historian Query with several boolean "Running" tags. My aim is to calculate running hours over a set time frame. I can do this using the "Duration On" aggregation mode but it gives me the result in seconds and I need this to display in hh:mm:ss format.

I believe the only way to achieve the result in hh:mm:ss format is by using a script data source but I am struggling to achieve the result. Any ideas?

Logging runtimes seems like a job for the PLC, but as far as purely converting seconds to hh.mm/mm.ss, here is mine. I'm sure there's a more efficient way to do it, but this works for me.

def toHHMMSS(val, hours, mins, secs):
    '''
    convert a value in hours, minutes, or seconds to a readable hours.minutes/minutes.seconds (hh.mm/mm.ss) format
    
    Args:
        val     (double)    : time value to convert
        hours   (bool)      : time value is in hours
        mins    (bool)      : time value is in minutes
        secs    (bool)      : time value is in seconds
        
    Returns:
        result          (double     : time to display
        result_label    (string)    : label for result (hh.mm or mm.ss))
    '''

    from math import floor
    
    ####convert any provided val to seconds####
    
    #value provided in seconds
    if secs:
        val_seconds = val
        
    #value provided in minutes
    elif mins:
        val_seconds = val*60.0
        
    #value provided in hours
    else:
        val_seconds = val*60.0*60.0
    
        
    ####hours calculation####
    
    #more than 1 full hour
    if val_seconds > 60*60:
        #calculate hours
        val_hours = val_seconds/(60.0*60.0)
        #get whole number of hours
        whole_hours = floor(val_hours)
        #get remaining number of minutes
        rem_minutes = (val_hours - whole_hours)*60.0
        
        #set whole and fractional results (hh.mm)
        result_whole = whole_hours
        result_rem = rem_minutes/100.0
        result_label = 'hh.mm'
    
    #less than 1 full hour
    else:
        ####minutes calculation####
        
        #more than 1 full minute
        if val_seconds > 60:
            #calculate minutes
            val_minutes = val_seconds/60.0
            #get whole number of minutes
            whole_minutes = floor(val_minutes)
            #get remaining number of seconds
            rem_seconds = (val_minutes - whole_minutes)*60.0
            
        #less than 1 full minute
        else:
            #no full minutes
            whole_minutes = 0
            #get remaining number of seconds
            rem_seconds = val_seconds
            
            
        #set whole and fractional results (hh.mm)
        result_whole = whole_minutes
        result_rem = rem_seconds/100.0
        result_label = 'mm.ss'
    
    #combine whole number with decimal
    result = result_whole+result_rem
    
    return result, result_label
1 Like

You could pair the above with something like this general purpose mapColumn function for datasets, to update all the values in a particular column of the dataset as one operation:

You get less control over the boundary conditions, but you can also use our FormatUtil internal library to achieve the same result:

Quick question.

This script returns two values, the float and a string label.
How would I reference each using runScript() in an expression binding (if possible)?

edit: I was able to do it with runScript(...)[0] and [1]