Ga direct naar


Calculate ISO 8601 week and year in javascript

Wednesday 22 April 2009 14:18

For one of our projects I needed to be able to calculate the current weeknumber and corresponding year of a given date... in javascript. Not really a big deal, just calculate the number of weeks between the requested date and the 1st of January that year and you're done, right? Wrong!

By Taco van den Broek

Why do we use ISO week numbering?

In our framework (including modules) we use the ISO 8601 standard for week numbering. We've chosen this standard because it is very related to western european workweeks: weeks start on monday and the first week of the year is the first week with a workday in it. Our clients are mostly businesses operating in western europe and therefore this standard is the most obvious choice.

Why this article?

Our framework is built using PHP. In PHP requesting the week and / or year number according to this standard is easy:

// Week and year number using date()
$w = date('W');
$y = date('o');

// Week and year number using strftime()
$w = strftime('%V');
$y = strftime('%G');

But in javascript no built-in methods are available to calculate these values.

ISO 8601 week numbering

This (part of the) standard primarily defines two rules:

  1. Weeks start on monday
  2. The first week of the year is the week with the first thursday of that year

An alternative to the second rule is that the first week of the year is the week with the 4th of January in it. This rule is mostly more convenient in coding.

As a result of this second rule the week with the 1st of January isn't always week number 1. Another result is that some years have 53 weeks instead of 52.

Extending the javascript Date class

To be able to get the week and year numbers in javascript we extend the Date class with two methods: getWeek() and getWeekYear().

Extending a class in javascript can be done by adding properties to the prototype object of that class. In the code below we define Date.prototype.getWeek() and Date.prototype.getYearWeek(), these new methods can be called directly on instances of the Date class:

var today = new Date();
alert("The current week number is: " + today.getWeek());

Date.getWeek()

First, take a look at the code below:

/**
* Get the ISO week date week number
*/
Date.prototype.getWeek = function () {
// Create a copy of this date object
var target = new Date(this.valueOf());

// ISO week date weeks start on monday
// so correct the day number
var dayNr = (this.getDay() + 6) % 7;

// ISO 8601 states that week 1 is the week
// with the first thursday of that year.
// Set the target date to the thursday in the target week
target.setDate(target.getDate() - dayNr + 3);

// Store the millisecond value of the target date
var firstThursday = target.valueOf();

// Set the target to the first thursday of the year
// First set the target to january first
target.setMonth(0, 1);
// Not a thursday? Correct the date to the next thursday
if (target.getDay() != 4) {
target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7);
}

// The weeknumber is the number of weeks between the
// first thursday of the year and the thursday in the target week
return 1 + Math.ceil((firstThursday - target) / 604800000); // 604800000 = 7 * 24 * 3600 * 1000
}

