43

In a project I am working on, I need to validate a date entered into an <input type="date">

With Safari 4/5 (on Mac OSX) the Javascript fails to parse dates of the format YYYY-MM-DD, returning NaN instead of the expected epoch timestamp.

I am using the following technique to validate the field just before the form is submitted:

//value = '2010-06-21'
var stamp = Date.parse(value);
if (isNaN(stamp)) {
    //notify user
} else {
    value = new Date(stamp).format_mysql();
}

Where format_mysql() is a prototype function that formats the date (correctly) into MySQL Date-Time format (YYYY-MM-DD).

Replacing the -'s with /'s (YYYY/MM/DD) yields a "correct" timestamp.

I should note that the field should accept any date format, not just YYYY-MM-DD, and that though I would like to, I cannot use libraries like Date.js

How can I fix this, or is there a better way to parse/validate a date?

Austin Hyde
  • 26,347
  • 28
  • 96
  • 129
  • I have Safari 5.17, and this work fine for me (tested on iOS 5.12 aswell): when = Date.parse("2013-01-29 08:30"); – Orwellophile Jan 26 '13 at 08:36
  • The strings accepted by the `Date()` constructor vary across browsers, but the safest one does seem to be YYYY/MM/DD--see http://dygraphs.com/date-formats.html . – Noyo Jul 01 '15 at 13:42

4 Answers4

64

The behavior of the Date.parse method is implementation dependent, on ECMAScript 5, this method can parse ISO8601 formatted dates, but I would recommend you to make the parsing manually.

Some time ago I've made a simple function, that can handle a format specifier argument:

function parseDate(input, format) {
  format = format || 'yyyy-mm-dd'; // default format
  var parts = input.match(/(\d+)/g), 
      i = 0, fmt = {};
  // extract date-part indexes from the format
  format.replace(/(yyyy|dd|mm)/g, function(part) { fmt[part] = i++; });

  return new Date(parts[fmt['yyyy']], parts[fmt['mm']]-1, parts[fmt['dd']]);
}

parseDate('06.21.2010', 'mm.dd.yyyy');
parseDate('21.06.2010', 'dd.mm.yyyy');
parseDate('2010/06/21', 'yyyy/mm/dd');
parseDate('2010-06-21');

Also you could detect the ECMAScript 5 behavior to parse ISO formatted dates, you can check if the Date.prototype.toISOString is available, e.g.:

if (typeof Date.prototype.toISOString == "function") {
  // ES5 ISO date parsing available
}
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 3
    +1, you beat me to it. As advice to the OP, I would consider moving away from a single input element for dates to avoid the situation where a user inputs dates in their own locale (e.g. mixing up dd and mm). Either that or use a read-only input and a date picker control. – Andy E Jun 21 '10 at 15:33
  • Unless I understand wrongly, your function would only apply when I know the date format before hand, correct? @Andy E's head: Separate inputs is a good idea, and something I will look into. Thanks for the tip. – Austin Hyde Jun 21 '10 at 15:56
  • @Austin, yes, you have to know the format somehow, otherwise you will have a lot of ambiguities, e.g. `"01/02/2010"` vs `"02/01/2010"`, which one January 2, and which is February 1? – Christian C. Salvadó Jun 21 '10 at 16:10
  • @CMS how to just display dd/mm/yyyy using ur function –  Nov 09 '11 at 07:38
  • @CMS output using ur function Sun May 03 1908 00:00:00 GMT+0530 (India Standard Time) but just need 03/09/1908 –  Nov 09 '11 at 07:41
  • 1
    I found that `typeof Date.prototype.toISOString == "function"` returned true on Safari 5.1.2 so I had to change this check for `Date.parse` – brendo Dec 12 '11 at 23:55
  • As brendo mentioned, older versions of Safari on iOS have the `toISOString` function but cannot parse correctly. Use something like this to detect... `var canParseIso = !isNaN( Date.parse('2012-01-01T01:01:00.000Z') )` – two-bit-fool Oct 17 '12 at 01:23
23

Generally DD-MM-YYYY format is not support in safari.

value = 2010/06/21 ; //should work.

(or)

value = new Date('2010-06-21'.replace(/-/g, "/"));

Vishnu Vardhana
  • 497
  • 5
  • 9
3

the field should accept any date format

You don't mean what you think you mean.

  • It's difficult to reliably distinguish between M/D/Y (US) and D/M/Y (UK). D.M.Y is more common in the UK, but by no means universal.
  • Good luck with dates before around 1600 — the Gregorian (solar) calendar was introduced in 1582, and was only (mostly universally) adopted in the 20th century (Wikipedia gives 1929). February 30 was a valid date Sweden.
  • OS X gives you a choice of 13 (!) calendars, though the default is Gregorian.

Instead, I recommend using a calendar widget. I think JQuery has one, but ICBW.

tc.
  • 33,468
  • 5
  • 78
  • 96
  • 1
    Well, I'm targeting a fairly small US audience, looking at dates from 2000+, and more than likely the Gregorian calendar. Yes, jQuery has a datepicker widget as part of its UI package, but I cannot use it :(. When I say "any date format" I mean "June 21, 2010", "6/21/10", "2010-06-21", etc all represent the same date. – Austin Hyde Jun 21 '10 at 15:49
  • If you mean "any vaguely common date format in the US", you probably have to write your own. – tc. Jun 28 '10 at 11:10
1

You can get an unambiguous date from input, but you still need to check it, so a user doesn't give you April 31.

<fieldset id= "localdate"> 
<select id= "monthselect" size= "1"> 
<option selected= "selected"> January</option> 
<option> February</option> 
<option> March</option> 
<option> April</option> 
<option> May</option> 
<option> June</option> 
<option> July</option> 
<option> August</option> 
<option> September</option> 
<option> October</option> 
<option> November</option> 
<option> December</option> 
</select> 
<label> Date: <input name= "inputdate" size= "2" value= "1"> </label> 
<label> Year: <input name= "inputyear" size= "4" value= "2010"> </label> 
</fieldset> 

shortcut for document.getElementsByName

function byName(s, n){ n= n || 0; return document.getElementsByName(s)[n]; }

function getDateInput(){
    var day, y= parseInt(byName('inputyear').value),
    m= byName('monthselect').selectedIndex,
    d= parseInt(byName('inputdate').value);

    if(!y || y<1000 || y> 3000) throw 'Bad Year '+y;
    if((!d || d<1 || d> 32) throw 'Bad Date '+d;
    day= new Date(y,m,d);
    if(day.getDate()!= d) throw 'Bad Date '+d;
    value= day.format_mysql();
}

you can preset the fields to reflect the current date onload

onload= function(){
    var now= new Date();
    byName('inputyear').value= now.getFullYear();
    byName('monthselect').selectedIndex= now.getMonth();
    byName('inputdate').value= now.getDate();
}
kennebec
  • 102,654
  • 32
  • 106
  • 127