Example Code with Locale-Formatted Data
Review examples of code that handles data that can be formatted based on the user’s locale. See how code can break when it relies on a specific format for dates, times, and currencies. And review examples of how to fix these issues by using standard methods for handling these types of data.
Required Editions
| Available in: both Salesforce Classic and Lightning Experience |
| Available in: all editions |
Currency, date, time, datetime, integer, name, and address formats can change when a user changes locales. When you enable the International Components for Unicode (ICU) locale formats, the date, time, datetime, integer, and currency formats change for some locales.
Let’s look at some code examples that handle these data types and the errors that can occur if we don’t use locale-neutral methods in code.
Extract Components of a Date
In this example, we want to validate a date: October 12, 2021. To keep the example simple, we show only a portion of the process, where the code validates the month, day, and year values. The month value must be less than or equal to 12. The day value must be less than or equal to 31. And the year value must have exactly 4 digits. Only dates that pass all three tests are then extracted to the respective values for additional processing.
Here’s an example that uses the Apex Pattern and Matching classes to determine the date. The pattern matching assumes that the date format is MM/dd/yyyy.
Date myDate = Date.newInstance(2021, 10, 20);
String formattedDate = myDate.format();
//Simple regex not having all validations for days in months nor accounting for leap year
Pattern datePattern = Pattern.compile('(0?[1-9]|1[0-2])\\/(0?[1-9]|[2][0-9]|3[01])\\/([0-9]{4})');
Matcher matcher = datePattern.matcher(formattedDate);
if(matcher.matches()) {
Integer day = Integer.valueOf((matcher.group(1))); //10
Integer month = Integer.valueOf((matcher.group(2))); //20
Integer year = Integer.valueOf((matcher.group(3))); //2021
// Further validation logic for max days in month/ leap year and post processing
}
Apex code formats the date according to the context user’s locale. In this example, the context user has chosen the Spanish (United States) [es_US] locale.
When the ICU locale formats are enabled, this validation fails. The ICU format for the user’s locale is different than the corresponding Oracle’s Java Development Kit (JDK) format.
- JDK short date format for es_US: 10/20/2021
- ICU short date format for es_US: 20/10/2021
The format() method of the Apex Date class returns the date
as a string using the locale of the context user. In this case, because the ICU locale formats
are enabled and the user’s locale is es_US, it returns 20/10/2021.
The code then uses the placement in the date output to assign the month, day, and year values, assuming a format of MM/dd/yyyy. So the code assigns 20 as the month, and because 20 is greater than 12, the pattern matching incorrectly determines that this date is invalid.
To avoid these kinds of issues, use the built-in Apex methods to extract the required values.
In this case, month(), day(), and year().
Here’s an example of using those methods to set the month, day, and year values.
Date myDate = Date.newInstance(2021, 10, 20);
// assign month, day, and year values for validation
Integer month = myDate.month(); //10
Integer day = myDate.day(); //20
Integer year = myDate.year(); //2021
// Further validation logic
Validate a Datetime Value
This Apex example uses the Datetime.parse method to create
a datetime from a string. The passed value matches the JDK locale format for the English (United
States) [en_US] locale.
This validation fails when the ICU locale formats are enabled, because the datetime format for en_US is different between JDK and ICU. The ICU format includes a comma after the date.
- JDK datetime format for es_US: 10/14/2011 11:46 AM
- ICU datetime format for es_US: 10/14/2011, 11:46 AM
Datetime dt = DateTime.parse('10/14/2011 11:46 AM');
String myDtString = dt.format();
system.assertEquals(myDtString, '10/14/2011, 11:46 AM');
To fix this issue, update the passed value to the ICU locale format. If an external system is passing the value, contact the sender to update the format of the source data. Whenever possible, ask the sender to send the date in a locale-neutral format. It’s best to handle format updates this way because locale-neutral formats don’t require updates to your code when the format changes.
However, it’s not always possible to have the sender update the format of the data passed to your org. If that happens, because you know the format of the data being sent, you can reformat it. In these cases, convert the data into a locale-neutral format.
Here are two common methods used to format a datetime in Apex. With both of these methods, the resulting datetime is locale-neutral: it’s displayed in the user’s chosen locale. For more information about the methods available for all formats, see the Apex Reference Guide.
- Extract the date components to separate parameters: year, month, day, hours, minutes, and
seconds. Then, to create a locale-neutral date, use the
DateTime.newInstanceGMT()method of the Datetime Apex class.//Before using the code below, set the year, month, day, hour, minutes, and seconds components //by extracting values from the passed date and complete any necessary validation //set the datetime value by passing the parameters DateTime myDt = DateTime.newInstanceGMT(year, month, day, hour, minutes, seconds); - Reformat the passed data in the standard date format yyyy-MM-dd HH:mm:ss in the locale time
zone. Then, to create a locale-neutral date, use the
valueOf(String dateTimeString)method of the Datetime class.//Before using the code below, convert the passed datetime data to a string in the format yyyy-MM-dd HH:mm:ss //and assign that value to the stringDate parameter //set the datetime value by passing the string DateTime myDt = date.valueOf(stringDate);
This approach to converting formats works as long as the format of the data passed to your org doesn’t change. If your external source updates their format, you must update the methods you use to extract or convert the data.
Pass a Time to an External System
In this simple example, an event is occurring at 3:30 PM GMT on November 18, 2021. We want to pass the date and time as separate values to an external system.
//create Datetime to pass externally in GMT
DateTime event1_dt = DateTime.newInstanceGMT(2021, 11, 18, 15, 30, 0);
//split into event date and event time for external system
Date event1_date = event1_dt.date();
Time event1_time = event1_dt.time();
This code works if the external system expects the date and time in neutral formats. But what if the external system needs the date and time of an event in a specific format? Let’s assume that the external system expects the date and time in these formats: 18/11/2021 and 3:30 PM.
In this case, to apply the expected formats to the event date and time, use the Datetime
format()
method.
//create Datetime to pass externally
DateTime event1_dt = DateTime.newInstance(2021, 11, 18, 15, 30, 0);
//split into event date and event time for external system, applying expected format
String event1_date = event1_dt.format('dd/MM/yyyy');
String event1_time = event1_dt.format('h:mm a');

