How to get a specify date in every month in Javascript?

I need to find this month, previous month and the next month of a specific date.

For example, date was set to 31 of every month, what I expect to get the date is 2018-02-28, 2018-03-31 and 2018-04-30. For those dates which has no 31, than it becomes the day before.

And finally generate 2 period, 2018-02-28 to 2018-03-29, 2018-03-30 to 2018-04-31. I don't know how to handle feb and the month which less than 31.

var d = new Date();
var tyear = d.getFullYear(); //2018
var tmonth = d.getMonth();  //2  
new Date(2018, tmonth-1, 31);//output 2018-03-02 not what I wanted

6 answers

  • answered 2018-03-13 20:27 Scott Marcus

    I think you're going to need an array with 12 numbers in it. Each number is the amount of days in each month and the numbers in the array go in order (first number is 31 because January has 31 days, second is 28 or 29 for Feb), etc. Then you'll get the month number from your input date and look in the array at the number corresponding to the month number +/- 1.

    You'll then need to construct a date for the previous month and the next month based on the number of days in the current month.

    See comments inline:

    let daysInMonths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    
    document.getElementById("date").addEventListener("input", function(){
      console.clear();
      
      // Create new Date based on value in date picker
      var selectedDate = new Date(this.value + 'T00:00');
    
      var year = selectedDate.getYear();
    
      // Determine if it is a leap year (Feb has 29 days) and update array if so.
      if (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) {
        daysInMonths[1] = 29;
      }
    
      var selectedDateMonth = selectedDate.getMonth();
         
      // Get previous month number (if current month is January, get December)
      let prevMonth = selectedDateMonth > 0 ? selectedDateMonth - 1 : 11;
      
      let prevMonthDate = null;
      
      // If selected date is last day of month...
      if(selectedDate.getDate() === daysInMonths[selectedDateMonth]){
        // Create new date that takes the selected date and subtracts the correct amount of
        // days from it based on a lookup in the array.
        var newDate1 = new Date(selectedDate.getTime());
        prevMonthDate = 
         new Date(newDate1.setDate(selectedDate.getDate() - daysInMonths[selectedDateMonth]));
      } else {
        // Create a new date that is last month and one day earlier
        var newDate2 = new Date(selectedDate.getTime());
        prevMonthDate = 
          new Date(new Date(newDate2.setDate(selectedDate.getDate() - 1))
            .setMonth(selectedDate.getMonth() - 1));
      }
      
      // Get next month (if current month is December, get January
      let nextMonth = selectedDateMonth < 11 ? selectedDateMonth + 1 : 0;  
      
      let nextMonthDate = null;
      
      // Same idea for next month, but add instead of subtract.
      
      // If selected date is last day of month...
      if(selectedDate.getDate() === daysInMonths[selectedDateMonth]){
        var newDate3 = new Date(selectedDate.getTime());
        nextMonthDate = 
         new Date(newDate3.setDate(selectedDate.getDate() + daysInMonths[selectedDateMonth + 1]));
      } else {
        var newDate4 = new Date(selectedDate.getTime());
        nextMonthDate = new Date(new Date(newDate4.setDate(selectedDate.getDate() + 1)).setMonth(selectedDate.getMonth() + 1));
      }  
    
      console.log("Last month date: " + prevMonthDate.toLocaleDateString());
      console.log("Next month date: " + nextMonthDate.toLocaleDateString());  
    });
    <p>Pick a date: <input type="date" id="date"></p>

  • answered 2018-03-13 20:27 Ele

    Use this approach:

    Javascript Date Object – Adding and Subtracting Months

    From the Author

    There is a slight problem with the Javascript Date() Object when trying to advance to the next month or go back to the previous month.

    For example, if your date is set to October 31, 2018 and you add one month, you'd probably expect the new date to be November 30, 2018 because November 31st doesn't exist. This, however, isn't the case.

    Javascript automatically advances your Date object to December 1st. This functionality is very useful in most situations(i.e. adding days to a date, determining the number of days in a month or if it's a leap year), but not for adding/subtracting months. I've put together some functions below that extend the Date() object: nextMonth() and prevMonth().

    function prevMonth() {
      var thisMonth = this.getMonth();
      this.setMonth(thisMonth - 1);
      if (this.getMonth() != thisMonth - 1 && (this.getMonth() != 11 || (thisMonth == 11 && this.getDate() == 1)))
        this.setDate(0);
    }
    
    function nextMonth() {
      var thisMonth = this.getMonth();
      this.setMonth(thisMonth + 1);
      if (this.getMonth() != thisMonth + 1 && this.getMonth() != 0)
        this.setDate(0);
    }
    
    Date.prototype.nextMonth = nextMonth;
    Date.prototype.prevMonth = prevMonth;
    
    var today = new Date(2018, 2, 31); //<----- March 31st, 2018
    
    var prevMonth = new Date(today.getTime());
    prevMonth.prevMonth();
    console.log("Previous month:", prevMonth);
    
    console.log("This month:", today)
    
    var nextMonth = new Date(today.getTime());
    nextMonth.nextMonth();
    console.log("Next month:", nextMonth);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

  • answered 2018-03-13 20:27 Ian McLaird

    Date handling is tricky at the best of times. Don't do this yourself. Use Moment.js.

    var target = 31;
    var today = moment().date(target).calendar();
    //  today == '03/31/2018'
    
    var nextMonth =  moment().date(target).add(1, 'month').calendar();
    // nextMonth == '04/30/2018'
    
    var lastMonth = moment().date(target).subtract(1, 'month').calendar()
    // lastMonth == '02/28/2018'
    

  • answered 2018-03-13 20:27 Jonathan

    Dates and time zones are a real pain in JS, so challenge accepted.

    I broke it down in two steps:
    - Count the days of prev and next month
    - Compare with selected day and pick the lowest number

    Testcases included

    function createUTCDate(year, month, day) {
      return new Date(Date.UTC(year, month, day));
    }
    
    function splitDate(date) {
      return {
        year: date.getUTCFullYear(),
        month: date.getUTCMonth(),
        day: date.getUTCDate()
      };
    }
    
    function numberOfDaysInMonth(year, month) {
      return new Date(year, month + 1, 0).getDate();
    }
    
    function dateNextMonth(dateObj) {
      const daysNextMonth = numberOfDaysInMonth(dateObj.year, dateObj.month + 1);
      const day = Math.min(daysNextMonth, dateObj.day);
      return createUTCDate(dateObj.year, dateObj.month + 1, day);
    }
    
    function datePreviousMonth(dateObj) {
      const daysPrevMonth = numberOfDaysInMonth(dateObj.year, dateObj.month - 1);
      const day = Math.min(daysPrevMonth, dateObj.day);
      return createUTCDate(dateObj.year, dateObj.month - 1, day);
    }
    
    const log = console.log;
    
    function print(dateString) {
      const date = new Date(dateString);
      const dateObj = splitDate(date);
      log("Previous: ", datePreviousMonth(dateObj).toISOString());
      log("Selected: ", date.toISOString());
      log("Next: ", dateNextMonth(dateObj).toISOString());
      log("--------------");
    }
    
    const testCases = [
      "2018-03-01 UTC",
      "2018-03-31 UTC",
      "2018-01-01 UTC",
      "2018-12-31 UTC"
    ];
    
    testCases.forEach(print);

    Please note that the hack with new Date(xxx + " UTC") is not according to spec and is just there for testing purposes. Results may vary per browser. You should choose an input format and construct your dates accordingly.

  • answered 2018-03-13 20:27 RobG

    A simple algorithm is to add months to the original date, and if the new date is wrong, set it to the last day of the previous month. Keeping the original date values unmodified helps, e.g.

    /* @param {Date} start - date to start
    ** @param {number} count - number of months to generate dates for
    ** @returns {Array} monthly Dates from start for count months
    */
    function getMonthlyDates(start, count) {
      var result = [];
      var temp;
      var year = start.getFullYear();
      var month = start.getMonth();
      var startDay = start.getDate();
      for (var i=0; i<count; i++) {
        temp = new Date(year, month + i, startDay);
        if (temp.getDate() != startDay) temp.setDate(0);
        result.push(temp);
      }
      return result;
    }
    
    // Start on 31 Jan in leap year
    getMonthlyDates(new Date(2016,0,31), 4).forEach(d => console.log(d.toString()));
    // Start on 31 Jan not in leap year
    getMonthlyDates(new Date(2018,0,31), 4).forEach(d => console.log(d.toString()));
    
    // Start on 30 Jan
    getMonthlyDates(new Date(2018,0,30), 4).forEach(d => console.log(d.toString()));
    // Start on 5 Jan
    getMonthlyDates(new Date(2018,0,5), 4).forEach(d => console.log(d.toString()));

  • answered 2018-03-13 20:27 Wayne Fung

    I handle it in a foolish way by concatenating string

    let daysInMonths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    let months = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"];
    
    var target = nexttarget = lasttarget = "29"; //target day
    
    if (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)) {
        daysInMonths[1] = 29;
    }
    
    function findLastDay(target, month){
        if(target > daysInMonths[month]){
            target = daysInMonths[month];
        }
        return target;
    }
    

    then

    var d = new Date();
    var year = d.getFullYear();
    var month = d.getMonth();
    
    target = findLastDay(target, month);
    
    var this_month = year+"-"+months[month]+"-"+target;
    console.log(this_month);//2018-03-29
    
    // next month
    if(month == 11){
       nextmonth = 0;
       nextyear = year + 1;
    }else{
       nextmonth = month+1;
       nextyear = year;
    }
    
    nexttarget = findLastDay(nexttarget, nextmonth);
    
    var next_month = nextyear+"-"+months[nextmonth]+"-"+nexttarget;
    console.log(next_month);//2018-04-29
    
    //last month
    if(month == 0){
       lastmonth = 11;
       lastyear = year - 1;
    }else{
       lastmonth = month - 1;
       lastyear = year;
    }
    
    lasttarget = findLastDay(lasttarget, lastmonth);
    
    var last_month = lastyear+"-"+months[lastmonth]+"-"+lasttarget;
    console.log(last_month);//2018-02-28