Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

JavaScript JSON Date Parsing and real Dates


:P
On this page:

JSON has become a rather prevalent serialization format in recent years. For Web based applications one could say it’s become the serialization format. It’s used for transport serialization for just about anything REST related these days, for configuration management, for storage and data representation in many database formats (NoSql). But yet there’s at least one very annoying problem with JSON: It doesn’t serialize dates. Or more precisely – it serializes dates just fine, it just doesn’t deserialize them.

I’ve talked about this topic a few times in the past, but I thought I’d revisit because it’s one that comes up quite frequently, and because in the interceding years I’ve started using a more flexible solution than what I previously discussed. In this post I’ll discuss the problem and a few workarounds as well as small JSON extension library that you can use to globally parse JSON with proper date conversion with minimal fuss.

The code and small support library discussed in this post is up on GitHub in my json.date-extensions library, so you can download and play around with this yourself. I find this functionality indispensable and use as part of my own libraries in just about every client-side application that uses AJAX.

JSON Dates are not dates – they are Strings

The problem with dates in JSON – and really JavaScript in general – is that JavaScript doesn’t have a date literal. You can represent strings, numbers, Booleans and even objects, arrays and RegEx expressions with language specific literals, but there’s no equivalent literal representation for dates. Which seems like a rather major omission given that dates are pretty important in computing and business environments.

So in order to represent dates in JavaScript, JSON uses a specific string format – ISO 8601 – to encode dates as string. There’s no ‘official’ standard for what the date format should look like, although it’s been more or less agreed upon that the JSON Date encoding format should be ISO 8601 as that’s what all the major browser native JSON parsers use. Dates are encoded as ISO 8601 strings and then treated just like a regular string when the JSON is serialized and deserialized.

You can serialize to this format, but there’s no direct deserialization back to a date from it.

A JSON encoded date looks like this:

"2014-01-01T23:28:56.782Z"

The date is represented in a standard and sortable format that represents a UTC time (indicated by the Z). ISO 8601 also supports time zones by replacing the Z with + or – value for the timezone offset:

"2014-02-01T09:28:56.321-10:00"

There are other variations of the timezone encoding in the ISO 8601 spec, but the –10:00 format is the only TZ format that current JSON parsers support. In general it’s best to use the UTC based format (Z) unless you have a specific need for figuring out the time zone in which the date was produced (possible only in server side generation).

There have been other date formats in the past – namely Microsoft’s MS AJAX style date (/Date(454123112)/) which Microsoft used in their original AJAX and REST products (Microsoft AJAX and WCF REST specifically), but which thankfully have disappeared in new frameworks (ASP.NET Web API, Nancy, ServiceStack etc.) in favor of ISO style dates that are now ubiquitous.

How Date Parsing Works with JSON Serializers

To encode a date with JSON you can just use the standard JSON serializer’s stringify() method:

var date = new Date();
console.log(date); // Wed Jan 01 2014 13:28:56 GMT-1000 (Hawaiian Standard Time) 
        
var json = JSON.stringify(date);
console.log(json);  // "2014-01-01T23:28:56.782Z"

This produces a JSON encoded string value:

"2014-01-01T23:28:56.782Z"

If you now want to deserialize that JSON date back into a date you’ll find that JSON.parse() doesn’t do the job. If you try:

// JSON encoded date
var json = "\"2014-01-01T23:28:56.782Z\"";

var dateStr = JSON.parse(json);  
console.log(dateStr); // 2014-01-01T23:28:56.782Z

You’ll find that you get back… drum roll… a string in dateStr:

2014-01-01T23:28:56.782Z

No quotes this time – it’s no longer a JSON string, but a string nevertheless. This is because the JSON created is in fact a string and JSON.parse() doesn’t know anything other than that the encoded date value is a string, so that’s what you get back. Fun, ain’t it?

Decoding the Date

