2
\$\begingroup\$

I am using the below function to set month information on context passed. It is working fine, but please review it and let me know if there are any potential flaws or any improvements in this code.

/* Expects month to be in 1-12 index based. */
var monthInformation = function (year, month) {
    /* Create a date. Usually month in JS is 0-11 index based but here is a hack that can be used to calculate total days in a month */
    var date = new Date(year, month, 0);
    /* Get the total number of days in a month */
    this.totalDays = date.getDate();
    /* End day of month. Like Saturday is end of month etc. 0 means Sunday and 6 means Saturday */
    this.endDay = date.getDay();
    date.setDate(1);
    /* Start day of month. Like Saturday is start of month etc. 0 means Sunday and 6 means Saturday */
    this.startDay = date.getDay();
    /* Here we generate days for 42 cells of a Month */
    this.days = [];
    /* Here we calculate previous month dates for placeholders if starting day is not Sunday */
    var prevMonthDays = 0;
    if (this.startDay !== 0) {
        prevMonthDays = new Date(year, month - 1, 0).getDate() - this.startDay;
    }
    /* This is placeholder for next month. If month does not end on Saturday, placeholders for next days to fill other cells */
    var count = 0;
    // 42 = 7 columns * 6 rows. This is the standard number. Verify it with any standard Calendar
    for (var i = 0; i < 42; i++) {
        var day = {};
        /* So start day is not Sunday, so we can display previous month dates. For that below we identify previous month dates */
        if (i < this.startDay) {
            day.date = (prevMonthDays = prevMonthDays + 1);
            /* belong to next month dates. So, month does not end on Saturday. So here we get next month dates as placeholders */
        } else if (i > this.totalDays + (this.startDay - 1)) {
            day.date = (count = count + 1);
            /* belong to current month dates. */
        } else {
            day.date = (i - this.startDay) + 1;
        }
        this.days[this.days.length] = day.date;
    }
};


/* Usage below */
monthInformation.call(context, 4, 2015);
\$\endgroup\$
5
  • \$\begingroup\$ Will this work as expected for month=1: Date(year, month - 1, 0)? \$\endgroup\$ – cha Apr 16 '15 at 21:59
  • \$\begingroup\$ @cha Yes it works, I assume that you are referring January. I verified it. It is working. \$\endgroup\$ – Prathap Reddy Apr 16 '15 at 22:00
  • \$\begingroup\$ Welcome to Code Review! I hope you get some good answers! \$\endgroup\$ – Phrancis Apr 16 '15 at 22:09
  • \$\begingroup\$ because it is Javascript you need to test it in every browser you have access to. This statement Date(year, month, 0); is questionable. I think it is an undocumented feature and is implementation-specific \$\endgroup\$ – cha Apr 16 '15 at 22:16
  • \$\begingroup\$ @cha It works. It is kind of hack being used from many years by many developers. AFAIK it was one of the best answers given on SO \$\endgroup\$ – Prathap Reddy Apr 16 '15 at 22:28
2
\$\begingroup\$

This could use some breathing space.

this.endDay = date.getDay();
date.setDate(1);
this.startDay = date.getDay();
this.days = [];

Compare it to this.

this.endDay = date.getDay();

date.setDate(1);

this.startDay = date.getDay();
this.days = [];

See how the extra vertical space calls out the change in state?


Magic Number Alert

This needs an extra variable, a comment, or maybe even both.

