0

I'm trying to add days or month to a datetime. What determines rather it should add days or month to the datetime is what dayOrMonth ends with. So for example if dayOrMonth ends with MM it should add month, if it ends with DD it should add days.

dayOrMonth could look like this "90DD" (should add 90 days) and "90MM" should add 90 month.

I'm thinking about creating an extension method of some kind, but I'm struggling abit with the approach to this, as adding more if statements is not an option.

                    //... Set payment dates.
                    string dayOrMonth;
                    for (int x = 0; x < installmentDates.Count; x++)
                    {
                        if (installmentDates.Count > 0)
                        {
                            installmentdDateRow[colName] = installmentdDateRow[colName] + Convert.ToDateTime(installmentDates[x]).ToString("dd'-'MM'-'yyyy") + "\n";

                            //... Future payment dates.
                            int futurePaymentColumn = installmentdFuturePayments.Table.Columns.IndexOf(colName);
                            if (colName == "1. rate" && installmentDates.Count - 1 == x)
                            {
                                installmentdFuturePayments[futurePaymentColumn + 1] = installmentdFuturePayments[futurePaymentColumn + 1] + Convert.ToDateTime(installmentDates[x]).AddMonths(3).ToString("dd'-'MM'-'yyyy") + "\n";
                            }
                            if (colName == "2. rate" && installmentDates.Count - 1 == x && Functions.GetProductInfo(unit.Key.ToString().Split('-')[0])[9] != "€ 0,00")
                            {
                                installmentdFuturePayments[futurePaymentColumn + 1] = installmentdFuturePayments[futurePaymentColumn + 1] + Convert.ToDateTime(installmentDates[x]).AddMonths(3).ToString("dd'-'MM'-'yyyy") + "\n";
                            }
                        }
                    }
Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • Try parsing the string between numeric and character. Then use your character value as a condition and the numeric as a value to call DateTime.AddMonths(numeric). Hope this helps. If extra conditions is not in your wheel house, you can overload the DateTime.AddMonths/AddDays to accept the string in the format you provided. – davedno Nov 17 '19 at 15:18
  • That will lead to more if statements - witch is not an option. – Nicklas Christensen Nov 17 '19 at 15:20
  • Overload the DateTime functions then. I edited my comment, one way or another youre going to add if statements, just a matter of where. – davedno Nov 17 '19 at 15:21
  • I know, but this will be used so many places in my code, so I'd rather do it inside an extension than replicating it all over my code, just can crack it so to speach – Nicklas Christensen Nov 17 '19 at 15:27
  • The code you provided here does not use the `dayOrMonth` variable, and seems to be unrelated to the rest of your question. – Matt Johnson-Pint Nov 17 '19 at 21:45
  • @Matt Johnson-Pint It's just to give an understanding of what I was trying to achieve. Just imagine the variable format as described then you should understand. (I didn't post the full code). – Nicklas Christensen Nov 17 '19 at 22:51

3 Answers3

1

You have described an input string composed of two parts:

  • An integer magnitude to apply to some operation
  • A two character string defining the operation to use

As such, you know there should always be at least three characters. You also know the trailing two characters define the operation, so you can use Substring to separate those characters from the rest of the string.

An extension method is a great idea. It should include tests to help enforce the format of the input string, and make it easy to parse the numeric component.

public static DateTime ApplyInput(this DateTime dt, string input)
{
    if (input == null)
    {
        throw new ArgumentNullException(nameof(input), "The input string must not be null.");
    }

    if (input.Length < 3)
    {
        throw new ArgumentException("The input string is too short to include both a number and an operation.", nameof(input));
    }

    string numberChars = input.Substring(0, input.Length - 2);
    if (!int.TryParse(numberChars, out int number))
    {
        throw new ArgumentException("The start of the input string must be an integer.", nameof(input));
    }

    string endingChars = input.Substring(input.Length - 2);
    switch (endingChars.ToUpperInvariant())
    {
        case "MM":
            return dt.AddMonths(number);
        case "DD":
            return dt.AddDays(number);
        default:
            throw new ArgumentException($"The characters {endingChars} were not recognized as a valid operation.", nameof(input));
    }
}

This approach will perform better than using RegEx, Contains, or Replace. It is also extensible by adding more case statement to the switch.

