Re: Check DateTime format

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance

From: Dan S (DanS_at_discussions.microsoft.com)
Date: 07/22/04


Date: Thu, 22 Jul 2004 08:38:39 -0700

Interesting....thanks for the response Jon!

-- 
Dan S
"Jon Skeet [C# MVP]" wrote:
> Dan S <DanS@discussions.microsoft.com> wrote:
> > My application asks the user to enter in a date - in the mm/dd/yyyy
> > format. Is there any quick and easy way to verify the date the user
> > enters is formatted correctly? Right now I'm calling DateTime.Parse()
> > and catching the FormatException but it seems this is a bit
> > inefficient - catching the exception that is. There is some pretty
> > obvious delay while it traces back up the call stack. Is there a
> > better way? Something that returns a bool possibly?
> 
> After the first time, which may involve a delay due to loading 
> resources, throwing an exception is likely to be very fast - far faster 
> than a user can actually notice. On my laptop I can throw a hundred 
> thousand exceptions in a second - I think that's rather more than a 
> user is likely to enter.
> 
> I'm not saying that exceptions are always a nice way to go, but I 
> wouldn't dismiss them for performance reasons - at least not in this 
> case.
> 
> You can use a regular expression to check the format, of course - but 
> you may well find that doing so is more expensive than just trying to 
> parse the date and catching the exception.
> 
> You could probably do slightly better with a hard-coded test, something 
> like:
> 
> static readonly char[] LowerBounds = "00/00/1000".ToCharArray();
> static readonly char[] UpperBounds = "19/39/2999".ToCharArray();
> static bool IsProbablyValidDate(string date)
> {
>     if (date.Length != 10)
>     {
>         return false;
>     }
>     for (int i=0; i < date.Length; i++)
>     {
>         char c=date[i];
>         if (c < LowerBounds[i] || c > UpperBounds[i])
>         {
>             return false;
>         }
>     }
>     return true;
> }
> 
> That gets rid of *many* invalid dates, but not all - you'll still need 
> to call DateTime.Parse (or preferrably DateTime.ParseExact) and catch 
> the potential exception, unless you want to do all the parsing 
> correctly.
> 
> Note that it also requires the leading zeroes for months and days - if 
> you don't want that, it becomes slightly trickier.
> 
> (That only deals with dates in years 1000-2999; if you need to deal 
> with earlier or later years, change the 7th character in 
> LowerBounds/UpperBounds.)
> 
> Here's a benchmark to compare the three approaches mentioned:
> 
> using System;
> using System.Windows.Forms; // For MethodInvoker
> using System.Text.RegularExpressions;
> using System.Globalization;
> 
> delegate void DoSomething();
> 
> class Test
> {
>     static string[] invalid = {"123123", "wibble", "32/12/3223",
>             "14/23/1999", "04/35/1992", "02/29/2003"};
>     
>     static string[] valid = {"12/02/2321", "02/12/2312", "02/29/2004",
>             "01/30/2000"};
>     
>     const int Iterations = 100000;
>     
>     static void Main()
>     {
>         Time (new MethodInvoker(TestRegex));
>         Time (new MethodInvoker(TestHardCoded));
>         Time (new MethodInvoker(TestNoPreCheck));
>     }
>     
>     static void Time(MethodInvoker test)
>     {
>         DateTime start = DateTime.Now;
>         test();
>         DateTime end = DateTime.Now;
>         
>         Console.WriteLine ("{0}: {1}", test.Method.Name, end-start);
>     }
>     
>     static readonly Regex Expression = new Regex
>        (@"\d{1,2}\/\d{1,2}\/\d{4}", RegexOptions.Compiled);
>     static void TestRegex()
>     {
>         for (int i=0; i < Iterations; i++)
>         {
>             foreach (string x in invalid)
>             {
>                 if (Expression.IsMatch(x))
>                 {
>                     try
>                     {
>                         DateTime.ParseExact(x, "dd/mm/yyyy",
>                               CultureInfo.InvariantCulture);
>                         throw new Exception("Invalid date passed");
>                     }
>                     catch
>                     {
>                     }
>                 }
>             }
>             foreach (string x in valid)
>             {
>                 if (Expression.IsMatch(x))
>                 {
>                     try
>                     {
>                         DateTime.ParseExact(x, "dd/mm/yyyy",
>                               CultureInfo.InvariantCulture);
> 
>                     }
>                     catch
>                     {
>                         throw new Exception("Valid date failed");
>                     }
>                 }
>                 else
>                     throw new Exception("Valid date failed");
>             }
>         }
>     }
>     
>     static void TestHardCoded()
>     {
>         for (int i=0; i < Iterations; i++)
>         {
>             foreach (string x in invalid)
>             {
>                 if (IsProbablyValidDate(x))
>                 {
>                     try
>                     {
>                         DateTime.ParseExact(x, "dd/mm/yyyy",
>                               CultureInfo.InvariantCulture);
> 
>                         throw new Exception("Invalid date passed");
>                     }
>                     catch
>                     {
>                     }
>                 }
>             }
>             foreach (string x in valid)
>             {
>                 if (IsProbablyValidDate(x))
>                 {
>                     try
>                     {
>                         DateTime.ParseExact(x, "dd/mm/yyyy",
>                               CultureInfo.InvariantCulture);
> 
>                     }
>                     catch
>                     {
>                         throw new Exception("Valid date failed");
>                     }
>                 }
>                 else
>                     throw new Exception("Valid date failed");
>             }
>         }
>     }
>     
>     static void TestNoPreCheck()
>     {
>         for (int i=0; i < Iterations; i++)
>         {
>             foreach (string x in invalid)
>             {
>                 try
>                 {
>                     DateTime.ParseExact(x, "dd/mm/yyyy",
>                                         CultureInfo.InvariantCulture);
>                     throw new Exception("Invalid date passed");
>                 }
>                 catch
>                 {
>                 }
>             }
>             foreach (string x in valid)
>             {
>                 try
>                 {
>                     DateTime.ParseExact(x, "dd/mm/yyyy",
>                                         CultureInfo.InvariantCulture);
>                 }
>                 catch
>                 {
>                     throw new Exception("Valid date failed");
>                 }
>             }
>         }
>     }
> 
>     static readonly char[] LowerBounds = "00/00/1000".ToCharArray();
>     static readonly char[] UpperBounds = "19/39/2999".ToCharArray();
>     static bool IsProbablyValidDate(string date)
>     {
>         if (date.Length != 10)
>         {
>             return false;
>         }
>         for (int i=0; i < date.Length; i++)
>         {
>             char c=date[i];
>             if (c < LowerBounds[i] || c > UpperBounds[i])
>             {
>                 return false;
>             }
>         }
>         return true;
>     }
> }
> 
> The results on my laptop were:
> TestRegex: 00:00:09.3437500
> TestHardCoded: 00:00:04.3437500
> TestNoPreCheck: 00:00:12.5156250
> 
> Changing the regex to require exactly two digits instead of 1 or 2 for 
> the month and day sped it up very slightly, but not really 
> significantly.
> 
> -- 
> Jon Skeet - <skeet@pobox.com>
> http://www.pobox.com/~skeet
> If replying to the group, please do not mail me too
> 


