When developing simple validation logic in Asp.Net Mvc you can use the built in validators. One of them is the RegularExpression Validator. I had a simple scenario with a property of  DateTime type called StartDate. Validation format (yyyy-mm-dd). It should be a simple task , but details are always messy.

I created a simple pattern and tested it with various examples in one of the Regex Editors.

DateTime regular expression problem

 [RegularExpression(@"^(19|20)dd([- /.])(0[1-9]|1[012])2(0[1-9]|[12][0-9]|3[01])$")]
 public DateTime StartDate { get; set; }

To check if this solution works , two unit tests were created. First implementation of tests used the Regex class , but then I found out that you can use  Attribute classes inside your code. I changed tests and used the RegularExpressionAttribute class inside testThose tests are better because , with Regex our , we are checking if regex pattern is correct. With Attribute class used inside the test , we are testing actual scenario that is happening inside our app .

Problematic Tests

Result - SUCCESS

[TestFixture]
 class CalendarEventRegExpTests
 {
    private string regex = @"^(19|20)(dd)[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$";

    [Test]
     public void Should_match()
     {
        var dateTime = "2011-10-1";
        var attribute = new RegularExpressionAttribute(regex);
        Assert.IsTrue(attribute.IsValid(dateTime));
     }

Result - SUCCESS

[Test]
 public void Should_not_match()
 {
  var dateTime = "01-10-2001";
  var attribute = new RegularExpressionAttribute(regex);
  Assert.IsFalse(attribute.IsValid(dateTime));
  }
}

Yey green light, They passed so it’s working ! I tested the app and … validation was always incorrect . First thought , my pattern is incorrect. But , it is working inside RegEx Editor so I it has to be correct.

Whats Wrong

It seems that when DateTime object is passed to the RegExpAttribute , something weird is happening and validation fails. I have simulated this scenario with simple test.

Result - FAIL

[Test]
public void Should_match()
{
    var dateTime = new DateTime(2011,11,10);
    var attribute = new RegularExpressionAttribute(regex);
    Assert.IsTrue(attribute.IsValid(dateTime));
}

Maybe it’s the problem with the type of the object. This test converts DateTime object to string fail.

Result - FAIL

[Test]
public void Should_match()
{
   var dateTime = new DateTime(2011,10,10);
   var attribute = new RegularExpressionAttribute(regex);
   Assert.IsTrue(attribute.IsValid(dateTime.ToString()));
}

Then I realized that .ToString() , method by default creates string including the hh-mm-ss. In my scenario those parameters were initialized with zeros My simple regex pattern wont match this string. Correctly formatted string passes the Test.

Result - SUCCESS

[Test]
public void Should_match()
{
    var dateTime = new DateTime(2011,10,10);
    var attribute = new RegularExpressionAttribute(regex);
    Assert.IsTrue(attribute.IsValid(dateTime.ToString("yyyy-MM-dd")));
}

It’s time to look inside the RegularExpressionAttribute.  Let me “Reflect” or “DotPeek”  that for you.

Here is the code inside the class.

public override bool IsValid(object value)
{
   this.SetupRegex();
   string input = Convert.ToString(value, (IFormatProvider) CultureInfo.CurrentCulture);
   if (string.IsNullOrEmpty(input))
   {
      return true;
   }
   else
   {
       Match match = this.Regex.Match(input);
       if (match.Success && match.Index == 0)
          return match.Length == input.Length;
       else
          return false;
    }
}

The Highlighted part is the problem. .IsValid() method uses default.ToString()DateTime is parsed to the string with hh-mm-ss and that’s the root of the problem.

Conclusion

There is a simple solution to this problem. You just need to attach DisplayFormat.

Lessons Learned

- “green light” in test doesn’t mean that your code is working .

- create tests on “real” data

- try simulate environment and context as much as possible. To many assumptions and your test isn’t testing real scenario. In my case , I  used the string inside test when my app used DateTime object.

Share