10
$\begingroup$

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?

$\endgroup$
4
  • 1
    $\begingroup$ Could you add some detail about why you don't want to use AbsoluteTiming? It's not clear to me why you wouldn't just format the first output from that function. $\endgroup$
    – dionys
    May 7, 2016 at 18:45
  • $\begingroup$ @dionys No reason, I just don't know how to format AbsoluteTiming. There wasn't anything on it in the wolphram help files, and I'm a beginner. $\endgroup$ May 7, 2016 at 19:20
  • $\begingroup$ The deltaTimes = 3700; line looks like a leftover. $\endgroup$
    – Karsten7
    May 7, 2016 at 21:06
  • $\begingroup$ @Karsten7. Ah, yes it is. I was setting deltaTime manually to test earlier. $\endgroup$ May 7, 2016 at 21:07

5 Answers 5

7
$\begingroup$
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

$\endgroup$
9
  • $\begingroup$ I'm getting: Quantity::unkunit: Unable to interpret unit specification MixedUnit[{Hours,Minutes,Seconds}] when I run the quantity one. $\endgroup$ May 7, 2016 at 20:40
  • $\begingroup$ Here's an image: imgur.com/poRDnVM $\endgroup$ May 7, 2016 at 20:46
  • 1
    $\begingroup$ You were right: $Version returns "10.3.1 for Linux ARM (32-bit) (January 11, 2016)" for the Pi and is up-to-date. $\endgroup$ May 8, 2016 at 22:02
  • 1
    $\begingroup$ @Elem-Teach-w-Bach-n-Math-Ed Starting version 9.0, you can use the built-in MixedRadixQuantity. The part UnitConvert[...] & in Karsten code could be replaced by MixedRadixQuantity[{0, 0, #}, {"Hours", "Minutes", "Seconds"}] &. $\endgroup$
    – user31159
    May 9, 2016 at 7:07
  • 2
    $\begingroup$ @Elem-Teach-w-Bach-n-Math-Ed Not documented, but this also works: UnitConvert[Quantity[#, "Seconds"], MixedRadix["Hours", "Minutes", "Seconds"]] &. $\endgroup$
    – user31159
    May 9, 2016 at 7:15
3
$\begingroup$

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

$\endgroup$
3
$\begingroup$

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"}
$\endgroup$
1
  • 1
    $\begingroup$ Another way: formatTiming = StringRiffle[Map[IntegerString[#1, 10, If[#1 > 99, Unevaluated[], 2]] &, DMSList[Round[#]/3600]], ":"] & $\endgroup$ Jun 1, 2016 at 3:49
2
$\begingroup$

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"]}
$\endgroup$
1
$\begingroup$

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} *)
$\endgroup$
1
  • $\begingroup$ Thanks. Going to give this a try for comparison sake with the other answers to see which runs most efficiently. $\endgroup$ May 7, 2016 at 21:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.