The good news – and maybe not very widely known news at that – is that ISO dates can be easily converted into JavaScript dates by use of the flexible JavaScript Date constructor. The date constructor accepts a wide variety of inputs to construct a date, ISO 8601 amongst them.

The following turns the ISO date back into a ‘real’ date:

// JSON encoded date
var json = "\"2014-01-01T23:28:56.782Z\"";

var dateStr = JSON.parse(json);  
console.log(dateStr); // 2014-01-01T23:28:56.782Z
        
var date = new Date(dateStr);
console.log(date);  // Wed Jan 01 2014 13:28:56 GMT-1000 (Hawaiian Standard Time)

Note that the string that is passed to new Date() has to be a plain string, not a JSON string so make sure if there are quote wrappers around a JSON string to remove the quotes.

This works nicely for single dates, but unfortunately it’s a bit of a pain if you get back complex objects that contain dates: An object with a few date fields or an array containing objects with date fields requires that you to fix up each of the values at the point of use. In an application means you’d have to manually replace dates every time you use them or do any sort of date math.

It works, but you have to be careful in your usage of these pseudo date strings, always remembering that they have to be converted manually when you use them.

JSON Parser Extensions

One way around this date problem is to extend the JSON parser to automatically convert the ISO string dates into real JavaScript dates.

The JSON.parse() method supports an optional filter function parameter that can be passed to transform values as they are parsed. We can check each of the key value pairs for each property as its parsed and look for strings that look like dates and automatically transform them into dates.

It’s only a short bit of code:

if (window.JSON && !window.JSON.dateParser) {
    var reISO = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*))(?:Z|(\+|-)([\d|:]*))?$/;
    var reMsAjax = /^\/Date\((d|-|.*)\)[\/|\\]$/;
   
    JSON.dateParser = function (key, value) {
        if (typeof value === 'string') {
            var a = reISO.exec(value);
            if (a)
                return new Date(value);
            a = reMsAjax.exec(value);
            if (a) {
                var b = a[1].split(/[-+,.]/);
                return new Date(b[0] ? +b[0] : 0 - +b[1]);
            }
        }
        return value;
    };

}

This parser also handles MS AJAX dates. If you don’t care about that you can remove that bit of code making the date parser a bit faster.

Using this date parser you can now add that to the call to JSON.parse():

var date = JSON.parse(json,JSON.dateParser);  
console.log(date);

which produces a JavaScript date.

This also works on complex objects so if you have and object like this:

{   
    "id": "312saAs1",    
    "name": "Jimmy Roe",
    "entered": "2014-01-01T23:28:56.782Z",
    "updated": "2014-01-01T23:28:56.782Z"
}

both the entered and updated properties will be parsed to dates. If you have an array of these objects – all the dates are still converted. This much better but still limited in that you have to manually call JSON.parse() in order to apply the filter function.

Taking over JSON.parse()

This works fine if you manually parse the JSON yourself, but it’s unlikely that you do your own JSON parsing in your applications. Most likely JSON is parsed for you by a library of some sort like jQuery, or AngularJs or any other framework. In that case you have no control over the JSON parsing because the parsing happens deeply buried inside of a framework. jQuery and other libraries allow overriding their processing as well, but you don’t really want to do this for every application/page and each framework you use.

The common denominator here is the JSON parser itself. What if we can just take over the parser? What if we could – at the beginning of the page perhaps – override the JSON parser to actually use our behavior instead of the stock behavior?

To do this we can create a few additional functions and provide for a way to switch the parser:

