1

I am trying to calculate number of weeks in a month using moment js. But I am getting wrong results for some months like May 2015 and August 2015.

I am using this code.

var start = moment().startOf('month').format('DD');
var end = moment().endOf('month').format('DD');
var weeks = (end-start+1)/7;
weeks = Math.ceil(weeks);

Is there any prebuilt method in moment JS for getting number of weeks.

h_a86
  • 281
  • 1
  • 8
  • 16
  • So are `start` and `end` ints or strings? – DWB Feb 07 '15 at 17:04
  • Possible repo of [this](http://stackoverflow.com/questions/21737974/moment-js-how-to-get-week-of-month-google-calendar-style) and [this](http://stackoverflow.com/questions/11448340/how-to-get-duration-in-weeks-with-moment-js)?? – Ethaan Feb 07 '15 at 17:04
  • 1
    What results are you getting and what were you expecting? – Xotic750 Feb 07 '15 at 17:45

15 Answers15

3

I have created this gist that finds all the weeks in a given month and year. By calculated the length of calendar, you will know the number of weeks.

https://gist.github.com/guillaumepiot/095b5e02b4ca22680a50

# year and month are variables
year = 2015
month = 7 # August (0 indexed)
startDate = moment([year, month])

# Get the first and last day of the month
firstDay = moment(startDate).startOf('month')
endDay = moment(startDate).endOf('month')

# Create a range for the month we can iterate through
monthRange = moment.range(firstDay, endDay)

# Get all the weeks during the current month
weeks = []
monthRange.by('days', (moment)->
    if moment.week() not in weeks
        weeks.push(moment.week())
)

# Create a range for each week
calendar = []
for week in weeks
    # Create a range for that week between 1st and 7th day
    firstWeekDay = moment().week(week).day(1)
    lastWeekDay = moment().week(week).day(7)
    weekRange = moment.range(firstWeekDay, lastWeekDay)

    # Add to the calendar
    calendar.push(weekRange)

console.log calendar
guillaumepiot
  • 381
  • 2
  • 7
2

Can be easily done using raw javascript:

function getNumWeeksForMonth(year,month){
              date = new Date(year,month-1,1);
              day = date.getDay();
              numDaysInMonth = new Date(year, month, 0).getDate();
              return Math.ceil((numDaysInMonth + day) / 7);
}

You get the day index of the first day, add it to the number of days to compensate for the number of days lost in the first week, divide by 7 and use ceil to add 1 for the simplest overflow in the next week

Noor
  • 19,638
  • 38
  • 136
  • 254
2

EDIT:

NEW and hopefully very correct implementation:

function calcWeeksInMonth(date: Moment) {
  const dateFirst = moment(date).date(1);
  const dateLast = moment(date).date(date.daysInMonth());
  const startWeek = dateFirst.isoWeek();
  const endWeek = dateLast.isoWeek();

  if (endWeek < startWeek) {
    // Yearly overlaps, month is either DEC or JAN
    if (dateFirst.month() === 0) {
      // January
      return endWeek + 1;
    } else {
      // December
      if (dateLast.isoWeekday() === 7) {
        // Sunday is last day of year
        return endWeek - startWeek + 1;
      } else {
        // Sunday is NOT last day of year
        return dateFirst.isoWeeksInYear() - startWeek + 1;
      }
    }
  } else {
    return endWeek - startWeek + 1;
  }
}

Outputs the following values for the following dates:

calcWeeksInMonth(moment("2016-12-01")); // 5

calcWeeksInMonth(moment("2017-01-01")); // 6
calcWeeksInMonth(moment("2017-02-01")); // 5
calcWeeksInMonth(moment("2017-03-01")); // 5
calcWeeksInMonth(moment("2017-04-01")); // 5
calcWeeksInMonth(moment("2017-05-01")); // 5
calcWeeksInMonth(moment("2017-06-01")); // 5
calcWeeksInMonth(moment("2017-07-01")); // 6
calcWeeksInMonth(moment("2017-08-01")); // 5
calcWeeksInMonth(moment("2017-09-01")); // 5
calcWeeksInMonth(moment("2017-10-01")); // 6
calcWeeksInMonth(moment("2017-11-01")); // 5
calcWeeksInMonth(moment("2017-12-01")); // 5

calcWeeksInMonth(moment("2018-01-01")); // 5

OLD and very incorrect implementation:

calcWeeksInMonth(date) {
    const dateFirst = moment(date).date(1)
    const dateLast = moment(date).date(date.daysInMonth())
    const startWeek = dateFirst.week()
    const endWeek = dateLast.week()
    if (endWeek < startWeek) {
        return dateFirst.weeksInYear() - startWeek + 1 + endWeek
    } else {
        return endWeek - startWeek + 1
    }
}

This seems to output correct results, feedback welcome if there is something I missed!

jonruna
  • 111
  • 3
  • Same bug: Dec 2017 outputs 6 weeks instead of 5 – torvin Mar 12 '20 at 02:47
  • @torvin I updated my answer with a corrected implementation. Would be awesome if you had the time to validate it as well at some point! In any case, thanks for the feedback on the old implementation! – jonruna Mar 14 '20 at 06:35
  • @jonruna Here is a take on the "old and incorrect implementation" (hopefully correct, though!): https://stackoverflow.com/a/60693500/352375 – Erik Mar 15 '20 at 17:49
2

It display the list of weeks in a month with 'moment.js'.
It has been written in typescript with angular 6+.

Install moment with 'npm i moment'

Inside the ts file.

weeks_in_month() {
    let year = 2019;  // change year
    let month = 4; // change month here
    let startDate = moment([year, month - 1])
    let endDate = moment(startDate).endOf('month');

    var dates = [];
    var weeks = [];

    var per_week = [];
    var difference = endDate.diff(startDate, 'days');

    per_week.push(startDate.toDate())
    let index = 0;
    let last_week = false;
    while (startDate.add(1, 'days').diff(endDate) < 0) {
      if (startDate.day() != 0) {
        per_week.push(startDate.toDate())
      }
      else {
        if ((startDate.clone().add(7, 'days').month() == (month - 1))) {
          weeks.push(per_week)
          per_week = []
          per_week.push(startDate.toDate())
        }
        else if (Math.abs(index - difference) > 0) {
          if (!last_week) {
            weeks.push(per_week);
            per_week = [];
          }
          last_week = true;
          per_week.push(startDate.toDate());
        }
      }
      index += 1;
      if ((last_week == true && Math.abs(index - difference) == 0) ||
        (Math.abs(index - difference) == 0 && per_week.length == 1)) {
        weeks.push(per_week)
      }
      dates.push(startDate.clone().toDate());
    }
    console.log(weeks);
}

Result:

Array of date moments.

[Array(6), Array(7), Array(7), Array(7), Array(3)]

0: (6) [Mon Apr 01 2019 00:00:00 GMT+0530 (India Standard Time),  
Tue Apr 02 2019 00:00:00 GMT+0530 (India Standard Time),  
Wed Apr 03 2019 00:00:00 GMT+0530 (India Standard Time),  
Thu Apr 04 2019 00:00:00 GMT+0530 (India Standard Time),  
Fri Apr 05 2019 00:00:00 GMT+0530 (India Standard Time),  
Sat Apr 06 2019 00:00:00 GMT+0530 (India Standard Time)]

1: (7) [Sun Apr 07 2019 00:00:00 GMT+0530 (India Standard Time),  
Mon Apr 08 2019 00:00:00 GMT+0530 (India Standard Time),  
Tue Apr 09 2019 00:00:00 GMT+0530 (India Standard Time),  
Wed Apr 10 2019 00:00:00 GMT+0530 (India Standard Time),  
Thu Apr 11 2019 00:00:00 GMT+0530 (India Standard Time),  
Fri Apr 12 2019 00:00:00 GMT+0530 (India Standard Time),  
Sat Apr 13 2019 00:00:00 GMT+0530 (India Standard Time)]

2: (7) [Sun Apr 14 2019 00:00:00 GMT+0530 (India Standard Time),  
Mon Apr 15 2019 00:00:00 GMT+0530 (India Standard Time),  
Tue Apr 16 2019 00:00:00 GMT+0530 (India Standard Time),  
Wed Apr 17 2019 00:00:00 GMT+0530 (India Standard Time),  
Thu Apr 18 2019 00:00:00 GMT+0530 (India Standard Time),  
Fri Apr 19 2019 00:00:00 GMT+0530 (India Standard Time),  
Sat Apr 20 2019 00:00:00 GMT+0530 (India Standard Time)]

3: (7) [Sun Apr 21 2019 00:00:00 GMT+0530 (India Standard Time),  
Mon Apr 22 2019 00:00:00 GMT+0530 (India Standard Time),  
Tue Apr 23 2019 00:00:00 GMT+0530 (India Standard Time),  
Wed Apr 24 2019 00:00:00 GMT+0530 (India Standard Time),  
Thu Apr 25 2019 00:00:00 GMT+0530 (India Standard Time),  
Fri Apr 26 2019 00:00:00 GMT+0530 (India Standard Time),  
Sat Apr 27 2019 00:00:00 GMT+0530 (India Standard Time)]

4: (3) [Sun Apr 28 2019 00:00:00 GMT+0530 (India Standard Time),  
Mon Apr 29 2019 00:00:00 GMT+0530 (India Standard Time),  
Tue Apr 30 2019 00:00:00 GMT+0530 (India Standard Time)]
Kanish Mathew
  • 825
  • 10
  • 6
1

function getWeekNums(momentObj) {
    var clonedMoment = moment(momentObj), first, last;

    // get week number for first day of month
    first = clonedMoment.startOf('month').week();
    // get week number for last day of month
    last = clonedMoment.endOf('month').week();

    // In case last week is in next year
    if( first > last) {
        last = first + last;
    }
    return last - first + 1;
}
JVitela
  • 2,472
  • 2
  • 22
  • 20
  • 4
    Old thread - I write this to go on record. If you run this with December 2017, the method returns 2. The reason this happens is because the first week (48) is greater than the last week (1), which is the first week of the next month. You try accounting for that with the "first > last" block, however, this ends up making the last week 49. Math should be: if( first > last ){ last = 52+last } – eugene Mar 17 '18 at 01:12
  • @eugene but if you do that you have to also edit the return : not add 1. am I correct? – tatsu May 03 '18 at 09:46
1

javaScript version here

var year = 2021
        var  month = 6 
        var  startDate = moment([year, month])

        //Get the first and last day of the month
       var firstDay = moment(startDate).startOf('month')
       var  endDay = moment(startDate).endOf('month')
     
        //Create a range for the month we can iterate through
       var  monthRange = moment.range(firstDay, endDay)
       
        //Get all the weeks during the current month
       var  weeks = []
        var indexOf = [].indexOf;

        monthRange.by('days', function (moment) {
            var ref;
            if (ref = moment.week(), indexOf.call(weeks, ref) < 0) {
                return weeks.push(moment.week());
            }
        });
      

        var calendar, firstWeekDay, i, lastWeekDay, len, week, weekRange;

        calendar = [];

        for (i = 0, len = weeks.length; i < len; i++) {
            week = weeks[i];
            // Create a range for that week between 1st and 7th day
            firstWeekDay = moment().week(week).day(0);
            lastWeekDay = moment().week(week).day(6);
            weekRange = moment.range(firstWeekDay, lastWeekDay);
         
            // Add to the calendar
            calendar.push(weekRange);
        }
Gopi Linga
  • 11
  • 1
  • above Scripts are must – Gopi Linga Jun 04 '21 at 05:39
  • Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation](https://meta.stackexchange.com/q/114762/349538) would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you’ve made. – jasie Jun 04 '21 at 06:43
0

This is the best way out , works well

moment.relativeTime.dd = function (number) {
    // round to the closest number of weeks
    var weeks = Math.round(number / 7);
    if (number < 7) {
        // if less than a week, use days
        return number + " days";
    } else {
        // pluralize weeks
        return weeks + " week" + (weeks === 1 ? "" : "s"); 
    }
}

Source:How to get duration in weeks with Moment.js?

Community
  • 1
  • 1
m0bi5
  • 8,900
  • 7
  • 33
  • 44
0

I have not seen a solution that works in all circumstances. I tried all of these but they all are flawed in one way or another. Ditto with several moment.js github threads. This was my crack at it:

getNumberOfWeeksInMonth = (momentDate) => {
  const monthStartWeekNumber = momentDate.startOf('month').week();

  const distinctWeeks = {
    [monthStartWeekNumber]: true
  };

  let startOfMonth = momentDate.clone().startOf('month');
  let endOfMonth = momentDate.clone().endOf('month');

  //  this is an 'inclusive' range -> iterates through all days of a month
  for (let day = startOfMonth.clone(); !day.isAfter(endOfMonth); day.add(1, 'days')) {
    distinctWeeks[day.week()] = true
  }

  return Object.keys(distinctWeeks).length;
}
Bigtrizzy
  • 103
  • 2
  • 7
0
  function weeksInMonth(date = null){
    let firstDay = moment(date).startOf('month');
    let endDay = moment(date).endOf('month');

    let weeks = [];
    for (let i = firstDay.week(); i <= endDay.week(); i++){
      weeks.push(i)
    }

    return weeks;
  }
0

Here is a simple way of doing it (based on a solution posted above):

const calcWeeksInMonth = (momentDate) => {
  const dateFirst = moment(momentDate).date(1)
  const dateLast = moment(momentDate).date(momentDate.daysInMonth())
  const startWeek = dateFirst.isoWeek()
  const endWeek = dateLast.isoWeek()

  if (endWeek < startWeek) {
    // cater to end of year (dec/jan)
    return dateFirst.weeksInYear() - startWeek + 1 + endWeek
  } else {
    return endWeek - startWeek + 1
  }
}

As far as I can tell, it works correctly for any date thrown at it, but feedback is always welcome!

Erik
  • 188
  • 1
  • 7
  • November 2020 returns 6 weeks when it should be 5. – zomars Oct 28 '20 at 22:00
  • 1
    @zomars [ISO weeks](https://en.wikipedia.org/wiki/ISO_week_date) start with Monday, so November 2020 does have 6 weeks. Nov 1st is a Sunday (last day of the week), and Nov 30th falls on a Monday (first day of the week). If you prefer to work with localized weeks of the year, simply substitute `week` for `isoWeek` in the code above. This should give you 5 weeks assuming you are in a locale where Sunday is the first day of the week. – Erik Nov 16 '20 at 15:02
0

Throwing this into the mix

import moment from "moment";
export const calcWeeksInMonth = date => {
  let weekMonthEnds = moment(date)
    .date(moment(date).daysInMonth())
    .week();
  let weekMonthStarts = moment(date)
    .date(1)
    .week();

  return weekMonthEnds < weekMonthStarts
    ? moment(date).isoWeeksInYear() - weekMonthStarts + 1
    : weekMonthEnds - weekMonthStarts + 1;
};
0
var month = moment().month();
var startOfMonth = month.startOf("month");
var endOfMonth = month.endOf("month");
var startWeekNumber = startOfMonth.isoWeek();
var endWeekNumber = endOfMonth.isoWeek();
var numberOfWeeks = (endWeekNumber - startWeekNumber + 1);
console.log(numberOfWeeks);
Ruby Nanthagopal
  • 596
  • 1
  • 5
  • 17
0

If you have selectedDate value that is give you opportunity to detect which month is active now:

private calculateNumberOfWeeks(): number {
    const end = moment(this.selectedDate).endOf('month');

    const startDay = moment(this.selectedDate)
      .startOf('month')
      .day();

    const endDay = end.day();
    const endDate = end.date();

    return (startDay - 1 + endDate + (endDay === 0 ? 0 : 7 - endDay)) / 7;
  }
0

/UPDATE/
Solution below did not take in consideration jump to the new year. Here is the improved solution.

const getNumberOfWeeksInAMonth = (currentMoment: moment.Moment) => {
    const currentMomentCopy = cloneDeep(currentMoment)
    const startOfMonth = currentMomentCopy.startOf('month')
    const startOfISOWeek = startOfMonth.startOf('isoWeek')

    let numberOfWeeks = 0;
    
    do {
        numberOfWeeks++
        MomentManager.addWeek(startOfISOWeek)
    } while (currentMoment.month() === startOfISOWeek.month())

    return numberOfWeeks;
}

I have found another solution with momentjs.

const getNumberOfWeeksInMonth = (moment: moment.Moment) => {
  const startWeek = moment.startOf('month').isoWeek()
  const endWeek = moment.endOf('month').isoWeek()
  return endWeek - startWeek + 1
}
0

Simply way using es6 to get an array of week numbers for a given year and month.

/**
 * Returns an array of week numbers for a given year and month, where each week
 * contains at least one day of the given month.
 *
 * @param {number} year - The year of the month (e.g., 2023)
 * @param {number} month - The month as a number (e.g., 0 for January, 11 for December)
 * @returns {number[]} An array of week numbers (e.g., [1, 2, 3, 4])
 */
const getWeeksOfMonth = (year, month) => {
  const isWeekInMonth = (month, week) => week.startOf('week').month() === month || week.endOf('week') === month
  return Array
    .from({ length: moment().year(year).weeksInYear() }, (_, i) => i + 1)
    .filter((weekNumber) => isWeekInMonth(month, moment().year(year).isoWeek(weekNumber)))
}