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$
  • 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 '16 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$ – Elem-Teach-w-Bach-n-Math-Ed May 7 '16 at 19:20
  • $\begingroup$ The deltaTimes = 3700; line looks like a leftover. $\endgroup$ – Karsten 7. May 7 '16 at 21:06
  • $\begingroup$ @Karsten7. Ah, yes it is. I was setting deltaTime manually to test earlier. $\endgroup$ – Elem-Teach-w-Bach-n-Math-Ed May 7 '16 at 21:07
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

| improve this answer | |
$\endgroup$
  • $\begingroup$ I'm getting: Quantity::unkunit: Unable to interpret unit specification MixedUnit[{Hours,Minutes,Seconds}] when I run the quantity one. $\endgroup$ – Elem-Teach-w-Bach-n-Math-Ed May 7 '16 at 20:40
  • $\begingroup$ Here's an image: imgur.com/poRDnVM $\endgroup$ – Elem-Teach-w-Bach-n-Math-Ed May 7 '16 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$ – Elem-Teach-w-Bach-n-Math-Ed May 8 '16 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 '16 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 '16 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

| improve this answer | |
$\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"}
| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Another way: formatTiming = StringRiffle[Map[IntegerString[#1, 10, If[#1 > 99, Unevaluated[], 2]] &, DMSList[Round[#]/3600]], ":"] & $\endgroup$ – J. M.'s discontentment Jun 1 '16 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"]}
| improve this answer | |
$\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} *)
| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks. Going to give this a try for comparison sake with the other answers to see which runs most efficiently. $\endgroup$ – Elem-Teach-w-Bach-n-Math-Ed May 7 '16 at 21:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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