if (this.JSON && !this.JSON.dateParser) {
    var reISO = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*))(?:Z|(\+|-)([\d|:]*))?$/;
    var reMsAjax = /^\/Date\((d|-|.*)\)[\/|\\]$/;

    /// <summary>
    /// set this if you want MS Ajax Dates parsed
    /// before calling any of the other functions
    /// </summary>
    JSON.parseMsAjaxDate = false;

    JSON.useDateParser = function(reset) {
        /// <summary>
        /// Globally enables JSON date parsing for JSON.parse().
        /// replaces the default JSON parser with parse plus dateParser extension 
        /// </summary>    
        /// <param name="reset" type="bool">when set restores the original JSON.parse() function</param>

        // if any parameter is passed reset
        if (typeof reset != "undefined") {
            if (JSON._parseSaved) {
                JSON.parse = JSON._parseSaved;
                JSON._parseSaved = null;
            }
        } else {
            if (!JSON.parseSaved) {
                JSON._parseSaved = JSON.parse;
                JSON.parse = JSON.parseWithDate;
            }
        }
    };

    JSON.dateParser = function(key, value) {
        /// <summary>
        /// Globally enables JSON date parsing for JSON.parse().
        /// Replaces the default JSON.parse() method and adds
        /// the datePaser() extension to the processing chain.
        /// </summary>    
        /// <param name="key" type="string">property name that is parsed</param>
        /// <param name="value" type="any">property value</param>
        /// <returns type="date">returns date or the original value if not a date string</returns>
        if (typeof value === 'string') {
            var a = reISO.exec(value);
            if (a)
                return new Date(value);

            if (!JSON.parseMsAjaxDate)
                return value;

            a = reMsAjax.exec(value);
            if (a) {
                var b = a[1].split(/[-+,.]/);
                return new Date(b[0] ? +b[0] : 0 - +b[1]);
            }
        }
        return value;
    };

    JSON.parseWithDate = function(json) {
        /// <summary>
        /// Wrapper around the JSON.parse() function that adds a date
        /// filtering extension. Returns all dates as real JavaScript dates.
        /// </summary>    
        /// <param name="json" type="string">JSON to be parsed</param>
        /// <returns type="any">parsed value or object</returns>
        var parse = JSON._parseSaved ? JSON._parseSaved : JSON.parse;
        try {
            var res = parse(json, JSON.dateParser);
            return res;
        } catch (e) {
            // orignal error thrown has no error message so rethrow with message
            throw new Error("JSON content could not be parsed");
        }
    };

    JSON.dateStringToDate = function(dtString, nullDateVal) {
        /// <summary>
        /// Converts a JSON ISO or MSAJAX date or real date a date value.
        /// Supports both JSON encoded dates or plain date formatted strings
        /// (without the JSON string quotes).
        /// If you pass a date the date is returned as is. If you pass null
        /// null or the nullDateVal is returned.
        /// </summary>    
        /// <param name="dtString" type="var">Date String in ISO or MSAJAX format</param>
        /// <param name="nullDateVal" type="var">value to return if date can't be parsed</param>
        /// <returns type="date">date or the nullDateVal (null by default)</returns> 
        if (!nullDateVal)
            nullDateVal = null;
            
        if (!dtString)
            return nullDateVal; // empty

        if (dtString.getTime)
            return dtString; // already a date
            
        if (dtString[0] === '"' || dtString[0] === "'")
            // strip off JSON quotes
            dtString = dtString.substr(1, dtString.length - 2);

        var a = reISO.exec(dtString);
        if (a)
            return new Date(dtString);

        if (!JSON.parseMsAjaxDate)
            return nullDateVal;

        a = reMsAjax.exec(dtString);
        if (a) {
            var b = a[1].split(/[-,.]/);
            return new Date(+b[0]);
        }
        return nullDateVal;
    };
}

You can find the current source on GitHub.

First there’s a specific function added to the JSON object called .parseWithDate() which can be called internally or explicitly instead of .parse(). .parseWithDate() is just a wrapper around .parse() with the dateParser applied so dates are automatically converted.

We can then create a .useDateParser() function which can swap the parser by replacing the stock parser with JSON.parseWithDate(). By replacing the JSON.parse() method with .parseWithDate() the date parsing behavior is basically made global and is applied to all JSON.parse() operations. .useDateParser() is meant to be called at the beginning of the page before any JSON requests are made, and affects any JSON parsing requests made after it. There’s also support for restoring the original parser by passing any parameter.

