Working in Java time

Learn the basics of calculating elapsed time in Java

This article builds on the information presented in my Calculating Java Dates article (JavaWorld, December 29, 2000). Here I’ve listed some key points from that article that should be familiar to you. If these points are not clear to you, I recommend that you read “Calculating Java Dates” for further explanation.

  1. Java reckons time in milliseconds before or after the start of January 1, 1970.
  2. The Date class’s constructor Date() returns an object that represents the moment the object was created. Date‘s getTime() method returns a long value whose number equals the number of milliseconds before or after January 1, 1970.
  3. The DateFormat class is used to convert Dates to Strings, and vice versa. The static getDateInstance() method returns a DateFormat object in the default format; the getDateInstance(DateFormat.FIELD) returns a DateFormat object with a specified format. The format(Date d) method returns a String that represents the date, such as “January 1, 2002.” Conversely, the parse(String s) method returns a Date object based on the date the String argument represents.
  4. The appearance of Strings returned by the format() method can vary according to the regional settings on the computer where the program is being run.
  5. The GregorianCalendar class has two important constructors: GregorianCalendar(), which returns an object that represents the moment it was created, and the GregorianCalendar(int year, int month, int date) constructor used to create an object that represents an arbitrary date. The GregorianCalendar class’s getTime() method returns a Date object. The add(int field, int amount) method calculates dates by adding or subtracting units of time like days, months, or years.

GregorianCalendar and time

Two GregorianCalendar class constructors can be used to deal with time. The first creates an object that represents a date, hour, and minute:

GregorianCalendar(int year, int month, int date, int hour, int minute)

The second creates an object that represents a date, hour, minute, and second:

GregorianCalendar(int year, int month, int date, int hour, int minute, int second)

First, I should note that each constructor requires date information (year, month, and day) in addition to time information. If you want to talk about 2:30 p.m., you must specify the date.

Also, each GregorianCalendar constructor creates an object that represents a moment in time calculated to the nearest millisecond. Thus, if your constructor takes arguments for only the year, month, and date, then the values for hours, minutes, seconds, and milliseconds are set to zero. Similarly, if your constructor takes arguments for year, month, date, hours, and minutes, then seconds and milliseconds are set to zero.

DateFormat and time

To create a DateFormat object to display time and date, you can use the static method getDateTimeInstance(int dateStyle, int timeStyle). That method specifies the date and time styles you wish to use. If you are happy with the default styles, you can substitute the shorter getDateTimeInstance().

To create a DateFormat object to display just the time, you can use the static method getTimeInstance(int timeStyle).

The program below shows how the getDateTimeInstance() and getTimeInstance() methods work:

import java.util.*;
import java.text.*;
public class Apollo {
   public static void main(String[] args) {
      GregorianCalendar liftOffApollo11 = new GregorianCalendar(1969, Calendar.JULY, 16, 9, 32);
      Date d = liftOffApollo11.getTime();
      DateFormat df1 = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
      DateFormat df2 = DateFormat.getTimeInstance(DateFormat.SHORT);
      String s1 = df1.format(d);
      String s2 = df2.format(d);
      System.out.println(s1);
      System.out.println(s2);
   }
}          

On my computer, the above program displays the following:

Jul 16, 1969 9:32:00 AM

9:32 AM

(Output can vary according to your computer’s regional settings.)

Calculating elapsed time

You may sometimes need to calculate elapsed time; for instance, you may want to know the duration of a manufacturing process, given the starting and ending times. A rental company that rents items by the hour or day may also find it useful to calculate elapsed time. Similarly, in the financial world, it is often necessary to calculate interest payments over elapsed time.

To complicate the issue, humans calculate elapsed time in at least two ways. You can say that a day has gone by when 24 hours have elapsed, or when the calendar changes from one day to the next. I’ll now discuss those two ways of thinking.

Elapsed time, case 1: Full units

In this case, a day has not elapsed until 24 hours have passed, an hour has not elapsed until 60 minutes have passed, a minute has not elapsed until 60 seconds have passed, and so on. Under this method, 23 hours of elapsed time would translate to zero days.