Note that .ToUpperInvariant() makes the operation characters case-insensitive so you can pass mm or dd if you like. If you don't wan't that behavior, then simply remove .ToUpperInvariant().

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • I cannot rep / accept your answer during to my rep, but I've ended up using a code that reminds alot of yours. Thanks for the inspiration! – Nicklas Christensen Nov 17 '19 at 23:13
  • You can *accept* the answer by clicking on the checkmark near the voting buttons. You can vote up after you have 15 rep, but there is no rep requirement to accept. – Matt Johnson-Pint Nov 17 '19 at 23:31
0

Using StackOverflow - Seperating characters and numbers

You can use a regular expression to seperate the numbers from the characters in a given string like so:

Regex re = new Regex(@"([a-zA-Z]+)(\d+)");
Match result = re.Match(input);

string alphaPart = result.Groups[1].Value;
string numberPart = result.Groups[2].Value;

Then you can create a factory method or a project wide reference where you can use that code snippet to achieve what youre asking:

public DateTime AddDaysOrMonths(string input, DateTime dt)
{
     Regex re = new Regex(@"([a-zA-Z]+)(\d+)");
     Match result = re.Match(input);

     string alphaPart = result.Groups[1].Value;
     string numberPart = result.Groups[2].Value;

     if(alphaPart == "DD")
     {
       int days;
       if(Integer.TryParse(numberPart,out days) == true)
       {
           dt.AddDays(days)
       }
     }
     else if (alphaPart == "MM")
     {
       int months;
       if(Integer.TryParse(numberPart,out months) == true)
       {
           dt.AddMonths(months);
       }
     }
    return dt;
}

Ofcourse, you should implement more extenstive error/null checking and better string comparison but this should be enough to get you going in the right direction.

davedno
  • 349
  • 1
  • 5
  • 15
  • Seperating the numbers from DD/MM was not the problem. – Nicklas Christensen Nov 17 '19 at 15:46
  • I didnt say it was. I showed how you can both separate and use that separation to solve your problem. @NicklasChristensen – davedno Nov 17 '19 at 15:53
  • I like my solution more, as regex takes alot of time to execute. But thanks for the input.. – Nicklas Christensen Nov 17 '19 at 15:59
  • @NicklasChristensen and a string replace doesnt? Glad you found what worked. Best of luck to you. – davedno Nov 17 '19 at 16:00
  • It's faster than what you came up with.. Go try both scenarios, and see what takes the most ms to execute, because I know the answer to that... Good luck davedno. – Nicklas Christensen Nov 17 '19 at 16:18
  • @NicklasChristensen That may be so but the difference is negligible with the input your passing.The solution above is more production ready, while yours may raise your professors eye brows in college. – davedno Nov 17 '19 at 16:37
  • You make statements that are not true, keep living in that belief that it's more production ready, everyone will tell you to avoid regex, unless absolutely needed – Nicklas Christensen Nov 17 '19 at 19:46
  • Note that you aren't actually using the results from the `AddDays` and `AddMonths` operations in this code. (`DateTime` is immutable.) – Matt Johnson-Pint Nov 17 '19 at 21:07
0

I solved my problem by creating this extension method

  public static DateTime test1(this DateTime d, string inputType)
    {
        if (inputType.Contains("DD"))
        {
            d = d.AddDays(Convert.ToInt32(inputType.Replace("DD", "")));
        }
        if (inputType.Contains("MM"))
        {
            d = d.AddMonths(Convert.ToInt32(inputType.Replace("MM", "")));
        }
        return d;
    }
  • 2
    You just did exactly what you said you didn't want to do..."That will lead to more if statements - witch is not an option". Add some error checking in there! – Idle_Mind Nov 17 '19 at 15:48
  • As I also mentioned "I know, but this will be used so many places in my code, so I'd rather do it inside an extension than replicating it all over my code". Witch is exactly what I did. – Nicklas Christensen Nov 17 '19 at 15:49
  • 1
    As currently written, you can add both MM and DD to ONE string and it would add both days and months: "90DDMM" – Idle_Mind Nov 17 '19 at 15:49
  • Use `inputType.EndsWith("DD")` instead of `Contains` – Sushant Yelpale Nov 17 '19 at 15:54
  • As currently written.. I do not need both, I need either one or the other depending on what dayOrMonth ends with (DD/MM) – Nicklas Christensen Nov 17 '19 at 15:55
  • For home project this might fly, but in reality this is no-go. Never, ever, trust blindly input. (1) your checking is wrong in regard what you asked for, you asked about suffix, now you check entire string (2) you double check instead of if-else-if (3) there is no else (fallback) clause so you can pass "hello world" as `inputType`. – greenoldman Nov 18 '19 at 06:04