Your code generally looks about right. This would validate, however, at the server side, the way you generally have it and if it was working. Moreover, I'm unsure of why some examples are only overriding the ValidationResult version of the IsValid method, and yet other examples are only overriding the bool variant.
If you want client side validation, you need to implement IClientValidatable , and register the adapter on the client side.
Below is some code I was able to make work. I needed to include my js file after my validation inclusion, and since I was using DevExpress with include client libraries, I needed to run it after them as well.
You'll also (for client side to work), want to use EditorFor or TextBoxFor type methods to add the fields, but this appears to be something you are already doing. Do, however, check your html and look for said validation proprieties being added.
I've tested this with my MM/dd/yyyy fields.
RangeSmallDateTime.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace YourNamespaceHere.Filters
{
public class RangeSmallDateTime : ValidationAttribute, IClientValidatable
{
private static DateTime _minValue = new DateTime(1900, 1, 1);
private static DateTime _maxValue = new DateTime(2079, 6, 6);
public override bool IsValid(object value)
{
DateTime? dateValue = value as DateTime?;
if (!dateValue.HasValue)
{
return true;
}
else
{
return (dateValue.Value >= _minValue && dateValue.Value <= _maxValue);
}
}
public override string FormatErrorMessage(string name)
{
return string.Format("The '{0}' field has an invalid value.", name);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "rangesdt";
yield return rule;
}
}
}
SmallDateTimeValidator.js:
$.validator.unobtrusive.adapters.addBool("rangesdt");
$.validator.addMethod("rangesdt", function (value, element) {
if (value == null) {
return true;
}
var date = new Date(value); // MM/dd/yyyy
var minDate = new Date(1900,1,1);
var maxDate = new Date(2079, 6, 6);
if (!dates.inRange(date, minDate, maxDate))
return false;
return true;
});
// Source: http://stackoverflow.com/questions/497790
var dates = {
convert: function (d) {
// Converts the date in d to a date-object. The input can be:
// a date object: returned without modification
// an array : Interpreted as [year,month,day]. NOTE: month is 0-11.
// a number : Interpreted as number of milliseconds
// since 1 Jan 1970 (a timestamp)
// a string : Any format supported by the javascript engine, like
// "YYYY/MM/DD", "MM/DD/YYYY", "Jan 31 2009" etc.
// an object : Interpreted as an object with year, month and date
// attributes. **NOTE** month is 0-11.
return (
d.constructor === Date ? d :
d.constructor === Array ? new Date(d[0], d[1], d[2]) :
d.constructor === Number ? new Date(d) :
d.constructor === String ? new Date(d) :
typeof d === "object" ? new Date(d.year, d.month, d.date) :
NaN
);
},
compare: function (a, b) {
// Compare two dates (could be of any type supported by the convert
// function above) and returns:
// -1 : if a < b
// 0 : if a = b
// 1 : if a > b
// NaN : if a or b is an illegal date
// NOTE: The code inside isFinite does an assignment (=).
return (
isFinite(a = this.convert(a).valueOf()) &&
isFinite(b = this.convert(b).valueOf()) ?
(a > b) - (a < b) :
NaN
);
},
inRange: function (d, start, end) {
// Checks if date in d is between dates in start and end.
// Returns a boolean or NaN:
// true : if d is between start and end (inclusive)
// false : if d is before start or after end
// NaN : if one or more of the dates is illegal.
// NOTE: The code inside isFinite does an assignment (=).
return (
isFinite(d = this.convert(d).valueOf()) &&
isFinite(start = this.convert(start).valueOf()) &&
isFinite(end = this.convert(end).valueOf()) ?
start <= d && d <= end :
NaN
);
}
}