Relevant Pages

  • Re: Check DateTime format
    ... > inefficient - catching the exception that is. ... You can use a regular expression to check the format, ... static void Time ... static readonly Regex Expression = new Regex ...
    (microsoft.public.dotnet.general)
  • Re: Fastest way to split a file by columns?
    ... ByteBuffer buf = new ByteBuffer; ... // process exception. ... static void initializeStream() throws Exception { ... if (pos>= buf.length) { ...
    (comp.lang.java.programmer)
  • Re: Threading
    ... now I see why I was sceptic about the thread abort exception. ... tried a similar scenario in the default domain. ... static void ThreadProc() ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: ?RE: Could events ever ?overrun? ?or otherwise be missed by a hand?ler?
    ... You can potentially walk the delegate list and invoke by hand which can handle the exceptions and continue calling it... ... Console.WriteLine("Doh, Exception, but continuing."); ... > static void ThrowException() ...
    (microsoft.public.dotnet.framework)
  • Re: HexString to Byte array: Speed Freaks! Can you code this faster in C#?
    ... Jon Skeet wrote: ... Apparently, when this idiom is used, the jitter eliminates the array ... tell from the for loop control statement that an out of bounds condition ... > static void ParseJon() ...
    (microsoft.public.dotnet.languages.csharp)