4
$\begingroup$

What's the best approach to overriding DisplayForm for specific types of Quantity eg time especially w/ mixed units?

Current is too verbose, eg:

enter image description here

Would rather have control as follows:

Quantity[MixedMagnitude@{0, 2, 3}, 
 MixedUnit@{"Hours", "Minutes", "Seconds"}]

0 h 2 min 3 s

  1. Would prefer either:

:02:03

Or

02:03s

even if not standard, ie hide hours if zero.

  1. Quantity[2,"Minutes"] show as m rather than min - which is used to disambiguate from unit Meters, but it's often obvious from context that it's time units.

  2. Finally, to display even 0 time, which currently doesn't have a DisplayForm:

    Quantity[MixedMagnitude@{0, 0, 0}, MixedUnit@{"Hours", "Minutes", "Seconds"}]

What's the best approach, eg UpSet?

$\endgroup$

2 Answers 2

6
$\begingroup$

Code

The function QuantityUnits`Private`QuantityUnitBox handles the display form of quantities. We modify only the definition related to mixed magnitudes and mixed units:

(* From a fresh kernel *)

System`Quantity;

Unprotect[QuantityUnits`Private`QuantityUnitBox];

QuantityUnits`Private`QuantityUnitBox[
    QuantityUnits`Private`QuantityUnitBox`q : 
       Quantity[
          QuantityUnits`Private`QuantityUnitBox`m_MixedMagnitude, 
          QuantityUnits`Private`QuantityUnitBox`u_MixedUnit, 
          QuantityUnits`Private`QuantityUnitBox`opts___?OptionQ
       ],
    QuantityUnits`Private`QuantityUnitBox`form_
] /; QuantityQ[QuantityUnits`Private`QuantityUnitBox`q] := With[

   {q = QuantityUnits`Private`QuantityUnitBox`q, 
    form = QuantityUnits`Private`QuantityUnitBox`form, 
    m = First@QuantityUnits`Private`QuantityUnitBox`m, 
    u = First@QuantityUnits`Private`QuantityUnitBox`u},

    If[u === {"Hours", "Minutes", "Seconds"} || u === {"Minutes", "Seconds"},
       myQuantityDisplay[m, u],
       QuantityUnits`Private`ToMixedUnitQuantityTemplateBox[q, form]
    ]

];

Protect[QuantityUnits`Private`QuantityUnitBox];

Compared to the original version, we just added an If statement with a test on the form of the mixed units. The function myQuantityDisplay will fire for OP's requirements only, otherwise the standard display will be kept.

(To change the display of any time quantities, probably UnitDimensions and TimeUnit could be used in some way for the test.)

We are left with the definition of myQuantityDisplay

myQuantityDisplay[{m_, s_}, {"Minutes", "Seconds"}] := 
    IntegerString[m, 10, 2] <> ":" <> IntegerString[s, 10, 2] <> "s";

myQuantityDisplay[{0, m_, s_}, {"Hours", "Minutes", "Seconds"}] := 
    IntegerString[m, 10, 2] <> ":" <> IntegerString[s, 10, 2] <> "s";

myQuantityDisplay[{h_, m_, s_}, {"Hours", "Minutes", "Seconds"}] := 
    IntegerString[h, 10, 2] <> ":" <> IntegerString[m, 10, 2] <> ":" <> 
    IntegerString[s, 10, 2] <> "s";

The above modification only affects the display form, so that we can still perform any operation or standard evaluation on mixed quantites. Also, as mentioned, only the mixed units specifications given in the If statement are concerned.

(See Comments below for point 3 of OP's question.)

A few examples

a) Modified display

Quantity[MixedMagnitude[{1, 2, 3}], MixedUnit[{"Hours", "Minutes", "Seconds"}]]
% // FullForm

enter image description here

b) Standard display

Quantity[MixedMagnitude[{14, 35}], MixedUnit[{"Hours", "Minutes"}]]

enter image description here

c) Arithmetic operations

q = Quantity[MixedMagnitude[{14, 35}], MixedUnit[{"Minutes", "Seconds"}]]
3 q

enter image description here

Comments

The definitions of the function QuantityUnits`Private`QuantityUnitBox can be accessed with PrintDefinitions. Otherwise, from the PrintDefinitions of Quantity, one has to search for the function QuantityUnits`QuantityBox in

MakeBoxes[x : Quantity[_, u_?QuantityUnits`Private`LooksLikeAUnitQ, ___], fmt_] ...

and from its definitions look at the second cell.

Among all the definitions of QuantityUnits`Private`QuantityUnitBox, we can see one responsible for the format of mixed quantities (modified above), and one that concerns the display (or lack of it) for repeated zeros in the mixed magnitude. So to achieve point 3 of OP's question, simply do

QuantityUnits`Private`$ShowZero = True;

Note that the function myQuantityDisplay should have definitions that are within the scope of MakeBoxes (as we can see from above). I think we can get more fancy outputs than the one I gave by tweaking instead the functions used by QuantityUnits`Private`ToMixedUnitQuantityTemplateBox, though this requires more spelunking.

$\endgroup$
1
  • $\begingroup$ Xavier, thank you for this info, this more involved than I thought it would be. Does every expression with a DisplayForm have its own _UnitBox? $\endgroup$ Mar 27, 2016 at 21:01
4
$\begingroup$

This might give you a start. You will need to Unprotect MixedMagnitude and MixedUnit, so the usual caveats apply.

Unprotect[MixedMagnitude, MixedUnit];

Quantity[HoldPattern@MixedMagnitude[x_List], 
  MixedUnit[{"Hours", "Minutes", "Seconds"}]] ^:= 
 StringRiffle[
  IntegerString[#, 10, 2] & /@ If[x[[1]] == 0, Rest[x], x], ":"]

Quantity[MixedMagnitude@{0, 2, 3}, 
 MixedUnit@{"Hours", "Minutes", "Seconds"}]
(* "02:03" *)

Quantity[MixedMagnitude@{10, 20, 30}, 
 MixedUnit@{"Hours", "Minutes", "Seconds"}]
(* "10:20:30" *)
$\endgroup$

Your Answer

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

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