Chapter 3. Dates, Time, and Timers

3.0. Introduction

JavaScript’s date and time functionality is quite extensive, and more than sufficient for most applications.

The Date object contains a number representing the date and time, rather than a string representation. The numeric value for the Date object is the number of seconds since January 01, 1970 UTC. Leap seconds are ignored.

Strings used to create dates are parsed and converted into this numeric value. Older browsers required that this string be UTC (Coordinated Time Universal or Greenwich Mean Time) format. Beginning with ECMAScript 5, ISO 8601 parsing is supported, which I’ll cover later in the chapter.

The Date Object

Dates are managed through the Date object. You can create a date using a variety of techniques (see Recipe 3.1 for a discussion of different approaches), and modify the date using an extensive number of methods.

You can also access every aspect of a date: year, month, day of week, time, and so on, using specialized get and set methods, described in Tables 3-1 and 3-2.

Table 3-1. Date object get methods

Method

Purpose

getDate

Returns day of the month (0–31)

getDay

Returns day of the week (0–6)

getFullYear

Returns 4-digit full year

getHours

Returns local hour (0–23)

getMilliseconds

Returns local milliseconds (0–999)

getMinutes

Returns local minute (0–59)

getMonth

Returns local month (0–11)

getSeconds

Returns local second (0–59)

getTime

Returns number of seconds since January 1, 1970 00:00:00 UTC

getTimezoneOffset

Returns time zone from UTC

getUTCDate

Returns day of month in UTC time (0–31) method (Date)

getUTCDay

Returns day of the week in UTC time (0–6)

getUTCFullYear

Returns 4-digit UTC year

getUTCHours

Returns UTC hours (0–23)

getUTCMilliseconds

Returns UTC milliseconds (0–999)

getUTCMinutes

Returns UTC minutes (0–59)

getUTCMonth

Returns UTC month (0–11)

getUTCSeconds

Returns UTC seconds (0–59)

Table 3-2. Date object set methods

Method

Purpose

setDate

Sets the day of month (1–31)

setFullYear

Sets 4-digit full year

setHours

Sets the hour (0–23)

setMilliseconds

Sets the date’s milliseconds (0–999)

setMinutes

Sets the date’s minutes (0–59)

setMonth

Sets the month (0–11)

setSeconds

Sets the seconds (0–59)

setTime

Sets the date’s time as milliseconds since January 1, 1970 00:00:00 UTC

setUTCDate

Sets the date’s day of month in UTC

setUTCFullYear

Sets the full year in UTC

setUTCHours

Sets the date’s hours in UTC

setUTCMilliseconds

Sets the date’s milliseconds in UTC

setUTCMinutes

Sets the date’s minutes in UTC

setUTCMonth

Sets the month in UTC

setUTCSeconds

Sets the seconds in UTC

You can also calculate a future date by adding a number of days or weeks to any given date.

JavaScript Timers

JavaScript also provides another way to work with time, through the use of recurring or one-time-only timers. I’ve always thought these should be a component of the Date object, but they’re actually Window object methods: setInterval and setTimeout.

The difference between the two is that setInterval creates a recurring timer that re-fires until canceled, while setTimeout creates a one-time-only timer. Both take a timer value, in milliseconds, as well as an expression to evaluate when the timer fires.

3.1. Printing Out Today’s Date

Problem

You want to print out the current date and time to a web page.

Solution

Create a new Date object, without any parameters, and output its value to the web page:

var dtElem = document.getElementById("date");
var dt = new Date();
dtElem.innerHTML = "<p>" + dt + "</p>";

Discussion

When you construct a new Date object, you can pass various parameters to the constructor to create a specific date:

var dt = new Date(milliseconds); // milliseconds since 1 January 1970 00:00:00 UTC
var dt2 = new Date(dateString); // string representing a valid date
var dt3 = new Date(year,month,date[,hour,minute,second,millisecond]);

If you don’t specify the time parameters of a date, as shown in the last example, they’re set to zero by default. At a minimum, you must provide the month, day, and year. If you don’t provide any form of a date string to the Date constructor, the Date object is set to the local date and time of the computer used to access the web page.

You can access components of the date using a variety of Date methods. You can directly print the entire date, as shown in the solution, and the resulting string will look like the following:

Thu Oct 01 2009 20:34:26 GMT-0500 (CST)

If you prefer a different format, you can access the individual components of the Date, using methods such as getMonth, getFullYear, getTime, getDate, and then build the date string:

var dt = new Date();
var month = dt.getMonth();
month++;
var day = dt.getDate();
var yr = dt.getFullYear();
dtElem.innerHTML = "<p>" + month + "/" + day + "/" + yr;

The above outputs the following string to the page:

10/1/2009

The month is a zero-based integer, which is why I had to increment the month value in the example to get the actual numeric month value. To get the month name, you’ll most likely want to use an array:

var months = ['January','February','March','April','May','June','July','August',
'September','October','November','December'];
var month = dt.getMonth();
var monthString = months[month];

3.2. Printing Out the UTC Date and Time

Problem

You want to print out the current UTC (universal time) date and time, rather than the local time.

Solution

Use the UTC JavaScript methods in order to access the current date and time as universal time:

var dateElement = document.getElementById("date");
var today = new Date();
var utcDate = today.toUTCString();
dateElement.innerHTML = "<p>local datetime: " + today + " UTC datetime: " + 
utcDate + "</p>";

Discussion

The Date toUTCString method returns the date/time string formatted in universal convention. This not only returns the UTC equivalent of the local datetime, it also returns it the UTC format, which varies just slightly from the datetime for the local time. The printout from the solution would be:

local datetime: Thu Oct 08 2009 13:58:35 GMT-0500 (CDT)
UTC datetime: Thu, 08 Oct 2009 18:58:35 GMT

There are a couple of differences between the two date printouts. First of all, the time zone designation differs, which we would expect. I’m currently in Central Daylight Time (CDT), which is five hours behind universal time (UTC/GMT). In addition, the day of week in the UTC string is followed by a comma, which doesn’t occur with the local time printout.

Rather than the entire date string, you can access the UTC equivalent of the month, day, year, and time using the relevant Date methods. Instead of getMonth, use getUTCMonth, and so on. Using these getUTC methods with the local date, you could build a printout string identical to that given with the local time, or to match any other formatting, such as the ISO 8601 standard formatting. There are equivalent methods to set each of these values.

See Also

The get methods are detailed in Table 3-1 and the set methods are detailed in Table 3-2.

3.3. Printing Out an ISO 8601 Formatted Date

Problem

You need a date string with the date formatted according to the ISO 8601 standard format.

Solution

Construct the Date, access the individual elements, and create the ISO 8601 formatted string:

 var dt = new Date();

// get month and increment
var mnth = dt.getUTCMonth();
mnth++;

var day = dt.getUTCDate();
if (day < 10) day="0" + day;
var yr = dt.getUTCFullYear();

var hrs = dt.getUTCHours();
if (hrs < 10) hrs = "0" + hrs;

var min = dt.getUTCMinutes();
if (min < 10) min = "0" + min;

var secs = dt.getUTCSeconds();
if (secs < 10) secs = "0" + secs;

var newdate = yr + "-" + mnth + "-" + day + "T" + hrs + ":" + min + ":" + secs + "Z";

Discussion

The ISO 8601 is an international standard that defines a representation for both dates and times. It’s not unusual for applications that provide APIs to require ISO 8601 formatting. It’s also not unusual for most dates to and from APIs to be in UTC, rather than local time.

The solution shows one variation of ISO 8601 formatting. Others are the following:

  • 2009

  • 2009-10

  • 2009-10-15

  • 2009-10-15T19:20

  • 2009-10-15T19:20:20

  • 2009-10-15T19:20:20.50

The values are year, month, date, then “T” to represent time, and hours, minutes, seconds, and fractions of sections. The time zone also needs to be indicated. If the date is in UTC, the time zone is represented by the letter “Z”, as shown in the solution:

2009-10-15T14:42:51Z

Otherwise, the time zone is represented as +hh:mm to represent a time zone ahead of UTC, and -hh:mm to represent a time zone behind UTC.

The solution assumes you want to access the current UTC time. If you need to convert a given date into an ISO 8601 formatted UTC date, create the Date using the date string, and then use the UTC get methods to get the date components, as shown in the solution:

var dt = "October 15, 2009 15:10:10";

Eventually, you won’t need this special functionality to print out an ISO 8601 formatted date, because one of the new Date extensions released with ECMAScript 5 is a new method, toISOString:

