Fun With JavaScript: The “Princess Diana Is Still Dead” Count-Up Timer
In response to yet another story about Princess Diana, headlined “Report: Princess Diana driver was drunk”:
Yes, it took the tragic death of a tragic woman and the absurdity of that death still being in the news for me to finally get around to writing a bigger, better count-up / countdown timer.
The problem with most count-up and countdown timers is they only work with hours, minutes and seconds; days, if you’re lucky. But often, you want to be able to count up or down a longer period, months and even years away. This code will let you do that.
The basic idea behind a count-up / countdown timer is simple: From a given date, calculate the amount of time that has passed since (count-up) or the amount of time that has yet to pass (countdown). You simply subtract the earlier date from the later date and count the seconds between them.
Before we begin, some quick notes about how JavaScript handles time:
- Like *nix and PHP, JavaScript measures time since the Unix epoch (January 1, 1970). Unlike PHP, which uses default timestamps of seconds since the epoch, JavaScript timestamps are always in microtime, or thousandths of seconds, since the epoch. For example, the PHP timestamp for midnight, December 10, 2006, is 1165726800; in JavaScript, it’s 1165726800000.
- When you ask for the current time in JavaScript, JavaScript uses the system time of the Web browser’s computer. Therefore, you can never be certain what time is created if you simply call the default Date() constructor in JavaScript. Keep this in mind: If you don’t explicitly construct a time in JavaScript, “now” could be any old time.
For our count-up timer, we need to construct the date of Diana’s death, which converts to 3 a.m. UTC on August 31, 1997. We’re going to use UTC for the death time because we want to make sure that no matter what time the user visits, his time zone settings don’t affect how long, relative to his time zone, Diana has been dead.
For example, New York City is 5 or 6 hours behind Paris time, depending on the time of year; Moscow is 2 hours ahead. If we simply used the time “4 a.m., August 31, 1997″ to compute the difference between the current time and the time we are after, the count-up timer for New York users would be slow by 5 or 6 hours, and for Moscow users, fast by 2 hours.
We use local time for the end date because people are interested in the time of events relative to where they are. For example, when the ball falls on Times Square every New Year’s Eve, 16 other time zones have already celebrated the new year. Nonetheless, to New York revelers, it’s officially the new year only when clocks in the Big Apple strike midnight.
We also need to declare the string variable we’ll use to output the count-up time.
function countUpTimer() { var begin = new Date('31 August, 1997 3:00:00 UTC'); var now = new Date(); var out = "";
Since JavaScript measures time as an integer, getting the difference between our two dates is as simple as subtracting the time of Diana’s death from the current time, then dividing the result by 1000 to get whole seconds:
var seconds = now - begin; seconds /= 1000;
We’ll proceed with getting the years, months, days, hours and seconds by first dividing the seconds variable by the number of seconds in the given unit of that time; then, we’ll subtract the number of seconds in that unit of time times the number of units.
For example, there are 60 seconds in a minute. If we have 132 seconds, and we divide that by 60, we get 2.2. If we round that off, we have 2 minutes.
If we then subtract, from the seconds (132), the number of minutes (2) times the number of seconds in a minute (60), we get 12 seconds remaining [132 - (2 * 60) = 12]. Thus, we know 132 seconds equals 2 minutes, 12 seconds.
var dy = Math.floor(seconds / 31536000); seconds -= (dy * 31536000); seconds -= (countLeapYears(begin, now) * 86400);
Notice the last line of the code above.
The script generally expects a year to last 31,536,000 seconds. However, leap years have an extra day, which means an extra 84,600 seconds.
So, we need to find out how many leap days occurred between our starting and ending years. For each of those leap years, we subtract an extra 84,600 seconds from the total number of seconds between our dates.
We’ll do that with a function called countLeapYears, and another function called isLeapYear.
The isLeapYear function will return a Boolean, based on whether the four-digit year argument we pass to it is divisible by 4 or 400, but not by 100:
function isLeapYear(someyear) { if(someyear % 4 == 0) { if(someyear % 100 != 0) { return true; } else if(someyear % 400 == 0) { return true; } } return false; }
The countLeapYears function will return an integer: namely, the number of leap days between two dates.
We pass whole dates to the function because we need to know if the starting date happened after Feb. 29, and to maintain consistency in the parameters. If a start date occurs after Feb. 29, then even if it is a leap year, leap day has already passed; it’s no longer a factor.
Note that the built-in JavaScript method getMonth() returns an integer from 0 to 11, where 0 is January and 11, December. This, February is indicated via getMonth() by the integer 1.
function countLeapYears(startDate, endDate) { var x = 0; var startYear = startDate.getFullYear(); var startMonth = startDate.getMonth(); var endYear = endDate.getFullYear(); if(startMonth > 1) { startYear++; } for(var i = startYear; i < endYear; i++) { if(isLeapYear(i)) { x++; } } return x; }
Back to the main function. With the years computed and the years’ seconds subtracted, we can move on to months:
var extrasecs = getExtraMonthDays(now); extrasecs *= 86400; extrasecs += seconds; var dmo = Math.floor(extrasecs / 2592000); seconds -= (dmo * 2592000);
As the code comments above note, we expect a month to be 30 days long, but not all are 30 days long. So, we need, once again, to compute the number of “extra seconds” that have passed up to the present date.
We do that by using the function getExtraMonthDays(), which requires the current date as an argument. It will return an integer indicating the number of “extra” days to subtract from our seconds variable.
function getExtraMonthDays(nowDate) { var thismonth = nowDate.getMonth(); var thisyear = nowDate.getFullYear(); var days = 0; var out; if(thismonth > 0) { days++; } if(thismonth > 1) { days = days - 2; } if(isLeapYear(thisyear) && thismonth > 1) { days++; } if(thismonth > 2) { days++; } if(thismonth > 4) { days++; } if(thismonth > 6) { days++; } if(thismonth > 7) { days++; } if(thismonth > 9) { days++; } return days; }
Once again, whatever integer the function returns, we multiply by 84,600 and subtract from the seconds variable.
Fortunately, the length of days, hours and minutes never changes (at least, in the calendar sense), so we’re all done with special considerations. From here on out, breaking our timestamp down to its component parts is all simple math.
var dd = Math.floor(seconds / 86400); seconds -= (dd * 86400); var dh = Math.floor(seconds / 3600); seconds -= (dh * 3600); var dm = Math.floor(seconds / 60); seconds -= (dm * 60); seconds = Math.floor(seconds); out += dy.toString() + " year"; if(dy != 1) { out += "s"; } out += ", "; out += dmo.toString() + " month"; if(dmo != 1) { out += "s"; } out += ", "; out += dd.toString() + " day"; if(dd != 1) { out += "s"; } out += ", "; out += dh.toString() + " hour"; if(dh != 1) { out += "s"; } out += ", "; out += dm.toString() + " minute"; if(dm != 1) { out += "s"; } out += " and "; out += seconds.toString() + " second"; if(seconds != 1) { out += "s"; }
To finish, we assign our output string to a DIV named divTime, then use setTimeout() to recall the script every second [i.e., 1000 milliseconds]:
document.getElementById('divTime').innerHTML = out; setTimeout('countUpTimer()', 1000); }
To make the whole thing work, we just add the countUpTimer() to the body tag’s onload event:
<body onload="countUpTimer()">To customize this count-up timer, save the HTML demo page — located here — to your computer and edit the JavaScript in the HTML source.
I distribute code under the latest version of the Creative Commons Attribution / Share-Alike License.































Leave a comment