2

I'm using this object:

[Required]
[Display(Name = "AuditDate", ResourceType = typeof(Resources.Audit))]
[DisplayFormat(DataFormatString = "{dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime dateAudit { get; set; }

In this part of my form:

@Html.TextBox("dateAudit", String.Format("{0:d}", Model.dateAudit.ToString("dd'/'MM'/'yyyy"), new { @class = "form-control" }))

When I try to enter a date, I cannot submit the form (DatePicker dialog appears) because there is trouble with the field. I added a ValidationMessageFor in the field which tells me that the field doesn't contain a valid date:

@Html.ValidationMessageFor(model => model.dateAudit, "", new { @class = "text-danger" })

This is the HTML error message generated at this point for that field:

<input data-val="true" data-val-date="The field Date must be a date." data-val-required="The field Date is required." id="dateAudit" name="dateAudit" type="text" value="10/03/2015" class="hasDatepicker input-validation-error">

I also tried setting the culture for all applications in the config file:

<globalization uiCulture="fr-FR" culture="fr-FR" />

If I select a date like 1st of March, formatted as 01/03/2015, there's no problem and the model binding recognizes it, but when I select 20th of March, the field fails to validate (I suspect that it thinks I'm trying to enter 20 as the month).

I found this library which could help with client-side validation, but it looks like it isn't compliant with all browsers so I can't use it. I'm considering dropping client-side validation completely and only validating the date field in the back-end.

Thanks for your help.

EDIT: I forget to advice you that I used that jquery ui re-definition for datepicker:

/* French initialisation for the jQuery UI date picker plugin. */
/* Written by Keith Wood (kbwood{at}iinet.com.au),
              Stéphane Nahmani (sholby@sholby.net),
              Stéphane Raimbault <stephane.raimbault@gmail.com> */
(function (factory) {
    if (typeof define === "function" && define.amd) {

        // AMD. Register as an anonymous module.
        define(["../datepicker"], factory);
    } else {

        // Browser globals
        factory(jQuery.datepicker);
    }
}(function (datepicker) {

    datepicker.regional['fr'] = {
        closeText: 'Fermer',
        prevText: 'Précédent',
        nextText: 'Suivant',
        currentText: 'Aujourd\'hui',
        monthNames: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
            'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
        monthNamesShort: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',
            'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
        dayNames: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
        dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
        dayNamesMin: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],
        weekHeader: 'Sem.',
        dateFormat: 'dd/mm/yy',
        firstDay: 1,
        isRTL: false,
        showMonthAfterYear: false,
        yearSuffix: ''
    };
    datepicker.setDefaults(datepicker.regional['fr']);

    return datepicker.regional['fr'];

}));
Sparky
  • 98,165
  • 25
  • 199
  • 285
clement
  • 4,204
  • 10
  • 65
  • 133
  • Is your String.Format supposed to contain `new { @class = "form-control" }` inside it? I think it's supposed to be outside the format method call. – cbr Mar 10 '15 at 08:11
  • Thanks for your reply @GrawCube! The form-control is a CSS class applyed to the input in order to style it with the twitter bootstrap. I don't think it make some changes for validation. I just tried to remove that class, there still have the issue. – clement Mar 10 '15 at 08:14
  • jquery Globalize should work. Which version of jQuery are you using? –  Mar 10 '15 at 08:22
  • @StephenMuecke: thanks again :-) jquery Globalize, I don't want to use it because it's not enough browser compliant for customers... https://github.com/jquery/globalize#browser-support – clement Mar 10 '15 at 08:31

4 Answers4

1

