Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

moment.js date validation from array of formats

I need to test an input for proper date format. I want to accept several date formats so I created a validating function that test if at least one of the formats is OK and in that case return true. I use moment.js to test the date. When I simply type the condition with hard-coded string date formats, the function works properly:

var multiDateValidator = function (value)
{
    if ((moment(value, 'DD/MM/YYYY', true).isValid()) ||
        (moment(value, 'D/M/YYYY', true).isValid()) ||
        (moment(value, 'DD.MM.YYYY', true).isValid()) ||
        (moment(value, 'D.M.YYYY', true).isValid()) ||
        (moment(value, 'DD. MM. YYYY', true).isValid()) ||
        (moment(value, 'D. M. YYYY', true).isValid())) {
        return true;
    }

    return false;
};

But if I want to use the list of allowed date formats, it doesn't work, it never returns true.

var allowedDateFormats = ['DD/MM/YYYY', 'D/M/YYYY', 'DD.MM.YYYY', 'D.M.YYYY', 'DD. MM. YYYY', 'D. M. YYYY'];

var multiDateValidator = function (value)
{
    allowedDateFormats.forEach(function(dateFormat)
    {
        if (moment(value, dateFormat, true).isValid()) {
            return true;
        }
    });

    return false;
};

What's wrong with the second function? I know I'm not too good at JavaScript, but it should work, shouldn't it?

like image 847
Ondrej Vencovsky Avatar asked Jun 16 '17 09:06

Ondrej Vencovsky


2 Answers

The is no need to use for or forEach loop. Moment provides moment(String, String[], String, Boolean); method for parsing string using multiple formats.

As the docs says:

If you don't know the exact format of an input string, but know it could be one of many, you can use an array of formats.

Starting in version 2.3.0, Moment uses some simple heuristics to determine which format to use. In order:

  • Prefer formats resulting in valid dates over invalid ones.
  • Prefer formats that parse more of the string than less and use more of the format than less, i.e. prefer stricter parsing.
  • Prefer formats earlier in the array than later.

Here a working live sample:

var allowedDateFormats = ['DD/MM/YYYY', 'D/M/YYYY', 'DD.MM.YYYY', 'D.M.YYYY', 'DD. MM. YYYY', 'D. M. YYYY'];

var multiDateValidator = function (value){
  return moment(value, allowedDateFormats, true).isValid();
};

var test = ['01/01/2017', '01.01.2017', '2017-Jan-01'];
for(var i=0; i<test.length; i++){
  console.log(test[i], multiDateValidator(test[i]));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
like image 88
VincenzoC Avatar answered Sep 28 '22 08:09

VincenzoC


As i mentioned in my comment, return true returns to the inner function, but it doesnt stop the outer function from executing, so your outer function gets executed everytime (ie every time return false is executed)

my solution would be:

var allowedDateFormats = ['DD/MM/YYYY', 'D/M/YYYY', 'DD.MM.YYYY', 'D.M.YYYY', 'DD. MM. YYYY', 'D. M. YYYY'];

var multiDateValidator = function (value)
{  
  for(var dateFormat of allowedDateFormats) {
    if (moment(value, dateFormat, true).isValid()) {
      return true;
    }
  }  
  return false;
};
like image 21
Nodir Rashidov Avatar answered Sep 28 '22 08:09

Nodir Rashidov