25

I am using Moment.js and it is great. The problem I have now is that I can't figure out how to get the week of the month a certain date is. I can only find "week of year" in the Moment js docs. For example, if I choose today's date (2/12/2014), I would like to know that this date is in the second week of this month of february and consequently, it is the second wednesday of the month. Any ideas?

EDIT: I guess some clarification is necessary. What I need most is the nth number of a certain day in a month. For example, (from the comments) Feb 1, 2014 would be the first Saturday of the month. Feb 3, 2014 would be the first Monday of the month even though it is "technically" the second week of the month. Basically, exactly how google calendar's repeat function classifies days.

train
  • 610
  • 2
  • 11
  • 20
  • 1
    How do you want to define the behaviour? 1 February 2014 was a Saturday. Was 3 February in the first week of the month, or the second? – matthewk Feb 12 '14 at 20:00
  • For 1 Feb 2014, I would consider that the first saturday of the month. For 3 Feb 2014, I would consider that the first monday of the month. Basically, exactly how google calendar classifies it. – train Feb 12 '14 at 20:04

14 Answers14

41

It seems that moment.js does not have the method that implements the functionality that you are looking for. However, you can find the nth number of a certain day of the week in a month is using the Math.ceil of the date / 7 For example:

var firstFeb2014 = moment("2014-02-01"); //saturday
var day = firstFeb2014.day(); //6 = saturday
var nthOfMoth = Math.ceil(firstFeb2014.date() / 7); //1

var eightFeb2014 = moment("2014-02-08"); //saturday, the next one
console.log( Math.ceil(eightFeb2014.date() / 7) ); //prints 2, as expected

It looks like this is the number you are looking for, as demonstrated by the following test

function test(mJsDate){
   var str = mJsDate.toLocaleString().substring(0, 3) +
             " number " + Math.ceil(mJsDate.date() / 7) +
             " of the month";
   return str;
}

for(var i = 1; i <= 31; i++) {
   var dayStr = "2014-01-"+ i;
   console.log(dayStr + " " + test(moment(dayStr)) );
}

//examples from the console:
//2014-01-8 Wed number 2 of the month
//2014-01-13 Mon number 2 of the month
//2014-01-20 Mon number 3 of the month
//2014-01-27 Mon number 4 of the month
//2014-01-29 Wed number 5 of the month
Giovanni Filardo
  • 1,012
  • 8
  • 14
25

When calculating the week of the month based on a given date, you have to take the offset into account. Not all months start on the first day of the week.

If you want to take this offset into account, you can use something something like the following if you are using moment.

function weekOfMonth (input = moment()) {
  const firstDayOfMonth = input.clone().startOf('month');
  const firstDayOfWeek = firstDayOfMonth.clone().startOf('week');

  const offset = firstDayOfMonth.diff(firstDayOfWeek, 'days');

  return Math.ceil((input.date() + offset) / 7);
}
Robin Malfait
  • 330
  • 4
  • 6
  • 2
    Whilst this doesn't solve the OPs problem, it solves mine - I needed to find the week of the month as you would look at a calendar, i.e Monday -> Sunday, where the first 'week' could include days from the previous month, thanks – andy mccullough Nov 08 '17 at 12:45
6

Simple using moment.js

function week_of_month(date) {

        prefixes = [1,2,3,4,5];

    return prefixes[0 | moment(date).date() / 7] 

}
Nishchit
  • 18,284
  • 12
  • 54
  • 81
4

I made some modifications based on feedback.

let weeks = moment().weeks() - moment().startOf('month').weeks() + 1;
weeks = (weeks + 52) % 52;

On days passing through the next year, the week value will be negative so I had to add 52.

dubucha
  • 1,027
  • 10
  • 16
  • 1
    what's difference between `moment().add(0, 'month').startOf('month').weeks()` & `moment().startOf('month').weeks()`, is there any situation when both returns different values – Shivam Singh Sep 30 '19 at 08:47
  • 1
    For 2021-12-31, It return -47 as week number – Murugan.P Jun 11 '21 at 10:07
3

This library adds the function moment.weekMonth() https://github.com/c-trimm/moment-recur

Keith Holliday
  • 1,692
  • 21
  • 15
1

What about something like:

weekOfCurrentMonth = (moment().week() - (moment().month()*4));

This takes the current week of the year, and subtracts it by the 4 times the number of previous months. Which should give you the week of the current month

