Strange behaviour with spliting a string in Javascript

2.5k views Asked by At

I am trying to do something relatively simple. I have a date in this format dd/MM/yyyy eg:

var newDate = "‎11‎/‎06‎/‎2015";

And I want to convert it to a date.

This code only works in Chrome and Firefox:

new Date(newDate)

In IE11 I get Nan

So I am trying to do this:

var parts = newDate.split("/");
var year = parts[2].trim();
var month = parts[1].trim();
var day = parts[0].trim();
var dt = new Date(Number(year), Number(month) - 1, Number(day));

Which should work, but I have encountered a very strange bug.

If you try this code:

function myFunction() {
  var newDate = "‎11‎/‎06‎/‎2015";
  var parts = newDate.split('/');
  var year = parts[2].trim();

  var a = year;
  var b = Number(year);
  var c = parseInt(year, 10);
  var d = parts;
  var n = a + "<br>" + b + "<br>" + c + "<br>" + d;
  document.getElementById("demo").innerHTML = n;
}
<p>Click the button to see the parse error.</p>

<button onclick="myFunction()">Try it</button>

<p id="demo"></p>

Then in IE it adds a mystery character and it prints out ý2015 and in chrome it prints out ?2015.

In fact the value of parts in IE is : ý11ý,ý06ý,ý2015 In Chrome: ?11?,?06?,?2015

I can't understand where these mystery characters come from! My original string is just "‎11‎/‎06‎/‎2015"

There seems to be no way for be to do something so simple, such as parsing an integer from a simple string.

Fiddle doesn't show the hidden characters but I believe they are still there because Number("2015") results in NaN as you can see clearly here Any ideas?

UPDATE

There are indeed hidden characters in the string, and after investigation I found out that they are created like this:

var date = new Date();
var dateToSave = date.toLocaleDateString();

but only in IE.

In Chrome or Firefox the output doesn't contain the U+200E left-to-right mark but in IE it does!

Removing toLocaleDateString() and replacing it with kendo.toString(selectedValue, "dd/MM/yyyy") fixed the problem.

For the record I also tried moment.js and the line: moment(selectedValue).format("DD/MM/YYYY") but for some reason in IE11 there was one hidden U+200E character at the very beginning of the result string.

3

There are 3 answers

3
Scimonster On BEST ANSWER

I ran "‎11‎/‎06‎/‎2015".split('').map(function(s){return s.charCodeAt(0)}) (to get the Unicode values) in my console, and found something interesting: [8206, 49, 49, 8206, 47, 8206, 48, 54, 8206, 47, 8206, 50, 48, 49, 53]

You have a U+200E left-to-right mark in there. I don't know how it got there.

Remove it, and you'll be fine.

Here, you can copy and paste the string from me: "11/06/2015".

0
Deep Kakkar On

For older browsers, you need to write a function that will parse the string.

The following function will create a Date.fromISO method- if the browser can natively get the correct date from an ISO string, the native method is used.Some browsers got it partly right, but returned the wrong timezone, so just checking for NaN may not do.

(function(){
var D= new Date('2011-06-02T09:34:29+02:00');
if(!D || +D!== 1307000069000){
    Date.fromISO= function(s){
        var day, tz,
        rx=/^(\d{4}\-\d\d\-\d\d([tT ][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
        p= rx.exec(s) || [];
        if(p[1]){
            day= p[1].split(/\D/);
            for(var i= 0, L= day.length; i<L; i++){
                day[i]= parseInt(day[i], 10) || 0;
            };
            day[1]-= 1;
            day= new Date(Date.UTC.apply(Date, day));
            if(!day.getDate()) return NaN;
            if(p[5]){
                tz= (parseInt(p[5], 10)*60);
                if(p[6]) tz+= parseInt(p[6], 10);
                if(p[4]== '+') tz*= -1;
                if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
            }
            return day;
        }
        return NaN;
    }
}
else{
    Date.fromISO= function(s){
        return new Date(s);
    }
}
})()

Result will be:

var start_time = '2012-06-24T17:00:00-07:00';
var d =  Date.fromISO(start_time);
var month = d.getMonth();
var day = d.getDate();

alert(++month+' '+day); // returns months from 1-12

Below function worked for IE 8 and below.

// parse ISO format date like 2013-05-06T22:00:00.000Z
 function convertDateFromISO(s) {
 s = s.split(/\D/);
  return new Date(Date.UTC(s[0], --s[1]||'', s[2]||'', s[3]||'', s[4]||'', s[5]||'', s[6]||''))
  }

You can test like below:

   var currentTime = new Date(convertDateFromISO('2013-05-06T22:00:00.000Z')).getTime();
   alert(currentTime);
1
Jordan Running On

Scimonster astutely figured out your problem, and dan explained how to strip non-ASCII characters, but there's an easier way: Just use a regular expression that matches digits only. That way you don't have to use split or trim or strip anything out:

function go() {
  var newDate = "‎11‎/‎06‎/‎2015";
  var expr = /\d+/g;
  var parts = newDate.match(expr);
  
  document.getElementById("result").innerHTML =
    "Parts: " + parts +
    "<br>Year: " + parts[0] +
    "<br>Month: " + parts[1] +
    "<br>Day: " + parts[2];
}
<button onclick="go()">Try me</button>
<div id="result"/>

This will work whether your string is "‎11‎/‎06‎/‎2015" or "11-6-2015" or junk11/06/2016junk.