skip to Main Content

I have written a JavaScript function which generates some year/month entries going 24 months back in time for a js chart. I need every month even if there is no data.

function getLast24Months() {
    let result = [];
    let now = new Date();
    for (let i = 0; i < 24; i++) {
        let yyyy = now.getFullYear();
        let mm = now.getMonth() + 1;
        mm = mm < 10 ? `0${mm}` : `${mm}`;
        result.unshift(`${yyyy}-${mm}`);
        now.setMonth(now.getMonth() - 1);
    }
    return result;
}

Usually this works great but today I noticed there were two months in 2023-03.

Why It Fails:

Lack of Reset: There’s no explicit reset to the first day of the current month before the loop starts. While new Date() initializes to the current date and time, it doesn’t guarantee that the month’s first day is targeted, especially when manipulating months.

Misinterpretation of setMonth: The setMonth method changes the month of a Date object, but it doesn’t automatically adjust the day of the month to the first day of the new month. This means that when you subtract a month from a date that is not the first day of the month, the resulting date will be incorrect.

2

Answers


  1. Chosen as BEST ANSWER

    Setting date to 1st of the month does the trick:

    function getLast24Months() {
    const result = [];
    const now = new Date();
    now.setDate(1);
    for (let i = 0; i < 24; i++) {
    let yyyy = now.getFullYear();
    let mm = now.getMonth() + 1;
    mm = mm < 10 ? `0${mm}` : `${mm}`;
    result.unshift(`${yyyy}-${mm}`);
    now.setMonth(now.getMonth() - 1);
    }
    return result;
    }
    

  2. This is happening because not all months are equal. If you think about what you are trying to do in plain language, consider the following:
    If the date is the 31st May 2024, what is the date one month ago? It can’t be the 31st April, because April only has 30 days. You have to define how you wish this to be handled. Do you want the last day of April? Do you want the date it would be if April did have 31 days (i.e. the 1st day of May)?

    The only way to handle this is for your code to know about the length of each of the months, and handle the case where you go "off the end of a month" explicitly for each.

    If you want the last day of the month in these situations, the best way to do this might be to determine whether the month you are currently looking at has more days than the previous one, and if so, go to the first day of the month and subtract one day, rather than subtracting a whole month from the date. This would also get around the "leap year" problem and side-step having to work out whether a particular February has 29 or 28 days in it, by relying on the system’s internal date representation to subtract one day correctly from March 1st.

    Note that your code as above, if rewritten to handle this, would still "count down" from the end of the month, i.e. it would go May 31, April 30, Mar 30 (not Mar 30). You need to consider how you want it to actually behave.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search