Get week of the month

This is an old question, here is my cross-browser solution based on:

  1. Weeks start on Sunday
  2. The first week of a month is the one that contains the first of the month

So in March 2013:

  • Fri 1 Mar is the first day of week 1
  • Sun 3 Mar is the start of week 2
  • Sun 31 Mar is the start of week 6 (and is the only day in the that week)
  • Mon 1 Apr is the first day of week 1 in April.
    Date.prototype.getWeekOfMonth = function(exact) {
        var month = this.getMonth()
            , year = this.getFullYear()
            , firstWeekday = new Date(year, month, 1).getDay()
            , lastDateOfMonth = new Date(year, month + 1, 0).getDate()
            , offsetDate = this.getDate() + firstWeekday - 1
            , index = 1 // start index at 0 or 1, your choice
            , weeksInMonth = index + Math.ceil((lastDateOfMonth + firstWeekday - 7) / 7)
            , week = index + Math.floor(offsetDate / 7)
        ;
        if (exact || week < 2 + index) return week;
        return week === weeksInMonth ? index + 5 : week;
    };
    
    // Simple helper to parse YYYY-MM-DD as local
    function parseISOAsLocal(s){
      var b = s.split(/\D/);
      return new Date(b[0],b[1]-1,b[2]);
    }

    // Tests
    console.log('Date          Exact|expected   not exact|expected');
    [   ['2013-02-01', 1, 1],['2013-02-05', 2, 2],['2013-02-14', 3, 3],
        ['2013-02-23', 4, 4],['2013-02-24', 5, 6],['2013-02-28', 5, 6],
        ['2013-03-01', 1, 1],['2013-03-02', 1, 1],['2013-03-03', 2, 2],
        ['2013-03-15', 3, 3],['2013-03-17', 4, 4],['2013-03-23', 4, 4],
        ['2013-03-24', 5, 5],['2013-03-30', 5, 5],['2013-03-31', 6, 6],
        ['2013-04-01', 1, 1]
    ].forEach(function(test){
      var d = parseISOAsLocal(test[0])
      console.log(test[0] + '        ' + 
      d.getWeekOfMonth(true) + '|' + test[1] + '                  ' +
      d.getWeekOfMonth() + '|' + test[2]); 
    });

You don’t need to put it directly on the prototype if you don’t want to. In my implementation, 6 means “Last”, not “Sixth”. If you want it to always return the actual week of the month, just pass true.

EDIT: Fixed this to handle 5 & 6-week months. My “unit tests”, feel free to fork: http://jsfiddle.net/OlsonDev/5mXF6/1/.

Leave a Comment