To calculate elapsed time this way, you start by calculating elapsed milliseconds. To do so, first convert each date to the number of milliseconds since the start of January 1, 1970. You then subtract the first millisecond value from the second millisecond value. Here is a sample calculation:

import java.util.*;
public class ElapsedMillis {
   public static void main(String[] args) {
      GregorianCalendar gc1 = new GregorianCalendar(1995, 11, 1, 3, 2, 1);
      GregorianCalendar gc2 = new GregorianCalendar(1995, 11, 1, 3, 2, 2);
      // the above two dates are one second apart
      Date d1 = gc1.getTime();
      Date d2 = gc2.getTime();
      long l1 = d1.getTime();
      long l2 = d2.getTime();
      long difference = l2 - l1;
      System.out.println("Elapsed milliseconds: " + difference);
   }
}     

The above program prints the following:

Elapsed milliseconds: 1000

That program also causes some confusion. The GregorianCalendar class’s getTime() returns a Date object, while the Date class’s getTime() method returns a long number representing the milliseconds before or after the beginning of January 1, 1970. So even though the methods have the same name, their return types are different!

You can convert milliseconds to seconds using simple integer division, as in the following code fragment:

long milliseconds = 1999;
long seconds = 1999 / 1000;

That way of converting milliseconds to seconds eliminates fractions, so 1,999 milliseconds equals 1 second, while 2,000 milliseconds equals 2 seconds.

To calculate larger units — such as days, hours, and minutes — given a number of seconds, you can use the following process:

  1. Calculate the largest unit, reducing the number of seconds accordingly
  2. Calculate the next largest unit, reducing the number of seconds accordingly
  3. Repeat until only seconds remain

For example, if your elapsed time is 10,000 seconds, and you want to know how many hours, minutes, and seconds that value corresponds to, you start with the largest value: hours. Divide 10,000 by 3,600 (seconds in an hour) to calculate the number of hours. Using integer division, the answer is 2 hours (fractions are dropped in integer division). To calculate the remaining seconds, reduce 10,000 by 3,600 times 2 hours: 10,000 – (3,600 x 2) = 2,800 seconds. So you have 2 hours and 2,800 seconds.

To convert 2,800 seconds to minutes, divide 2,800 by 60 (seconds per minute). With integer division, the answer is 46. And 2,800 – (60 x 46) = 40 seconds. The final answer is 2 hours, 46 minutes, and 40 seconds.

The above calculation is shown in the following Java program:

import java.util.*;
public class Elapsed1 {
   public void calcHMS(int timeInSeconds) {
      int hours, minutes, seconds;
      hours = timeInSeconds / 3600;
      timeInSeconds = timeInSeconds - (hours * 3600);
      minutes = timeInSeconds / 60;
      timeInSeconds = timeInSeconds - (minutes * 60);
      seconds = timeInSeconds;
      System.out.println(hours + " hour(s) " + minutes + " minute(s) " + seconds + " second(s)");
   }
   public static void main(String[] args) {
      Elapsed1 elap = new Elapsed1();
      elap.calcHMS(10000);
   }
}  

Output from the above program is:

2 hour(s) 46 minute(s) 40 second(s)

The above program calculates the number of hours correctly, even when the elapsed time is less than an hour. For example, if you use the above program to calculate 1,000 seconds, the output is:

0 hour(s) 16 minute(s) 40 second(s)

To show a real-world example, the following program calculates elapsed time based on the Apollo 11 flight to the moon:

