51

In IE 11, I'm getting funny results with ToLocaleDateString(). The string returned looks fine in the browser, e.g. "1/28/2014 11:00:46 AM", but then if I copy and paste that value into a plain text editor, it looks like this: "?1?/?28?/?2014 ?11?:?00?:?46? ?AM".

Interestingly, if I paste the text into a Microsoft product, it looks fine... The issue is that if you try to use the value programmatically to create a date, it's invalid. You can test this by just opening up a console in IE11 and creating a new date, using ToLocaleDateString() on it, and then trying to use the resulting string to create a new date in javascript or in the language of your choice (I'm using ASP.NET here...).

Am I doing something wrong, or is there some other way I'm supposed to be interacting with the javascript Date? How can I get rid of those funky symbols?

Edit: Thanks to the comment below I was able to figure out what the unshown characters are, they're Left-To-Right marks. Depending on the editor I paste the values into and the encoding that the editor is set to use, the text will show up differently: sometimes with "?", sometimes without.

ctb
  • 1,212
  • 1
  • 11
  • 24
  • What "plain text editor" are you using, and what encoding does it use? – Bergi Jan 28 '14 at 18:40
  • 1
    Please do a `var str=(new Date).toLocaleDateString();for(var i=0;i – Bergi Jan 28 '14 at 18:41
  • 2
    I was using "TextPad" as my editor. It shows up that way in ANSI, DOS, UTF-8, and Unicode. When I paste it into notepad, it doesn't show up. Running your javascript yields: var str=(new Date).toLocaleDateString();for(var i=0;i – ctb Jan 28 '14 at 19:59
  • Thanks, though it would have been better if you [edit]ed your post and put the results there in a tabular format :-) – Bergi Jan 28 '14 at 22:33
  • I'm having the same issue. To answer ctb, here's the results from IE11: undefined 0 8206 ? 1 55 7 2 8206 ? 3 47 / 4 8206 ? 5 54 6 6 8206 ? 7 47 / 8 8206 ? 9 50 2 10 48 0 11 49 1 12 52 4 – Josh Pollard Jul 07 '14 at 01:44
  • 14
    It is a bug in IE11 https://connect.microsoft.com/IE/feedback/details/811107/ You can use toLocaleDateString().replace(/\u200E/g, '') to workaround it. – Chase Aug 28 '15 at 20:25
  • 1
    Thanks, that's been driving me insane – Gordon Thompson Feb 03 '16 at 13:54
  • Thanks, Chase!!! – Shulyk Volodymyr Apr 10 '19 at 16:28
  • const currentDay = new Date().toLocaleString('en-us', { weekday: 'long' }); I was trying to do a string compare with day of the week like this currentDay === "Monday" and IE11 was always returning false... because the string length of currentDay is 7 where as Monday is only 6. After adding .replace(/\u200E/g, '') it worked. – giri-jeedigunta Sep 30 '19 at 12:14

5 Answers5

49

I fixed that with the following replace(/[^ -~]/g,'') as in

(new Date("7/15/2014").toLocaleString().replace(/[^ -~]/g,'')
vldmrrr
  • 748
  • 6
  • 8
28

The issue is that if you try to use the value programmatically to create a date, it's invalid.

...

Am I doing something wrong, or is there some other way I'm supposed to be interacting with the javascript Date?

Yes, you are doing it wrong. You shouldn't be using a function intended to format something for locale-specific human display and expect the output to be machine parsable. Any of the output of toLocaleString, toLocaleDateString, or toLocaleTimeString are meant for human-readable display only. (As Bergi clarified in comments, toString is also meant for human display, but ECMA §15.9.4.2 says it should round-trip)

You are likely getting the LTR markers because your display locale is RTL. Besides this, consider that the locale will always affect the output. Perhaps your locale uses dd/mm/yyyy formatting instead of mm/dd/yyyy formatting. Or perhaps your locale requires Asian or Arabic characters. These are all considerations when determining a display format, but are never appropriate for machine parsing.

Also consider that the ECMAScript specification does not define any particular formatting rules for the output of these methods, and different browsers will yield different results.

If the intent is anything other than to display to the user, then you should use one of these functions instead:

  • toISOString will give you an ISO8601/RFC3339 formatted timestamp
  • toGMTString or toUTCString will give you an RFC822/RFC1123 formatted timestamp
  • getTime will give you an integer Unix Timestamp with millisecond precision

All of the above will return a UTC-based value. If you want the local time, you can either build your own string with the various accessor functions (getFullYear, getMonth, etc...), or you can use a library such as moment.js:

This uses moment.js to return an ISO8601 formatted local time + offset from a date:

moment(theDate).format()   // ex:  "2014-08-14T13:32:21-07:00"
Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • I think I understand. So, if you were building a control that asked for a birthdate, and you had to accommodate for other foreign locales (RTL, dd/mm/yy, etc.), how would you get something safely parsable (such as a UTC timestamp) back to the server, while still presenting the selected date to the user in a localized format? – ctb Aug 14 '14 at 21:28
  • That's exactly why so many date input controls have you pick from a calendar rather than parsing input. There are some ways though. Moment.js has input parsing for various locales, and the HTML5 date/time/datetime inputs have some as well. The `Date` object can do some parsing too, and it should work with whatever the current locale is. But there's no one way to do it that will accept everything a user might enter. Most of the time, you would want to give your user some hint of expected input format. – Matt Johnson-Pint Aug 14 '14 at 22:18
  • Then again, if it were a simple date (such as a birthdate), I likely wouldn't want to convert that to UTC. There's a difference between a whole date and a date+time. The javascript `Date` object is really a date+time object. – Matt Johnson-Pint Aug 14 '14 at 22:20
  • 2
    While the `toString` output is meant for human-readable display, it is not *only* meant for that. Have a look at [§15.9.4.2](http://es5.github.io/#x15.9.4.2): "*With a `Date` object `x`, it is expected that `x.valueOf() === Date.parse(x.toString())`*" - of course that doesn't make it an inter-implementation exchange format, but it needs to parsable somehow. – Bergi Aug 15 '14 at 00:36
  • 1
    @Bergi - Thanks. And in the same section, it says that `toLocaleString` is *not* required to do so. Edited my answer. :) – Matt Johnson-Pint Aug 15 '14 at 00:39
3

For completeness, answer form:

On my system, IE 11's Date object's method toLocaleDateString results in "7/6/2014" when run in the Console which is represented as the following bytes:

00000000  22 e2 80 8e 37 e2 80 8e  2f e2 80 8e 36 e2 80 8e  |"â.Z7â.Z/â.Z6â.Z|
00000010  2f e2 80 8e 32 30 31 34  22                       |/â.Z2014"|

The non-printables are 0xe2 0x80 0x8e throughout which is the UTF-8 representation of Unicode Code Point U+200E. Which is, as the comments above say, the LEFT-TO-RIGHT MARK.

This JSFiddle doesn't seem to have trouble using the value returned from toLocaleDateString() to get back to a date. At least in my IE 11.0.9600.17239 with Update Version 11.0.11 (KB2976627). So maybe only the console adds the extra characters?

opello
  • 3,254
  • 2
  • 23
  • 23
  • This explains what the characters are, but that was not the mystery. I think the bigger question was how to get back to an actual date if you're starting out with the output from ToLocaleDateString() – ctb Aug 14 '14 at 18:58
  • I updated the answer to include a JSFiddle link that doesn't seem to experience the problem. – opello Aug 14 '14 at 20:13
3
function FixLocaleDateString(localeDate) {
    var newStr = "";
    for (var i = 0; i < localeDate.length; i++) {
        var code = localeDate.charCodeAt(i);
        if (code >= 47 && code <= 57) {
            newStr += localeDate.charAt(i);
        }
    }
    return newStr;
}

Only returns digits and the / character. Seems to make this work:

new Date(FixLocaleDateString(new Date("7/15/2014").toLocaleString()));

Returns the correct date. Without the call to FixLocaleDateString(), the result would be an invalid date.

Evan Machusak
  • 694
  • 4
  • 9
  • This looks like it'd work, so I'll mark it as an answer. It just feels like a hack though. Is it just wrong to try to get a Date object from the output of ToLocaleDateString()? What if a date picker populates an input with ToLocaleDateString()? How should you get the date on the server side? If you just grab the value from the input, it's going to have those Left-To-Right chars, which will mess you up. – ctb Aug 14 '14 at 19:08
  • Depending on the server side technology you're using, the left/right characters are correctly handled (i.e., ignored). I'm using this with an MVC 5 web service, transmitting the inputs as strings which I then use parse via DateTime.Parse - which can handle strings in any encoding, including strings containing whitespace characters like the Unicode byte order marks and the like. Make no mistake. This is a hack. It's a work around for a bug in IE's implementation of JavaScript. Webkit browsers don't suffer from this problem, so FixLocaleDateString is not necessary for them. – Evan Machusak Aug 28 '14 at 14:42
1
var startDateConverted = new Date(start).toLocaleString().replace(/[^A-Za-z 0-9 \.,\?""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]*/g, '')

if you also want to remove time use .split(' ').slice(0, -1).join(' ');

nikunjM
  • 560
  • 1
  • 7
  • 21