Previous Page
Next Page

Recipe 14.8. Calculating Elapsed Time or Intervals Between Dates

Problem

You wan t to calculate an elapsed time, elapsed date, or relative time.

Solution

For simple elapsed time, you can add and subtract from the epoch milliseconds, or use the value returned by getTimer( ). For more complex conversions, use the methods of the custom DateUtilities class.

Discussion

For simple conversions such as adding or subtracting an hour, day, or week to or from a date, simply add or subtract from the date's epoch milliseconds value. For this purpose, note that a second is 1,000 milliseconds, a minute is 60,000 milliseconds, an hour is 3,600,000 milliseconds, a week is 604,800,000 milliseconds, and so on. Unless you have a knack for remembering these conversion values, storing them as constants is a convenient option. The constants have already been defined in the custom ascb.util.DateUtilities class as follows:

public static const MILLISECOND:Number = 1;
public static const SECOND:Number = MILLISECOND * 1000;
public static const MINUTE:Number = SECOND * 60;
public static const HOUR:Number = MINUTE * 60;
public static const DAY:Number = HOUR * 24;
public static const WEEK:Number = DAY * 7;

You can use the Date.time property to retrieve a date's current value in epoch milliseconds, and then assign a new value to the time property relative to the current value. The following example adds one day to a given Date object:

var example:Date = new Date(2010, 0, 5, 10, 25);

// Displays: Tue Jan 5 10:25:00 GMT-0800 2010
trace(example);

// Add one day to the previous date by setting the new date/time 
// to the original date/time plus DateUtilities.DAY (the number
// of milliseconds in a day). 
example.time += DateUtilities.DAY;

// Displays: Wed Jan 6 10:25:00 GMT-0800 2010
trace(example);

You'll often want to calculate an elapsed time to create a timer for a game or other activity. Calculating the elapsed time is simply a matter of recording the time during initialization and then comparing it to the current time later during execution. You can use a Date object to make those calculations; however, it is much more efficient to use the flash.util.getTimer( ) function. The getTimer( ) function returns the number of milliseconds since the Player started running. By checking its value at successive times, the getTimer( ) function can also be used to determine the elapsed time. The following example uses the getTimer( ) function to repeatedly update the displayed text:

package {

    import flash.display.Sprite;
    import flash.display.TextField;
    import flash.util.Timer;
    import flash.events.TimerEvent;
    import flash.util.getTimer;
    
    public class Example extends Sprite {
        
        private var _text:TextField;
        private var _start:uint;
        
        public function Example(  ) {
            _start = getTimer(  );
            _text = new TextField(  );
            addChild(_text);
            var timer:Timer = new Timer(1000);
            timer.addEventListener(TimerEvent.TIMER, onTimer);
            timer.start(  );
        }
        
        private function onTimer(event:TimerEvent):void {
            _text.text = (getTimer(  ) - _start) + " milliseconds";
        }
        
    }
}

Here, the previous example is tweaked to create a countdown timer:

package {

  import flash.display.Sprite;
  import flash.display.TextField;
  import flash.util.Timer;
  import flash.events.TimerEvent;
  import flash.util.getTimer;
    
  public class Example extends Sprite {
        
    private var _text:TextField;
    private var _timer:Timer;
    private var _start:uint;
    private var _count:uint = 20;
        
    public function Example(  ) {
      _start = getTimer(  );
      _text = new TextField(  );
      addChild(_text);
      _timer = new Timer(500);
      _timer.addEventListener(TimerEvent.TIMER, onTimer);
      _timer.start(  );
    }
        
    private function onTimer(event:TimerEvent):void {
      var elapsed:int = Math.round(_count - (getTimer(  ) - _start) / 1000);
      _text.text = ellapsed + " seconds";
      if(elapsed < 0) {
         _text.text = "---------";
         _timer.stop(  );
      } 
    }
        
  }
}

The earlier example calculated elapsed times using Date objects. When it comes to adding and subtracting years and months from dates, you cannot rely on constants. This is because the number of milliseconds in a month varies with the number of days in the month, and leap years have more milliseconds than other years. However, the Date class handles wraparound calculations transparently when using the getter and setter methods. The most effective way to handle date math is to use the custom DateUtilities.addTo( ) method and have it perform the calculations for you. The method takes up to eight parameters, seven of which are defined in Table 14-2. The first is required, and it is the Date object to which you want to add (or subtract as the case may be). The parameters defined in Table 14-2 are optional numeric parameters, each of which can be positive or negative.

Table 14-2. Parameters for the addTo( ) method
Parameter Description
                              years

A number of years to add to the date.
                              months

A number of months to add to the date.
                              days

A number of days to add to the date.
                              hours

A number of hours to add to the date.
                              minutes

A number of minutes to add to the date.
                              seconds

A number of seconds to add to the date.
                              milliseconds

A number of milliseconds to add to the date.

The following shows how you might use the addTo( ) method to add (and subtract) years, months, and days to a date:

var example:Date = new Date(2010, 0, 5, 10, 25);