import java.util.*;
public class LunarLanding {
   public long getElapsedSeconds(GregorianCalendar gc1, GregorianCalendar gc2) {
      Date d1 = gc1.getTime();
      Date d2 = gc2.getTime();
      long l1 = d1.getTime();
      long l2 = d2.getTime();
      long difference = Math.abs(l2 - l1);
      return difference / 1000;
   }
   public void calcHM(long timeInSeconds) {
      long hours, minutes, seconds;
      hours = timeInSeconds / 3600;
      timeInSeconds = timeInSeconds - (hours * 3600);
      minutes = timeInSeconds / 60;
      System.out.println(hours + " hour(s) " + minutes + " minute(s)" );
   }
   public static void main(String[] args) {
      GregorianCalendar lunarLanding = new GregorianCalendar(1969, Calendar.JULY, 20, 16, 17);
      GregorianCalendar lunarDeparture = new GregorianCalendar(1969, Calendar.JULY, 21, 13, 54);
      GregorianCalendar startEVA = new GregorianCalendar(1969, Calendar.JULY, 20, 22, 56);
      GregorianCalendar endEVA = new GregorianCalendar(1969, Calendar.JULY, 21, 1, 9);
      LunarLanding apollo = new LunarLanding();
      long eva = apollo.getElapsedSeconds(startEVA, endEVA);
      System.out.print("EVA duration = ");
      apollo.calcHM(eva);
      long lunarStay = apollo.getElapsedSeconds(lunarLanding, lunarDeparture);
      System.out.print("Lunar stay = ");
      apollo.calcHM(lunarStay);
   }
}          

Output from the above program is:

EVA duration = 2 hour(s) 13 minute(s)

Lunar stay = 21 hour(s) 37 minute(s)

So far, I have made calculations based on simple formulas like: “1 minute = 60 seconds,” “1 hour = 60 minutes,” and “1 day = 24 hours.”

What about “1 month = ? days” and “1 year = ? days”?

Months can consist of 28, 29, 30, or 31 days; years can be 365 or 366 days. Therefore, problems arise when you try to calculate full units of time for months and years. For example, if you use the average number of days in a month (approximately 30.4375), and you calculate the number of elapsed months based on the following two intervals:

  • July 1, 2:00 a.m. to July 31, 10:00 p.m.
  • February 1, 2:00 a.m. to February 29, 10:00 p.m.

the first calculation will result in 1 month; the second will result in zero months!

So, think very carefully before you calculate elapsed time in full units for months and years.

Elapsed time, case 2: Time unit change

The definition of time unit change is relatively simple: If you are counting days, you simply count the number of times the date has changed. For example, if something starts on the 15th and ends on the 17th, 2 days have passed. (The date changed first to the 16th, then to the 17th.) Similarly, if a process starts at 3:25 in the afternoon and finishes at 4:10 p.m., 1 hour has passed because the hour has changed once (from 3 to 4).

Libraries often calculate time in this manner. For example, if I borrow a book from my library, I don’t need to have the book in my possession for a minimum of 24 hours for the library to consider it borrowed for one day. Instead, the day that I borrow the book is recorded on my account. As soon as the date switches to the next day, I have borrowed the book for one day, even though the amount of time is often less than 24 hours.

When calculating elapsed time in the sense of time unit change, it doesn’t usually make sense to calculate more than one unit of time. For example, if I borrow a library book at 9:00 p.m., and return it the next day at noon, I can calculate that I’ve borrowed the book for one day. However, there is little sense in asking, “One day and how many hours?” Since the book was loaned for a total of 15 hours, is the answer one day and negative nine hours? Therefore, for this tutorial, I will calculate time unit change for only one unit of time.

Algorithm for calculating time unit of change

This is how you calculate time unit of change between two dates:

  1. Make copies of the two dates. The clone() method can make copies for you.
  2. Using the copies of the dates, set all fields that are smaller than the unit of change to each field’s minimum value. For example, if you are counting elapsed days, then set hours, minutes, seconds, and milliseconds to zero. In this case, use the clear() method to set time fields to their lowest value.
  3. Take the earlier date and add one to the field you are counting, repeating until the two dates are equal. The number of times you add one is the answer. You can use the before() and after() methods, which return a boolean value, to test whether one date is before or after another date.

The following class has methods for counting days and months.

