moment.js date validation from array of formats
Asked Answered
V

3

7

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?

Volgograd answered 16/6, 2017 at 9:31 Comment(4)
Scope. 2 functions in your code. return true is inside the 2nd one, return false is in 1st one. If you add console.log("some text"); above return true, you will see what i mean.Terminator
You're right, I cannot use .forEach(), I have to use for...of. Can you post your comment as an Answer I can confirm? Thank youVolgograd
i already posted my comment as an answer with a very lazy solution that still worksTerminator
**edited answer, used for of this timeTerminator
M
9

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>
Menard answered 16/6, 2017 at 10:48 Comment(0)
T
3

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;
};
Terminator answered 16/6, 2017 at 9:50 Comment(2)
Even if I changed the accepted answer to VincenzoC's one afterwards, your explanation of scopes in my function was very useful to me. Thanks.Volgograd
Sure! I use momentjs a lot and didnt know you could do that without loops! Nice!Terminator
J
0

You can pass to moment array of allowed formats -

const allowedDateFormats = [
  "DD/MM/YYYY",
  "D/M/YYYY",
  "DD.MM.YYYY",
  "D.M.YYYY",
  "DD. MM. YYYY",
  "D. M. YYYY",
];

const multiDateValidator = (dateStr) =>
  moment(dateStr, allowedDateFormats, true).isValid();
Jefferson answered 3/11, 2023 at 6:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.