Saturday, April 30, 2011

The Highly Useful Java TimeUnit Enum

Although it is part of the java.util.concurrent package, the TimeUnit enum is useful in many contexts outside of concurrency. In this post, I look at how the TimeUnit enum can be used even in code that does not directly deal with concurrent functionality before examining how this enum is an example of many broader concepts in Java development.

Most of us who have probably seen (or implemented, but we'll blame the other developer for it now) code like that shown in the next code listing. In this code listing, a number of provided milliseconds is converted to a whole number of days by dividing by a previously determined single hard-coded number (86400000, the number of milliseconds in one day).

   /**
    * Convert provided number of milliseconds into number of days.
    *
    * @param numberMilliseconds Number of milliseconds to be converted into days.
    * @return Number of days corresponding to number of provided milliseconds.
    */
   private static long convertMilliSecondsToDaysViaSingleMagicNumber(final long numberMilliseconds)
   {
      // 86400000 = 86400 seconds in a day multipled by 1000 ms per second
      return numberMilliseconds / 86400000;
   }

There are some problems with the approach taken by the above code listing. The most obvious issue may be the use of the magic number 86400000. Although most of us recognize 86400 as the number of seconds in a day, this may not be obvious to everyone and then there's the issue of it being 1000 times greater than that number. The comment in the code listing helps by explaining the underlying meaning of the numbers, but wouldn't it be nice if the code spoke more clearly for itself?

The next code listing shows an arguable slight improvement. Rather than using a single hard-coded number, individual hard-coded numbers are used that are more readable because they are separate. A reader of the code has a better chance of seeing how the number was constructed.

   /**
    * Convert provided number of milliseconds into number of days.
    *
    * @param numberMilliseconds Number of milliseconds to be converted into days.
    * @return Number of days corresponding to number of provided milliseconds.
    */
   private static long convertMilliSecondsToDaysViaMoreExplanatoryMagicNumbers(final long numberMilliseconds)
   {
      // 60 seconds in minute, 60 minutes in hour, 24 hours in day, and
      // one thousand milliseconds in a second
      return numberMilliseconds / (60 * 60 * 24 * 1000);
   }

Even though the individual numbers might make it easier to see what's happening in the conversion, the comment still might be useful in ensuring that the proper function is understood well. Magic numbers are also still involved and most code analysis tools will report issues with their use. The next code example attempts to deal with the issue of magic numbers.

   private final static int NUMBER_MILLISECONDS_IN_SECOND = 1000;
   private final static int NUMBER_SECONDS_IN_MINUTE = 60; 
   private final static int NUMBER_MINUTES_IN_HOUR = 60;
   private final static int NUMBER_SECONDS_IN_HOUR =
      NUMBER_SECONDS_IN_MINUTE * NUMBER_MINUTES_IN_HOUR;
   private final static int NUMBER_HOURS_IN_DAY = 24;
   private final static int NUMBER_MINUTES_IN_DAY =
      NUMBER_HOURS_IN_DAY * NUMBER_MINUTES_IN_HOUR;
   private final static int NUMBER_SECONDS_IN_DAY =
      NUMBER_HOURS_IN_DAY * NUMBER_SECONDS_IN_HOUR;
   private final static int NUMBER_MILLISECONDS_IN_DAY =
      NUMBER_SECONDS_IN_DAY * NUMBER_MILLISECONDS_IN_SECOND;

   /**
    * Convert provided number of milliseconds into number of days.
    *
    * @param numberMilliseconds Number of milliseconds to be converted into days.
    * @return Number of days corresponding to number of provided milliseconds.
    */
   private static long convertMilliSecondsToDaysViaDefinedConstant(final long numberMilliseconds)
   {
      return numberMilliseconds / NUMBER_MILLISECONDS_IN_DAY;
   }

The approach in the code above is commonly seen in Java code. The "magic" numbers are now defined as constants that can be reused in more than just one place. Although this is arguably an improvement, TimeUnit allows us to make a further improvement to this code.

   /**
    * Convert provided number of milliseconds into number of days.
    *
    * @param numberMilliseconds Number of milliseconds to be converted into days.
    * @return Number of days corresponding to number of provided milliseconds.
    */
   private static long convertMillisecondsToDaysViaTimeUnit(final long numberMilliseconds)
   {
      return TimeUnit.MILLISECONDS.toDays(numberMilliseconds);
   }

This code takes advantage of TimeUnit's MILLISECONDS enum constant and toDays(long) method to easily perform this conversion is a standardized and highly readable way. There isn't a magic number in sight!

The above example demonstrates how TimeUnit can be used even when concurrency is not involved. Besides MILLISECONDS, other time unit representations provided by TimeUnit include DAYS, HOURS, MICROSECONDS, MINUTES, NANOSECONDS, and SECONDS. These cover the most commonly used time units one would need.

The methods on the TimeUnit enum allow easy conversion from the unit represented by the enum constant to a different unit of time. There is a general conversion method TimeUnit.convert(long, TimeUnit) that can be used for this purpose. More specific methods are also available for converting to specific types of time units so that the second parameter need not be applied. These methods include the already demonstrated toDays(long) as well as toHours(long), toMicros(long), toMillis(long), toMinutes(long), toNanos(long), and toSeconds(long). Although most of this enum was introduced with J2SE 5, the methods toMinutes(long), toHours(long), and toDays(long) were introduced with Java SE 6.

The enum constants and methods on TimeUnit defined so far are not specifically associated with concurrency and are generally useful. The TimeUnit enum offers three additional methods of interest. TimeUnit.sleep(long) provides a more readable Thread.sleep(long, int). The enum constant of the TimeUnit implies the applicable unit of time, so only a base number needs to be provided. The implication here, of course, is that more obvious numbers can be provided for sleeping rather than needing to worry about expressing a large number in milliseconds or even remembering that the method requires the time be specified in milliseconds.

Two other related useful methods available in TimeUnit are TimeUnit.timedJoin(Thread,long) [convenience method for Thread.join] and TimeUnit.timedWait(Thread,long) [convenience method for Object.wait].

I have used this post to demonstrate how TimeUnit is most obviously useful: it helps developers to write clear code without use of magic numbers for converting between different time measurement units. This is handy in its own right because different APIs often expect different time units. However, TimeUnit has benefits beyond its obvious intended functionality benefits. The TimeUnit enum shows off the power of Java enums and how this power can be leveraged. I look at this next.

Most of us who transitioned from C++ to Java missed having an enum in versions of Java prior to J2SE 5. Fortunately, the wait was worth it as the Java enum is far superior to the C++ enum. There are numerous ways in which the Java enum is better than the C++ enum, but one of the main advantages is the ability to implement methods on the enum. This was shown in the above example where a toDays(long) method allowed for easy conversion of milliseconds via the MILLISECONDS.toDays(long) call. A Java enum is much more than simply an encapsulation of a finite set of integral values. The ability to add behaviors to these enum constants is very powerful.

There are two main approaches for defining methods on an enum. One approach is to define a method at the overall enum level and override it individually at each enum constant's level. The other approach is to implement the method once for the entire enum and all of its enum constants with no need to override the single definition. In other words, one approach is to write an implementation of a method for each enum constant and the other approach writes a method that all the enum constants share. The TimeUnit enum demonstrates both approaches. Its general convert method and all of the convenient toXXXXX methods (where XXXXX are things like Hours or Days) are written specifically for each enum constant and the parent method at the overall enum level throws an AbstractMethodError if not properly overridden by each enum constant (fortunately it always is!). The remaining public methods (timedWait, timedJoin, and sleep) are written with the second approach: a single method implementation exists for each of these that is used by any enum constant defined for TimeUnit.

Besides its usefulness in providing highly readable time unit conversions and besides its usefulness in demonstrating the significant advantages of the Java enum, TimeUnit provides an example of one other "often true" principle in Java: highly and generally useful classes (or enum in this case) can often be found in the SDK where you might least expect it. Although the usefulness of TimeUnit is obvious in concurrent applications, its usefulness goes beyond concurrent functionality. This is not the only case where a more generally useful construct is available in the JDK in a more particular package. I have often seen this in projects I've worked on as well. Often a team will put together a nice class or enum for their own use that is more generally applicable, but which ends up remaining in their rather particular package instead of being in a more generally accessible package.

When we build our own time conversion routines, we typically see hard-coded numbers (or constants defined as) with values such as 1000, 60, and 24. So, it is not surprising that the source code for TimeUnit defines these as constants that it uses in its own conversions. Eventually, the rubber must hit the road and these conversions must take place with these hard numbers. The difference is that use of TimeUnit allows us to have those numbers defined and used outside of our direct code in a well-tested and standardly available enum. It is also interesting to note that hard-coded integers were used in early versions of TimeUnit, but were eventually replaced with internally defined constants:
// Handy constants for conversion methods
static final long C0 = 1L;
static final long C1 = C0 * 1000L;
static final long C2 = C1 * 1000L;
static final long C3 = C2 * 1000L;
static final long C4 = C3 * 60L;
static final long C5 = C4 * 60L;
static final long C6 = C5 * 24L;

This post has been lengthy already, but I'd like to stuff one more thing in. This is a simple Groovy script that uses TimeUnit to demonstrate how many hours, minutes, seconds, milliseconds, microseconds, and nanoseconds are in a single day.

showTimeUnitConversionFactors.groovy
#!/usr/bin/env groovy
// showTimeUnitConversionFactors.groovy
import java.util.concurrent.TimeUnit
println "IN A SINGLE DAY"
println "\tHours:   ${TimeUnit.DAYS.toHours(1)}"
println "\tMinutes: ${TimeUnit.DAYS.toMinutes(1)}"
println "\tSeconds: ${TimeUnit.DAYS.toSeconds(1)}"
println "\tMilliseconds: ${TimeUnit.DAYS.toMillis(1)}"
println "\tMicroseconds: ${TimeUnit.DAYS.toMicros(1)}"
println "\tNanoseconds:  ${TimeUnit.DAYS.toNanos(1)}"

The output of running this Groovy script is shown next:



Conclusion

The TimeUnit enum is obviously useful for converting between units of time in a highly readable and standardized approach. Its value goes beyond that, however, because this SDK enum is an example of the power of the Java enum and demonstrates multiple ways to harness that power.


Additional Resources

There are several other really insightful blog posts regarding TimeUnit. These include The usefulness of java.util.concurrent.TimeUnit, Java TimeUnit: More than just a unit of time, Finding the difference between two dates in Java, and Time Unit Conversion in Java.

8 comments:

Martijn Verburg said...

I'd almost forgotten about this guy and was reminded of him by Ben Evans (a concurrency expert here in London). This is like one of those best hidden secrets in the Java API! Thanks for highlighting it :)

Dustin said...

Martijn,

Thanks for the feedback.

I have had similar experiences with this enum. I had been made aware of its existence back in 1.5, but still wrote some code since then with custom-defined constants until I reviewed a colleague's code that used this enum. This colleague sounds similar to Ben in that he writes concurrent code far more often than I do and so he was "at home" with this enum and used it in all types of code.

Dustin

steve said...

Isn't this just plain wrong? Since when do all days have the same amount of seconds?

amadablam said...

Hi Dustin, thanks for the useful article. I'm from JavaWorld's link.
So I would like to translate your article into Japanese and publish as my personal blog. Can I possibly do that?

Dustin said...

Amadablam,

Thanks for asking about translation of this blog into Japanese and making that translation available on your blog. I think it's a great idea and only ask that you please provide a link back to the original.

Thanks for a great idea and for taking the time to do this.

Dustin

Wayne said...

@steve - you're right, the number of seconds in a "day" isn't *always* 86400 (standard/daylight time, a sidereal day and a stellar day are different, and even leap seconds have to be injected occasionally to account for variations in the solar day) ... but it seems a bit pedantic to dwell on that ;)

Dustin said...

One of the top current DZone links references Emil Mikulic's "Time and what programmers should know about it" post. It's a brief summary of things that are well worth reviewing for anyone writing code against dates and times.

Dustin

Dustin said...

Tim Halloran's recent post TimeUnit rocks! provides a nice introduction to using TimeUnit in its original intended context: concurrent Java.

Toward the end of his post, Halloran makes observations similar to mine in the post above. He writes, "[TimeUnit] is also a great example of what can done with the enum type in the Java programming language" and adds, "TimeUnit is a cool little class that probably sould have been java.lang.TimeUnit."