2

I have the following strings:

a='Check 134', a='2020-01-15T10:47:54Z', a='1234', a= some object

I want to check, whether the string is a date or not.

I tried:

new Date(a) instanceof Date &&
        !isNaN(new Date(a).getTime())

, but it returns true for a='Check 123', whenever it has numbers.

What could be a better solution?

Note: I do not want to use momentjs, because of project restrictions

Sarah Mandana
  • 1,474
  • 17
  • 43
  • have you tried typeof? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof – Smough Mar 11 '20 at 08:56
  • Is "1234" considered a valid date…? It *could* be interpreted as a UNIX timestamp, but it's probably not. Do you have a better definition for what you'd consider a valid date? Perhaps you'll want to enforce a particular format using a regex? – deceze Mar 11 '20 at 08:57
  • My dates are like this: 2020-01-15T10:47:54Z – Sarah Mandana Mar 11 '20 at 08:58
  • @Smough I wasn't able to find anything for typeof date. It's for strings, numbers etc. and my date is considered as string – Sarah Mandana Mar 11 '20 at 08:59
  • 1
    `/\d+-\d+-\d+T\d+:\d+:\d+Z/.match(a)`…? – deceze Mar 11 '20 at 09:05
  • please prefer this [answer](https://stackoverflow.com/a/10589791/5740276) – Savaj Patel Mar 11 '20 at 11:49

7 Answers7

2

Actually, the question should be: how to determine if a given string can be converted to a real Date object?

Now, more things than you like may be converted to a Date (e.g., try new Date("") or new Date(0)). So you may want to use your own restrictions as to what you want to convert. Here's a method that restricts input to either a real Date object or a String:

const canConvertToDate = trial => 
  [String, Date].includes(trial.constructor) && 
  !isNaN(new Date(trial));

console.log(`canConvertToDate(\`\`): ${canConvertToDate(``)}`);
console.log(`canConvertToDate(\`no dice\`): ${canConvertToDate(`no dice`)}`);
console.log(`canConvertToDate(new Date): ${canConvertToDate(new Date)}`);
console.log(`canConvertToDate(\`2020-03-03 00:00:00\`): ${
  canConvertToDate(`2020-03-03 00:00:00`)}`);
console.log(`canConvertToDate(0): ${canConvertToDate(0)}`);
console.log(`canConvertToDate(\`Wed, 11 Mar 2020 09:27:50 GMT\`): ${
  canConvertToDate(`Wed, 11 Mar 2020 09:27:50 GMT`)}`);
console.log(`canConvertToDate(new Date().toUTCString()): ${
  canConvertToDate(new Date().toUTCString())}`);
console.log(`canConvertToDate(134): ${canConvertToDate(134)}`);

// Please note the browser difference (Chrome / Firefox)
console.log(`canConvertToDate(\`134\`): ${canConvertToDate(`134`)}`);
.as-console-wrapper { top: 0; max-height: 100% !important; }

The previous snippet will not always give you the desired result (see comments).

Alternatively you can also write a parser of some kind to determine if the given string can be converted to Date, to be more (but not 100%) certain that a string is convertable to Date. Something like:

console.log(`tryParseDate(\`\`): ${tryParseDate(``).date}`);
console.log(`tryParseDate(new Date): ${tryParseDate(new Date).date}`);
console.log(`tryParseDate(\`Wed, 11 Mar 2020 09:27:50 GMT\`): ${
  tryParseDate(`Wed, 11 Mar 2020 09:27:50 GMT`).date}`);
console.log(`tryParseDate(\`check 134\`): ${tryParseDate(`check 134`).date}`);
console.log(`tryParseDate(\`3-3-2005\`, \`ddmmyyyy\`): ${
  tryParseDate(`03-03-2005`, `ddmmyyyy`).date}`);
console.log(`tryParseDate(\`12-22-1998 22:22:10.345\`, \`mmddyyyy\`): ${
  tryParseDate(`12-22-1998 22:22:10.345`, `mmddyyyy`).date}`);
console.log(`tryParseDate(\`29-02-2019 22:22:10.345\`, \`ddmmyyyy\`): ${
  tryParseDate(`29-02-2019 22:22:10.345`, `ddmmyyyy`).date}`);

function tryParseDate(someString, format = `yyyymmdd`) {
  const invalid = {cando: false, date: new Date(`invalid`)};
  
  if (someString.constructor !== String) { return { ...invalid, date: `Invalid Date: input not a string` }; }
  
  const between = (val, lower, upper) => val >= lower && val <= upper;
  const d = someString.split(/[/\- T:.]/g);
  
  if (d.length < 3) { return {...invalid, date: `Invalid Date: can't split to anything useful`}; }
  
  const formats = format.match(/(yyyy)|(mm)|(dd)/gi);
  const values = {
      year: +d[formats.findIndex(v => v === `yyyy`)], 
      month: +d[formats.findIndex(v => v === `mm`)], 
      date: +d[formats.findIndex(v => v === `dd`)] };

  const cando = !isNaN(values.year) && values.year > 0 &&
                !isNaN(values.month) && between(values.month, 1, 12)
                !isNaN(values.date) && between(values.date, 1, 31);
  
  if (!cando) { 
    return {...invalid, date: `Invalid Date: the given value is not valid`}; 
  }
  
  const date2Parse = `${d[formats.findIndex(v => v=== `yyyy`)]}/${
      d[formats.findIndex(v => v=== `mm`)]}/${
        d[formats.findIndex(v => v=== `dd`)]} ${
          d.slice(3).map((v, i) => !i ? v : i > 2 ? `.${v}` : `:${v}`).join(``)}`;
  const parsed = new Date(date2Parse);
  const checkLeap = parsed.getMonth() + 1 === values.month;
  const error = `Impossible Date: ${
    !checkLeap ? `${values.year} not a leap year` : `parsing failed`}`;
  return isNaN(parsed) || !checkLeap 
    ? {...invalid, date: error}
    : {cando: true, date: parsed};
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • There will be edge cases, for example, `Check 134` – StepUp Mar 11 '20 at 11:04
  • @StepUp: checked (see answer). What's the edge? – KooiInc Mar 11 '20 at 11:25
  • I've tried to use `console.log(Check 134: ${ canConvertToDate('Check 134')});` and it returns `true` – StepUp Mar 11 '20 at 11:30
  • 1
    Yes, that's in Chrome and it is a bug we should file I suppose. The Date from such a string deliver by Chrome: `Fri Jan 01 0134 00:00:00 GMT+0019 (Central European Standard Time)`. Firefox gives `Invalid Date` – KooiInc Mar 11 '20 at 11:35
  • ok, I see, could you check also `console.log(Check 1234': ${ canConvertToDate('1234')});` – StepUp Mar 11 '20 at 11:38
  • 1
    Done. Still, conversion from strings or from a few numbers is notoriously difficult and not simple. So something like `isDate` should be just the first line of defence imho. After that it would be wise to check if a converted `Date` conforms to what you reasonably can expect from it. – KooiInc Mar 11 '20 at 12:02
1

Since you already know the format of the dates that you want to check, use may be momentjs and check against the format

var moment = require("moment");
var res = moment("Check 123", "YYYY-MM-DD", true).isValid();
console.log(res);
joy08
  • 9,004
  • 8
  • 38
  • 73
  • @MoannaTabala offcourse it will return false,as the pattern does not match. This approach would best work, when you know the format ,that is being taken as second parameter in the function – joy08 Mar 11 '20 at 10:14
1

It is possible to check whether the string contains year, month, day. In addition, we can add some conditions to have stricter rules to define whether string is Date:

const isDate = str => {
    let [y,M,d,h,m,s] = str.split(/[- : T Z]/);
    return (y && M <= 12 && d <= 31) ? true : false;
} 

An example:

const isDate = str => {
    let [y,M,d,h,m,s] = str.split(/[- : T Z]/);
    return (y && M <= 12 && d <= 31) ? true : false;
}

console.log('Check 134', isDate('Check 134'))
console.log('2020-01-15T10:47:54Z', isDate('2020-01-15T10:47:54Z'))
console.log('1234', isDate('1234'))
console.log('13-13-13', isDate('13-13-13'))
StepUp
  • 36,391
  • 15
  • 88
  • 148
0

You can try to use Date.parse() and then check for a positive number (date > 0). For example:

Date.parse('2020-01-15T10:47:54Z') // 1579085274000
Date.parse('123') // -23225875200000
Date.parse('Check 134') // -57938551324000

https://jsfiddle.net/zmLbh0tu/

Nick
  • 383
  • 1
  • 4
  • 16
0

Here is a simple function that uses Date.parse() internally; when you pass a string with whitespaces to Date.parse(), it will ignore non-digit chars and will return positive; hence you need to remove the space characters before passing it to Date.parse()

const a= 'Check 134';
const b= '2020-01-15T10:47:54Z';
const c= '1234';
const tricky = '1'; // a number from -12 to 13 is valid

function isValidDate (str) {
  // optional condition to eliminate the tricky ones
  // since chrome will prepend zeros (000...) to the string and then parse it
  let noSpace = str.replace(/\s/g, '')
  if( noSpace.length < 3) {
    return false
  }
  return Date.parse(noSpace) > 0
}

console.log(a,isValidDate(a))
console.log(b,isValidDate(b))
console.log(c,isValidDate(c))
console.log(tricky,isValidDate(tricky))

// only in chrome
console.log("'1' is ", Date.parse('1') > 1 ," since it can be ", new Date('1').toString())

Edit: there are still some caveats to work Date in Chrome since it has open issue on that, there is lots of discussion around it, check this SO question the tricky corner cases in chrome are a more than that; It seems the best way to run it Chrome is to know your expected input type and validate it according to it; (e.g. RFC 2822/ISO 8601 date format). there are robust regexes for known date formats to use and confirm but trying to validate all available date formats in chrome at the moment have lots corner cases and potential false positives

Mechanic
  • 5,015
  • 4
  • 15
  • 38
-1

You can use Date.parse(). If it returns positive value then it's valid otherwise not. For ex:-

Date.parse("2020-01-15T10:47:54Z")
Nikhil G
  • 2,096
  • 14
  • 18
-2

Just use typeof operator. No need to user any external libraries.

if (typeof date === 'object') {
    // date is object
}
if (typeof date === 'string') {
//date is string
}