To use this you would call JSON.useDateParser() somewhere at the top of your script hierarchy:

<script src="jquery.min.js"></script>
<script src="JsonDateExtensions.js"></script>
<script>
    // use date parser for all JSON.parse() requests
    // make sure to call before any JSON conversions
    JSON.useDateParser();
</script>
<script>
    // other libs
    // page script code etc.
</script>

Let’s look at an example. Assume we have a JSON object we’re requesting from the server that has a couple of date properties – update and entered.

{   
    "id": "312saAs1",    
    "name": "Jimmy Roe",
    "entered": "2014-01-01T13:13:34.441Z",
    "updated": "2014-01-03T23:28:56.782Z"
}

Now we want to access this JSON data with jQuery’s $.getJSON() like this:

$.getJSON("JsonWithDate.json")
    .done(function (data) {
        console.log("result.entered: " + data.entered +
                    "  result.updated: " + data.updated);
    });

If you do nothing, you get the default JSON parsing in jQuery which doesn’t convert dates and leaves the ISO strings:

result.entered: 2014-01-01T23:28:56.782Z
result.Updated: 2014-01-01T23:28:56.782Z

But, if you now add the generic function code shown above followed by a call to JSON.useDateParser() near the top of the script hierarchy of the page:

JSON.useDateParser()

and then re-run that same jQuery AJAX call you now get:

result. Entered: Wed Jan 01 2014 13:34:56 GMT-1000 (Hawaiian Standard Time)
result.Updated: Wed Jan 03 2014 23:28:56 GMT-1000 (Hawaiian Standard Time)

The latter creates real dates. As will any other framework that uses the native JSON parsers in JavaScript. This means that by replacing the JSON.parse() method we have effectively overriden the JSON parser so that all JSON.parse() calls now do the date parsing including the external jQuery.getJSON() call or any other framework code that I don’t control. In effect, the date parsing support is now global.

Because the setting is page or scope global, you can also reset the original parser by using:

JSON.useDateParser(false)

This restores the default JSON parser and restores the original ISO date string behavior.

Caveats of overriding JSON Parsing

There are a couple of caveats to doing a wholesale behavior change like this to the JSON parser. First you are in fact changing behavior when you apply this parser and if there is existing code that relies on the existing date ISO format of dates, that will of course cause problems. I’ve been using automatic date conversions for quite a while though with a variety of frameworks, and I haven’t run into any problem yet, so this is probably not a big issue, but still be aware of it.

The other caveat of a JSON parse filter is that it is a filter and it slows down JSON parsing. Because parse filters are fired for every key of a JSON document and the code then checks each string value for two RegEx expressions, there’s definitely some overhead. Not a huge amount (about 3-5% in my informal tests of a 10k JSON blocks) but there is definitely some overhead… Probably not enough to worry about, but make sure you test your specific scenarios especially if you have large JSON data sets you’re pushing around in your app.

Do you really need Dates?

A discussion of dates in serialization wouldn’t be complete without mentioning that the best way to handle dates in UI applications is to not use dates. Ha! No I’m not trying to be snarky, but the best date format is one that you don’t have to format – on the client.

If you’re pushing data from the server to display on the client, consider formatting the date on the server. Native JavaScript date formatting sucks unless you add yet another library (moment.js is a good one) to your page, and you have all the tools necessary to format dates easily on your server. Rather than pushing down date values to the client consider pushing preformatted date strings in your ViewModels. This alone will reduce date usage significantly on the client.

Also when you’re doing date input from the user, it’s often much easier to just accept string values and push those to the server. The JSON.NET parser understands a lot of different date formats including human readable ones and can parse that out of JSON when you push your dates back up to the server using ASP.NET Web API or ASP.NET MVC.

With the advent of purely client side SPA style applications this is getting more common now, but often this still can be addressed via server side formatting.

If you can avoid parsing dates on the client, avoid it and format on the server.

