views:

413

answers:

4

I have this code in an HTML page:

alert(JSON.stringify(new Date()));

I'm including the latest json2.js (2009-09-29 version) in my page to support older browsers without JSON.stringify(). I also have jquery-1.3.2.js included. I believe in newer browsers with native JSON support, it just passes through to the native JSON feature.

Here's the results I get in different browsers:

IE 8 on Windows XP: "2010-02-07T21:39:32Z"
Chrome 4.0 on Windows XP: "2010-02-07T21:39:59Z"
Firefox 3.0 of Windows XP: "2010-02-07T21:40:41Z"
Chrome 4.0 on Ubuntu linux:  "2010-02-07T21:41:49Z"
Firefox 3.0 on Ubuntu linux:  "2010-02-07T21:42:44Z"
Chrome 4.0 on Mac OSX: "2010-02-07T21:43:56Z"
Safari on Mac OSX: "2010-02-07T21:45:21Z"
Firefox 3.5 on Mac OSX: "2010-02-07T21:44:10.101Z"

Notice the last one? It contains milliseconds, and none of the others do. I don't have FF3.5 installed on any other systems, but I'm assuming they would have the same results.

Is there something I can do to make all dates on all platforms stringify the same? My backend REST service can be configured with a format string to deserialize JSON dates, but it can't support multiple formats, just one.

+1  A: 

Why not use the formateDate function in the Datepicker UI plugin for jQuery to generate the format your server-side requires?

Suppressingfire
I'm passing an array of objects that contain dates. This would mean going through the entire array and replacing the Date objects with string representations of them. I was hoping to not have to copy an existing array (generated by a 3rd-party tool) to my own version of it with strings for dates. However, this just might be what I need to do. Thanks for the suggestion!
Tauren
`formatDate` is not a jQuery function. Looks to be part of a datepicker control.
Crescent Fresh
+1  A: 

You could also adjust json2.js a bit to always use its own Date.prototype.toJSON instead of a possible native one. Here I uncommented two lines and it works correctly:

//    if (typeof Date.prototype.toJSON !== 'function') {

    Date.prototype.toJSON = function (key) {

        return isFinite(this.valueOf()) ?
               this.getUTCFullYear()   + '-' +
             f(this.getUTCMonth() + 1) + '-' +
             f(this.getUTCDate())      + 'T' +
             f(this.getUTCHours())     + ':' +
             f(this.getUTCMinutes())   + ':' +
             f(this.getUTCSeconds())   + 'Z' : null;
    };

    String.prototype.toJSON =
    Number.prototype.toJSON =
    Boolean.prototype.toJSON = function (key) {
        return this.valueOf();
    };

// }

(Last line above is also part of your code, but it gets wrongly formatted)

Marcel Korpel
Haha! I just posted this exact same thing only 5 seconds after you... I don't think you need to add the String, Number, and Boolean prototypes if you only want to customize Dates.
Tauren
+1  A: 

I got this working adding the following javascript:

// Added to make dates format to ISO8601
Date.prototype.toJSON = function (key) {
    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    return this.getUTCFullYear()   + '-' +
         f(this.getUTCMonth() + 1) + '-' +
         f(this.getUTCDate())      + 'T' +
         f(this.getUTCHours())     + ':' +
         f(this.getUTCMinutes())   + ':' +
         f(this.getUTCSeconds())   + '.' +
         f(this.getUTCMilliseconds())   + 'Z';
};

I'm sure this probably slows down the serialization, but it seems to make things consistent across browsers.

Tauren
A: 

// You might want to consider beefing up the server, to recognize any valid ISO 8601 time format:

'2010-02-08T03:37:34.327Z'

'2010-02-08T03:38:06Z'

'2010-02-08T03:38+01:00'

'2010-02-08T03:34:18-05:00'

'2010-02-08T03:34Z'

'2010-02-08'

This is a method to convert any iso string to a javascript date object. It could be used on the server with a little translation:

Date.from_iso= function(s){
    var D, M= [], hm, min= 0, d2,
    Rx=  /([\d:]+)(\.\d+)?(Z|(([+\-])(\d\d):(\d\d))?)?$/;
    D= s.substring(0, 10).split('-');
    if(s.length> 11){
        M= s.substring(11).match(Rx) || [];
        if(M[1]) D= D.concat(M[1].split(':'));
        if(M[2]) D.push(Math.round(M[2]*1000));// msec
    }
    for(var i= 0, L= D.length; i<L; i++){
        D[i]= parseInt(D[i], 10);
    }
    D[1]-= 1;
    while(D.length< 6) D.push(0);
    if(M[4]){
        min= parseInt(M[6])*60+ parseInt(M[7], 10);// timezone not UTC
        if(M[5]== '+') min*= -1;
    }
    try{
        d2= Date.fromUTCArray(D);
        if(min) d2.setUTCMinutes(d2.getUTCMinutes()+ min);
    }
    catch(er){
        // bad input
    }
    return d2;
}
Date.fromUTCArray= function(A){
    var D= new Date;
    while(A.length < 7) A.push(0);
    var T= A.splice(3, A.length);
    D.setUTCFullYear.apply(D, A);
    D.setUTCHours.apply(D, T);
    return D;
}
kennebec
Thanks for the code, it might come in handy in the client. I think I can use Joda Time to parse the different ISO formats on the server side. I'm using Jersey with Jackson JSON handling for doing REST in a java server. By default, it attempts to convert from several different formats, but unfortunately only one of the ones I need. It has a way to override the format, but with only a single format string. I'm sure there is a way I can get in deeper and override it better, but I'd have to put more time into doing that.
Tauren