trace(DateUtilities.addTo(example, 10));
trace(DateUtilities.addTo(example, -4));
trace(DateUtilities.addTo(example, 0, 1, 1));
trace(DateUtilities.addTo(example, 0, -1, -1));

/* Displays:
Sun Jan 5 10:25:00 GMT-0800 2020
Thu Jan 5 10:25:00 GMT-0800 2006
Sat Feb 6 10:25:00 GMT-0800 2010
Fri Dec 4 10:25:00 GMT-0800 2009
*/

This example demonstrates how to create a new Date object based on an elapsed time from an existing Date object. However, you may want to calculate the elapsed time between two existing Date objects, which is not as trivial as you might think. You might try subtracting the return value of the time property of one Date object from another. However, this doesn't offer a general solution for calculating the elapsed time between two Date objects. Although the operation yields the number of milliseconds between the two dates, the result isn't easy to manipulate when the times are not within the same day. Manually converting the number of milliseconds to a number of years, months, and days is difficult due to the varying number of days per month, leap year, etc. Furthermore, handling negative elapsed times can be cumbersome.

One convenient solution is to create a Date object representing an elapsed time. This lets you use the Date class's built-in methods to calculate the number of years, months, and days between two Date objects. You can use the UTC get methods to retrieve most of the offsets, as illustrated in the following example:

var one:Date = new Date(  );

var two:Date = DateUtilities.addTo(one, 4, 1, 3, 10);

var elapsed:Date = new Date(two.time - one.time);

trace(elapsed);  // Displays: Sun Feb 3 16:00:00 GMT-0800 1974
trace(elapsed.hoursUTC);  // Displays: 10
trace(elapsed.monthUTC); // Displays: 1

There are several caveats, however. Although ActionScript internally stores dates relative to the epoch time, most Date class methods return absolute values, not values relative to the Epoch. For example, the year for a date in 1971 is returned as 1971, not 1. For the elapsed time object to be useful, you need to subtract constants from the year and day values. (The month, hour, minute, second, and millisecond of the epoch time are all 0, so there is no need for custom methods to return relative values for these values.) Subtract 1970 from the year, and then subtract 1 from the day:

trace(elapsed.dateUTC);  // Displays: 4 (incorrect)
trace(elapsed.dateUTC - 1);  // Displays: 3 (correct)
trace(elapsed.dateUTC);  // Displays: 1974 (incorrect);
trace(elapsed.dateUTC - 1970); // Displays: 4 (correct);

To work with elapsed times more efficiently, you might want to use some of the methods of the custom DateUtilities class. The class has a static method called elapsed( ) that returns an object with the amount of elapsed time between two dates, as illustrated in the following example:

var one:Date = new Date(  );

var two:Date = DateUtilities.addTo(one, 4, 1, 3);

var elapsed:Object = DateUtilities.elapsed(two);
for(var item:String in elapsed) {
  trace(item + ": " + elapsed[item]);
}

/* Displays:
milliseconds: 0
seconds: 0
minutes: 0
hours: 0
days: 3
months: 1
years: 4
*/

If you pass only one parameter to the elapsed( ) method, it assumes that you want to find the amount of time that has elapsed between the specified date and the current date.

The DateUtilities class also has several additional static methods that return elapsed times between two dates in specific intervals (years, months, days, etc.). By default, each of those methods calculates the total number of specified intervals between the two dates, as shown here:

var one:Date = new Date(  );

var two:Date = DateUtilities.addTo(one, 4, 1, 3);

trace(DateUtilities.elapsedYears(two, one));        // Displays: 4
trace(DateUtilities.elapsedMonths(two, one));       // Displays: 49
trace(DateUtilities.elapsedDays(two, one));         // Displays: 1495
trace(DateUtilities.elapsedHours(two, one));        // Displays: 35880
trace(DateUtilities.elapsedMinutes(two, one));      // Displays: 2152800
trace(DateUtilities.elapsedSeconds(two, one));      // Displays: 129168000
trace(DateUtilities.elapsedMilliseconds(two, one)); // Displays: 129168000000

Optionally, you can pass a Boolean value as a third parameter to any of the methods. A value of TRue causes the methods to return the relative values instead. The elapsedYears( ) method is the exception, since it returns the same value either way, as shown in the following example:

var one:Date = new Date(  );

var two:Date = DateUtilities.addTo(one, 4, 1, 3);

trace(DateUtilities.elapsedMonths(two, one, true));        // Displays: 1
trace(DateUtilities.elapsedDays(two, one, true));          // Displays: 3
trace(DateUtilities.elapsedHours(two, one, true));         // Displays: 0
trace(DateUtilities.elapsedMinutes(two, one, true));       // Displays: 0
trace(DateUtilities.elapsedSeconds(two, one, true));       // Displays: 0
trace(DateUtilities.elapsedMilliseconds(two, one, true));  // Displays: 0


Previous Page
Next Page
Converted from CHM to HTML with chm2web Pro 2.85 (unicode)