You can create your own method to override the jquery.validator default method. Note the following code is probably an overkill for what you need (its part of my own plugin for a @Html.DatePicker() helper which renders a datepicker based on the server's CultureInfo)

// Override default date validator format to allow culture specific format
$.validator.methods.date = function (value, element) {
  return this.optional(element) || globalDate(value).isValid();
};

Note: Do not wrap the above method in document.ready()

Date.prototype.isValid = function () {
  return !isNaN(this.getTime());
}

var inputFormat = 'dd/MM/yyyy';

// datepicker.prototype.globalDate = function (value) {
function globalDate(value) {
  // Initialise a new date
  var date = new Date(0);
  if (value == undefined) {
    // Return todays date
    return date; // adjust to suit your needs
  }
  // Get the components of the format
  // The separator can be forward slash, hyphen, dot and/or space
  var regex = new RegExp(/([dMy]+)([\s/.-]+)([dMy]+)([\s/.-]+)([dMy]+)/);
  var format = regex.exec(inputFormat);
  // Get the components of the value
  regex = new RegExp(/(\d+)([\s/.-]+)(\d+)([\s/.-]+)(\d+)/);
  value = regex.exec(value);
  // Check the value is valid
  if (value === null || value[2] !== format[2] || value[4] !== format[4]) {
    // Its not valid
    date.setTime(Number.NaN);
    return date;
  }
  // TODO: What if year entered as 2 digits?
  var day = Number.NaN;
  var month = Number.NaN;
  var year = Number.NAN;
  if (format[1].charAt(0) === 'd') {
    // little-endian (day, month, year)
    day = parseInt(value[1]);
    month = parseInt(value[3]) - 1;
    year = parseInt(value[5]);
  } else if (format[1].charAt(0) === 'M') {
    // middle-endian (month, day, year)
    day = parseInt(value[3]);
    month = parseInt(value[1]) - 1;
    year = parseInt(value[5]);
  } else {
    // big endian (year, month, day)
    day = parseInt(value[5]);
    month = parseInt(value[3]) - 1;
    year = parseInt(value[1]);
  }
  date.setFullYear(year);
  date.setMonth(month);
  date.setDate(day);
  // Check its valid
  if (date.getDate() !== day || date.getMonth() !== month || date.getFullYear() !== year) {
    date.setTime(Number.NaN);
    return date;
  }
  return date;
}

Edit: Based on edited question (OP is using jQuery UI date picker) it could be just

$.validator.addMethod('date', function (value, element) {
  if (this.optional(element)) {
    return true;
  }
  var valid = true;
  try {
    $.datepicker.parseDate('dd/mm/yy', value);
  }
  catch (err) {
    valid = false;
  }
  return valid;
});
$('#dateAudit').datepicker({ dateFormat: 'dd/mm/yy' });

Side note: Recommend you use the strongly typed helper and pass the format string

@Html.TextBoxFor(m => m.dateAudit, "{0:dd/MM/yyyy}")
  • FYI This first method has a MAJOR flaw. You create a new date (current date) with " var date = new Date();". If Halloween (yay today) then this date becomes 10/31/2018. Lets say you're validating 6/1/2018. If you replace the year first (date.setFullYear(year);) and then the month (June) BEFORE you set the day, you get 6/31/2018. So before you can even get to the line that reads "date.setDate(day);" you've already got an invalid date. You could try to set the date first, but if you're validating a "31" date and it just happens to be February, you're also up the creek. This method does not work – Brian Lorraine Oct 31 '18 at 15:28
  • Maybe before you do the setmonth,setfullyear,setdate on the "date" object, set the date and month both to "1" first. Then do it again with the real numbers you want to change – Brian Lorraine Oct 31 '18 at 15:33
  • @BrianLorraine, Thanks - that was supposed to be `var date = new Date(0);` (now updated) –  Nov 01 '18 at 03:41
0

If you don't want to use Globalize library like @StephenMuecke advice you can set defaults of jquery datepicker like this in some kind of your init script or just in head of document:

$.datepicker.regional['fr'] = {
    clearText: 'Effacer', clearStatus: '',
    closeText: 'Fermer', closeStatus: 'Fermer sans modifier',
    prevText: '&lt;Préc', prevStatus: 'Voir le mois précédent',
    nextText: 'Suiv&gt;', nextStatus: 'Voir le mois suivant',
    currentText: 'Courant', currentStatus: 'Voir le mois courant',
    monthNames: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
    monthNamesShort: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun',
    'Jul', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc'],
    monthStatus: 'Voir un autre mois', yearStatus: 'Voir un autre année',
    weekHeader: 'Sm', weekStatus: '',
    dayNames: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
    dayNamesShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
    dayNamesMin: ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'],
    dayStatus: 'Utiliser DD comme premier jour de la semaine', dateStatus: 'Choisir le DD, MM d',
    dateFormat: 'dd/mm/yy', firstDay: 0,
    initStatus: 'Choisir la date', isRTL: false
};
$.datepicker.setDefaults($.datepicker.regional['fr']);
teo van kot
  • 12,350
  • 10
  • 38
  • 70
0

This occurs because your browser is not configured to format date according to the French Format: dd/MM/yyyy

Please tell your browser to be French

 $.datepicker.setDefaults( $.datepicker.regional[ "fr" ] );

In addition, you may need to use the EditorFor helper instead of TextBoxFor because EditorFor is kind intelligent more than TextBoxFor

   @Html.EditorFor(model=>model.dateAudit)

Note: you may need an editor and a display templates to design this helper.

Bellash
  • 7,560
  • 6
  • 53
  • 86
  • thanks for your reply, it makes sense but I already done the datepicker.setDefaults(datepicker.regional['fr']); in a init script (see my first post edited) – clement Mar 10 '15 at 09:34
-1

I've asked to remove the validation for Date fields because I didn't wanted to use the globalize trhird party module because of broiwser compatibility (browser-support for globalize module)

I've saw crappy but working solution in stackoverflow: ASP.NET MVC 4 avoid generation of data-val-date for datetime :

$.validator.addMethod('date', function (value, element) {
            return true; // since MVC data-val-date is put on EVERY vm date property. Default implementation does not allow for multiple cultures...
        });

I wait for nicer solution but it is my temporary solution...

Community
  • 1
  • 1
clement
  • 4,204
  • 10
  • 65
  • 133