1

I am creating a calendar with JavaScript and Moment.js and would like to know how to create an array that contains all 42 days (dates) that should be displayed for the selected month.

I am using the moment getDaysInMonth() function to help me get the days in the currently selected month then looping through them pushing all of the dates to an array.

I am struggling filling in the final days of the previous month and the first few days of the next month (that you would typically see greyed out in a calendar).

Here is an example showing Week 1 and Week 6 in the array. The dots show the rest of the dates in between that can be assumed:

[new Date("2020-07-26"), new Date("2020-07-27"), new Date("2020-07-28"), new Date("2020-07-29"), new Date("2020-07-30"), new Date("2020-07-31"), new Date("2020-07-01"),................................................... new Date("2020-08-30"), new Date("2020-08-31"), new Date("2020-09-01"), new Date("2020-09-02"), new Date("2020-09-03"), new Date("2020-09-04"), new Date("2020-09-05")],
hgb123
  • 13,869
  • 3
  • 20
  • 38
Blake Rivell
  • 13,105
  • 31
  • 115
  • 231
  • 1
    The week starts on Sunday, so check the day of the week for the 1st and back up the difference into the previous month. – Jay Buckman Aug 28 '20 at 17:04

2 Answers2

3

Moment has functions for getting the start and end of certain periods, including month and week. You can use those to get the start of the month, and the start of the week the start of the month is in. Then do the same for the ends. Then just iterate over days between them, adding as you go.

function getDaysForCalendarMonth(date) {
  var firstDayOfMonth = moment(date).startOf('month');
  var firstDayOfCal = firstDayOfMonth.clone().startOf('week');
  var lastDayOfMonth = firstDayOfMonth.clone().endOf('month');
  var lastDayOfCal = lastDayOfMonth.clone().endOf('week');
  var temp = firstDayOfCal.clone();
  var days = [temp.toDate()];
  while (temp.isBefore(lastDayOfCal) && days.length < 42) {
    temp.add(1, 'day');
    days.push(temp.toDate());
  }
  while (days.length < 42) {
    temp.add(1, 'day');
    days.push(temp.toDate());
  }
  return days;
}
console.log(getDaysForCalendarMonth(new Date(2020, 2, 1)));
console.log(getDaysForCalendarMonth(new Date(2020, 3, 1)));
console.log(getDaysForCalendarMonth(new Date()));
.as-console-wrapper { max-height: 100% !important; }
<script src="https://momentjs.com/downloads/moment.min.js"></script>
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
  • Thank you, I ended up coming up with my own solution but it wasn't as clean as this one because I didn't utilize startOf week and endOf week. Any chance you can give me an idea of how you would now take this array of 42 dates and break it into an array of 6 week objects so it is easy to loop through and display on the UI in a table? For example weeks: [{week: 1, dates: [], {week: 2, dates: []}, etc....}]. Basically every 7 days I would want to break it into a new week object. – Blake Rivell Aug 28 '20 at 19:19
  • There's a number of ways of doing that. The term usually used is "chunking" the array. I always end up at [Split array into chunks](https://stackoverflow.com/q/8495687/215552). – Heretic Monkey Aug 28 '20 at 19:22
  • Got it I will look into it. Thank you! One final thing: I noticed if I pass in new Date(2020,8,1) to your function it is incorrect, but new Date() works. Any idea why? – Blake Rivell Aug 28 '20 at 19:26
  • 1
    Months in JavaScript are 0 based, so today's month is 7. – Heretic Monkey Aug 28 '20 at 19:27
  • 1
    Uggh, my brain is friend right now. I really appreciate all of the help. Thank you again! I am just trying to challenge myself to build a calendar widget. – Blake Rivell Aug 28 '20 at 19:30
  • No problem. I've built a couple in my career, and I'll give you some free advice: stick with the standard American/Gregorian calendar. There are some really challenging calendars out there! :) – Heretic Monkey Aug 28 '20 at 19:34
  • @HereticMonkey How to do the same thing if there was no moment js? I wana build a custom calendar for my country which has a different calendar, I can only have start date of a month 1(january) and the total days of every month... – Sushilzzz Apr 08 '22 at 10:20
2

You could consider this solution (long but works)

  • get dates for current month: iterate through that month's date
    iteratedDate = startOfMonth.clone()
    while (iteratedDate.month() === month - 1) {
      currentMonth.push(iteratedDate.format("L"))
      iteratedDate.add(1, "day")
    }
    
  • get dates for final of previous month: start loop with the start of current month, push date and stop when iterated date is Sunday
    iteratedDate = startOfMonth.clone()
    while (iteratedDate.day() !== 0) {
      iteratedDate.subtract(1, "day")
      finalsOfPrevMonth.push(iteratedDate.format("L"))
    }
    
  • get dates for start of next month: start loop with end of current month, push incremented date till the total dates reach length of 42
    iteratedDate = endOfMonth.clone()
    while (finalsOfPrevMonth.length + currentMonth.length + startsOfNextMonth.length < 42) {
      iteratedDate.add(1, "day")
      startsOfNextMonth.push(iteratedDate.format("L"))
    }
    

Implementation

const getDates = (month, year) => {
  const startOfMonth = moment()
    .month(month - 1)
    .year(year)
    .startOf("month")
  const endOfMonth = moment()
    .month(month - 1)
    .year(year)
    .endOf("month")

  const finalsOfPrevMonth = []
  const currentMonth = []
  const startsOfNextMonth = []
  let iteratedDate = null

  iteratedDate = startOfMonth.clone()
  while (iteratedDate.day() !== 0) {
    iteratedDate.subtract(1, "day")
    finalsOfPrevMonth.push(iteratedDate.format("L"))
  }

  iteratedDate = startOfMonth.clone()
  while (iteratedDate.month() === month - 1) {
    currentMonth.push(iteratedDate.format("L"))
    iteratedDate.add(1, "day")
  }

  iteratedDate = endOfMonth.clone()
  while (finalsOfPrevMonth.length + currentMonth.length + startsOfNextMonth.length < 42) {
    iteratedDate.add(1, "day")
    startsOfNextMonth.push(iteratedDate.format("L"))
  }

  return [...finalsOfPrevMonth.reverse(), ...currentMonth, ...startsOfNextMonth]
}

console.log("March 2020")
console.log(getDates(3, 2020))
console.log("April 2020")
console.log(getDates(4, 2020))
console.log("May 2020")
console.log(getDates(5, 2020))
<script src="https://momentjs.com/downloads/moment.min.js"></script>

Assertion

March 2020

April 2020

hgb123
  • 13,869
  • 3
  • 20
  • 38