Returning to the leap year problem you can use exactly the same method to work out the number of days in a year.

var daysInYear=new Date(y+1,0,1) -new Date(y,0,1);

where the result is actually in milliseconds in the year.

Notice that in this case we don't have to worry about daylight saving because there are always two clock changes in a year and these cancel each other out - but there is no harm in doing the calculation in UTC.

It is easy to turn this into a function as well:

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

As before this sorts you leap years for you automatically.

Number Of Days In a Quarter

You can use the difference trick to work out other "number of days in" questions.

For example the number of days in a quarter i.e. three calender months, is just:

function daysInQuarter(q, y) { var y = y || new Date(Date.now()).getFullYear(); return toDays(Date.UTC(y, (q + 1) * 3, 1) - Date.UTC(y, (q * 3), 1)); }

Notice that in this case you do have to use UTC because there could be a daylight saving time change in the Spring and Autumn quarters.

First Named Day Of The Month

A common requirement is to find the date of the first named day of the month - e.g. the first Tuesday. I've seen some very strange ways of doing this job including using a for loop to step though days until one with the right name is found.

In fact you can go to any first named day very easily as long as you have a date function that can be use to find the name of the first day of the month - and JavaScript does have such a function.

To see how this works consider if you want the first Thursday i.e. day 4 and the first day of the month is a Monday i.e. day 1. It is clear that the date you want is three days further on i.e. 4-1. In general the difference between the first day and the number of the day you want tells you how far away the day you are looking for is from the start of the month. The only complication is that this can be negative and this gives the day you are looking for but in the previous month.

At this point you might think that we need an if statement to deal with the negative but the day we need is just a week later so if we add 7 to it we get the correct date in the current month. This is fine if it is negative but what if it is positive? We can deal with both cases by adding 7 and taking the result mod 7 which removes the effect of adding 7 in the positive case and doesn't change the result in the negative case.

Putting all this together gives:

function firstDayInMonth(day, m, y) { // day is in range 0 Sunday to 6 Saturday var y = y || new Date(Date.now()).getFullYear(); var m = m || new Date(Date.now()).getMonth(); return new Date(y, m, 1 + (day - new Date(y, m, 1).getDay() + 7) % 7); }

Nth Named Day Of The Month

Lots of activities happen on the 3rd Thursday of the month or similar and being able to generate this sort of date automatically is useful. It might sound difficult but if you have the first named day of the month its easy to work out the nth because its just n weeks later:

function nthDayInMonth(n, day, m, y) { // day is in range 0 Sunday to 6 Saturday var y = y || new Date(Date.now()).getFullYear(); var m = m || new Date(Date.now()).getMonth(); var d = firstDayInMonth(day, m, y); return new Date(d.getFullYear(), d.getMonth(), d.getDate() + (n - 1) * 7); }

You can set n to 1 for the first day, to 2 for the second and so on. So the 3rd Thursday in the current month is just

nthDayInMonth(3,4);

Notice that there can be a 5th day in a month but if there isn't then you get the first day in the following month. It is up to you to decide if this is a bug that needs to be checked for.

You can use similar techniques for things like the last named day of the month, the first named day after a given date and the nth named day after a given date.

Interval From Given Date

This is a very important and useful date calculation. Suppose you are charged for something from a fixed day of the month - the 11th say. What you need to know is how many days it has been since the last 11th of the month. If you are on a date after the 11th in the month, say the 15th day of the month then a simply date subtraction gives you the result. However if you are on a date before the 11th then it is the number of days to the 11th in the previous month that is of interest.

So if you have a fixed date d1 the 11th of the month say and today is d2 then the date interval calculated using the current month is:

new Date(d2.getFullYear(), d2.getMonth(),d2.getDate()- new Date(d2.getFullYear,d2.getMonth,11)

This will be positive if the current date has passed the 11th of the month but it will be negative if the current date is before the 11th.

This is another date calculation that seems to need an if statement but modular arithmetic makes it all workout just as in the case as with the first named day. In this case to correct the negative date we need to add the number of days in the previous month and to leave the positive date un-modified we need to take this mod the number of days in the previous month.

Putting this together:

function daysFromFixedDay(d) { var y = new Date(Date.now()).getFullYear(); var m = new Date(Date.now()).getMonth(); var d2 = new Date(Date.now()).getDate(); var dInPrevMonth = daysInMonth(m - 1, y); return (toDays( Date.UTC(y, m, d2) - Date.UTC(y, m, d))+ dInPrevMonth) % dInPrevMonth; }

You can see that we make use of the daysInMonth function to get the number of days in the previous month. Then we simply form the difference between the two dates in the same month and correct it if it is negative.

You can work out the interval to a given date in the same way.

Conclusion

Date calculations can be challenging and satisfying when you do manage to solve them.

If you would like to add or request any other date functions then either comment or email the editor.

Object creation is fundamental to all object-oriented languages, but in JavaScript is is left to the programmer to work out how best to do it and often the practice that you encounter isn't the best b [ ... ]