February 25, 2021
Hot Topics:

Back to Basics with Dates, Calendars, and Formatters

  • By Rob Lybarger
  • Send Email »
  • More Articles »

The first pair of lines arranges some method-local Calendar objects such that firstCal.before(secondCal) returns true. (The clone operation when setting firstCal prevents the modifications in firstCal from showing up in the caller's copy of calA.) The trick to the computation is to:

  1. Move the start date back to the beginning of its own year while keeping track of this amount in the "diff" accumulator variable as a negative quantity. Not doing this can introduce an off-by-one error during leap years on the following step. (Left as an exercise to the reader.)
  2. Incrementally adjust the year of the first date until it is the same as the year of the second date. The method call getActualMaximum(Calendar.DAY_OF_YEAR) will return the number of days in a year, given the current year value (in other words, it will return either 365 or 366, as appropriate) and this value is added to the "diff" accumulator variable.
  3. Add in the number of days into the year for the end date.

And there you have it. Pick two dates, feed a Calendar object representation of them into this method, and it will tell you how many days they are apart. This method could be modified to work on months instead of days, and there is even a convenient HOUR_OF_DAY field value to do similar comparisons on an hourly basis (which, if you or your code works in one of the US regions with DST, might be worth using at least two days out of the year).

Output Formatting

The standard API has some nice message formatting classes. Perhaps the more notable one is the java.text.MessageFormat class. If you aren't familiar with it, it seems almost too simple to be useful. Given some text with placeholders, and given a list of values to insert into those placeholders, it returns a String with the appropriate substitutions made. For example:

String msg = MessageFormat.format("Some string: {0} and some int:
   {1}", "hello!", 42);
System.out.println(msg);    // "Some string: hello! and some int: 42"
Warning: This static method variation uses a "varargs" parameter list, so you'll need to use one of the other instance methods in Java 1.4 and earlier.

If it doesn't seem like it is any better than a good "printf" or a manually concatenated String, you aren't thinking big enough. The real appeal of MessageFormat is that you can internationalize: put different format patterns in a collection of resource property files and use java.util.ResourceBundle to automatically grab the appropriate format pattern from the right file (depending on the user's locale). This lets the substitution placeholders be in different places (and even different orders), but the same API call builds the resulting string.

However, on its own, MessageFormat doesn't know how to pluralize or use other special words. Consider: "No files", "One file", and "Multiple files". For this, use the java.text.ChoiceFormat class. The concept here is that you specify interval ranges for which a given piece of text is used. I'll refer you to read the API documentation to understand the constructors and pattern formats (especially because the class operates not just on integer values, but on double-precision values) but by way example:

ChoiceFormat cf = new ChoiceFormat("0#No files| 1#One file|
   2#Multiple files");
System.out.println(cf.format(0));    // "No files"
System.out.println(cf.format(1));    // "One file"
System.out.println(cf.format(5));    // "Multiple files"

To get placeholder substitution, chain the result of the ChoiceFormat into a MessageFormat. In the following example, assuming that you are comparing some target date relative to today (and using the above code to compute the value of "dayDiff") :

ChoiceFormat choiceFormat = new ChoiceFormat(
   "-2#was {0} days ago| -1#was Yesterday| 0#is Today| "+
   "1#is Tomorrow| 2#is {0} days away");

int dayDiffAbs = (int)(Math.abs(dayDiff));

System.out.println("That date is " +
   MessageFormat.format(choiceFormat.format(dayDiff), dayDiffAbs));
If you started on December 1st, and you are checking how many days
until Christmas, the response is: "That date is 24 days away".
If you wait until December 24th, the response instead
is: "That date is Tomorrow".

Note in this case that you need the actual day difference, positive or negative, to feed into the ChoiceFormat object, but you want an absolute value to display the friendly message to the user. Of course, if you are worried about internationalization, things get a little trickier. (The literal "That date is", as well as the formatting pattern for the ChoiceFormat call itself, should come from a resource file, and the entire phrase should probably be assembled by yet another MessageFormat call using yet another pattern with placeholders in the resource file, but you should now have the idea.)


The biggest warning above was to never, ever use the "24*60*60*1000" gimmick to compute a date difference. You now have a better, safer approach that will automatically account for locale differences. You also have seen some of Java's built-in formatting tricks that help you give convenient messages to the user while being forward-thinking enough to support internationalization.

Page 2 of 2

This article was originally published on December 12, 2007

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date