Time and Timer Classes

There are two time-related classes in the app package: the class Time and the class Timer. MS Visual J Plus Plus provides the Time class to find out the current time in terms of fractions of seconds since a fixed time in the past. The Timer class is used to generate an event periodically. The purpose of these two classes is analogous to a watch that beeps every hour: the Time class is the watch, and the Timer class is the trigger for the beep.

In this section, we'll use the Time class to create a simple clock, and then add a Timer object to make the clock reflect real time.

Time Class

The following TimeDemo app will demonstrate the Time class. This simple app contains a button and an output edit box. When the user clicks the button, the app displays the current date, time, and day of the week.

Forms Designer work

To create the TimeDemo app, you start by creating a Windows app. After setting the form's text property to Time Demo, add an Edit control from the Toolbox. Clear the text property of the Edit control, and set the name property to outputEdit. Set the readOnly property to true to keep the user from changing text in the Edit control, which the app uses for output only. This control will display the time.

Now add a Button control to the form. Set the text property to Display Time. Then double-click the button to establish a button click event handler.

To account for changes the user might make to the size of the form, anchor the Edit control to the top, left, and right edges of the form and anchor the Button control to the bottom, left, and right edges.

code

The code for the TimeDemo app appears as follows:

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * This class demonstrates the Time class.
 */
public class Form1 extends Form
{
 public Form1()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm();
 }
 .
 .
 .
 /**
 * Define the days of the week.
 */
 private final static String[] daysOfWeek = new String[]
 {
 "illegal",
 "Mon",
 "Tues",
 "Wed",
 "Thur",
 "Fri",
 "Sat",
 "Sun"
 };
 /**
 * Display the time in the output control.
 */
 void displayTime(Time time, Control output)
 {
 // fetch the date with the month spelled out
 String dateString = time.formatLongDate();
 // now fetch the time
 String timeString = time.formatLongTime();
 // get the day of the week (use the daysOfWeek
 // array to convert the number into a string)
 String wdString = daysOfWeek[time.getDayOfWeek()];
 // display the results
 output.setText(dateString + " " +
 timeString + "(" +
 wdString + ")");
 }
 private void button1_click(Object source, Event e)
 {
 displayTime(new Time(), outputEdit);
 }
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 Edit outputEdit = new Edit();
 Button button1 = new Button();
 private void initForm()
 {
 // …created by Forms Designer…
 }
 /**
 * The main entry point for the app. */
 public static void main(String args[])
 {
 app.run(new Form1());
 }
}


Since TimeDemo doesn't do anything until the user clicks the Display Time button, the Form1() constructor doesn't do anything other than call initForm(). The button1_click() method, which is attached to the Display Time button, calls displayTime() to display the current date and time in the outputEdit Edit control.

The displayTime() method begins by creating a Time object. This object records the time it was created. Oddly enough, this time is recorded in the number of units of 100 nanoseconds since midnight, A.D. 100. The unit 100 nanoseconds equals one-tenth of a microsecond or 10-7 seconds.

The Time class provides a number of methods for converting the Time object's value into meaningful text. The TimeDemo program uses one of these methods, formatLongDate(), to create a date string. The LongDate part of the method name refers to the fact that the month in the returned date is spelled out as characters. (A short date format would use only numbers.) Next the formatLongTime() method returns a string representation of the time of day in the Time object's value.

There is no method to generate a text representation of the day of the week; the Time.getDayOfWeek() method returns a value from 1 to 7, with 1 representing Monday. TimeDemo uses the value returned by getDayOfWeek() as an index into the string array daysOfWeek. The daysOfWeek array returns the string that corresponds to the index value and stores it in the wdString variable.

NOTE
The first value in the daysOfWeek array is the string "illegal". This is just a placeholder because the smallest value that getDayOfWeek() returns is 1 rather than 0, and arrays in Visual J++ are zero-based. Thus, daysOfWeek[0] isn't used.

The three strings dateString, timeString, and wdString are then concatenated and displayed in the outputEdit object. The result is shown in Figure 10-1.

Screenshot

Screenshot-1. The output from TimeDemo shows the format generated by the formatLongDate() and formatLongTime() methods, with the day of the week added to the end.

