How to print 3600.125 seconds in "H:M:S.s" format in Julia

Viewed 749

I got some time intervals using the tic() and toc() functions which are in seconds. Let's suppose I have a time interval dt=3600.125 seconds. How can I print it in "H:M:S.s" format using Julia?

6 Answers

If you convert it to Dates formats then you can use this method.

julia> t1 = now()
2017-11-10T10:00:51.974
# Wait
julia> t2 = now()
2017-11-10T10:10:07.895
julia> x = Dates.canonicalize(Dates.CompoundPeriod(t2-t1))
9 minutes, 15 seconds, 921 milliseconds

julia> x.periods
3-element Array{Base.Dates.Period,1}:
 9 minutes      
 15 seconds      
 921 milliseconds

julia> x.periods[2]
15 seconds

julia> x.periods[2].value
18

Have a look at the dates section in the manual.

Not sure if this is the most efficient way, but this works for instance:

julia> Dates.format(DateTime("2017-10-01T01:02:03"), "H:M:S.s")
"1:2:3.0"

You can make your own function. The main function to know is divrem which gives you the divisor as well as the remainder in one convenient function call.

dt=3600.125 

function hmss(dt)
    (h,r) = divrem(dt,60*60)
    (m,r) = divrem(r, 60)
    #(s,r) = divrem(r, 60)
    string(Int(h),":",Int(m),":",r)
end

hmss(dt)

hmss(3452.98)
""" working properly only if 0<= sec <=86400 otherwise truncating """
function fmtsec(sec, fmt::Dates.DateFormat)
    # nanos = Dates.Nanosecond(sec * 1e9)  # we could get InexactError here!
    nanos = Dates.Nanosecond(trunc(Int, sec * 1e9))
    mytime = Dates.Time(nanos)
    Dates.format(mytime, fmt)
end

fmt = dateformat"H:M:S.s"  # Creating a DateFormat object is expensive. (see doc)

fmtsec(3600.125, fmt)  # "1:0:0.125"

EDIT: Without truncating nanoseconds we could get error ->

julia> tic();sleep(1);old_fmtsec(toc(), dateformat"S.s")
elapsed time: 1.002896514 seconds
ERROR: InexactError()
Stacktrace:
 [1] convert(::Type{Int64}, ::Float64) at ./float.jl:679
 [2] fmtsec(::Float64, ::DateFormat{Symbol("S.s"),Tuple{Base.Dates.DatePart{'S'},Base.Dates.Delim{Char,1},Base.Dates.DatePart{'s'}}}) at ./REPL[47]:2

julia> tic();sleep(1);old_fmtsec(toc(), dateformat"S.s")
elapsed time: 1.002857122 seconds
"1.002"

tic & toc are deprecated. toc could bring rounding error because it internally use time_ns but nanoseconds convert to seconds dividing by 1e9.

This date from toc() and time() in Julia uses UNIX timestamp, i.e., seconds elapsed since January 1, 1970

The same occurs with the function ctime(f) that returns timestamp from a file path f.

The conversion is very straightforward.

tic()
....
dif = toc()
# My local is Brazil, one should replace for its local format
Dt = DateTime(LibC.strftime(dif),"d/m/y H:M:S")

LibC does not have to be imported explicitly

I had a go now, but doing the carry over is a bit messy sorry:

using Dates
using Printf

function formatDuration(duration::Period, types::Vector{DataType}=[Hour, Minute, Second]; addUnit::Bool=false)
    periods = canonicalize(Dates.CompoundPeriod(duration)).periods

    missingTypes = Vector{DataType}(types)

    newPeriods = vcat(periods, map(t->t(0), missingTypes))
    sort!(newPeriods; rev=true)
    newPeriods1 = Vector{Period}()

    carryover = nothing
    for (i, p) in enumerate(newPeriods)
        typeofp = typeof(p)
        if !isnothing(carryover)
            newPeriod = convert(typeofp, floor(Millisecond(p) + Millisecond(carryover), typeofp(1)))
            carryover = nothing
        else
            newPeriod = p
        end
        if (typeof(newPeriod) in types)
            push!(newPeriods1, newPeriod)
            filter!(e->e≠typeofp,missingTypes)
        else
            carryover = newPeriod
        end
    end
    m = map(types) do t
        f = findfirst(x -> typeof(x) == t, newPeriods1);
        r = isnothing(f) ? 0 : newPeriods1[f].value;
        @sprintf("%02d",r) * (addUnit ? lowercase("$t"[1]) : "")
    end
    join(m, ":")
end

Usage examples:

@show formatDuration(Millisecond(3600.125*1000))
# returns "01:00:00"
@show formatDuration(Day(2), [Hour, Minute, Second])
# returns "48:00:00"
@show formatDuration(Second(100000000), [Hour, Minute, Second])
# returns "27777:46:40"
@show formatDuration(Second(100000001), [Day, Hour, Minute, Second, Millisecond])
# returns "1157:09:46:41:00"
@show formatDuration(Millisecond(10000000001), 
    [Week, Day, Hour, Minute, Second, Millisecond]; addUnit=true)
#returns "16w:03d:17h:46m:40s:01m"
Related