13

Is there a faster way then to simply catch an exception like below?

try
{
    date = new DateTime(model_.Date.Year, model_.Date.Month, (7 * multiplier) + (7 - dow) + 2);
}
catch (Exception)
{
    // This is an invalid date
}
Jon Seigel
  • 12,251
  • 8
  • 58
  • 92
leora
  • 188,729
  • 360
  • 878
  • 1,366
  • 2
    What do you mean, faster? This code runs very quickly unless you have an invalid date. Or are you asking if there's a way to do this with less code? – Jeff Sternal May 17 '10 at 13:16

9 Answers9

14
String DateString = String.Format("{0}/{1}/{2}", model_.Date.Month, (7 * multiplier) + (7 - dow) + 2),model_.Date.Year);

DateTime dateTime;
if(DateTime.TryParse(DateString, out dateTime))
{
    // valid
}

As the comment pointed out by GenericTypeTea, this code will not run any faster than what you have now. However, I believe you gain in readability.

Michael G
  • 6,695
  • 2
  • 41
  • 59
  • @Michael G - i don't have a date string. I just have the info above – leora May 17 '10 at 13:13
  • 7
    DateTime.TryParse throws an Exception internally anyway, this would be no faster. – djdd87 May 17 '10 at 13:18
  • 9
    @GenericTypeTea - That is wrong. DateTime.TryParse() does **not** throw an exception internally! It would be more accurate to think of it as if DateTime.Parse() uses DateTime.TryParse() to first decide if an exception needs to be thrown. – Joel Coehoorn May 17 '10 at 13:31
  • 5
    @Joel Coehoorn - Just fired up reflector to have a look and I've been operating under that misconception for quite some time. Thank you for correcting me. – djdd87 May 17 '10 at 13:44
  • 5
    -1 - This won't work if your regional settings are not month/day/year! – Cameron Peters May 17 '10 at 14:05
  • Suggest you use the overload for Parse that takes a FormatProvider and a Style, this way it does not matter what culture the code is running in. @Cameron Peters, you are correct that this code only works in en-US, but it is easily fixed. – Steve May 17 '10 at 14:12
  • 1
    using `DateTime.TryParse` to check the validity of a day/month/year is so wrong - it's a mostly unrelated function and m/d/y date format is used only in a small part of the world... – Pent Ploompuu May 17 '10 at 14:13
  • 5
    Why are we trying to DateTime.TryParse a DateTime? model_.Date is quite clearly a DateTime... :) – code4life May 17 '10 at 14:16
  • 1
    @PentPloompuu: m/d/y is US date format, and give the number of computers there, I'd say it is quite a large part of the world. – JBRWilkinson May 17 '10 at 14:45
  • @Pent Ploompuu: As far as I remember things, the date-parse-functions take into account locales. – Sebastian Mach May 17 '10 at 15:49
9

If your goal is to avoid using exceptions, you could write a custom validation method:

public bool IsValidDate(int year, int month, int multiplier, int dow)
{
    if (year < 1 | year > 9999) { return false; }

    if (month < 1 | month > 12) { return false; }

    int day = 7 * multiplier + 7 - dow;
    if (day < 1 | day > DateTime.DaysInMonth(year, month)) { return false; }

    return true;
}

This performs most of the same validations as the DateTime constructor you're using - it only omits the check to see if the resulting DateTime would be less than DateTime.MinValue or greater than DateTime.MaxValue.

If you mostly get good values, this would probably be slower overall: DateTime.DaysInMonth has to do a lot of the same things the DateTime constructor does, so it would add overhead to all the good dates.

Jeff Sternal
  • 47,787
  • 8
  • 93
  • 120
5

Hmm... think of it this way: the model_ class has a DateTime property

model_.Date

, so no need to validate the year & month. The only tricky part is the day of the month:

(7 * multiplier) + (7 - dow) + 2

So a very quick and efficient way to validate this (which is better than throwing and catching) is to use the DateTime.DaysInMonth method:

if ((multiplier <= 4) && 
    (DateTime.DaysInMonth(model_.Date.Year, model_.Date.Month) < 
        (7 * multiplier) + (7 - dow) + 2))
{
    // error: invalid days for the month/year combo...
}

Another benefit is that you don't need to instantiate a new DateTime just to validate this information.

P.S. Updated the code to ensure that multiplier is <= 4. That only makes sense, since any value >=5 will fail the DaysInMonth test...

code4life
  • 15,655
  • 7
  • 50
  • 82
  • +1, I just took a look at the question and thought about posting this. It's just lazy doing it any other way. Throwaway .NET nation mentality at its best. – Codesleuth May 17 '10 at 14:35
  • +1 if the speed of the algorithm is really a concern (the question is still a bit unclear), a guard clause checking multiplier <= 4 would be important. – Jeff Sternal May 17 '10 at 18:16
3

EDIT: Woops! DateTime.TryParse doesn't throw an exception internally. I was talking out my buttocks! Anyway...

DateTime.TryParse() will throw an exception internally and will cause a result identical to your code in your question. If speed is imporant, you'll have to write your own method.

This may seem like more code, but I think it'll be faster if you're expecting a large volume of errors:

public bool GetDate(int year, int month, int day, out DateTime dateTime)
{
    if (month > 0 && month <= 12)
    {
        int daysInMonth = DateTime.DaysInMonth(year, month);
        if (day <= daysInMonth)
        {
            dateTime = new DateTime(year, month, day);
            return true;
        }
    }
    dateTime = new DateTime();
    return false;
}

My above example won't handle all cases (i.e. I'm not handling years), but it'll point you in the right direction.

djdd87
  • 67,346
  • 27
  • 156
  • 195
  • 1
    Why would this be faster that TryParse()? – Paul Michaels May 17 '10 at 13:46
  • Seems kind of redundant to me for a method to return a `bool` **and** have an output variable of type `Nullable` (why not just `DateTime`?)... – Dan Tao May 17 '10 at 13:52
  • To be honest, I was operating under a misconception of DateTime.TryParse and thought it threw exceptions internally within itself. I've delved into reflector and have seen what it actually does and TryParse is probably faster if the dates are all perfect each time. However, I believe this method would be quicker if there were more incorrect dates... so if a large amount of rubbish is being checked, this method would be better, but if incorrect dates are the edge case, then TryParse would be better. – djdd87 May 17 '10 at 13:52
2

The fastest way to validate a date is an one-liner:

static bool IsValidDate(int year, int month, int day) =>
             !(year < 1 || year > 9999 ||
            month < 1 || month > 12 ||
            day < 1 || day > DateTime.DaysInMonth(year, month));

Using a DateTime constructor or any parse function or any exception handler will slow down the validation. TryParse is preferred only if you want to validate a string.

1

Look at the DateTime.TryParse method

derek
  • 4,826
  • 1
  • 23
  • 26
1

One thing though: Exceptions are for exceptional cases. Bogoformatted strings are not exceptional but expected, so TryParse is more appropriate.

Using try/catch for checking validness is misuse and misconception of exceptions, especially with a catch-all catch (the latter already caused me searching for hours for why something doesn't work, and that many times).

Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
0

I don't know about faster, but

DateTime.TryParse()

Should do the same thing.

I'd be interested if anyone can tell me if this is faster (in terms of processor time) than the way described in the question.

Paul Michaels
  • 16,185
  • 43
  • 146
  • 269
-2

If the parsing mechanism is not any faster, then you can use the slightly more verbose method of checking the properties of your model object for valid values directly.

cortijon
  • 983
  • 6
  • 13