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.
I fixed that with the following replace(/[^ -~]/g,'') as in
(new Date("7/15/2014").toLocaleString().replace(/[^ -~]/g,'')
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"
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.
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?
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(' ');
Related
I am building a self-depreciating list of dates and am using Date.parse() to convert rich text into a timestamp. Right now, we have a system where the user enters the date themselves, and I use a token on the backend to pull it through to that specific page.
I am trying to 'protect the user' from entering a wrong date format.
Currently, if they enter January 12th, 2016 it won't be able to parse and create a timestamp due to th. However, if they enter January 12, 2016 it can create a timestamp.
Is there a way to replace these ordinal numbers on a dynamic string? I've tried a simple jquery find/replace, but have had no luck with getting it to remove the ordinal date texts.
What I've tried:
$('.my-button').each(function() {
console.log($(this).text());
var text = $(this).text().replace('st, ', '');
$(this).text(text);
});
Any ideas on a solution, or a way to make Date.parse(): accept these extra characters?
For the simple case of removing an ordinal from a date string like "January 12th, 2016", you can use a regular expression like:
var re = /(\d{1,2})[a-z]{2}\b/i;
['January 12th, 2016','January 1st, 2016','January 2nd, 2016','20th February, 2015'].forEach(
function(s) {
document.write('<br>' + s.replace(re,'$1'));
}
);
However, parsing strings with Date.parse (and the Date constructor, they are equivalent for parsing) is strongly recommended against. Use a parser and define the format, do not leave it to chance.
There are libraries like moment.js, however it may be too big for your requirements. There are other dedicated parsers (check on GitHub), or if you only need to support one format, write one yourself. It only requires three or four of lines of code.
A leading zero for the day within a string seems to break the Javascript Date object in Chrome. There are also some inconsistencies between browsers, since Firefox handles the leading zero correctly, but fails when the zero is not included. See this example: https://jsfiddle.net/3m6ovh1f/3/
Date('2015-11-01'); // works in Firefox, not in Chrome
Date('2015-11-1'); // works in Chrome, not in Firefox
Why? Is there a good way to work around/with the leading zero?
Please note, the strings are coming from MySQL via AJAX and all dates will contain the leading zero, and I can fix this by formating the dates server-side. What format would work the best?
EDIT
Just to specify what my problem was, it looks like Chrome is applying a time zone to the YYYY-MM-DD format, which reverts the Nov. 1st date back to the Oct. 31st date (because of my EDT local time).
According to ECMA-262 (5.1):
The function first attempts to parse the format of the String according to the rules called out in Date Time String Format (15.9.1.15). If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
The date/time string format as described in 15.9.1.15 is YYYY-MM-DDTHH:mm:ss.sssZ. It can also be a shorter representation of this format, like YYYY-MM-DD.
2015-11-1 is not a valid date/time string for Javascript (note it's YYYY-MM-D and not YYYY-MM-DD). Thus, the implementation (browser) is able to do whatever it wants with that string. It can attempt to parse the string in a different format, or it can simply say that the string is an invalid date. Chrome chooses the former (see DateParser::Parse) and attempts to parse it as a "legacy" date. Firefox seems to choose the latter, and refuses to parse it.
Now, your claim that new Date('2015-11-01') doesn't work in Chrome is incorrect. As the string conforms to the date/time string format, Chrome must parse it to be specification compliant. In fact, I just tried it myself -- it works in Chrome.
So, what are your options here?
Use the correct date/time format (i.e. YYYY-MM-DD or some extension of it).
Use the new Date (year, month, date) constructor, i.e. new Date(2015, 10, 1) (months go from 0-11) in this case.
Whichever option is up to you, but there is a date/time string format that all specification compliant browsers should agree on.
As an alternative, why not use unix timestamps instead? In JavaScript, you would would multiply the timestamp value by 1000,
e.g
var _t = { time: 1446220558 };
var _d = new Date( _t.time*1000 );
Test in your browser console:
new Date( 14462205581000 );
// prints Fri Oct 30 2015 11:55:58 GMT-0400 (EDT)
There's a little benefit in it as well (if data comes via JS) - you'd save 2 bytes on every date element '2015-10-30' VS 1446220558 :)
I noticed some inconsistencies in Chrome vs. Firefox. I was basically trying to create a new date from a string I scraped off the Dom.
// Assume dateString was pull from a Dom node.
var dateString = 'Nov\xa025, 2013';
var date = new Date(dateString);
You will notice that dateString contains the ascii non-breaking space character in it. So when ran in the Chrome console, date == valid date. Firefox on the other hand doesn't like the ascii character and date != valid date.
The remedy would be to just replace the ascii with an actual space. I am curious as to if V8 is doing this cleaning of string for us, not just for new Date()?
No, both browsers parse string literals identically:
> 'Nov\xa025, 2013'.charCodeAt(3)
160
> 'Nov 25, 2013'.charCodeAt(3)
32
However, the Date constructors differ between the browsers. The EMCAScript specification requires only one date format (namely, YYYY-MM-DDTHH:mm:ss.sssZ) but custom date formats may be freely supported by an implementation:
If the String does not conform to that [ECMAScript-defined] format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
Apparently Chrome supports a custom date format that allows non-breaking spaces, where Firefox does not.
It leaves the string as is, you can try
dateString.charCodeAt(3)
to confirm that.
(EDIT: I am using chrome console for the code below)
I realize that javascript Date() is deprecated, but it's useful for something I'm currently working on; however there is a strange problem:
var x = new Date('999').getFullYear()
returns 999
while
var x = new Date('1000').getFullYear()
returns 999 as well, but
var x = new Date('10000').getFullYear()
returns 10000...
Does anyone know why do 4 digit numbers give the wrong .getFullYear()?
As the other answers suggest, you can't rely on Date.parse across browsers, especially if you are dealing with dates before 1000 CE.
As far as Chrome is concerned, it looks like the issue is how the browser deals with timezones. The following example suggests that before 1000 CE, Chrome parses the date in your local timezone on top of it; >= 1000 CE, it appears to first parse the date in UTC, then applies the timezone conversion:
> new Date('1000')
"Tue Dec 31 999 16:00:00 GMT-0800 (PST)"
> new Date('999')
"Tue Jan 01 999 00:00:00 GMT-0800 (PST)"
I'd be inclined to see this as a bug, but perhaps the Chromium team thinks it's a feature :).
The bottom line is that, if you want accurate date parsing for years, especially ancient years, you need to do some work yourself and/or use a library. Moment.js and Datejs both might help, but I doubt either deals well with ancient years. Moment.js seems to have the same issue (in Chrome):
> moment('999').year()
999
> moment('1000').year()
999
The best way I know of to accurately set dates based on ancient years is generally to a) always use UTC, and b) set the date manually:
var d = new Date();
d.setUTCFullYear('999');
d.getUTCFullYear(); // 999
d.setUTCFullYear('1000');
d.getUTCFullYear(); // 1000
In Chrome at least, this works with both strings and integers.
You might also be interested in the gregorian parser in the Timemap.js library, which handles ancient years with AD, CE, BC, and BCE extensions, as well as negative numbers.
Take a look at this page. You are passing a string which is passed to this parse method. The first argument isn't "years" unless you pass 3 integers. Finally while the class itself isn't being deprecated, some of it's members are (including getYear() which is being replaced with getFullYear() which you are using`)
This fiddle says otherwise on Firefox. Perhaps it's browser related?
alert(new Date('999').getFullYear()); yields NaN on Firefox 15.0.1
Hmm.. Chrome 21.0.1180.57 does yield '999'
var x = new Date('10000').getFullYear()
change it to
var x = new Date(10000).getFullYear()
you are sending an invalid datestring to the date constructor.
I need to format a javascript Date to send via json to the server. The server expects the time to be in the format according to this example
2011-08-31T06:49:28.931 -0700
which it conveniently tells me when I try to submit something like
2011-08-31T06:49:28.931 -07:00
The trouble I am having is with the timezone part, -0700. I've been looking at the Date API, and don't see a way to specify the timezone format. I can do d.getTimezoneOffset, but it returns 240 (Im in EDT I think) for me.
So, I can convert 240 to 0400 to represent 4 hours. I am worried however about correctness for other timezones. My questions are
1) How to convert the result of the getTimezoneOffset() into the required format, and how to determine what the sign should be (thats the part I am worried about)?
2) Is there a way to get the format off the date object itself so I don't have to do anything custom? If i do d.toString() I get "Wed Aug 31 2011 09:48:27 GMT-0400 (EDT)", so here the timezone part is in the format I want. So it might be possible. Maybe the best solution is to just use a regex to grab the timezone off d.toString()...
3) Extra credit: is the format the server requires some sort of standard?
Update: using match(/^.*GMT(-?\d*)/) returns "-0400" at index 1 of the array. Perhaps I should just use that? Im wondering if that regex will work for all timezones in the context of the sign.
Try this code:
var d=new Date(Date.now()); // sets your date to variable d
function repeat(str,count) { // EXTENSION
return new Array(count+1).join(str);
};
function padLeft(str,length,char) { // EXTENSION
return length<=str.length ? str.substr(0,length) : repeat(String(char||" ").substr(0,1),length-str.length)+str;
};
var str=padLeft(String(d.getFullYear()),4,"0")+"-"+
padLeft(String(d.getMonth()),2,"0")+"-"+
padLeft(String(d.getDate()),2,"0")+"T"+
padLeft(String(d.getHours()),2,"0")+":"+
padLeft(String(d.getMinutes()),2,"0")+":"+
padLeft(String(d.getSeconds()),2,"0")+"."+
d.getMilliseconds();
//str+=" GMT";
var o=d.getTimezoneOffset(),s=o<0?"+":"-",h,m;
h=Math.floor(Math.abs(o)/60);
m=Math.abs(o)-h*60;
str+=" "+s+padLeft(String(h),2,"0")+padLeft(String(m),2,"0");
alert(str);
You might want to use one of the date/time formatting libraries that bakes in support for this timezone format (such as http://jacwright.com/projects/javascript/date_format/). In any case, you're right: there really is no good way to control the format output.
As far as the regex goes I don't know that all browsers consistently use the GMT string format, so that may not be the best path forward.