Strange behaviour with spliting a string in Javascript
Asked Answered
P

3

8

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.

Piece answered 11/6, 2015 at 14:39 Comment(6)
Maybe it is no solution, but why don't you try to get the date by doing: var day = date[0]+date[1];Bawdry
I tryied yout code and when i retype the date it works fine, maybe you have some non prointable characters in thereAzelea
Interestingly, my edit makes the problem go away. It doesn't show strange characters in Chrome anymore. But yeah, you have unprintable characters in your string. If you edit the code, and put the cursor right after one of the / and the press the delete key, you will notice that / is not deleted. That means there is some other character that got deleted instead.Vaenfila
What are you using as an editor? Where did you copy the date string from?Schuler
parseInt parses a string into a number. Using it on numbers makes no sense, and can cause problems, e.g parseInt(1e300, 10).Spin
Right my string has hidden characters in it, if I retype it then it works so I need to find out how they get in there.Piece
R
16

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".

Retentive answered 11/6, 2015 at 14:46 Comment(3)
You can remove all non-ASCII characters with: newDate.replace(/[^\x00-\x7F]/g, "")Hypochondrium
I will accept this as an answer mostly because of the comment. This: newDate.replace(/[^\x00-\x7F]/g, "") makes everything work again and it's the simplest and quickest.Piece
You could also just do var parts = newDate.match(/\d+/g); Regardless of any extraneous characters it'll return [ "11", "06", "2015" ].Sweatbox
S
7

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.

Sweatbox answered 11/6, 2015 at 15:11 Comment(1)
If I could double-up-vote this, I would. Amazing work Jordan! This stumped me for almost a day until I added IE to my search and found this. So strange that this only happens in IE.Desalinate
S
0

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);
Sampling answered 11/6, 2015 at 14:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.