for(var i = 0; i < 42; i++) {

I've read over this loop at least 20 times now. I still have no idea why the number 42 is important here. Why is 42 the upper bound? The logic isn't obvious. You should find a way to make it obvious.

\$\endgroup\$
7
  • \$\begingroup\$ I completely agree with your first comment. And regarding 42, it is standard number for any calendar because sometimes, a month starts from Saturday, in such cases it will need an extra row to accommodate all the dates in calendar. Sometimes it may start from Sunday itself and does not need an extra. So to avoid all these consistencies I am using 42. It will satisfy all those cases so that always total number of rows remains same. if still I am not clear, please check OS calendar to see why I have used 42 (7 * 6). Please suggest me another approach if I am going in wrong direction \$\endgroup\$ – Prathap Reddy Apr 16 '15 at 22:19
  • \$\begingroup\$ I think you're on the right path, I'm just suggesting you find a way to make the code express that paragraph of text right there ^. \$\endgroup\$ – RubberDuck Apr 16 '15 at 22:20
  • \$\begingroup\$ here is a gist with full explanation gist.github.com/svapreddy/23e17073a90296aead22 \$\endgroup\$ – Prathap Reddy Apr 16 '15 at 22:27
  • \$\begingroup\$ May be I have to put an if condition for calculating prevMonthdays. If starting day is 0, I dont have to calculate that. \$\endgroup\$ – Prathap Reddy Apr 16 '15 at 22:29
  • \$\begingroup\$ Well there ya go. Why is that not your code? Dp you see what I'm getting at? \$\endgroup\$ – RubberDuck Apr 17 '15 at 1:05
0
\$\begingroup\$

This is old but I needed to determine the first and last days displayed on a calendar. I've tweaked your code a bit for my purposes, maybe it will be useful to someone.

const MonthInformation = function(year, oneBasedMonth){
    const month = oneBasedMonth - 1 /* month given to Date() starts at 0 = January */
    this.startDate = new Date(year, month, 1)
    this.endDate = new Date(year, month+1, 0) /* 0 `day` gets last day from prior month */

    /* result of getDay(): 0 means Sunday and 6 means Saturday */
    this.startDay = this.startDate.getDay()
    /* last day number = total days in current month */
    this.currentMonthTotalDays = this.endDate.getDate()
    this.totalWeeks = Math.ceil((this.currentMonthTotalDays + this.startDay) / 7)
    
    const prevMonthEndDate = new Date(year, month, 0)
    let prevMonthDay = prevMonthEndDate.getDate() - this.startDay + 1
    let nextMonthDay = 1
    this.dates = []
    
    for (let i = 0; i < (this.totalWeeks * 7); i += 1) {
        let date
        /* Previous month dates (if month does not start on Sunday) */
        if (i < this.startDay) {
            date = new Date(year, month-1, prevMonthDay)
            prevMonthDay = prevMonthDay + 1
        /* Next month dates (if month does not end on Saturday) */
        } else if (i > this.currentMonthTotalDays + (this.startDay - 1)) { 
            date = new Date(year, month+1, nextMonthDay)
            nextMonthDay = nextMonthDay + 1
        /* Current month dates. */    
        } else {
            date = new Date(year, month, (i - this.startDay) + 1)
        }
        this.dates.push(date)
    }
    return this
};

const myMonth = new MonthInformation(2018, 1)
console.log(myMonth.dates)

My version improves the original in my opinion because it builds a complete array of much more useful Javascript Date objects for a typical displayed calendar month with leading and trailing days, instead of just integers of day numbers (e.g. 1-31).

Also the original program retained extra, useless "placeholder" days, so that the array length was always 42 and output an extra week of day numbers from the next month for no reason, such as [...30, 31, 1, 2, 3, 4, 5, 6, 7, 8, 9]. Now, the array length is most often 35 but varies with the particular month. The number of weeks to be displayed is calculated first to determine the number of cycles in the for loop.

I renamed count to nextMonthDay because that's the value it is actually storing. I discarded the unused this.endDay.

I made the function into a constructor (to be used with the new keyword) so that you can assign a variable to the function multiple times and inspect the various values contained by the resulting object(s).

I think the comments are better now and it's easier to see what is going on.

Maybe personal preference, but I also made the function accept a month where 1 = January and 12 = December. I think it's more user-friendly.

Please add further improvements and help me learn something!

\$\endgroup\$
1

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.