import java.util.*;
public class ElapsedTime {
   public int getDays(GregorianCalendar g1, GregorianCalendar g2) {
      int elapsed = 0;
      GregorianCalendar gc1, gc2;
      if (g2.after(g1)) {
         gc2 = (GregorianCalendar) g2.clone();
         gc1 = (GregorianCalendar) g1.clone();
      }
      else   {
         gc2 = (GregorianCalendar) g1.clone();
         gc1 = (GregorianCalendar) g2.clone();
      }
      gc1.clear(Calendar.MILLISECOND);
      gc1.clear(Calendar.SECOND);
      gc1.clear(Calendar.MINUTE);
      gc1.clear(Calendar.HOUR_OF_DAY);
      gc2.clear(Calendar.MILLISECOND);
      gc2.clear(Calendar.SECOND);
      gc2.clear(Calendar.MINUTE);
      gc2.clear(Calendar.HOUR_OF_DAY);
      while ( gc1.before(gc2) ) {
         gc1.add(Calendar.DATE, 1);
         elapsed++;
      }
      return elapsed;
   }
   public int getMonths(GregorianCalendar g1, GregorianCalendar g2) {
      int elapsed = 0;
      GregorianCalendar gc1, gc2;
      if (g2.after(g1)) {
         gc2 = (GregorianCalendar) g2.clone();
         gc1 = (GregorianCalendar) g1.clone();
      }
      else   {
         gc2 = (GregorianCalendar) g1.clone();
         gc1 = (GregorianCalendar) g2.clone();
      }
      gc1.clear(Calendar.MILLISECOND);
      gc1.clear(Calendar.SECOND);
      gc1.clear(Calendar.MINUTE);
      gc1.clear(Calendar.HOUR_OF_DAY);
      gc1.clear(Calendar.DATE);
      gc2.clear(Calendar.MILLISECOND);
      gc2.clear(Calendar.SECOND);
      gc2.clear(Calendar.MINUTE);
      gc2.clear(Calendar.HOUR_OF_DAY);
      gc2.clear(Calendar.DATE);
      while ( gc1.before(gc2) ) {
         gc1.add(Calendar.MONTH, 1);
         elapsed++;
      }
      return elapsed;
   }
}

You can add additional methods to the above class to handle hours and minutes. Also, the algorithm for calculating the elapsed units of time can certainly be made more efficient, especially for dates that are far apart. However, for the purpose of an introductory tutorial, the algorithm has the advantage of being short and simple.

The following example uses the ElapsedTime class to calculate the number of days between two dates, then the number of months:

import java.util.*;
public class Example {
   public static void main(String[] args) {
      GregorianCalendar gc1 = new GregorianCalendar(2001, Calendar.DECEMBER, 30);
      GregorianCalendar gc2 = new GregorianCalendar(2002, Calendar.FEBRUARY, 1);
      ElapsedTime et = new ElapsedTime();
      int days = et.getDays(gc1, gc2);
      int months = et.getMonths(gc1, gc2);
      System.out.println("Days = " + days);
      System.out.println("Months = " + months);
   }
}

The above program may be useful when calculating, for example, the lateness of an airplane flight. It displays the following output:

Days = 33

Months = 2

(OK, the bit about the flight was an exaggeration; the calculation for days is more suited for things like library loans, but you see how the program works.)

Caveats

A word of caution about working with time: As you have seen in the example of elapsed time, it is very important to think through your definitions very carefully. This article has presented two common ideas about elapsed time, but the number of ways that people can think of elapsed time is limited only by human imagination.

Therefore, when writing a Java program, make sure your definitions agree with those who will be using and relying on the program. Also, testing programs thoroughly is especially important for those who deal with time.

Conclusion

This article, built upon my previous “Calculating Java Dates” article, has shown how to use the GregorianCalendar and DateFormat classes to work with time. You have also seen two ways to think of elapsed time, and two corresponding ways to use Java to measure those times. The information presented here, although basic, provides you with powerful tools for working with time in Java.

Robert Nielsen is a Sun Certified Java 2
Programmer. He holds a master’s degree in education, specializing
in computer-assisted instruction, and has taught in the computer
field for several years. He has also published computer-related
articles in various magazines.

Source: www.infoworld.com