Sometimes Dates are necessary

That leaves real date requirements on the client where the client has to actively work and manipulate dates and for those cases real JSON date parsing is highly useful.

It seems silly to actually having this discussion. A serializer without date formatting support just seems… silly, if you really think about it. But then this is JavaScript and there are lots of things that are just plain silly.

If you do need to get dates into the client and if you actually send dates from the server, then a global way of date deserialization certainly makes life a lot easier than having to manually translate dates at every usage point in your front end code. And for that these routines I described here have been very useful to me. I hope some of you get some utility out of them as well.

Resources

Posted in JavaScript  HTML  

The Voices of Reason


 

Bertrand Le Roy
January 07, 2014

# re: JavaScript JSON Date Parsing and real Dates

What if you want to include in your data a string that looks like a date, but it really is a real string?

Stephen
January 07, 2014

# re: JavaScript JSON Date Parsing and real Dates

Thanks for the great post as always....

I recently was on a project where JavaScript dates were driving me crazy, as I was pulling dates out of the database in Central Time and trying to use "new Date()" to turn that value into a JS date *still in Central*, new Date() kept converting the time to browser time, which is Eastern. I need to look more into using "Z" to keep it UTC or something rather than my super clunky workaround (using .match to hack up my ISO8601-formatted date and building a js date object from the parts)

Rick Strahl
January 07, 2014

# re: JavaScript JSON Date Parsing and real Dates

@Stephen - most JSON serializers (including JSON.NET) generate JSON dates with the Z UTC designation which should fix the timezone issue pretty easily, as long as your code knows how to properly deal with UTC dates. Personally I keep all dates in UTC format and convert to local time when needed only which seems easiest all around.

Rick Strahl
January 08, 2014

# re: JavaScript JSON Date Parsing and real Dates

@Bertrand - yes that's an issue, but a pretty unlikely one. I just can't think of a use case where you'd actually have ISO dates (or something that looks very similar as to match the RegEx) as a string value. Even if an ISO date was embedded in string content that wouldn't trigger because it's looking for line begin/end.

IAC, if you really needed to do that, then use the regular parser on the page and resort to manually parse the encoded dates on use. But again I can't think of a use case where this would happen and personally I'd be more than happy to live with that (infinitesimally small) risk.

SadDeveloper
January 09, 2014

# re: JavaScript JSON Date Parsing and real Dates

Hi Rick,

Thanks for the post, it's great. Could you please say in what browser you tested performance? Or even better post the test to http://jsperf.com/.

Thanks.

Adam
January 09, 2014

# re: JavaScript JSON Date Parsing and real Dates

Server-side we always use Utc dates, and in the current project I'm in, I decided to push Utc dates to the client as well. Previously we'd convert dates in the controllers when binding data to poco models.

I went with moment.js with timezone support to display times that are local to something as well as times that are local to the viewer. For example we can show the local time at a branch office, or events that are specific to another users timezone while simultaneously displaying data such as when a record may have been updated, but relative to the user.

I came across moment.js and really wanted to use it for their relative time display. Adding a simple js timer to update the document once a minute so that relative changes age as you sit and watch instead of being just a conversion when you first navigated.

It makes the MVC models much simpler as there are never any timezone conversions. It's important to note in JavaScript dates are always internally in UTC, and to be honest, when using moment.js, never had to even think of json parsing (I'm using MVC5 / Web Api 2).

The only "hard part" was converting Windows TimeZone's into IANA ones (I used NodaTime).

David Robbins
April 02, 2014

# re: JavaScript JSON Date Parsing and real Dates

Very nice!!! Dates and javascript are the ugly elephants in the room, especially if you end create a client solution for project management apps, with Gantt charts, timelines, etc. I'm so used to sighing in discouragement it makes me numb.

Looking to trying your solution out. Thanks Rick.

nomen
May 02, 2014

# re: JavaScript JSON Date Parsing and real Dates

