10

I'm doing some extended length calculations and was wondering how I could get AbsoluteTiming to display in hours, minutes, and seconds. I googled and found nothing, so I borrowed some code, edited it, and wrote:

SetAttributes[myAbsoluteTiming, HoldAll];
myAbsoluteTiming[calculation_] := 
 Module[{startTime, deltaTime, result}, startTime = SessionTime[];
  result = calculation;
  deltaTime = SessionTime[] - startTime;
  hms = {Floor[deltaTime/3600], 
    Floor[Mod[Floor[deltaTime], 3600]/60], 
    Mod[Mod[deltaTime, 3600], 60]};
  {hms, result}]

This appears to be about .0005 seconds slower than the AbsoluteTiming function.

Is that time difference accurate? Is there a better way of doing this? How would I improve upon my code?

Karsten7
  • 27,448
  • 5
  • 73
  • 134

5 Answers5

7
SetAttributes[hmsAbsTiming, HoldAllComplete];
hmsAbsTiming[calculation_] := 
 MapAt[IntegerDigits[IntegerPart[#], MixedRadix[{24, 60, 60}]] &,
  AbsoluteTiming[
   calculation
   ], 1]

If you prefer a Quantity object:

SetAttributes[hmsAbsTiming2, HoldAllComplete];
hmsAbsTiming2[calculation_] := 
 MapAt[UnitConvert[Quantity[#, "Seconds"], MixedUnit[{"Hours", "Minutes", "Seconds"}]] &,
  AbsoluteTiming[
   calculation
   ], 1]

There is a TimeString function in the GeneralUtilities` context that is very convenient here.

Needs["GeneralUtilities`"]

SetAttributes[hmsAbsTiming3, HoldAllComplete];
hmsAbsTiming3[calculation_] := MapAt[TimeString[#] &, AbsoluteTiming[calculation], 1]

Benchmark:

RepeatedTiming[hmsAbsTiming[Pause[0.001]], 30]
RepeatedTiming[hmsAbsTiming2[Pause[0.001]], 30]
RepeatedTiming[hmsAbsTiming3[Pause[0.001]], 30]

OutPNG

Karsten7
  • 27,448
  • 5
  • 73
  • 134
3

Here's my take, borrowing some code from here and here:

ic = Function[x, With[{r = Round[x]}, r + Chop[x - r]], Listable];

SetAttributes[myTiming, HoldAllComplete];
myTiming[calculation_, tf : (Timing | AbsoluteTiming) : AbsoluteTiming] := 
         Module[{timing = tf[calculation]},
                Print[StringTemplate["`h` hr `m` min `s` s", 
                                     InsertionFunction -> (ToString[#, TraditionalForm] &)]
                      @ AssociationThread[{"h", "m", "s"}, 
                        ic[N[DMSList[SetPrecision[timing[[1]], ∞]/3600]]]]]; 
                If[Length[timing] == 2, timing[[2]], Sequence @@ Rest[timing]]]

Some examples:

myTiming example

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
3

Here is a simple and universal function which formats timings (for real-time monitoring of elapsed time one should replace Round with Floor):

formatTiming = 
  StringJoin[{If[# >= 100, ToString@#, IntegerString[#, 10, 2]] &@Floor[#/3600], ":", 
       IntegerString[Floor[Mod[#, 3600]/60], 10, 2], ":", 
       IntegerString[Mod[#, 60], 10, 2]} &@Round[#]] &;

Examples of use:

formatTiming@1
"00:00:01"
formatTiming@60
"00:01:00"
formatTiming[200*3600 + 60*2 + 2]
"200:02:02"
formatTiming[AbsoluteTiming[Pause[1]][[1]]]
"00:00:01"

Benchmark:

Needs["GeneralUtilities`"]
RepeatedTiming[formatTiming[AbsoluteTiming[Pause[.001]][[1]]], 30]
{0.0010, "00:00:00"}
Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
2

Using Mathematica's built-in unit conversion & getting rid of the annoying "0 years, 0 hours ...":

timeConvert=Quantity@@@(Delete[#,List/@Position[#,0][[All,1]]]&[
Transpose[{#[[1,1]],#[[2,1]]}]&[
UnitConvert[Quantity[#,"Seconds"],
MixedRadix["Years","Months","Days","Hours","Minutes","Seconds"]]]])&; 

In[1]:=timeConvert[14695241.8]
Out[1]={Quantity[5, "Months"], Quantity[18, "Days"],Quantity[41.8, "Seconds"]}
1

I like @Karsten's approach of making a pure function that can be applied directly to format results from AbsoluteTiming, but if you're new to Mathematica this might be easier to follow:

SetAttributes[myAbsoluteTiming, HoldAllComplete];
myAbsoluteTiming[calculation_] := 
 Module[{time, result, days, hours, minutes, seconds, format},
        {time, result} = AbsoluteTiming[calculation];
        With[{dy = 86400, hr = 3600, mn = 60},
             days = Floor[time/dy];
             time = Mod[time, dy];
             hours = Floor[time/hr];
             minutes = Floor[Mod[time, hr]/mn];
             seconds = Floor[time - hr*hours - mn*minutes]];
             format = IntegerString[#, 10, 2] &;
             If[days > 0,
                {StringJoin[Riffle[format /@ {hours, minutes, seconds}, {":"}], " +", IntegerString[days], " days"], result},
                {StringJoin[Riffle[format /@ {hours, minutes, seconds}, {":"}]], result}]]

myAbsoluteTiming[Pause[1]]
(* {00:00:01, Null} *)
dionys
  • 4,321
  • 1
  • 19
  • 46