var dt = "October 15, 2009 15:10:10";
alert(dt.toISOString());

Currently only a few browsers support this new functionality (Firefox 3.5 and the WebKit nightly). Until there is broader support, you’ll still need the functionality outlined in the solution to output the correctly formatted ISO date. However, you can extend the function to check for the existence of toISOString first, and use it if supported.

See Also

A padding function, such as the one described in Recipe 1.10, can be used to pad the numbers. The W3C “Note on Date and Time Formats” describing the ISO 8601 format can be found at http://www.w3.org/TR/NOTE-datetime.

3.4. Converting an ISO 8601 Formatted Date to a Format Acceptable to the Date Object

Problem

You need to convert an ISO 8601 formatted date string into values that can be used to create a new Date object.

Solution

Parse the ISO 8601 string into the individual date values, and use it to create a new JavaScript Date object:

  var dtstr= "2009-10-15T14:42:51Z";

  dtstr = dtstr.replace(/\D/g," ");
  var dtcomps = dtstr.split(" ");

  // modify month between 1 based ISO 8601 and zero based Date
  dtcomps[1]--;

  var convdt = new
Date(Date.UTC(dtcomps[0],dtcomps[1],dtcomps[2],dtcomps[3],dtcomps[4],dtcomps[5]));

Discussion

If you attempt to create a JavaScript Date with an ISO 8601 formatted string, you’ll get an invalid date error. Instead, you’ll have to convert the string into values that can be used with the JavaScript Date.

The simplest way to parse an ISO 8601 formatted string is to use the String.split method. To facilitate the use of split, all nonnumeric characters are converted to one specific character. In the solution, the nonnumeric characters are converted to a space:

dtstr = dtstr.replace(/\D/g, " ");

The ISO formatted string would be converted to:

2009 10 15 14 42 51

ISO months are one-based, in values of 1 through 12. To use the month value in JavaScript Dates, the month needs to be adjusted by subtracting 1:

dtcomps[1]--;

Finally, the new Date is created. To maintain the UTC setting, the Date object’s UTC method is used to create the date in universal time, which is then passed to the Date constructor:

  var convdt = new
Date(Date.UTC(dtcomps[0],dtcomps[1],dtcomps[2],dtcomps[3],dtcomps[4],dtcomps[5]));

The task gets more challenging when you have to account for the different ISO 8601 formats. Example 3-1 shows a JavaScript application that contains a more complex JavaScript function that converts from ISO 8601 to allowable Date values. The first test in the function ensures that the ISO 8601 format can be converted to a JavaScript Date. This means that, at a minimum, the formatted string must have a month, day, and year.

Example 3-1. Converting ISO 8601 formatted dates to JavaScript Dates
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Converting ISO 8601 date</title>
<style type="text/css">
#dateSubmit
{
   background-color: #ff0;
   width: 200px;
   text-align: center;
   border: 1px solid #ccc;
}
</style>
<script type="text/javascript">
//<![CDATA[

window.onload=function() {
  document.getElementById("dateSubmit").onclick=convertDate;
}

function convertDate() {
  var dtstr = document.getElementById("datestring").value;
  var convdate = convertISO8601toDate(dtstr);
  document.getElementById("result").innerHTML=convdate;
}

function convertISO8601toDate(dtstr) {

  // replace anything but numbers by spaces
  dtstr = dtstr.replace(/\D/g," ");

  // trim any hanging white space
  dtstr = dtstr.replace(/\s+$/,"");

  // split on space
  var dtcomps = dtstr.split(" ");

  // not all ISO 8601 dates can convert, as is
  // unless month and date specified, invalid
  if (dtcomps.length < 3) return "invalid date";
  // if time not provided, set to zero
  if (dtcomps.length < 4) {
    dtcomps[3] = 0;
    dtcomps[4] = 0;
    dtcomps[5] = 0;
  }

  // modify month between 1 based ISO 8601 and zero based Date
  dtcomps[1]--;

  var convdt = new
Date(Date.UTC(dtcomps[0],dtcomps[1],dtcomps[2],dtcomps[3],dtcomps[4],dtcomps[5]));

  return convdt.toUTCString();
}

