Using jQuery to create a dynamic time countdown

This post is more than 2 years old.

A user wrote in with an interesting request. He wanted to take a form field, and as a user entered a date in it, display the "time till" the entered date. So for example, if I were to enter tomorrow, let's say an hour from now, we would get a message stating this was one day, one hour away. To make things even more interesting, he wanted to make it dynamic. So as you sat there and watched, the difference would actually count down. Here's how I solved it - and as always - I'm open to folks rewriting this in a better way! (If you do, please use pastebin, don't post your code in the comment.)

I thought I'd begin by simply creating a form that would:

  • Validate a string as a date
  • Figure the difference between now and then (assume the date is in the future)
  • Display the difference as a string (but not worry about updating for now)

Each of these pieces has some interesting twists to it. For my date validation, I turned to Google and it brought up this interesting article over at Stack Overflow. The suggested the following:

function isValidDate(d) { if ( Object.prototype.toString.call(d) !== "[object Date]" ) return false; return !isNaN(d.getTime()); }

This function checks to see if the passed object is a Date object first. It then uses the result of getTime() to validate a that it contains a real date. So in theory, all I have to do is get my string, run new Date() on it, and check it. This worked great. Kinda. I noticed that JavaScript had no problem accepting February 29, 2011 as a date. It simply considered it March 1. It also accepted 2 as a date in the past. Yes, just 2. For my demo here, I was ok with that since my code would end up ignoring past dates anyway. The February 29 thing I decided to just spin as a feature. Yep, that's how I roll.

The difference part then becomes simple math. You can subtract one date from another and get the difference in milliseconds. Once you have that, you do some division and you're good to go. Here's the entire version of my first draft below.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> $(document).ready(function() {

//http://stackoverflow.com/questions/1353684/detecting-an-invalid-date-date-instance-in-javascript
function isValidDate(d) {
	if ( Object.prototype.toString.call(d) !== "[object Date]" ) return false;
	return !isNaN(d.getTime());
}

$("#date").keyup(function() {
	var v = $(this).val();
	v = new Date(v);
	if(isValidDate(v)) {
		console.log(v + " is a date");
		var now = new Date();
		//assume v is a future date
		var difference = v-now;
		//only care if positive
		if(difference &gt; 0) {
			var days, hours, minutes, seconds = 0;
			
			days = Math.floor(difference / (24*60*60*1000));

			if(days &gt; 0) {
				difference -= days * (24*60*60*1000);
			}

			if(difference &gt; 0) {
				hours = Math.floor(difference / (60*60*1000));
				if(hours &gt; 0) {
					difference -= hours * (60*60*1000);
				}
			}
			
			if(difference &gt; 0) {
				minutes = Math.floor(difference / (60*1000));
				if(minutes &gt; 0) {
					difference -= minutes * (60*1000);
				}
			}
			
			if(difference &gt; 0) {
				seconds = Math.floor(difference / 1000);
			}
			$("#timeleft").html(days + " days, "+ hours + " hours, "+ 
								minutes + " minutes, " + seconds + " seconds.");
								
			console.log(days + ", "+hours+", "+minutes + ", "+ seconds);
		}
	}
});

}) </script>

<form id="mainForm"> Date: <input type="text" name="date" id="date"><br/> </form>

<div id="timeleft"></div>

You can demo this yourself here. And yes - notice I'm using console.log to help debug the application. Please do not post a comment saying the code isn't working in your browser if you don't have console support.

Ok - so now all I have to do is add in the timer aspect. I rearranged my code a bit and added an interval aspect to it. Here's the entire new template.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script> $(document).ready(function() {

var selectedDate;
var hb;

//http://stackoverflow.com/questions/1353684/detecting-an-invalid-date-date-instance-in-javascript
function isValidDate(d) {
	if ( Object.prototype.toString.call(d) !== "[object Date]" ) return false;
	return !isNaN(d.getTime());
}

