Formatting

Domains: Java

This lesson explains how to format numbers, currencies, dates, times, and text messages. Because end users can see these data elements, their format must conform to various cultural conventions. Following the examples in this lesson will teach you how to:

  • Format data elements in a locale-sensitive manner
  • Keep your code locale-independent
  • Avoid the need to write formatting routines for specific locales

Numbers and Currencies

Programs store and operate on numbers in a locale-independent way. Before displaying or printing a number, a program must convert it to a String that is in a locale-sensitive format. For example, in France the number 123456.78 should be formatted as 123 456,78, and in Germany it should appear as 123.456,78. In this section, you will learn how to make your programs independent of the locale conventions for decimal points, thousands-separators, and other formatting properties.

Using Predefined Formats

By invoking the methods provided by the NumberFormat class, you can format numbers, currencies, and percentages according toLocale. The material that follows demonstrates formatting techniques with a sample program called NumberFormatDemo.java.

Numbers

You can use the NumberFormat methods to format primitive-type numbers, such as double, and their corresponding wrapper objects, such as Double.

The following code example formats a Double according to Locale. Invoking the getNumberInstance method returns a locale-specific instance of NumberFormat. The format method accepts the Double as an argument and returns the formatted number in aString.

			static public void displayNumber(Locale currentLocale) {

    Integer quantity = new Integer(123456);
    Double amount = new Double(345987.246);
    NumberFormat numberFormatter;
    String quantityOut;
    String amountOut;

    numberFormatter = NumberFormat.getNumberInstance(currentLocale);
    quantityOut = numberFormatter.format(quantity);
    amountOut = numberFormatter.format(amount);
    System.out.println(quantityOut + "   " + currentLocale.toString());
    System.out.println(amountOut + "   " + currentLocale.toString());
}

This example prints the following; it shows how the format of the same number varies with Locale:

			123 456   fr_FR
345 987,246   fr_FR
123.456   de_DE
345.987,246   de_DE
123,456   en_US
345,987.246   en_US

Using Digit Shapes Other Than Arabic Numerals

By default, when text contains numeric values, those values are displayed using Arabic digits. When other Unicode digit shapes are preferred, use the java.awt.font.NumericShaper class. The NumericShaper API enables you to display a numeric value represented internally as an ASCII value in any Unicode digit shape. See Converting Latin Digits to Other Unicode Digits for more information.

In addition, some locales have variant codes that specify that Unicode digit shapes be used in place of Arabic digits, such as the locale for the Thai language. See the section Variant Code in Creating a Locale for more information.

Currencies

If you are writing business applications, you will probably need to format and display currencies. You format currencies in the same manner as numbers, except that you call getCurrencyInstance to create a formatter. When you invoke the format method, it returns a String that includes the formatted number and the appropriate currency sign.

This code example shows how to format currency in a locale-specific manner:

			static public void displayCurrency( Locale currentLocale) {

    Double currencyAmount = new Double(9876543.21);
    Currency currentCurrency = Currency.getInstance(currentLocale);
    NumberFormat currencyFormatter = 
        NumberFormat.getCurrencyInstance(currentLocale);

    System.out.println(
        currentLocale.getDisplayName() + ", " +
        currentCurrency.getDisplayName() + ": " +
        currencyFormatter.format(currencyAmount));
}

The output generated by the preceding lines of code is as follows:

			French (France), Euro: 9 876 543,21 €
German (Germany), Euro: 9.876.543,21 €
English (United States), US Dollar: $9,876,543.21

At first glance, this output may look wrong to you because the numeric values are all the same. Of course, 9 876 543,21 € is not equivalent to $9,876,543.21. However, bear in mind that the NumberFormat class is unaware of exchange rates. The methods belonging to the NumberFormat class format currencies but do not convert them.

Note that the Currency class is designed so that there is never more than one Currency instance for any given currency. Therefore, there is no public constructor. As demonstrated in the previous code example, you obtain a Currency instance using thegetInstance methods.

The sample InternationalizedMortgageCalculator.java also demonstrates how to use the Currency class. (Note that this sample does not convert currency values.)

The sample InternationalizedMortgageCalculator.java requires the following resource files:

The Currency class contains other methods to retrieve currency related information:

  • getAvailableCurrencies : Returns all available currencies in the JDK

  • getCurrencyCode : Returns the ISO 4217 numeric code for a Currency instance

  • getSymbol : Returns the symbol for a Currency instance. You can optionally specify as an argument a Locale object. Consider the following excerpt:

    					Locale enGBLocale = 
        new Locale.Builder().setLanguage("en").setRegion("GB").build();
    
    Locale enUSLocale =
        new Locale.Builder().setLanguage("en").setRegion("US").build();
    
    Currency currencyInstance = Currency.getInstance(enUSLocale);
    
    System.out.println(
        "Symbol for US Dollar, en-US locale: " +
        currencyInstance.getSymbol(enUSLocale));
    
    System.out.println(
        "Symbol for US Dollar, en-UK locale: " +
        currencyInstance.getSymbol(enGBLocale));
    

    The excerpt prints the following:

    					Symbol for US Dollar, en-US locale: $
    Symbol for US Dollar, en-UK locale: USD
    

    This excerpt demonstrates that the symbol of a currency can vary depending on the locale.

  • getDisplayName : Returns the display name for a Currency instance. Like the getSymbol method, you can optionally specify a Locale object.

Extensible Support for ISO 4217 Currency Codes

ISO 4217 is a standard published by the International Standards Organization. It specifies three-letter codes (and equivalent three-digit numeric codes) to represent currencies and funds. This standard is maintained by an external agency and is released independent of the Java SE platform.

Suppose that a country adopts a different currency and the ISO 4217 maintenance agency releases a currency update. To implement this update and thereby supercede the default currency at runtime, create a properties file named <JAVA_HOME>/lib/currency.properties. This file contains the key/value pairs of the ISO 3166 country code, and the ISO 4217 currency data. The value part consists of three comma-separated ISO 4217 currency values: an alphabetic code, a numeric code, and a minor unit. Any lines beginning with the hash character (#), are treated as comment lines. For example:

			# Sample currency property for Canada
CA=CAD,124,2

CAD stands for the Canadian dollar; 124 is the numeric code for the Canadian dollar; and 2 is the minor unit, which is the number of decimal places the currency requires to represent fractional currencies. For example, the following properties file will supercede the default Canadian currency to a Canadian dollar that does not have any units smaller than the dollar:

		CA=CAD,124,0

Percentages

You can also use the methods of the NumberFormat class to format percentages. To get the locale-specific formatter, invoke thegetPercentInstance method. With this formatter, a decimal fraction such as 0.75 is displayed as 75%.

The following code sample shows how to format a percentage.

			static public void displayPercent(Locale currentLocale) {

    Double percent = new Double(0.75);
    NumberFormat percentFormatter;
    String percentOut;

    percentFormatter = NumberFormat.getPercentInstance(currentLocale);
    percentOut = percentFormatter.format(percent);
    System.out.println(percentOut + "   " + currentLocale.toString());
}

This sample prints the following:

			75 %   fr_FR
75%   de_DE
75%   en_US

 

 

Dates and Times

Version note: This Date and Time section uses the date and time APIs in the java.util package. The java.time APIs, available in the JDK 8 release, provides a comprehensive date and time model that offers significant improvements over the java.util classes. The java.time APIs are described in the Date Time trail. The Legacy Date-Time Code page might be of particular interest.

Date objects represent dates and times. You cannot display or print a Date object without first converting it to a String that is in the proper format. Just what is the "proper" format? First, the format should conform to the conventions of the end user's Locale. For example, Germans recognize 20.4.09 as a valid date, but Americans expect that same date to appear as 4/20/09. Second, the format should include the necessary information. For instance, a program that measures network performance may report on elapsed milliseconds. An online appointment calendar probably won't display milliseconds, but it will show the days of the week.

This section explains how to format dates and times in various ways and in a locale-sensitive manner. If you follow these techniques your programs will display dates and times in the appropriate Locale, but your source code will remain independent of any specific Locale.

Using Predefined Formats

The DateFormat class allows you to format dates and times with predefined styles in a locale-sensitive manner. The sections that follow demonstrate how to use the DateFormat class with a program called DateFormatDemo.java.

Dates

Formatting dates with the DateFormat class is a two-step process. First, you create a formatter with the getDateInstance method. Second, you invoke the format method, which returns a String containing the formatted date. The following example formats today's date by calling these two methods:

			Date today;
String dateOut;
DateFormat dateFormatter;

dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, currentLocale);
today = new Date();
dateOut = dateFormatter.format(today);

System.out.println(dateOut + " " + currentLocale.toString());

The output generated by this code follows. Notice that the formats of the dates vary with Locale. Since DateFormat is locale-sensitive, it takes care of the formatting details for each Locale.

			30 juin 2009     fr_FR
30.06.2009       de_DE
Jun 30, 2009     en_US

The preceding code example specified the DEFAULT formatting style. The DEFAULT style is just one of the predefined formatting styles that the DateFormat class provides, as follows:

  • DEFAULT
  • SHORT
  • MEDIUM
  • LONG
  • FULL

The following table shows how dates are formatted for each style with the U.S. and French locales:

Sample Date Formats
Style U.S. Locale French Locale
DEFAULT Jun 30, 2009 30 juin 2009
SHORT 6/30/09 30/06/09
MEDIUM Jun 30, 2009 30 juin 2009
LONG June 30, 2009 30 juin 2009
FULL Tuesday, June 30, 2009 mardi 30 juin 2009

Times

Date objects represent both dates and times. Formatting times with the DateFormat class is similar to formatting dates, except that you create the formatter with the getTimeInstance method, as follows:

			DateFormat timeFormatter =
    DateFormat.getTimeInstance(DateFormat.DEFAULT, currentLocale);

The table that follows shows the various predefined format styles for the U.S. and German locales:

Sample Time Formats
Style U.S. Locale German Locale
DEFAULT 7:03:47 AM 7:03:47
SHORT 7:03 AM 07:03
MEDIUM 7:03:47 AM 07:03:07
LONG 7:03:47 AM PDT 07:03:45 PDT
FULL 7:03:47 AM PDT 7.03 Uhr PDT

Both Dates and Times

To display a date and time in the same String, create the formatter with the getDateTimeInstance method. The first parameter is the date style, and the second is the time style. The third parameter is the Locale . Here's a quick example:

			DateFormat formatter = DateFormat.getDateTimeInstance(
                           DateFormat.LONG, 
                           DateFormat.LONG, 
                           currentLocale);

The following table shows the date and time formatting styles for the U.S. and French locales:

Sample Date and Time Formats
Style U.S. Locale French Locale
DEFAULT Jun 30, 2009 7:03:47 AM 30 juin 2009 07:03:47
SHORT 6/30/09 7:03 AM 30/06/09 07:03
MEDIUM Jun 30, 2009 7:03:47 AM 30 juin 2009 07:03:47
LONG June 30, 2009 7:03:47 AM PDT 30 juin 2009 07:03:47 PDT
FULL Tuesday, June 30, 2009 7:03:47 AM PDT mardi 30 juin 2009 07 h 03 PDT
 

Customizing Formats

The previous section, Using Predefined Formats, described the formatting styles provided by the DateFormat class. In most cases these predefined formats are adequate. However, if you want to create your own customized formats, you can use the SimpleDateFormat class.

The code examples that follow demonstrate the methods of the SimpleDateFormat class. You can find the full source code for the examples in the file named SimpleDateFormatDemo.

About Patterns

When you create a SimpleDateFormat object, you specify a pattern String. The contents of the pattern String determine the format of the date and time. For a full description of the pattern's syntax, see the tables in Date Format Pattern Syntax.

The following code formats a date and time according to the pattern String passed to the SimpleDateFormat constructor. The String returned by the format method contains the formatted date and time that are to be displayed.

		Date today;
String output;
SimpleDateFormat formatter;

formatter = new SimpleDateFormat(pattern, currentLocale);
today = new Date();
output = formatter.format(today);
System.out.println(pattern + " " + output);

The following table shows the output generated by the previous code example when the U.S. Locale is specified:

Customized Date and Time Formats
Pattern Output
dd.MM.yy 30.06.09
yyyy.MM.dd G 'at' hh:mm:ss z 2009.06.30 AD at 08:29:36 PDT
EEE, MMM d, ''yy Tue, Jun 30, '09
h:mm a 8:29 PM
H:mm 8:29
H:mm:ss:SSS 8:28:36:249
K:mm a,z 8:29 AM,PDT
yyyy.MMMMM.dd GGG hh:mm aaa 2009.June.30 AD 08:29 AM

Patterns and Locale

The SimpleDateFormat class is locale-sensitive. If you instantiate SimpleDateFormat without a Locale parameter, it will format the date and time according to the default Locale. Both the pattern and the Locale determine the format. For the same pattern, SimpleDateFormat may format a date and time differently if the Locale varies.

In the example code that follows, the pattern is hardcoded in the statement that creates the SimpleDateFormat object:

		Date today;
String result;
SimpleDateFormat formatter;

formatter = new SimpleDateFormat("EEE d MMM yy", currentLocale);
today = new Date();
result = formatter.format(today);
System.out.println("Locale: " + currentLocale.toString());
System.out.println("Result: " + result);

When the currentLocale is set to different values, the preceding code example generates this output:

		Locale: fr_FR
Result: mar. 30 juin 09
Locale: de_DE
Result: Di 30 Jun 09
Locale: en_US
Result: Tue 30 Jun 09

Date Format Pattern Syntax

You can design your own format patterns for dates and times from the list of symbols in the following table:

Symbol Meaning Presentation Example
G era designator Text AD
y year Number 2009
M month in year Text & Number July & 07
d day in month Number 10
h hour in am/pm (1-12) Number 12
H hour in day (0-23) Number 0
m minute in hour Number 30
s second in minute Number 55
S millisecond Number 978
E day in week Text Tuesday
D day in year Number 189
F day of week in month Number 2 (2nd Wed in July)
w week in year Number 27
W week in month Number 2
a am/pm marker Text PM
k hour in day (1-24) Number 24
K hour in am/pm (0-11) Number 0
z time zone Text Pacific Standard Time
' escape for text Delimiter (none)
' single quote Literal '

Characters that are not letters are treated as quoted text. That is, they will appear in the formatted text even if they are not enclosed within single quotes.

The number of symbol letters you specify also determines the format. For example, if the "zz" pattern results in "PDT," then the "zzzz" pattern generates "Pacific Daylight Time." The following table summarizes these rules:

Presentation Number of Symbols Result
Text 1 - 3 abbreviated form, if one exists
Text >= 4 full form
Number minimum number of digits is required shorter numbers are padded with zeros (for a year, if the count of 'y' is 2, then the year is truncated to 2 digits)
Text & Number 1 - 2 number form
Text & Number 3 text form
 

Changing Date Format Symbols

The format method of the SimpleDateFormat class returns a String composed of digits and symbols. For example, in the String "Friday, April 10, 2009," the symbols are "Friday" and "April." If the symbols encapsulated in SimpleDateFormat don't meet your needs, you can change them with the DateFormatSymbols. You can change symbols that represent names for months, days of the week, and time zones, among others. The following table lists the DateFormatSymbols methods that allow you to modify the symbols:

DateFormatSymbol Methods
Setter Method Example of a Symbol the Method Modifies
setAmPmStrings PM
setEras AD
setMonths December
setShortMonths Dec
setShortWeekdays Tue
setWeekdays Tuesday
setZoneStrings PST

The following example invokes setShortWeekdays to change the short names of the days of the week from lowercase to uppercase characters. The full source code for this example is in DateFormatSymbolsDemo. The first element in the array argument of setShortWeekdays is a null String. Therefore the array is one-based rather than zero-based. The SimpleDateFormatconstructor accepts the modified DateFormatSymbols object as an argument. Here is the source code:

	Date today;
String result;
SimpleDateFormat formatter;
DateFormatSymbols symbols;
String[] defaultDays;
String[] modifiedDays;

symbols = new DateFormatSymbols( new Locale("en", "US"));
defaultDays = symbols.getShortWeekdays();

for (int i = 0; i < defaultDays.length; i++) {
    System.out.print(defaultDays[i] + " ");
}
System.out.println();

String[] capitalDays = {
    "", "SUN", "MON",
    "TUE", "WED", "THU",
    "FRI", "SAT"
};
symbols.setShortWeekdays(capitalDays);

modifiedDays = symbols.getShortWeekdays();
for (int i = 0; i < modifiedDays.length; i++) {
    System.out.print(modifiedDays[i] + " ");
}
System.out.println();
System.out.println();

formatter = new SimpleDateFormat("E", symbols);
today = new Date();
result = formatter.format(today);
System.out.println("Today's day of the week: " + result);

The preceding code generates this output:

	Sun Mon Tue Wed Thu Fri Sat 
 SUN MON TUE WED THU FRI SAT 

Today's day of the week: MON

Messages

We all like to use programs that let us know what's going on. Programs that keep us informed often do so by displaying status and error messages. Of course, these messages need to be translated so they can be understood by end users around the world. The sectionIsolating Locale-Specific Data discusses translatable text messages. Usually, you're done after you move a message String into a ResourceBundle. However, if you've embedded variable data in a message, you'll have to take some extra steps to prepare it for translation.

compound message contains variable data. In the following list of compound messages, the variable data is underlined:

		The disk named MyDisk contains 300 files.
The current balance of account #34-09-222 is $2,745.72.
405,390 people have visited your website since January 1, 2009.
Delete all files older than 120 days.

You might be tempted to construct the last message in the preceding list by concatenating phrases and variables as follows:

		double numDays;
ResourceBundle msgBundle;
// ...
String message = msgBundle.getString(
                     "deleteolder" +
                     numDays.toString() +
                     msgBundle.getString("days"));

This approach works fine in English, but it won't work for languages in which the verb appears at the end of the sentence. Because the word order of this message is hardcoded, your localizers won't be able to create grammatically correct translations for all languages.

How can you make your program localizable if you need to use compound messages? You can do so by using the MessageFormatclass, which is the topic of this section.

Caution:

Compound messages are difficult to translate because the message text is fragmented. If you use compound messages, localization will take longer and cost more. Therefore you should use compound messages only when necessary.

Similar pages

Page structure
Terms

Class

double

Output

Java

Object

Variables

Errors

short

Statements

Packages

Processes