The function will calculate the difference in weeks between the first thursday of the year (always week #1) and the thursday in the target week. To do this it will first create a new date object called target and change it so it points at the thursday in the target week. The millisecond value of that date object is then stored. Next, the first thursday of the year is found and the difference in weeks between these two thursdays, plus one, is returned. Because of the ceil any daylight saving differences are ignored.

Date.getWeekYear()

That was the getWeek method, the most important one of the two. The getWeekYear() method uses getWeek() to determine what year to return.

This method is very simple, just return the calendar year of the thursday in the requested week.

/**
* Get the ISO week date year number
*/
Date.prototype.getWeekYear = function ()
{
// Create a new date object for the thursday of this week
var target = new Date(this.valueOf());
target.setDate(target.getDate() - ((this.getDay() + 6) % 7) + 3);

return target.getFullYear();
}

So that's about all you'll need to calculate ISO 8601 year and week numbers in javascript, happy coding!


Optimisation

After reading the site from Dr J R Stockton that Rick recommended in his post I stole a simple trick to make the code a little shorter. Instead of handling the edge cases I first change the date to the thursday of the requested week so the week year number equals the calendar year. I did keep the code more verbose than Stockton did though, just so you understand what exactly happens.


Update (2011-09-28)

Bugs have been fixed. This code is tested for every date between 1970-01-01 and 2100-01-01 (compared with PHP's date('oW') output).

This software is subject to the MIT license: you are free to use it in any way you like as long as it keeps its license.

« Back

Reactions on "Calculate ISO 8601 week and year in javascript"

1 2 Last page
Jim
Placed on: 04-23-2009 21:21
Nice post!One can find it very useful while developing some web calender's,or designing motgage projects.
Rick
Placed on: 04-24-2009 04:05
Take a look at Dr J R Stockton Merlyn pages... quite a few javascript date functions: http://www.merlyn.demon.co.uk/js-date8.htm#YWD
Taco
Placed on: 04-24-2009 17:23
Taco van den Broek
User icon
@Jim: Thanks!

@Rick: That's a nice collection indeed, little bit too much information for a blogpost though Wink I did steal some little tricks from that site though, see the optimisation at the end of my article.
Edited
Taco has edited this message on: 04-24-2009 17:23
Jonas
Placed on: 06-07-2009 22:18
Changing the target to the thursday of the current week helped move me forward quite a bit.

Unfortunatly your version doesn't quite work. Take a look at for example the 4th of Jan 2010 as your target date.

I'm not entirely sure I've grasped all the concepts but I think it works with 2009 because jan4 happens to be a thursday. I believe you need to change the jan4 reference to the thursday of the week with jan4 in it as was done with the target.

I added this and it seems to work with the limited tests I've done.
var jan4DayNr = (jan4.getDay() + 6) % 7;
jan4.setDate(jan4.getDate() - jan4DayNr + 3);

You also need to do floor instead of ceil if you do it this way.

Thanks for all the help.
Martin
Placed on: 07-13-2009 09:44
@Jonas: You're right about 2010. Week 1 of that year seems to be missing!

But your solution can be quite confusing also... You're setting the variable jan4 to another date than Januray 4th. :-S
Martin
Placed on: 07-14-2009 08:57
@Jonas: I guess your solution still needs a little bit of testing too. It may be right about the first week of 2010. But after week 12/2010, comes another week 12. This is the case for 2011 also...
Andre Foeken
Placed on: 08-14-2009 14:30
Your forgot to Math.abs the DayDiff and use floor then
zajjar
Placed on: 01-04-2010 10:06
Yes, it's true :-/ Function getWeek() gives wrong results :-/
Here is the other solution. It runs perfect for me:

http://www.merlyn.demon.co.uk/weekcalc.htm#JS

ahhhahahaha
Placed on: 01-07-2010 05:56
the prototype is in here now pokemon
Ted Cowan
Placed on: 08-14-2010 23:07
Is your getWeek() function now correct as shown above or does it still have bugs as some have suggested?
Dennis
Placed on: 02-08-2011 17:14
The following function is tested to work correctly with all Dec 31st and Jan 1st between 1970 and 2030:

Date.prototype.getWeek = function () {
// Based on: http://techblog.procurio...year-in-javascript.html
var targetThursday = new Date(this.getFullYear(),this.getMonth(),this.getDate()); // Remove time components of date
targetThursday.setDate(targetThursday.getDate() - ((targetThursday.getDay() + 6) % 7) + 3); // Change date to Thursday same week
var firstThursday = new Date(targetThursday.getFullYear(), 0, 4); // Take January 4th as it is always in week 1 (see ISO 8601)
firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3); // Change date to Thursday same week
var weekDiff = (targetThursday - firstThursday) / (86400000*7); // Number of weeks between target Thursday and first Thursday
return 1 + weekDiff;
}
sisi
Placed on: 09-01-2011 15:56
Just another example of sloppy work with javascript iso week number my ....
Taco
Placed on: 09-28-2011 17:18
Taco van den Broek
User icon
I've updated the code to the version we are currently using and tested the code for all dates between 1970-01-01 and 2100-01-01.

Also, on request, the license for this software has been added.
Julien de Prabère
Placed on: 11-07-2011 02:16
I propose this function which takes into account the summer(daylight saving) time !

Date.prototype.getWeek = function() {var i,d,firstWeekMonday;
var i=1;while (new Date(this.getFullYear(),0,i).getDay()!=4) i++;// i the first Thursday;
var firstWeekMonday = new Date(this.getFullYear(),0,i-3,0,0,0,-1);// one millisecond before
d=Math.ceil(((this.valueOf()-firstWeekMonday.valueOf()-(this.getTimezoneOffset()-firstWeekMonday.getTimezoneOffset())*60000)/86400000)/7); return d?d:new Date(this.getFullYear()-1,11,31).getWeek();
}
Taco
Placed on: 11-10-2011 19:32
Taco van den Broek
User icon
Quote
I propose this function which takes into account the summer(daylight saving) time !

Date.prototype.getWeek = function() {var i,d,firstWeekMonday;
var i=1;while (new Date(this.getFullYear(),0,i).getDay()!=4) i++;// i the first Thursday;
var firstWeekMonday = new Date(this.getFullYear(),0,i-3,0,0,0,-1);// one millisecond before
d=Math.ceil(((this.valueOf()-firstWeekMonday.valueOf()-(this.getTimezoneOffset()-firstWeekMonday.getTimezoneOffset())*60000)/86400000)/7); return d?d:new Date(this.getFullYear()-1,11,31).getWeek();
}


Daylight saving time has no effect on the method as described in the post, due to the ceiling of the weeks. If it does I'ld like to know which date and time you are using because all dates between 1970-01-01 and 2100-01-01 have been tested (I think at 00:00:00)
Markus Andersson
Placed on: 12-05-2011 09:37
I´ve tried:

Date.prototype.getWeek = function() {var i,d,firstWeekMonday;
var i=1;while (new Date(this.getFullYear(),0,i).getDay()!=4) i++;// i the first Thursday;
var firstWeekMonday = new Date(this.getFullYear(),0,i-3,0,0,0,-1);// one millisecond before
d=Math.ceil(((this.valueOf()-firstWeekMonday.valueOf()-(this.getTimezoneOffset()-firstWeekMonday.getTimezoneOffset())*60000)/86400000)/7); return d?d:new Date(this.getFullYear()-1,11,31).getWeek();
}

But it won´t print it on display. What do I need to add in order to get the week on the display?
Taco
Placed on: 12-05-2011 13:57
Taco van den Broek
User icon
Quote
Markus Andersson wrote:
I´ve tried:

Date.prototype.getWeek = function() {var i,d,firstWeekMonday;
var i=1;while (new Date(this.getFullYear(),0,i).getDay()!=4) i++;// i the first Thursday;
var firstWeekMonday = new Date(this.getFullYear(),0,i-3,0,0,0,-1);// one millisecond before
d=Math.ceil(((this.valueOf()-firstWeekMonday.valueOf()-(this.getTimezoneOffset()-firstWeekMonday.getTimezoneOffset())*60000)/86400000)/7); return d?d:new Date(this.getFullYear()-1,11,31).getWeek();
}

But it won´t print it on display. What do I need to add in order to get the week on the display?


This code will only extend the Date class. In order to get the week of a certain date you'll have to instantiate a Date object and get the week. Like so:

1
2
var now = new Date();
alert(now.getWeek());
Robert
Placed on: 12-03-2012 10:07
As for Math.ceil() to get rid of Daylight Saving Time differences: aren't you assuming that Daylight Saving Time is not in effect in January? (You are forgetting about the Southern Hemisphere.)

You could use Math.round() instead.
Anna
Placed on: 06-10-2013 18:10
Does any one have a pointer to get a date object out of the week and year. The day of that week should always start on monday!
1 2 Last page

Log in to comment on news articles.

Procurios zoekt PHP webdevelopers. Werk aan het Procurios Webplatform en klantprojecten! Zie http://www.slimmerwerkenbijprocurios.nl/.


Hello!

We are employees at Procurios, a full-service webdevelopment company located in the Netherlands. We are experts at building portals, websites, intranets and extranets, based on an in-house developed framework. You can find out more about Procurios and our products, might you be interested.

This weblog is built and maintained by us. We love to share our ideas, thoughts and interests with you through our weblog. If you want to contact us, please feel free to use the contact form!


Showcase

  • Klantcase: Bestseller
  • Klantcase: de ChristenUnie
  • Klantcase: Evangelische Omroep
  • Klantcase: de Keurslager
  • Klantcase: New York Pizza
  • Klantcase: Verhage

Snelkoppelingen