The biggest problem I'm facing is that what you're calling a date is actually a time. A date is the thing calendars mark off. Representing dates with times is a big pain. They have different semantics, and you can't just assume that because a time is a date just because the "time part" is 0.

It seems like we just can't win. Postgres gets this right. But then the libraries I use get it wrong. So either I change the libraries, write a bunch of pointless and error prone code, or change my database schema and write a bunch of pointless and error prone code to group times by the date part.

Michael
August 28, 2014

# re: JavaScript JSON Date Parsing and real Dates

Thanks for this! I had real dates in my client, but after being JSONified and sent to the server, they ended up as strings in the database (mongodb). I added your code to my server and now I have real dates in my database.

Sime
September 04, 2014

# re: JavaScript JSON Date Parsing and real Dates

Thanks Rick for such a "sane" and considered article. I especially like the fact that you stress the common-sense approach of avoiding client-side date parsing whenever possible!

We have converted to using the DateTimeOffset data type in all our serverside (database and .NET code). It gives the best of both worlds - local time when you need it and UTC when you don't. We actuallt use DATETIMEOFFSET(0) in the database. This gives the precision we need in the same 8 bytes of storage as a UTC DATETIME.

I found it interesting and heartening to see that JSON / JavaScript is moving towards the ISO8601 standard which actually matches up pretty well with the DateTimeOffset datatype. Also, thanks for the link to moment.js looks very interesting.

Frederic
May 15, 2015

# re: JavaScript JSON Date Parsing and real Dates

Hi, we also had to create a "custom" date parser for our app.

We want to work with real "dates" format, because we sometimes need to re-format them in a format that our user can choose.

Our app (bridge24.com) connects to a lot of different api (basecamp, trello, asana, aceproject), and not everyone returns dates the same way...