//--><!]]>
</script>
</head>
<body>
<form>
<p>Datestring in ISO 8601 format: <input type="text" id="datestring" /></p>
</form>
<div id="dateSubmit"><p>Convert Date</p></div>
<div id="result"></div>
</body>
</html>

Another test incorporated into Example 3-1 is whether a time is given. If there aren’t enough array elements to cover a time, then the hours, minutes, and seconds are set to zero when the UTC date is created.

There are other issues related to dates not covered in the application. For instance, if the ISO 8601 formatted string isn’t in UTC time, converting it to UTC can require additional code, both to parse the time zone and to adjust the date to incorporate the time zone. I’ll leave that as an individual exercise, though.

Eventually, you won’t need this special processing, because ECMAScript 5 includes new support for ISO 8601 dates. This means you’ll be able to create a new Date object using an ISO 8601 formatted string. However, only a few browsers (Firefox 3.5 and WebKit-based browsers) currently support the new ISO 8601 formatting, so you’ll still need to provide the conversion functionality included in this section for the near future.

3.5. Creating a Specific Date

Problem

You want to create a Date object given a month, day, and year.

Solution

Construct a Date object, passing in the month, day, and year as parameters:

var month = 10; // Month 10, in zero based system, is November
var day = 18;
var year = 1954;
var dt = new Date(year,month,day); // time is set to zero by default

Discussion

The month, day, and year are integer values passed into the Date constructor. Because the time values were not given, they’re set to zero by default.

In the solution, a November date is wanted, which is typically written out as 11. However, months with the Date object are zero-based, which means that November would be designated, numerically, as 10.

3.6. Scheduling a Future Date

Problem

You want to generate a future date.

Solution

Use a combination of the Date object’s get and set methods in order to create a future date. In the following, a new date is created that’s 10 days in the future:

var futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 10);

Discussion

You can use a combination of get and set methods to find either a future or past date. Which methods you use depends on how you want to derive the new date. If you want to add or subtract days, you’ll use getDate and setDate; for years, use getFullYear and setFullYear; for hours, use getHours and setHours; and so on.

Here’s a list of the paired methods to use, and the incremental amounts:

  • getDate and setDate to adjust the date by days

  • getFullYear and setFullYear to adjust the date by years

  • getHours and setHours to adjust the date and time by hours

  • getSeconds and setSeconds to adjust the date and time by seconds

  • getMilliseconds and setMilliseconds to adjust the date and time by milliseconds

  • getMinutes and setMinutes to adjust the date and time by minutes

You can also use the UTC versions of the same methods.

To derive a date in the past, subtract the unit by the amount of the change:

var pastDate = new Date();
pastDate.setFullYears(pastDate.getFullYears() - 18);

3.7. Tracking Elapsed Time

Problem

You want to track the elapsed time between events.

Solution

Create a Date object when the first event occurs, a new Date object when the second event occurs, and subtract the first from the second. The difference is in milliseconds; to convert to seconds, divide by 1,000:

var firstDate;
window.onload=startTimer;

function startTimer(){
  firstDate = new Date();
  document.getElementById("date").onclick=doEvent;
}

function doEvent() {
  var secondDate = new Date();
  alert((secondDate - firstDate) / 1000);
}

Discussion

Some arithmetic operators can be used with Dates, but with interesting results. In the example, one Date can be subtracted from another, and the difference between the two is returned as milliseconds. However, if you “add” two dates together, the result is a string with the second Date concatenated to the first:

Thu Oct 08 2009 20:20:34 GMT-0500 (CST)Thu Oct 08 2009 20:20:31 GMT-0500 (CST)

If you divide the Date objects, again the Dates are converted to their millisecond value, and the result of dividing one by the other is returned. Multiplying two dates will return a very large millisecond result.

Note

Only the Date subtraction operator really makes sense, but it’s interesting to see what happens with arithmetic operators and the Date object.

3.8. Creating a Timeout

Problem

You want to trigger a timeout based on an event.

Solution

Use the window.setTimeout method to create a one-time-only timer:

window.onload=function() {
  setTimeout("alert('timeout!')",3000);
}

Discussion

The setTimeout method takes two parameters: the expression to process, and the time (in milliseconds) when the expression is evaluated. In the solution, the expression is code, contained in a text string, that’s processed three seconds after the setTimeout function is run.

The first parameter can also be the name of a function:

setTimeout(functionName, 2000);

In addition, you can create an expression that’s a combination of function and parameters by providing the optional parameters after the time:

setTimeout(functionName, 2000, param1, param2, ..., paramn);

You can cancel a timeout, using the clearTimeout method:

var timer1 = setTimeout(functionName, 2000);
...
window.clearTimeout(timer1);

There’s no absolute guarantee that the timer event fires when it is supposed to fire. Timers run on the same execution thread as all other User Interface (UI) events, such as mouse-clicks. All events are queued and blocked, including the timer event, until its turn. So, if you have several events in the queue ahead of the timer, the actual time could differ. Probably not enough to be noticeable to your application users, but a delay can happen.

See Also

John Resig offers an excellent discussion on how timers work, and especially the issues associated with event queues and single threads of execution, at http://ejohn.org/blog/how-javascript-timers-work/.

3.9. Creating Recurring Timers

Problem

You need to run the same function several times at regular intervals.

Solution

Use the Window.setInterval method to create a recurring timer:

var x = 0;
setInterval(moveElement,1000);

function moveElement() {
   x+=10;
  var left = x + "px";
  document.getElementById("redbox").style.left=left;
}

Discussion

Dynamic animations in a web page, SVG, or Canvas, are dependent on the setTimeout and setInterval methods. In particular, any flashing, moving, or following type of animation is dependent on a timer calling a method at specified intervals.

The setInterval method requires two parameters: the code or function to process, and the delay between timer events. The first parameter can be a function name:

setInterval(functionName,3000);

The first parameter can also be a function call with parameters in a text string:

setInterval ("alert('hello')", 3000);

The second parameter is the time, in milliseconds, of the timer delay. Unlike setTimeout, discussed in Recipe 3.8, the setInterval timer will continue to cycle until the JavaScript application (the web page) is unloaded, or until the clearInterval method is called:

var intervalid = setInterval(functionName, 3000);
...
clearInterval(intervalid);

If the first parameter is a function name, you can pass parameters, optionally, following the timer delay:

setInterval(functionName, 2000, param1, param2, ..., paramn);

Being able to pass parameters to the function is handy if you’re creating an animation and generating the parameters dynamically. Unfortunately, passing parameters in this way doesn’t work with IE8. However, you can instead use function closures with the timer, as covered in Recipe 3.10.

3.10. Using Function Closures with Timers

Problem

You want to provide a function with a timer, but you want to add the function directly into the timer method call.

Solution

Use an anonymous function as first parameter to the setInterval or setTimeout method call:

var x = 10;
var intervalId=setInterval(function() {
          x+=5;
          var left = x + "px";
          document.getElementById("redbox").style.left=left;}, 100);

Discussion

Recipes 3.8 and 3.9 use a function variable as the first parameter to the timer methods. However, you can also use an anonymous function, as demonstrated in the solution. This approach is especially helpful, because rather than have to use a global value that’s used in the timer function, you can use a variable local to the scope of the enclosing function.

Example 3-2 demonstrates the use of an anonymous function within a setInterval method call. The approach also demonstrates how the use of this function closure allows access to the parent function’s local variables within the timer method. In the example, clicking the red box starts the timer, and the box moves. Clicking the box again clears the timer, and the box stops. The position of the box is tracked in the x variable, which is within scope for the timer function, since it operates within the scope of the parent function.

Example 3-2. Using an anonymous function within a setInterval timer parameter
<!DOCTYPE html>
<head>
<title>interval and anonymous function</title>
<style>
#redbox
{
  position: absolute;
  left: 100px;
  top: 100px;
  width: 200px; height: 200px;
  background-color: red;
}
</style>
<script>

var intervalId=null;

window.onload=function() {
  document.getElementById("redbox").onclick=stopStartElement;
}
function stopStartElement() {
    if (intervalId == null) {
       var x = 100;
       intervalId=setInterval(function() {
                        x+=5;
                        var left = x + "px";
                        document.getElementById("redbox").style.left=left;}, 100);
    } else {
       clearInterval(intervalId);
       intervalId=null;
    }
}

</script>
</head>
<body>
<div id="redbox"></div>
</body>

See Also

See more on functions as parameters and anonymous functions in Chapter 6, especially Recipe 6.5.

Get JavaScript Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.