Tarandeep Gill
Tarandeep Gill

Reputation: 1534

Date parsing error in Safari

I have a date string with the following format:

2012-09-20T01:36:51.556Z

Its a date field returned from mongodb as is. Chrome, FF, and IE are able to parse this string, however, Safari fails with an error Invalid Date. I have tried DateJS, but it fails to parse this date too. Any ideas how can I easily parse this date? Or, what particular thing is causing Safari to fail?

I am using the native node driver for mongodb. And it returns date as a string in the above-mentioned format.

Upvotes: 2

Views: 734

Answers (3)

André Neves
André Neves

Reputation: 1

I had the same issue and this worked for me: https://github.com/csnover/js-iso8601

It overrides the Date.parse function.

Upvotes: 0

Tarandeep Gill
Tarandeep Gill

Reputation: 1534

I ended up converting the date to a TimeStamp on the server side, before sending it to client. NodeJS (which is of course, based on Chrome's JavaScript engine) parses this format just fine.

Upvotes: 0

Jim Davis
Jim Davis

Reputation: 1230

My DateExtensions should parse that:

http://depressedpress.com/javascript-extensions/dp_dateextensions/

After adding the library to the page you'd parse the date with the static Date.parseIso8601(date) method.

The extensions are a bit heavy for just that (although you can pull out just the data parsing stuff if you like).

I'm suprised that Safari is having such issues as the date appears, to me, to be a perfectly valid ISO 8601 date (as defined here: http://www.w3.org/TR/NOTE-datetime). This is about as plain vanilla as a date gets. Total guess, but have you tried replacing the "T" seperator with a space? It's not standard but I've seen a lot of implementations that use it that way (and my component allows it).

For what it's worth, here's my method pulled from DP_DateExtensions - I think it'll do the trick for you:

    // parseIso8601
    // Attempts to convert ISO8601 input to a date
Date.parseIso8601 = function(CurDate) {

        // Check the input parameters
    if ( typeof CurDate != "string" || CurDate == "" ) {
        return null;
    };
        // Set the fragment expressions
    var S = "[\\-/:.]";
    var Yr = "((?:1[6-9]|[2-9][0-9])[0-9]{2})";
    var Mo = S + "((?:1[012])|(?:0[1-9])|[1-9])";
    var Dy = S + "((?:3[01])|(?:[12][0-9])|(?:0[1-9])|[1-9])";
    var Hr = "(2[0-4]|[01]?[0-9])";
    var Mn = S + "([0-5]?[0-9])";
    var Sd = "(?:" + S + "([0-5]?[0-9])(?:[.,]([0-9]+))?)?";
    var TZ = "(?:(Z)|(?:([\+\-])(1[012]|[0]?[0-9])(?::?([0-5]?[0-9]))?))?";
        // RegEx the input
        // First check: Just date parts (month and day are optional)
        // Second check: Full date plus time (seconds, milliseconds and TimeZone info are optional)
    var TF;
    if ( TF = new RegExp("^" + Yr + "(?:" + Mo + "(?:" + Dy + ")?)?" + "$").exec(CurDate) ) {} else if ( TF = new RegExp("^" + Yr + Mo + Dy + "[Tt ]" + Hr + Mn + Sd + TZ + "$").exec(CurDate) ) {};
        // If the date couldn't be parsed, return null
    if ( !TF ) { return null };
        // Default the Time Fragments if they're not present
    if ( !TF[2] ) { TF[2] = 1 } else { TF[2] = TF[2] - 1 };
    if ( !TF[3] ) { TF[3] = 1 };
    if ( !TF[4] ) { TF[4] = 0 };
    if ( !TF[5] ) { TF[5] = 0 };
    if ( !TF[6] ) { TF[6] = 0 };
    if ( !TF[7] ) { TF[7] = 0 };
    if ( !TF[8] ) { TF[8] = null };
    if ( TF[9] != "-" && TF[9] != "+" ) { TF[9] = null };
    if ( !TF[10] ) { TF[10] = 0 } else { TF[10] = TF[9] + TF[10] };
    if ( !TF[11] ) { TF[11] = 0 } else { TF[11] = TF[9] + TF[11] };
        // If there's no timezone info the data is local time
    if ( !TF[8] && !TF[9] ) {
        return new Date(TF[1], TF[2], TF[3], TF[4], TF[5], TF[6], TF[7]);
    };
        // If the UTC indicator is set the date is UTC
    if ( TF[8] == "Z" ) {
        return new Date(Date.UTC(TF[1], TF[2], TF[3], TF[4], TF[5], TF[6], TF[7]));
    };
        // If the date has a timezone offset
    if ( TF[9] == "-" || TF[9] == "+" ) {
            // Get current Timezone information
        var CurTZ = new Date().getTimezoneOffset();
        var CurTZh = TF[10] - ((CurTZ >= 0 ? "-" : "+") + Math.floor(Math.abs(CurTZ) / 60))
        var CurTZm = TF[11] - ((CurTZ >= 0 ? "-" : "+") + (Math.abs(CurTZ) % 60))
            // Return the date
        return new Date(TF[1], TF[2], TF[3], TF[4] - CurTZh, TF[5] - CurTZm, TF[6], TF[7]);
    };
        // If we've reached here we couldn't deal with the input, return null
    return null;

};

I'm sure you could create a much smaller, more streamlined version (my code is written more for maintainability than compactness) - but this will manage most variations of ISO8601 date times and has never let me down. ;^)

Hope this helps!

Upvotes: 1

Related Questions