Find the next monthly expiration date I have an agreement with a customer. When the agreement ends, the customer needs to pay. The amount which the customer needs to pay increases for each month that has passed since the agreement was initiated. The maximum duration is 3 years, so I have a final expiration date. From this final expiration date, I must be able to find the periodic expiration date (the date when the payment increases). In other words, I need a function which does this:

DateTime GetNextExpirationDate(finalExpirationDate)


One of the issues which I am struggling with is that the algorithm needs to consider whether the expiration date in the current month has already passed (there will be a date each month). It also needs to consider that the day of the month for the final expiration date, might not exist for the periodic expiration dates (e.g. if the final expiration date is the 31st of something). 

I have some code which is working, but it appears somewhat redundant and with many steps. I was wondering, if perhaps it could be simplified (either a little or a lot).

public DateTime GetNextExpirationDate(DateTime finalExpirationDate)
{
    int expirationDay = finalExpirationDate.Day;
    int currentMonth = DateTime.Now.Month;
    int currentYear = DateTime.Now.Year;
    int daysInCurrentMonth = DateTime.DaysInMonth(currentYear, currentMonth);

    // If the month has less days that the day of the final expiration date,
    // then we need to use the maximum date of the month.
    int expirationDayThisMonth = Math.Min(expirationDay, daysInCurrentMonth);

    DateTime expirationDateThisMonth = new DateTime(currentYear, currentMonth, expirationDayThisMonth);

    // If the periodic expiration date in the current month is already surpassed,
    // then we need to the date in the next month.
    if (expirationDateThisMonth &lt; DateTime.Now)
    {
        DateTime dateInOneMonth = DateTime.Now.AddMonths(1);
        int nextMonthMonth = dateInOneMonth.Month;
        int nextMonthYear = dateInOneMonth.Year;
        int daysInNextMonth = DateTime.DaysInMonth(nextMonthYear, nextMonthMonth);

        int expirationDayNextMonth = Math.Min(expirationDay, daysInNextMonth);

        DateTime expirationDateNextMonth = new DateTime(nextMonthYear, nextMonthMonth, expirationDayNextMonth);

        return expirationDateNextMonth;
    }
    else
    {
        return expirationDateThisMonth;
    }            
}


Any ways to improve this?

Input/output examples:

Final expiration date: 15.12.2019
DateTime.Now: 12.03.2019
Output: 15.03.2019

Final expiration date 11.10.2019
DateTime.Now: 16.04.2019
Output: 11.05.2019


(I know that I cannot change DateTime.Now but it does work as an pseudo-input, which is why I included it).
When working with DateTime it's usually a good idea to not use a hardcoded DateTime.Now. This makes testing such methods very difficult because you cannot mock it so you have to write very tricky tests all with Now. You allso cannot easily switch to UtcNow etc. and you have to know which now it uses internally.

A much better solution would be to use another parameter like DateTime referenceDate. This way you can use any combinations of the two values and create more predictable and easier to understand tests.

Additionally I'd make this method static because it does not access any class fields etc.



Oh, I also need to add that you use excelent variable names! This is exactly how it should be done! ;-)