Mitch Dempsey
  • 38,725
  • 6
  • 68
  • 74
  • 1
    I've thought about something along those lines, but the problem is that not every month has 4 weeks. For example, last month, January 2014, had "technically" 5 weeks. – train Feb 12 '14 at 20:09
  • 1
    @train unless you only count whole weeks in which case it had 3! Unfortunately you have to pick a definition according to which each week will "belong" to a specific month, and there is no single agreed correct way to do this. – Daniel Earwicker Feb 12 '14 at 20:12
1

I think the answer to this question will be helpful, even though it doesn't use moment.js as requested:

Get week of the month

Community
  • 1
  • 1
matthewk
  • 1,841
  • 17
  • 31
1
function countWeekdayOccurrencesInMonth(date) {

    var m = moment(date),
            weekDay = m.day(),
            yearDay = m.dayOfYear(),
            count = 0;

    m.startOf('month');
    while (m.dayOfYear() <= yearDay) { 
        if (m.day() == weekDay) {
            count++; 
        }
        m.add('days', 1); 
    }

    return count;
}
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
1

There is a problem with @Daniel Earwicker answer. I was using his function in my application and the while loop was infinite because of the following situation:

I was trying to figure out which week of december (2016) was the day 31. the first day of december was day 336 of the year. The last day of december was day 366 of the year.

Problem here: When it was day 366 (31 of december, last day of the year) the code added another day to this date. But with another day added it would be day 1 of january of 2017. Therefore the loop never ended.

 while (m.dayOfYear() <= yearDay) { 

    if (m.day() == weekDay) {
        count++; 
    }
    m.add('days', 1); 
}

I added the following lines to the code so the problem would be fixed:

function countWeekdayOccurrencesInMonth(date) {

  var m = moment(date),
        weekDay = m.day(),
        yearDay = m.dayOfYear(),
        year = m.year(),
        count = 0;

 m.startOf('month');

 while (m.dayOfYear() <= yearDay && m.year() == year) {
    if (m.day() == weekDay) {
        count++;
    }
    m.add('days', 1);
 }

 return count;
}

It verifies if it is still in the same year of the date being veryfied

1

Here's Robin Malfait's solution implemented with the lightweight library date-fns

import {
    differenceInDays,
    startOfMonth,
    startOfWeek,
    getDate
} from 'date-fns'

const weekOfMonth = function (date) {
    const firstDayOfMonth = startOfMonth(date)
    const firstDayOfWeek = startOfWeek(firstDayOfMonth)
    const offset = differenceInDays(firstDayOfMonth, firstDayOfWeek)
    return Math.ceil((getDate(date) + offset) / 7)
}

export default weekOfMonth
Klemen Tusar
  • 9,261
  • 4
  • 31
  • 28
1

I'd do the following:

let todaysDate = moment(moment.now());
let endOfLastMonth = moment(get(this, 'todaysDate')).startOf('month').subtract(1, 'week');

let weekOfMonth = todaysDate.diff(endOfLastMonth, 'weeks');

That gets todaysDate and the endOfLastMonth and then uses Moment's built-in diff() method to compute the current month's week number.

Eric Hubbell
  • 49
  • 1
  • 6
0

It's not built-in, but basically you can subtract the week number of the start of the month from the week number of the date in question.

function weekOfMonth(m) {
  return m.week() - moment(m).startOf('month').week() + 1;
}

Credit goes to code by original author, give him a star if it helped you.

Azz
  • 99
  • 7
0

How about this?

const moment = require("moment");

// Generate Week Number of The Month From Moment Date
function getWeekOfMonth(input = moment()) {
    let dayOfInput = input.clone().day(); // Saunday is 0 and Saturday is 6
    let diffToNextWeek = 7 - dayOfInput;
    let nextWeekStartDate = input.date() + diffToNextWeek;
    return Math.ceil((nextWeekStartDate) / 7);
}
0

Simple code, but has been working for me.

const weekOfTheMonth = (myMomentDate) => {
    const startDay = moment(myMomentDate).startOf('week');
    const day = parseInt(startDay.format('DD'),10);
    if(day > 28){
      return 5;
    }

    if((day > 21) && (day <= 28) ){
      return 4;
    }

    if((day > 14) && (day <= 21) ){
      return 3;
    }

    if((day > 7) && (day <= 14) ){
      return 2;
    }

    return 1;
  }
viniciusalvess
  • 756
  • 8
  • 18