Timer Class

An inconvenient feature of the TimeDemo app is that the user must choose the Display Time button to see the current time. It would be much more convenient if the time display updated periodically on its own.

To do this, the program could sit in a for loop repeatedly creating the current time string and displaying it in the outputEdit Edit control. However, not only would this be a bad idea—it would consume a considerable amount of CPU processing power for such a simple task—it also wouldn't work. The problem is that the outputEdit Edit control would never get a chance to display the output results. A much better approach would be to update the display once per second. To do this, we need a timer, and this is exactly what the Timer class is for.

The following TimerDemo app demonstrates the use of the Timer class. This demo displays the time and date in the same format as TimeDemo, and it automatically updates the time display once per second. In addition, TimerDemo adds a stopwatch feature.

Forms Designer work

The TimerDemo app requires two Edit controls: one to display the date and time, and the other to display the stopwatch value. The app also requires two Button controls: one to start the stopwatch and one to stop it. Take a second to look ahead to Figure 10-2 to see how these objects are laid out on the form.

Use the Properties window to change the name property of the upper Edit control to dateOutputEdit. Set this Edit control's readOnly property to true, and set its text property to empty. Since this control is uppermost in the form, set its anchor property to top, left, and right.

Now set the name property of the lower Edit control to secsOutputEdit. As we did with dateOutputEdit, set the readOnly property to true and the text property to empty. Anchor this control to the left, right, and bottom sides of the form, and set its textAlign property to Right. Create a Label control just to the right of this Edit control, set the label's text property to secs, and anchor the label to the bottom and right sides of the form to match the secsOutputEdit object.

Now name the leftmost button startClock and label it Start Clock. You will need to anchor it to the bottom and left of the form. Name the rightmost button stopClock, and label it Stop Clock. Anchor this button to the bottom and right of the form. Finally, use the active properties window to create a click event handler for each button.

code

The code for the TimerDemo app is shown below.

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
import com.ms.wfc.html.*;
/**
 * This class demonstrates the Timer class by
 * creating a clock with a built-in stopwatch.
 */
public class Form1 extends Form
{
 // the following Timer object clicks every second
 // to update the display
 Timer timer;
 public Form1()
 {
 // Required for Visual J Plus Plus Form Designer support
 initForm();
 // create a timer to be used to update the
 // clock periodically
 timer = new Timer(this.components);
 timer.addOnTimer(new EventHandler(this.handleClockTick));
 // start the clock to click every 100th of a second
 timer.setInterval(10);
 timer.setEnabled(true);
 }
 /**
 * Form1 overrides dispose so it can clean up the
 * component list.
 */
 public void dispose()
 {
 super.dispose();
 // turn the timer off to make sure that it can be
 // disposed of with the rest of the form's container
 timer.setEnabled(false);
 // now dispose of the contents of the container
 components.dispose();
 }
 /**
 * Define the days of the week.
 **/
 // array unchanged from TimeDemo app…
 /**
 * Display the current time in the outputEdit window.
 */
 private void displayTime(Time time, Control output)
 {
 // unchanged from TimeDemo app…
 }
 /**
 * Display the elapsed time from startTime in
 * the output control.
 */
 public void displayElapsedTime(Time startTime,
 Time currentTime,
 Control output)
 {
 // now get the difference between the
 // current time and the start time
 long deltaTime = currentTime.toLong() -
 startTime.toLong();
 int deltaMilliSeconds = (int)(deltaTime / Time.UNITS_PER_MILLISECOND);
 float deltaSeconds = ((float)deltaMilliSeconds) / 1000.0F;
 // display the delta time
 output.setText(Float.toString(deltaSeconds));
 }
 // save the start time for the stopwatch
 Time startTime = null;
 // only update the time display when the second
 // changes
 long lastSecond = 0;
 /**
 * Handle the 1-millisecond clock tick.
 */
 private void handleClockTick(Object source, Event e)
 {
 // capture the current time
 Time currentTime = new Time();
 // if the second has changed…
 long second = currentTime.toLong() / Time.UNITS_PER_SECOND;
 if (second != lastSecond)
 {
 lastSecond = second;
 // update the date and time display
 displayTime(currentTime, dateOutputEdit);
 }
 // if the stopwatch is enabled…
 if (startTime != null)
 {
 // then display the elapsed time
 displayElapsedTime(startTime, currentTime,
 secsOutputEdit);
 }
 }
 private void startClock_click(Object source, Event e)
 {
 // start the stopwatch by noting the current time
 // as the start time
 startTime = new Time();
 }
 private void stopClock_click(Object source, Event e)
 {
 // stop the stopwatch by erasing the start time
 startTime = null;
 }
 /**
 * NOTE: The following code is required by the Visual J Plus Plus form
 * designer. It can be modified using the form editor. Do not
 * modify it using the code editor.
 */
 Container components = new Container();
 Edit dateOutputEdit = new Edit();
 Edit secsOutputEdit = new Edit();
 Label label1 = new Label();
 Button startClock = new Button();
 Button stopClock = new Button();
 private void initForm()
 {
 // …this code created by the Forms Designer…
 }
 /**
 * The main entry point for the app. */
 public static void main(String args[])
 {
 app.run(new Form1());
 }
}


