Date Hacks - Doing JavaScript Date Calculations
Written by Ian Elliot   
Thursday, 19 April 2018
Article Index
Date Hacks - Doing JavaScript Date Calculations
Days In The Year

OK, you have mastered the way your particular language represents dates and times but.. this is just the start. Doing arithmetic with dates can go well beyond just working out the interval between two fixed points. What about the third Thursday in the month or how many days are we from the previous 11th of the month? Find out how to hack dates.


JavaScript Data Structures 

Cover

Contents

  1. The Associative Array
  2. The String Object
  3. The Array object
  4. Speed dating - the art of the JavaScript Date object
  5. Doing JavaScript Date Calculations
  6. A Time Interval Object
  7. Collection Object
  8. Stacks, Queue & Deque
  9. The Linked List
  10. A Lisp-like list
  11. The Binary Tree
  12. Bit manipulation
  13. Typed Arrays I
  14. Typed Arrays II
  15. Master JavaScript Regular Expressions
    * First Draft

While we are focused on JavaScript it is worth saying that dates and times in other languages and even spreadsheets follow the same pattern. You can generally reason about dates and times in this very general way.

In JavaScript we have a Date object which computes the number of milliseconds from a fixed date.  All dates and times are related to this fixed date by the amount of time passed since midnight Jan 1, 1970.

You can create a date object for a given date using:

var d=new Date(year,month,day);

You can also specify an exact time following the date in the order hours, minutes, seconds and milliseconds.

To get the current date and time you can use:

var n=Date.now();

Notice that n isn't a date object but the number of millisecond since midnight Jan 1, 1970. If you want it as a date object use:

var d=new Date(Date.now());

You can compute the difference between two dates and times simply using subtraction:

var interval=n-d;

Notice that the result of the subtraction, i.e. interval in this case, isn't a date but the number of milliseconds between the two dates.That is the difference between two date objects or a date object and a numeric value isn't a date object but a numeric value.

JavaScript applies an automatic type conversion when you perform arithmetic on date objects. If you want to explicitly get the number of milliseconds use  getTime.

That is :

var interval=n.getTime()-d.getTime();

also gives you the number of milliseconds between the two dates. 

The Number Of Days Between Two Dates

You might think that this first date calculation is going to be trivial, but as JavaScript doesn't provide any facilities for working with dates as fractional days it is a little more difficult than you might imagine - and there are lots of cases of "getting it wrong".

If you want to convert the millisecond values into days and fractional days, as used in many other languages and spreadsheets all you have to do is divide by the number of milliseconds in a day i.e.

var days=interval/24/60/60/1000;

In fact it is useful to have a function that converts to days:

function toDays(d) {
 d = d || 0;
 return d / 24 / 60 / 60 / 1000;
}

You can be sure that this gives the correct answer because there are always 1000 milliseconds in a second, 60 seconds in a minute and 60 minutes in an hour but...

The problem is that there aren't always 24 hours in a day.

The assumption that a day is 24 hours is probably the biggest common error in working with dates in this form. 

The reason that a day isn't always 24 hours is that there are daylight saving changes to the time. When the clocks go forward the day is just 23 hours and when they go back the day is 25 hours. If you take the difference between two dates and they include a daylight saving change then working out the number of days between them by division by the number of milliseconds in 24 hours will give you a result that is slightly wrong. 

So how do you calculate the number of days between two dates?

The first thing to say is that if you are really interested in working with an interval that represents days and fractional days then the simple calculation is what you want. If you subtract two dates that are across a daylight saving time change then there really is one hour more or less between them and a whole number of days haven't passed. 

However if you are interested in extracting the whole number of days from the fractional part you have to take care. As the difference is simply one hour i.e. about .042 of a day you can round the result. 

var days=Math.round(interval/24/60/60/1000);

If you don't like this method then an alternative way is to do the calculation in UTC which doesn't have daylight saving but doesn't correspond to local time - unless you live on the prime meridian that is.

So to compute the exact number of days between two dates we need to convert them to UTC. Unfortunately there is no standard JavaScript function which will do this but we can make one very easily:

function toUTC(d) {
 if(!d || !d.getFullYear)return 0;
 return Date.UTC(d.getFullYear(),
              d.getMonth(),d.getDate());
}

Notice that this doesn't return a date object but a numeric value corresponding to the number of milliseconds since midnight Jan 1, 1970. 

Now that you can work out the UTC date you can get the number of days between two dates as follows

function daysBetween(d1,d2){
 return toDays(toUTC(d2)-toUTC(d1));
}

Number Of Days In The Month

Months are the biggest problem in date calculations for the simple reason you can never rely on how long they are.  

So how do you find out the number of days in a given month? 

The first thing to say is that JavaScript numbers the months starting from 0 for January. 

So if m is the number of the month that you want to know the number of days of all you have to do is find the number of days between the first of month m and the first of month m+1.

in JavaScript this is:

var m=1;
var y=2013;
var daysInMonth=toDays(
            new Date(y,m+1,1)- new Date(y,m,1)); 
console.log(daysInMonth);

The m variable sets the month in question - remember that January is zero and y sets the year. The line that does the work is:

new Date(y,m+1,1)-new Date(y,m,1);

This gives the length of the month in milliseconds and to convert to days we just divide by the number of milliseconds in a day. 

Of course this gives you the exact fractional number of days in the month taking into account any day light saving changes. If you use this method you will get the wrong answer for any month were the clocks change. 

The correct way to find the number of days in a month is to use UTC:

var daysInMonth=toDays(
             Date.UTC(y,m+1,1)- Date.UTC(y,m,1));

You may also notice that there is an "end case" that we have to consider. What happens when you try to find the number of days in December? The problem is that you need to subtract its first day from the first day of January of the next year. 

So to make this work you have to remember to test for month=December, i.e. m==11 in JavaScript and add one to the year and set m to January. In most languages you don't have to do this because the date constructor function nearly ways interprets the 13th month of the current year as being the same as the first month of the next year. So it is with JavaScript and this means you can work with dates that strictly aren't proper dates. For example 

new Date(2013,12,1);

is the same as

new Date(2014,1,1);

This is a very useful feature that saves a lot of testing for end cases. 

If you are thinking that storing the days of the month in a lookup table would be much easier then you are correct apart from one small problem - leap years. If you use a table lookup you need to remember to change the number of days in February when it is a leap year. Using the calculation method you can let the leap years look after themselves.

You can, of course make it simpler by converting it into a function: 

function daysInMonth(m, y) {
 var y = y ||
     new Date(Date.now()).getFullYear();
 return toDays(Date.UTC(y, m + 1, 1) -
                     Date.UTC(y, m, 1));
}

If you don't specify a year parameter then the current year is used as a default. 



Last Updated ( Thursday, 19 April 2018 )