Determining the next payment renewal date I am not very good a JavaScript, surely not date manipulation, but I hacked together the script below with the purpose to determine the next payment renewal date based on a start date and payment term. The script does what it is supposed to do. The script is part of a data transformation, which runs for every row in the data transformation (Pentaho). It has 1.1 million rows, so performance is very important. Who has ideas on increasing performance (currently only 18 rows / sec on my i5).

var renewaldate = new Date(START_DATE.getTime());
var renewalday = START_DATE.getDate();
var renewalmonth = START_DATE.getMonth();
var renewalyear = START_DATE.getYear();
var paymentterm= 12 / PAYMENTS_PER_YEAR;
var today = new Date();

while (renewaldate - today &lt; 0) {
    renewalmonth = renewalmonth + paymentterm;
    if (renewalmonth &gt; 12) {
        renewalmonth = renewalmonth - 12;
        renewalyear++;
    }
    renewaldate.setFullYear(renewalyear, renewalmonth, renewalday);
}
An interesting question..

I am assuming that the values of PAYMENTS_PER_YEAR can only be these:

var paymentFrequencies = [ 1 , 2 , 3 , 4 , 6 , 12 ];


If that is the case, then the renewalyear can only be todays year or tomorrows year.

I would create a cache that pre-calculates for each month and each payment frequency what the next payment month is, and whether a year will be skipped.

Then you can simply use a look up to determine when the next payment month is, there should be no faster way than that. 

I am currently looking into building just that, it is definitely possible, but slightly harder than expected ;)

This is what I have, I tested it a bit, the principal should be clear:



function createCache( monthsOffset ){
  var cache = {},
      paymentFrequencies = [ 1 , 2 , 3 , 4 , 6 , 12 ],
      todaysMonth = (new Date ()).getMonth() + monthsOffset,
      renewalYear, renewalMonth, targetMonth, frequency, monthsShort, nextYear, term;
  for( var month = 0 ; month &lt; 12 ; month++ ){
    cache[month] = {};
    for( var i = 0 ; i &lt; paymentFrequencies.length ; i++ ){
      frequency = paymentFrequencies[i];
      term = 12 / frequency;
      targetMonth = todaysMonth &lt; month ? todaysMonth + 12 : todaysMonth;
      monthsShort = ( targetMonth - month ) % term;
      cache[month][frequency] = todaysMonth + (monthsShort ? term - monthsShort : 0);
      
    }
  }
  return cache;
}

var stillThisMonth = createCache( 0 ),
    nextMonth = createCache(1);

function determineRenewalDate( startDate , paymentsPerYear  ){

    var today = new Date(),
        todaysYear =  today.getFullYear();
        startMonth =  startDate.getMonth();
  /*Compare the days, if we renew say monthly, and we start on the 15th, 
    but today is the 20, then we need to renew for next month, if we are the
    the 8th then you can stick to this month */    
  var todaysDayOfTheMonth = today.getDate(),
      startDayOfTheMonth = startDate.getDate();
  //One more cool thing, `setFullYear` and `new Date` will roll over the year if you set the month too high
  if( todaysDayOfTheMonth &gt; startDayOfTheMonth  ){
     return new Date( todaysYear, nextMonth[startMonth][paymentsPerYear], startDayOfTheMonth );
  } else {
     return new Date( todaysYear, stillThisMonth[startMonth][paymentsPerYear], startDayOfTheMonth );
  }    
}


document.write(  determineRenewalDate( new Date(2010 , 1 , 1 ) , 12 ) + '&lt;br&gt;' );
document.write(  determineRenewalDate( new Date(2010 , 1 , 2 ) , 2 ) + '&lt;br&gt;' );