Once the TimerDemo() constructor has called initForm() to set up the WFC controls, it creates a Timer object. This object is attached to the same components container that holds the controls we built with the Forms Designer, such as the output Edit objects and the buttons.

TIP
You are not required to attach your Timer objects to a Container object. It's a good idea, however, because if you do the resources associated with the Timer object are automatically disposed of when the Container object's dispose() method is invoked.

TimerDemo then creates an EventHandler object to handle the timer event. In this case, EventHandler points at the method handleClockTick(). Once the timer event handler has been established by a call to addOnTimer(), the constructor sets the tick interval to 10. This causes the Timer object to create a timer event every 10 milliseconds. Finally, calling setEnabled(true) enables the timer.

NOTE
Timers are created in a stopped state. The program should not start the timer until it has established the timer interval and event handler.

The dispose() method, which I normally haven't been showing in the program listings in this tutorial (although it is in the code on the companion CD), is automatically created by the Forms Designer. This code shows dispose() edited so that it turns off the timer by calling the setEnabled(false) method. Once the timer is disabled, the dispose() method calls components.dispose() to release the resources of the objects—including the timer—that are attached to the components container.

NOTE
The resources associated with a timer can't be disposed of as long as the timer is active.

The date and time are updated by the timer event handler, so the methods startClock_click() and stopClock_click() are very short. The startClock_click() method simply sets the stopwatch's startTime variable to the current time, and stopClock_click() sets the variable to null. This signals to the timer event handler that the stopwatch is active or inactive, respectively.

The handleClockTick() method that handles the timer event is the heart of this app. This method begins by noting the current time in tenths of a millisecond. It then converts this time into seconds. If the resulting second variable value is different from the lastSecond variable value, the app updates the date and time display in the dateOutputEdit Edit control by calling displayTime(). The trick of comparing second to lastSecond keeps the handleClockTick() method from needlessly updating the date and time every 10 milliseconds when the control displays the time only to the nearest second.

The handleClockTick() method continues by checking the startTime object. If it contains a null value, the stopwatch is stopped. Otherwise, it contains the start time of the stopwatch. In this case, handleClockTick() invokes displayElapsedTime() to display the stopwatch value in the secsOutputEdit Edit control.

The displayTime() method and the daysOfWeek array are identical to those of the same name in the TimeDemo app.

The displayElapsedTime() method sends to the output Edit control the difference in seconds between currentTime and startTime. It begins by calculating the difference between the two timer values and converting the resulting long delta value into an integer variable deltaMilliSeconds. The value of deltaMilliSeconds is divided by 1000, converting milliseconds to seconds, and assigned to deltaSeconds. The deltaSeconds variable is of float type so that it can retain the fractional part of the computation results—that is, the 10ths and 100ths of a second. Finally, the deltaSeconds value is converted into a string and displayed in the output Edit control.

The result is shown in Figure 10-2.

Screenshot

Screenshot-2. The TimerDemo app automatically updates the date and time display every second, plus it creates a user-controlled stopwatch accurate to one 100th of a second. Comments