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.
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
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.
3.3. Printing Out an ISO 8601 Formatted Date
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 Date
s, 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.
<!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
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
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:
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
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 Date
s,
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 Date
s 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.
3.8. Creating a Timeout
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
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.
<!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.