JavaScript Remoting and Formatting Dates

03/05/2013

First off, I’m back! You may have noticed my blog posts and community involvement over the past 6 months has been almost nonexistent. In my previous job I had slowly started to move alway from being involved in app development and lacked the daily intercourse with code that inspired so many of my previous posts. If you haven’t heard I left my previous job of six years to join Arrowpointe and together with Scott Hemmeter we plan to do great things in the geolocation space. In my new position as Director of Application Development I am more involved once again with building apps and solving technical problems. So now…let’s talk about JavaScript Remoting and formatting dates on the client side.

If you don’t already know, JavaScript Remoting is awesome. It allows you to directly call Apex methods from JavaScript, returns the response in JSON, and is super snappy and reponkinsive (sound). You can read more about it here. What is not awesome about JavaScript Remoting is how it handles Date and DateTime fields. In actuality it handles these fields fine but the platform itself leaves something to be desired. Let’s take a look at the following JavaScript that returns the CreatedDate field from an Account.

sObject.query('select Id, Name, CreatedDate from Account limit 1',function(result,event){
    console.log(result[0].CreatedDate);
    var d = new Date(result[0].CreatedDate);
    console.log(d);
});

The output will be the following.

1362028097000 //The number of milliseconds since January 1st, 1970
Wed Feb 27 2013 21:08:17 GMT-0800 (PST)

(Oh…and if you are wondering what that sObject.query() magic is, stay tuned….that is the subject of an upcoming blog post.)

Hmm… well neither of those are too pretty and being the astute global developers we are we want to make sure the Date/Time is formatted to the users locale. Something like “2/27/2013 9:08 PM” in America and “27/02/2013 21:08″ in UK. Let’s go follow the options I went through trying to figure this out.

1) JavaScript Date.toLocaleString()
The Date object in JavaScript does have a toLocaleString() method but don’t even think about using it. Frist, it’s inconsistent and doesn’t always work like you would expect it to. Second, when salesforce.com returns the time portion of a date it is already adjusted based on the users selected time zone, which is nice, but toLocalString() method shifts it again making the time wrong.

2) Use a Library like jQuery Localize or Date.js
Yes, this will be perfect! False. I thought I could use the salesforce.com locale values in tandem with these libraries but the values salesforce.com uses for locales are in a non-standard format. It’s actually not salesforce.com’s fault as I think (hope) they created their whole localization engine and rules before these standards where in place. The two options here is to have users enter and store another locale value that is standards compliant. Or for each of the existing salesforce.com locale values (100+) create a little JavaScript file that has the formatting rules. Neither of which is ideal.

3) Go to stackexchange.com and as for help
Here someone pointed me to a JavaScript object called UserContext (can view by entering UserContext in browser console when logged in to salesforce.com) and it provides all sorts of great info about the user but specifically it included the Date and DateTime masks for date formatting, M/d/yyyy and M/d/yyyy h:mm a. Progress! Yet after looking for a library that could take this format and parse it correctly I once again came up dry. After looking at UserContext object in more detail and digging through some other salesforce.com JavaScript files I found the solution.

Buried within the salesforce.com JavaScript files there is an object called DateUtil with two very useful methods, getDateTimeStringFromUserLocale(date) and getDateStringFromUserLocale(date). These two methods do exactly what they look like. You pass in a normal JavaScript date variable and they will return a nice formatted String based on the user’s selected locale.

So we are good now right? Not exactly. At the very top of the JavaScript File in which these methods exist is this nice little message from salesforce.com:

/*
 * This code is for Internal Salesforce use only, and subject to change without notice.
 * Customers shouldn't reference this file in any web pages.
 */

Well, that’s no fun! But rules were meant to be broken right…..right? Ya! We are hardcore rule breaking developers! In fact I’ve got some NWA and Wu Tang Clan playing right now… at the same time! Let’s be safe about our rule breaking though, and seriously, this could break at anytime so proceed at your own risk.

First we need to have some default date/time formatting should our call to the DateUtil method encounter any issues. Let’s revisit the original example with some slight modifications. Make note of the comments in the code.

sObject.query('select Id, Name, CreatedDate from Account limit 1',function(result,event){
    //Convert CreatedDate to a JavaScript date oject
    var d = new Date(result[0].CreatedDate);
 
    //Remove first 4 characters (day), the seconds and the GMT offset
    var dateString = d.toString();
    dateString = dateString.substring(4,dateString.lastIndexOf(':'));
 
    //Attempt to format datetime using sfdc DateUtil method
    try{ 
        dateString = DateUtil.getDateTimeStringFromUserLocale(d);
    }catch(err){
        //Fail silently or alert devs DateUtil method is no longer working
    }
 
    //Output the formatted date string
    console.log(dateString);
});

The result will be the following output:

2/27/2013 9:08 PM //If DateUtil method was success
 
Feb 27 2013 21:08 //DateUtil failed and we used default formatting

Beautiful!

There is one last issue that may creep up. If your Visualforce page has the showHeader attribute set to false these salesforce.com JavaScript files will not be loaded into the page. The easy fix is to simply include a Visualforce inputField component that is bound to a Date or DateTime field in salesforce.com and hide it. This will load the date formatting JavaScript files. Like so:

<div style="display:none;">
    <apex:inputField value="{!opp.CloseDate}"/>
</div>

Hope this was helpful and stay tuned for more client side JavaScript related blog posts.