The only thing we're sure of, is that the 10 first characters are yyyy-mm-dd.
Sometimes, it stops there (for a value like "Due Date", there is no "time" for that data.

There is 3 different cases we need to handle.

1. The "easy" one: full string including "timezone" at the end
(yyyy-MM-ddTHH:mm:ss-hh:mm)
easy, just use new Date(input)

2. the "no-timezone" one
(yyyy-MM-ddTHH:mm:ss)
We need to keep it like that when we display it. If we do a "new Date' on it, it apply the current local timezone, and changes the date. So we use "substring" to extract y,m,d,h,m,s, and we call new Date(y,m,d,h,m,s,0). That way, the time is the same in the "date" object than it was on the original string.

3. the "no-time" date
(yyyy-MM-dd)
For that case, like the last one, if we use new Date(value), it applies a local timezone, so it add our localtimezone (in our case, EST, it substract 5 hours), so the day change. "2015-04-19" becomes "2015-04-18T19:00". To prevent it, we need to substr y-m-d from the string, and use new Date(y,m,d,0,0,0,0).

We may need to "mix" these 3 cases, example:
(yyyy-MM-ddT00:00:00), what do we do with that? no timezone, but time provided, and all at 00:00:00... Case 3 will work, and return data like case 2 do. Date object will be in "current timezone"' with hour at 00:00.

It works fine for now with all different API we're connecting to.

Yes, there is certainly some performance issues of all these substring ... but we don't "feel" it, and at the end, our dates are easier to work with.

Harvey Mushman
August 09, 2015

# re: JavaScript JSON Date Parsing and real Dates

Interesting and very informative!

Wondering if it would make sense to write up an example that could be used as a directive in AngularJS? Seems to me that if it really only effects the <input type=date... why not just add on the element that is effected say fix-iso-date?

app.directive('fixisodate', function...

Is this something you might want to post? <g>

Bill Kunneke
October 19, 2015

# re: JavaScript JSON Date Parsing and real Dates

Small world. I did FoxPro development for many years and the last time I touched it was probably 15 years ago. I know manage a couple of Java teams and was Googling JSON date parsing and look who I run across, Rick Strahl. Doubtful you would remember me, but it's good to see you are still alive and kicking.

Surinder
July 19, 2016

# re: JavaScript JSON Date Parsing and real Dates

Thanks for this informative post. Currently I am working on an application and I need to pass Java 8 LocalDate from MVC controller to Ajax response in Jquery Data tables. I did not find anything to deserialize Localdate JSON string back to LocalDate in UI. So I manually had to append year, month and day to build date.
Is there a better way to handle Localdates in such siituation.

Please let e know, I will get rid of messy code in JS file I wrote. Below is my sample code columnDef column

columnDefs : [ {
targets : [ 6 ],
render : function(data, type, row) {
/* var json = JSON.stringify(data);
var date = JSON.parse(json);
console.log("json: " + json + "date : " + data); */
var month = data.month;
var day = data.day;
return data.year+"-"+month+"-"+day;
}
}

Johan
January 12, 2017

# re: JavaScript JSON Date Parsing and real Dates

I want to add that in ISO 8601 so called "time zone designator" is just an offset from UTC and not an actual time zone. For example, given time zone and valid local time instant in that time zone you can calculate offset from UTC but you cannot infer time zone from ISO 8601 date with some offset. Different time zones may have same UTC offset for many reasons (daylight saving time). Same time zone at different time instants may have different offset from UTC.


Aluan
April 17, 2017

# re: JavaScript JSON Date Parsing and real Dates

Excellent article. I think this is an elegant solution. I really like the advice to just avoid dates in the first place. In addition to preventing a large class of errors, using server formatted strings also makes client side databinding code simpler and staves off any temptation to write god awful, stateful and logic-ridden models.


N. Peter
May 03, 2017

# re: JavaScript JSON Date Parsing and real Dates

Today I had to post a javascript datetime to C# controller. (from UI to asp.net MVC Controller) Finally I had to remove timezone information, otherwise the posted result (the hours part of the time) was tranformed due to timezone information.

(ok, it is not about JSON, but may help someone who is surfing on the information ocean)


tz1969
March 02, 2018

# re: JavaScript JSON Date Parsing and real Dates

Just a nitpicky detail: actually, the last part of date in ISO8601 format ("-10:00", "+05:00", "Z", etc) are not timezones, they're offsets - just a difference from UTC, with "Z" being just a shortcut for "zero" (because it's also valid to have "+00:00").

A timezone is a place on Earth, and this place can have one or more offsets during history, including Daylight Saving Time changes (the offset during standand and summer times and when - the exact instant - those changes occur), or just "the governement decided to change the country's offset from now on, because reasons".

All these historical information is enclosed by the timezone, and they're not supported by ISO8601, AFAIK.

You can see more details at:


David
June 20, 2018

# re: JavaScript JSON Date Parsing and real Dates

Hi Rick,

This post and the json.date-extensions library on GitHub has been really helpful to me. Today however I came across runtime error that is originating from json.date-extensions library in the following function:

        JSON.parseWithDate = function(json, chainFilter) {
            /// <summary>
            /// Wrapper around the JSON.parse() function that adds a date
            /// filtering extension. Returns all dates as real JavaScript dates.
            /// </summary>    
            /// <param name="json" type="string">JSON to be parsed</param>
            /// <returns type="any">parsed value or object</returns>
            var parse = JSON._parseSaved ? JSON._parseSaved : JSON.parse;
            try {
                var res = parse(json, createDateParser(chainFilter));
                return res;
            } catch (e) {
                // orignal error thrown has no error message so rethrow with message
                throw new Error("JSON content could not be parsed");
            }
        };

The problem is with the chainFilter parameter. From studying the code it seems this parameter should be a function, but something is calling this function and passing a number for chainFilter. This is causing the runtime error.

I notice in this blog post, you don't mention the chainFilter parameter. What does this parameter do and is needed? Is it supposed to be the equivalent of the reviver function on the default JSON.parse fuction (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse)?

Thank you David


Rick Strahl
June 20, 2018

# re: JavaScript JSON Date Parsing and real Dates

@David - you need to find whatever is calling the routine with a number. The code isn't breaking inside of this function it's breaking inside of the API, which expects a function and is likely trying to execute the number (which fails). There's likely another filter attached to the JSON parsing chain that's causing this to happen. You should be able to look at the call stack when you see the number and see where it's being called from. Hopefully it's externally tracable code and not internal traces.


David
June 21, 2018

# re: JavaScript JSON Date Parsing and real Dates

Hi Rick,

Thanks a lot for your help. I'm using Angular 6 and the grid component from Telerik (Kendo UI for Angular). It seems the Telerik grid component is doing something unexpected with the JSON parser and that is clashing with the json.date-extensions library. This is going to be a real pain to fix 😦

Thanks heaps for you response,

David


Ken
August 09, 2018

# re: JavaScript JSON Date Parsing and real Dates

Hmm, I wouldn't say that the risk is necessarily low. Moreover if there's anything JavaScript taught us, it's that you probably shouldn't silently coerce types based on their content. 😉

Have you considered an implementation on the string prototype? It won't do it automatically, but it will make it a lot more convenient.

String.prototype.toDate = function() {return new Date(this)}
Date.toDate = function() {return this}

This technique essentially gives you an method you can use on either a date or a date-liked string. If conversion has already happened, nothing more happens. If conversion has not happened, it happens. Eg:

let myVal = '2014-01-01T23:28:56.782Z'   <-- Presumably came from JSON deserializatoin
let myDate = myVal.toDate()

At this point, if you pass myDate to a function, you can call toDate() again just to be sure.

function foobar(myDate) {
    myDate = myDate.toDate()
}

Not an ideal solution, but better (IMO) than silently changing a variable's type based on its contents, which I consider a troubling proposal.

Having said that, I'm half-inclined to just go back to xmlrpc. At least it (de-)serialized dates without intervention.


1LadyInDC
March 21, 2019

# re: JavaScript JSON Date Parsing and real Dates

Thanks so much Rick! Parsing dates with JSON had me STUCK for like two whole days and then I came across this!


Jeremy
October 30, 2019

# re: JavaScript JSON Date Parsing and real Dates

@Ken - I would typically agree with you about "you probably shouldn't silently coerce types based on their content," except in this case we're talking about content that was originally "coerced" from a date to a string due to the lack of a literal date representation in JS. The only reason any "coercion" is needed at all is due to the serialization process encoding dates as strings. Considering how easy it is to detect valid date strings with a regex, I really don't think his approach is a particularly dangerous one, especially with try/catch blocks and fallbacks in place.

That being said, I do like your idea of putting a "toDate" method on the string prototype


Rodd
October 12, 2021

# re: JavaScript JSON Date Parsing and real Dates

Thanks Rick. I'm just getting into a situation that requires me to work with times as well as dates. I'm curious how you deal with archived data when locale time may have changed. A client may be running in EST when a record is created and then be running in EDT when they later view an archived record. In my mind, storing the value in UTC would give the wrong time when viewing the record at a later time. IOW, if I started my meeting with a client at 8:00am EST, I want to reflect that the meeting started at 8:00am not 9:00am even though I'm now operating in EDT. Have you ever had to deal with something like this? Do you still store everything in UTC or is this an exception where it is necessary to store the time offset of the original date/time?


Simon
March 02, 2022

# re: JavaScript JSON Date Parsing and real Dates

This looks like a really good, robust solution and it's going in our project today. In a previous life, I had a custom reviver function to do this in my custom Http library. In the modern world, with Axios, that's no longer an option, so globally modifying the JSON object will do the trick. Axios is hermetically sealed, which is a bit surprising.

If I can make 2 comments: Native javascript date serialization no longer sucks, and, as a result, moment.js is no longer necessary (or maintained). Second, it would be great if this was an npm.


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024