My Rails application returns JSON object with two Time attributes as a String but Firefox doesn't parse them correctly to Date objects.
A sample JSON object:
{
from: '/Date(2014-11-14 11:26:00)/',
to: '/Date(2014-11-15 12:00:00)/',
...
}
The JSON objects are created with the following Ruby method:
def create_json
{
from: '/Date(' + (self.starts_at.to_s) + ')/',
to: '/Date(' + (self.ends_at.to_s) + ')/',
...
}
In order to parse the JSON object, a regex extracts the argument from the string value and passes it as a string or a numeric value to the Date constructor and tries to instantiate two new Date objects:
var from = new Date('2014-11-14 11:26:00')
var to = new Date('2014-11-15 12:00:00')
This works in Chrome but fails in Firefox.
I tested a couple of Ruby (on Rails) methods to find out which works best to instantiate new Date objects in JavaScript. The results are:
Ruby Class Time .to_s (Link):
The output of this method works in Google Chrome but doesn't work in Firefox.
Rails .to_formatted_s(:rfc822) (Link):
The output of this method works in Google Chrome and works limited in Firefox.
It didn't work for me in Firefox when I was trying to parse dateStrings that were using German day abbreviations (e.g. 'Do, 13 Nov 2014 ...' instead of 'Thu, 13 Nov 2014 ...')
Ruby Class Time .to_i.to_s (Link)
The output of this method doesn't work in Google Chrome and doesn't work in Firefox either because it returns seconds instead of milliseconds since the Epoch.
converted Ruby class Time (... .to_i * 1000).to_s:
The output of this method works in Google Chrome and in Firefox - finally ;-).
Hope this saves you some pain!
Related
I have already tried out this question but it didn't solve my question.
I have a PHP server which sends a date via JSON to the user where it is then processed by Javascript:
PHP: 'date' => date('D M d Y H:i:s O', strtotime($array['Time']))
Javascript var time = new Date(data.date).toLocaleString()
But instead of getting 18. January 2015 ..., I get 3. March 5877521 -596:-31:-23 GMT+0:53:28. What is wrong there?
Some things you might need to know: The server has the central european timezone as well as the date sent. I am trying (above is only an example) to internationalize the date with javascript.
Why pass a string? JS's date constructor will accept a timestamp:
var time = new Date(<?php echo strtotime($array['Time']) ?>000);
Note the 000 in there. JS uses milliseconds, while strtotime returns in seconds, so effectively you'd be building:
var time = new Date(12345678000);
^^^^^^^^---seconds from php
^^^---instant conversion to milliseconds.
I had to parse the timestamp as in Marc B's answer into an int (why ever?):
I solved it now: new Date(parseInt(data.date)) works
First, note that this error is quite common on the web, common enough that it's not [just] a "you" problem.
The reason for the weird March date millions of years in the future seems to be a bug in specific JavaScript engines. I noticed the same date appeared when some tests of ours were run in Chutzpah, a Jasmine test runner for Windows, when our test had an invalid date cast to a locale string and we ran only on its embedded setup from the command line (aka, not in a separate browser).
For our case, it turns out it was PhantomJS that was causing the issue. (PhantomJS used WebKit as its engine. I'd imagine your issue was from an engine with a similar lineage.)
Here's a minimal example that causes the error:
in a file called phantonTest.js
console.log(new Date('').toLocaleString('en-us', { timeZoneName: 'short' }));
phantom.exit();
then execute it...
>phantomjs.exe phantomTest.js
March 3, 5877521 at -8:-31:-23 GMT-4:56:02
You get a slightly date, but are performing a similar operation. You likely have an invalid date coming from strtotime($array['Time']) and the user a similar browser engine. QED, etc.
I have the following code
var date = Ext.Date.format('2013-04-02', 'Y-n-j')
var formattedDate=Ext.Date.format('2013-04-02', 'M d')
console.log(date);
console.log(formattedDate);
On Chrome it correctly prints out the date on the console. In Safari it fails. Who is correct and more importantly what is the best way to handle this?
Safari throws following error:
TypeError: 'undefined' is not an object (evaluating 'utilDate.monthNames[month].substring')
I can't explain why it would work in Chrome, but you're missing a single quote after the second .format(
Safari is silently not known for this bug, seen on ipad : not handling such a native Date creation new Date("2013-04-02").
This may be the issue and there you will have to do the splitting work before and so provide directly the Date object : new Date(2013, 3, 2) instead of your date string.
That fully explains your issue : a not parsed Date answers no month with getMonth, thus there is no monthName for this month that is not a month and thus substring fails when called from something that doesn't have this method since it's not a string.
I'm returning some json from one of my controllers similar to the following:
render :json => {
:date => "new Date(new Date('Jan 01 2000').getTime() + #{500.seconds.to_i} * 1000)"
}
I'm expecting it to return a new date set to 500 seconds past midnight (or exactly 00:08:20). This json gets passed and processed by a JavaScript function that's expecting a date, however it seems to be interpreting it as a string.. I get an error message similar to:
Type mismatch. Value new Date(new Date('Jan 01 2000').getTime() + 500
* 1000) does not match type date
Running that in the Firebug console produces a perfectly valid date, however. How can I get JavaScript to inerpet my json as a date?
Edit
To provide a little more context I'm sending a series of JSON requests to a JavaScript function that build charts using Google's Visualization API.
JSON is not Javascript, it is just a data serialization format, much like XML.
What you have is actually a string of Javascript code that you need to interpret (or avoid having to interpret, preferably). The only way for that to be valid JSON is, indeed, for it to be a string value. Even then, it should not parse correctly without being inside an array or an object property.
You should refactor whatever's returning that Javascript code to instead return just 'Jan 01 2000', and then do that processing in Ruby. Or however it would be best refactored; it's hard to tell without more context.
I've been banging my head over this one all day. No matter how I initialize a Javascript Date I cannot seem to get a valid Date object... I'm assuming the Date is invalid and not working properly by inspecting it with Chrome's debugger, which has the value '__proto__: Invalid Date'.
I've tried all of the following:
var d = new Date();
var d = new Date('2012-10-08');
var d = new Date('2012-10-08 00:00:00');
var d = new Date(Date('2012-10-08'));
var d = new Date(Date.parse('2012-10-08'));
var d = new Date(2012,10,08);
var d = new Date("October 13, 1975 11:13:00");
Along with countless other attempts.
This is presenting a problem in iOS where I'm trying to get values from these Date objects but every function just returns NaN. I'd prefer to avoid having to use external libraries or have to convert YYYY-MM-DD format into any other format since I'm trying to get this to work with an HTML5 input type="date" with minimal code for a mobile site.
Essentially this boils down to: What are the parameters that make a Date object valid?!
Do not trust the Date object to parse strings, you must do it manually. Given the format 2012-10-08,
function stringToDate(s) {
s = s.match(/\d+/g);
if (s) {
return new Date(s[0], --s[1], s[2]);
}
}
You may want to do some validation of the input string and the resulting date object, the above just shows the conversion.
Edit
BTW, the only string format that seems to be parsed consistently in all browsers is the US-specific month/date/year format. There is no specification to support its use, nor is there any reason to believe browsers will continue to support it other than pragmatism and "legacy" reasons.
For the vast majority of regions, '2/3/2012' is interpreted as 2 March, so getting 3 February might be unexpected.
Once older versions of IE are no longer in use (probably a few years yet), it should be safe to use the ISO8601 extended format per ECMA-262. However, even browsers that support it are inconsitent. e.g given:
new Date('2011-02-29');
Firefox 15 returns 'Invalid Date', IE 9 and Chrome 22 return a date object for 1 March, 2011.
There are three ways of calling the method:
The number of milliseconds from the epoch:
new Date(milliseconds)
Any IETF-compliant RFC 2822 timestamp:
new Date("November 2, 1988 10:00:00");
Individual args:
new Date(year, month, day [, hour, minute, second, millisecond])
new Date(1988,11,02,10,0,0);
If your main concern is about parsing, have a look at Moment.js which is clearly superior to parsing things yourself. (IMHO)
Turns out jQuery doesn't bind the .change() event to input type="date" properly in iOS. Changed the event to .blur() and everything seems to work now. However, it still seems it is impossible to create a valid date object in Chrome... an issue for another day.
Thanks for the help everyone!
I'm building a Windows 8 Metro app (aka "Modern UI Style" or "Windows Store app") in HTML5/JavaScript consuming JSON Web Services and I'm bumping into the following issue: in which format should my JSON Web Services serialize dates for the Windows 8 Metro JSON.parse method to deserialize those in a date type?
I tried:
sending dates using the ISO-8601 format, (JSON.parse returns a string),
sending dates such as "/Date(1198908717056)/" as explained here (same result).
I'm starting to doubt that Windows 8's JSON.parse method supports dates as even when parsing the output of its own JSON.stringify method does not return a date type.
Example:
var d = new Date(); // => a new date
var str = JSON.stringify(d); // str is a string => "\"2012-07-10T14:44:00.000Z\""
var date2 = JSON.parse(str); // date2 is a string => "2012-07-10T14:44:00.000Z"
Here's how I got this working in a generic way (though it I'd rather find a format supported out-of-the-box by Windows 8's JSON.parse method):
On the server, I'm serializing my strings using:
date1.ToString("s");
This uses the ISO 8601 date format which is always the same, regardless of the culture used or the format provider supplied (see here for more information).
On the client-side, I specified a "reviver" callback to JSON.parse which looks for dates using a regexp and converts them into a date object automatically.
In the end, the deserialized object will contain actual JavaScript date types and not strings.
Here's a code sample:
var responseResult = JSON.parse(request.responseText, function dateReviver(key, value) {
if (typeof value === 'string') {
var re = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)$/
var result = re.exec(value);
if (result) {
return new Date(Date.UTC(+result[1], +result[2] - 1, +result[3], +result[4],+result[5], +result[6]));
}
});
Hope this helps,
Carl
This is not something that's unique to Windows 8's JSON.parse – it's the by-design behavior of the ECMA standard JSON parser. Therefore, there is (and can be) no "out-of-the-box support" for dates.
Per spec, JSON values can only be a String, Number, Boolean, Array, Object, or null. Dates are not supported. (IMO, this is an oversight on the part of the spec, but it's what we have to live with.)
Since there is no date type, your app has to work out how to handle dates on its own. The best way to handle this is to send dates as ISO 8601 strings (yyyy-MM-dd'T'HH:mm:ss'Z') or as milliseconds since the epoch (Jan 1 1970 00:00:00 UTC). The important part here is to make sure time is in UTC.
If performance is important, I would not use a reviver callback with JSON.parse. I did a lot of testing, and the overhead involved with invoking a function for every single property in your object cuts performance in half.
On the other hand, I was honestly surprised with how well testing a regex against every string value stood up against only parsing known property names. Just make sure you define the regex once, outside the loop!
Obviously, the absolute fastest ways to turn JSON values into Dates is if you know exactly what properties need to be parsed for dates. However, given the surprisingly good performance of the regex-based search methods, I don't think it's worth the extra complexity unless you really need the extra performance.
A note on using ISO strings vs milliseconds since epoch: tested independently, milliseconds wins. In IE, there's no difference, but Firefox really seems to struggle with ISO strings. Also note that the Date constructor takes milliseconds in all browsers. It also takes a the ISO string, but not in IE ≤ 8.