0

I was looking all over the internet for this functionality but couldn't find any acceptable solution Simple question, How can you add 1.5 month to a DateTime, AddMonth accepts only integer as a parameter. and yes i know i can use AddDays, but it brings many other questions of how to calculate proper number of days depends on a moths you are looking at

my own solution is bellow, but for sure it is not the perfect one

public static DateTime AddMonths(DateTime val, double months)
{
    int integer =(int) Math.Truncate(months);
    double fraction = months - integer;

    val = val.AddMonths(integer);

    double days = DateTime.DaysInMonth(val.Year, val.Month) * fraction;

    val = val.AddDays(days);

    return val;
}

Update: This is a business project requirements. One of the forms has Issue Date (DateTime) and Term Period (double) that can be defined as fraction of a month. My question was exactly of how can it be handled properly with my version of the code. I know this code isn't the best, and I specified in my question that it raises lots of questions that you guys listed in your comments. So do you guys have any suggestions of how to handle all the scenarios? do you guys have any better code for the function

Alex G
  • 595
  • 6
  • 21
  • 3
    The question is still ambiguous; if the date is 2-20-17, which month is the month for the half (Feb, Mar or Apr)? – Erik Philips Oct 27 '17 at 21:40
  • 1
    check out FluentDateTime https://github.com/FluentDateTime/FluentDateTime its a nuget package and can be found on GitHub. I use it in some of the projects I am in charge of – Aaron. S Oct 27 '17 at 21:42
  • What is your definition of .5 of a month? – Kenneth K. Oct 27 '17 at 21:43
  • 1
    Yeah, one could say 1/2 a month is 1/24th of a year, or on February 20th you could say it's `8 days + (15.5 - (15.5*8/28th))`. Both of those answers could be correct. – zzxyz Oct 27 '17 at 21:49
  • Although I suppose this ambiguity remains for integer months to some degree, and obviously there is an accepted method of resolving that. – zzxyz Oct 27 '17 at 21:58
  • Do you not have something like a business rule that specifies that a month is considered to be equal to 30 days? – Chris Thompson Oct 27 '17 at 22:19
  • This is a business project requirements. One of the forms has Issue Date (DateTime) and Term Period (double) that can be defined as fraction of a month. My question was exactly of how can it be handled, and I specified in my question that it raises lots of questions that you guys listed in your comments. So do you guys have any suggestions of how to handle all the scenarios? do you guys have any better code for the function? – Alex G Oct 28 '17 at 00:49

2 Answers2

3

I would write this as an extension method with the following logic, which is really similar to yours. In fact I think the only difference is that you calculate the days based on the number of days in the starting month, and I calculate them based on the difference of adding an extra month (at the end):

  1. Separate the whole months and the partial month into two variables
  2. Add the whole months to the Date
  3. To calculate the days:
    1. Add another whole month
    2. Subtract the date in step 2 from the it
    3. Get the total days difference
    4. Multiply the total days by the partial month value captured in step 1
  4. Add that many days to the month

Probably makes more sense in code:

public static class Extensions
{
    public static DateTime AddMonths(this DateTime startingValue, double months)
    {
        // Cast to an int to avoid recursing back to this method
        var wholeMonths = (int)Math.Floor(months);
        var partialMonth = months - wholeMonths;

        var result = startingValue.AddMonths(wholeMonths);
        return result.AddDays(Math.Floor(
            result.AddMonths(1).Subtract(result).TotalDays * partialMonth));
    }
}

Usage

static void Main(string[] args)
{
    var today = DateTime.Now;
    var later = today.AddMonths(3.5);

    Console.WriteLine($"Today: {today}");
    Console.WriteLine($"Today plus 3.5 months: {later}");

    Console.Write("\nDone!\nPress any key to exit...");
    Console.ReadKey();
}

enter image description here

Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • Correct me if I am wrong: the line result.AddMonths(1).Subtract(result).TotalDays calculates the number of days in the upcoming month... the months that fails on date after you added all the wholeInteger number of monthin my code I am trying to do the same with the line DateTime.DaysInMonth(val.Year, val.Month) I just wasn't sure if I need to get the number of days of the val.Month or the next one. you suggest to get the next month? – Alex G Oct 28 '17 at 01:20
  • Yeah, that's right - it adds the whole months first, then calculates the percentage to add based on the number of days that would be added if we added another full month. Probably doesn't make too much difference either way, though. – Rufus L Oct 28 '17 at 01:22
  • My understanding, it is about the same solution that I proposed. I guess it has the same issue as my code if for example after we added whole number of months it falls on March 1, the extension method gives more flexibility.. thanks – Alex G Oct 28 '17 at 01:24
  • I double checked your solution: It adds one month that has the number of days of the month it in,for January it adds 31 days, for Februrary it adds 28 days. which is about the same what my code does? do I miss something? – Alex G Oct 28 '17 at 01:53
1

Without a full set of business requirements, I think the simplest solution would be to take the generally accepted number of days in a month, which is approximately 30.4167, and use that number for the final calculation.

public static DateTime AddMonths(DateTime val, double months)
{
    // expand out the number of days in a month to a wider value than 30.4167
    const double daysInMonth = 30.41666667;

    double days = months * daysInMonth;
    // could also just use val.AddDays(days);
    TimeSpan ts = TimeSpan.FromDays(days);
    DateTime dt = val + ts;

    return dt;
}

Let's accommodate leap year (per comments) by calculating the total number of days in a year:

public static DateTime AddMonths(DateTime val, double months)
{
    double daysInYear = new DateTime(val.Year, 12, 31).DayOfYear;
    double daysInMonth = daysInYear / 12;

    return val.AddDays(months * daysInMonth);
}
Metro Smurf
  • 37,266
  • 20
  • 108
  • 140
  • 1
    "Better" is subjective, but this looks like it will perform faster. It could literally be reduced to `return val.AddDays(months * 30.41666667);`. It definitely returns different results than yours though...the time of day changes too. – Rufus L Oct 28 '17 at 02:12
  • @RufusL - little float point rounding leap year :P - good point. Added support with a quick calc based on the value passed in. – Metro Smurf Oct 28 '17 at 02:45
  • @AlexG - not quite following what you mean by "is it a better way?" I agree with with Rufus that this is fully subjective. And without a set of business requirements, this is definitely better IMO. – Metro Smurf Oct 28 '17 at 02:46