function showDiff() {
	var now = new Date();
	//assume v is a future date
	var difference = selectedDate-now;
	//only care if positive
	if(difference &gt; 0) {
		var days, hours, minutes, seconds = 0;
		
		days = Math.floor(difference / (24*60*60*1000));

		if(days &gt; 0) {
			difference -= days * (24*60*60*1000);
		}

		if(difference &gt; 0) {
			hours = Math.floor(difference / (60*60*1000));
			if(hours &gt; 0) {
				difference -= hours * (60*60*1000);
			}
		}
		
		if(difference &gt; 0) {
			minutes = Math.floor(difference / (60*1000));
			if(minutes &gt; 0) {
				difference -= minutes * (60*1000);
			}
		}
		
		if(difference &gt; 0) {
			seconds = Math.floor(difference / 1000);
		}
		$("#timeleft").html(days + " days, "+ hours + " hours, "+ 
							minutes + " minutes, " + seconds + " seconds.");
							
		console.log(days + ", "+hours+", "+minutes + ", "+ seconds);
	}
}

$("#date").keyup(function() {
	var v = $(this).val();
	v = new Date(v);
	if(isValidDate(v)) {
		console.log(v + " is a date");
		selectedDate = v;
		//clear in case it was run
		clearInterval(hb);
		hb = setInterval(showDiff, 1000);
	} else {
		clearInterval(hb);
	}
});

}) </script>

<form id="mainForm"> Date: <input type="text" name="date" id="date"><br/> </form>

<div id="timeleft"></div>

Basically I took my display code and made it it's own function while also moving some of my values out into a global scope. This will not correctly handle things if the interval hits 0, but, it's a good example I think though. You can demo this one below.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Bernhard Dཫler posted on 10/30/2011 at 5:53 PM

There's a jQuery plugin Pretty Date: http://ejohn.org/blog/javas...

Comment 2 by James Moberg posted on 10/30/2011 at 6:25 PM

Working with dates can be difficult sometimes. I started using the Datejs library with jQuery and it makes it extremely easy to add date-based logic. I've created JS/CFM scripts to perform date/time validation, auto-date/time population, time clock functions, count downs, etc using it. It even allows the user to enter values like "now" and "next saturday" and automatically converts them to valid dates.
http://www.datejs.com/

For a sample of how easy it is to write a date-based rule using Datejs, check out this:
http://www.ssmedia.com/util...

Comment 3 by Simon posted on 10/30/2011 at 8:07 PM

If only the date/time decremented as soon as it was displayed ;-)

Comment 4 by Raymond Camden posted on 10/31/2011 at 3:26 AM

@Simon: Err, what?

Comment 5 by Phillip Senn posted on 10/31/2011 at 6:23 PM

Only 54 days until Christmas! Raymond, are you taking requests? I'd like to see a proof-of-concept where two people are dragging a jQueryUI themed div around on the screen together. Think "playing cards". Preferably with node.js, but you mentioned BlazeDS before when I mentioned node.

Comment 6 by Raymond Camden posted on 10/31/2011 at 6:25 PM

Node.js is still on my list of things to learn. Now that conference season is past I'll try to prioritize it. But BlazeDS would make it easy too. You have to use the Flash shim though. It's not a 100% JS solution.

Comment 7 by Paul Connell posted on 10/31/2011 at 8:25 PM

Ray, Simon was pointing out that (at least for him and I) it doesn't seem to DO anything once you put the date in and press enter. Clicking the back button on the blank screen that it goes to seems to trigger it to display correctly at that point. (FF 7.0.2 with Firebug installed, not sure if that makes a difference).

Comment 8 by Raymond Camden posted on 10/31/2011 at 8:26 PM

Oh sorry - so if you don't hit enter, it should work. You were expect to just type in and not hit enter. Obviously this would be part of a larger form with more fields and stuff.

Comment 9 by Paul Connell posted on 10/31/2011 at 8:28 PM

Thanks, just noticed it after posting - I'm a web 1.0 guy obviously!

Comment 10 by Simon posted on 11/1/2011 at 1:04 AM

Actually, it worked well for me. I was kind of just kidding saying that I wanted the date/time to do a count down.

Comment 11 by Raymond Camden posted on 11/1/2011 at 1:05 AM

No worries. :)

Comment 12 by Simon posted on 11/1/2011 at 1:08 AM

@ray Node.js does look really interesting. However, with Zeus socket connections will be CFeasy. I think that will simplify the process of pushing data tremendously and CFers can do what they do best...build solutions. Is BlazeDS still going to be the underlying socket server?

Comment 13 by Raymond Camden posted on 11/1/2011 at 1:09 AM

Sorry - I don't think I can comment on that yet.