Locale Aware DateTime Component
I am working with a client that is deploying some existing Visualforce pages to international Users. The Visualforce includes some Date and DateTime values so I checked them to make sure they would render appropriately for people accessing the logic internationally. It turns out that the original Visualforce code uses the <apex:outputText>
tag to drive formatting for much of the Date/DateTime values within the pages. Therefore, I am converting the occurrences of each <apex:outputText>
tag to the Visualforce component I built for rendering either Date or DateTime values based upon the User's individual locale settings.
My original DateTime formatting component works just fine but I wanted to improve it a bit. The enhancements support two goals I have for the code:
- The ability to handle new & future locale settings as they are made generally available by salesforce.com.
- Overall reduction in the total number of lines of code used for the logic.
The bulk of the improvements to the original code are being done in the Visualforce controller class.
/*
Created by: Greg Hacic
Last Update: 28 January 2016 by Greg Hacic
Questions?: greg@interactiveties.com
Notes:
- controller for the localeFormattedDatetime Visualforce component
- tests located at localeFormattedDatetimeTest.cls
*/
public class localeFormattedDatetime {
public Datetime datetimeValue {get;set;} //property that reads the DateTime value from the component attribute tag
public String getFormattedDatetime() {
String datetimeFormatted; //variable for the DateTime
if( datetimeValue != null ) { //if the DateTime variable is not null
datetimeFormatted = datetimeValue.format(); //the datetimeValue as a string using the locale of the User
}
return datetimeFormatted; //return the string
}
}
If you compare the code above to the controller I wrote back in 2011 you will see that I am removing the original method that was used in formatting the DateTime value based upon a hardcoded string, which was stored in a rather long map declaration. By switching from that map to the use of the standard DateTime class' format()
method, I am essentially making the logic future-proof. Meaning that the standard format()
method will always convert the DateTime value to a string based upon the User's locale settings and, therefore, inherently convert any new locales that salesforce.com decides to support in the coming years.
The component itself remains relatively unchanged from what I originally wrote except I decided to use camelcase for my naming conventions now.
<apex:component access="global" controller="localeFormattedDatetime">
<!--
Created by: Greg Hacic
Last Update: 28 January 2016 by Greg Hacic
Questions?: greg@interactiveties.com
NOTES:
- when adding to your Salesforce org set the name of component to localeFormattedDatetime
-->
<apex:attribute assignTo="{!datetimeValue}" description="The DateTime value to be rendered based upon the user's locale" name="datetimeProvided" type="Datetime"></apex:attribute>
{!formattedDatetime}
</apex:component>
The last part of this logic is the Apex unit tests. I am tweaking my original Apex test class to better support best practices that have evolved since the original logic was written. That new Apex test class is below.
/*
Created by: Greg Hacic
Last Update: 28 January 2016 by Greg Hacic
Questions?: greg@interactiveties.com
NOTES:
- tests the localeFormattedDatetime class (100.00% coverage)
*/
@isTest
private class localeFormattedDatetimeTest {
//English (United States)
@isTest //defines method for use during testing only
static void en_US() {
//BEGIN: perform some setup steps...
UserRole role = [SELECT Id, Name FROM UserRole LIMIT 1]; //grab a UserRole entry
Profile p = [SELECT Id FROM Profile LIMIT 1]; //grab a profile for a new testing user
//create a test user
List<User> users = new List<User>();
users.add(new User(Alias = 'test', Email = 'test@ities.co', EmailEncodingKey = 'ISO-8859-1', FirstName = 'Test', LanguageLocaleKey = 'en_US', LastName = 'User', LocaleSidKey='en_US', ProfileId = p.Id, TimeZoneSidKey = 'America/Denver', UserName = 'test@ities.co', UserRoleId = role.Id));
insert users;
//END: perform some setup steps...
System.runAs(users[0]) { //run as the newly created User
Test.startTest(); //denote testing context
localeFormattedDatetime controller = new localeFormattedDatetime(); //construct the controller
controller.datetimeValue = DateTime.valueOf('2007-01-01 2:35:21'); //set the DateTime variable to 1 January 2007
System.assertEquals('1/1/2007 2:35 AM', controller.getFormattedDatetime()); //validate the results
Test.stopTest(); //revert from testing context
}
}
//Spanish (Paraguay)
@isTest //defines method for use during testing only
static void es_PY() {
//BEGIN: perform some setup steps...
UserRole role = [SELECT Id, Name FROM UserRole LIMIT 1]; //grab a UserRole entry
Profile p = [SELECT Id FROM Profile LIMIT 1]; //grab a profile for a new testing user
//create a test user
List<User> users = new List<User>();
users.add(new User(Alias = 'test', Email = 'test@ities.co', EmailEncodingKey = 'ISO-8859-1', FirstName = 'Test', LanguageLocaleKey = 'en_US', LastName = 'User', LocaleSidKey='es_PY', ProfileId = p.Id, TimeZoneSidKey = 'America/Denver', UserName = 'test@ities.co', UserRoleId = role.Id));
insert users;
//END: perform some setup steps...
System.runAs(users[0]) { //run as the newly created User
Test.startTest(); //denote testing context
localeFormattedDatetime controller = new localeFormattedDatetime(); //construct the controller
controller.datetimeValue = DateTime.valueOf('2005-03-07 5:02:21'); //set the DateTime variable to 7 March 2005
System.assertEquals('07/03/2005 05:02 AM', controller.getFormattedDatetime()); //validate the results
Test.stopTest(); //revert from testing context
}
}
//French (Canada)
@isTest //defines method for use during testing only
static void fr_CA() {
//BEGIN: perform some setup steps...
UserRole role = [SELECT Id, Name FROM UserRole LIMIT 1]; //grab a UserRole entry
Profile p = [SELECT Id FROM Profile LIMIT 1]; //grab a profile for a new testing user
//create a test user
List<User> users = new List<User>();
users.add(new User(Alias = 'test', Email = 'test@ities.co', EmailEncodingKey = 'ISO-8859-1', FirstName = 'Test', LanguageLocaleKey = 'en_US', LastName = 'User', LocaleSidKey='fr_CA', ProfileId = p.Id, TimeZoneSidKey = 'America/Denver', UserName = 'test@ities.co', UserRoleId = role.Id));
insert users;
//END: perform some setup steps...
System.runAs(users[0]) { //run as the newly created User
Test.startTest(); //denote testing context
localeFormattedDatetime controller = new localeFormattedDatetime(); //construct the controller
controller.datetimeValue = DateTime.newInstance(2011, 1, 3, 12, 41, 15); //set the DateTime variable to 3 January 2011
System.assertEquals('2011-01-03 12:41', controller.getFormattedDatetime()); //validate the results
Test.stopTest(); //revert from testing context
}
}
//Chinese (Taiwan)
@isTest //defines method for use during testing only
static void zh_TW() {
//BEGIN: perform some setup steps...
UserRole role = [SELECT Id, Name FROM UserRole LIMIT 1]; //grab a UserRole entry
Profile p = [SELECT Id FROM Profile LIMIT 1]; //grab a profile for a new testing user
//create a test user
List<User> users = new List<User>();
users.add(new User(Alias = 'test', Email = 'test@ities.co', EmailEncodingKey = 'ISO-8859-1', FirstName = 'Test', LanguageLocaleKey = 'en_US', LastName = 'User', LocaleSidKey='zh_TW', ProfileId = p.Id, TimeZoneSidKey = 'America/Denver', UserName = 'test@ities.co', UserRoleId = role.Id));
insert users;
//END: perform some setup steps...
System.runAs(users[0]) { //run as the newly created User
Test.startTest(); //denote testing context
localeFormattedDatetime controller = new localeFormattedDatetime(); //construct the controller
controller.datetimeValue = DateTime.newInstance(2011, 1, 3, 12, 41, 15); //set the DateTime variable to 3 January 2011
System.assertEquals('2011/1/3 PM 12:41', controller.getFormattedDatetime()); //validate the results
Test.stopTest(); //revert from testing context
}
}
}
Rendering the Visualforce component within a Visualforce page is unchanged.
<c:localeFormattedDatetime datetimeProvided="{!Account.CreatedDate}"></c:localeFormattedDatetime>