1
\$\begingroup\$

I am working on a calendar widget for a project. For any given date, I need to build a 2D array representing the "Month" that the date falls in, where the rows represent weeks in the month and the cells in those rows are individual days. The weeks must start on Monday, and if the month does not start on a Monday, it must "backfill" in the days from the previous month. Similarly, if the last week in the month isn't "full" because it didn't end on a Sunday, fill the last week with the beginning of the following month.

For example, let's use today, May 26th of 2016. Given an input of new Date(2016, 04, 26), I would want an output of an array with 6 elements (the weeks), each element itself an array containing 7 date objects (the days). Because May started on a Sunday, the first date in the first week would actually be April 25th (the previous Monday). Because May ends on a Tuesday, the last date in the last week would be June 5th (the following Sunday).

function getCalendarMatrix(date){
    var calendarMatrix = []

    var startDay = new Date(date.getFullYear(), date.getMonth(), 1)
    var lastDay = new Date(date.getFullYear(), date.getMonth()+1, 0)

    // Modify the result of getDay so that we treat Monday = 0 instead of Sunday = 0
    var startDow = (startDay.getDay() + 6) % 7;
    var endDow = (lastDay.getDay() + 6) % 7;

    // If the month didn't start on a Monday, start from the last Monday of the previous month
    startDay.setDate(startDay.getDate() - startDow);

    // If the month didn't end on a Sunday, end on the following Sunday in the next month
    lastDay.setDate(lastDay.getDate() + (6-endDow));

    var week = []
    while(startDay <= lastDay){
        week.push(new Date(startDay));
        if (week.length === 7){
          calendarMatrix.push(week);
          week = []
        }
        startDay.setDate(startDay.getDate() + 1)
    }

    return calendarMatrix;
}

My code seems like it's working well, but I'm seeking input on any flaws this might have or how I could go about this in a "better" way.

\$\endgroup\$
1
\$\begingroup\$

I have only minor comments.


This logic appears twice, do is a good candidate to extract to a helper function to eliminate duplicate code:

(startDay.getDay() + 6) % 7;

Instead of this:

// If the month didn't start on a Monday, start from the last Monday of the previous month
startDay.setDate(startDay.getDate() - startDow);

It would be slightly more readable to move this code into a helper function with a descriptive name, for example:

function getStartingMonday(startDay, startDow) {
    // If the month didn't start on a Monday, start from the last Monday of the previous month
    return new Date(startDay.getDate() - startDow));
}

This way, the code in the caller will be shorter, and the implementation detail is not really important, it's enough to know that the returned date is the sizing day and it's a Monday.

The same goes for getting the last Sunday.


You're using semicolons inconsistently, sometimes omitting it. It would be better to use a consistent style, for example put it always.


The === is redundant here:

    if (week.length === 7){

You create week yourself, inside the function, so you know it's an array, and therefore must have an integer .length property, so a simple == is enough.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.