Chapters
If we want more control of how our string is formatted, we can use format specifiers to do just that. Some methods support the use of format specifiers like the printf() and format() methods. Format specifiers start with "%" symbol, followed by characters(converters) that do the formatting and optionally, other formatting elements like flags. These converters indicate the type to be formatted.
argument_index is a decimal integer indicating the position of the argument in the argument list. The first argument is referenced by "1$", the second by "2$", etc. Use the
flags is a set of characters that modify the output format. The set of valid flags depends on the conversion. In other words, some flag may not work with some converters.
width is a positive decimal integer indicating the minimum number of characters to be written to the output.
precision is a non-negative decimal integer preceded by dot(.) and it's usually used to restrict the number of characters. The specific behavior depends on the conversion.
conversion or converter indicates the type to be formatted.
Conversion elements are divided into different categories as follows:
General: may be applied to any argument type.
Character: may be applied to basic types which represent Unicode characters like char, byte, Character,etc. This conversion may also be applied to the types int and Integer when
Numeric: may be applied to integral and floating-point types like int, float, Double, etc.
Date/Time: may be applied to Java types which are capable of encoding a date or time like long, Calendar, Date, etc.
Percent: produces a literal '%' ('\u0025')
Line Separator: produces the platform-specific line separator.
Conversion elements, except for percent(%%) and line separator(%n), expect arguments. This tutorial is just a summary of the formatting tutorial in java. Visit the Formatter documentation to read the full, informative and insightful documentation about formatting in java.
We can format strings and numbers by using the printf() method or the format() method. These two methods are present in some streams like PrintStream. format() method can also be seen in String class.
Let's try formatting strings and display them on the console.
So, this is the format string:
Let's evaluate this string starting from the left side. There's no specifier in this sebsequence:
So, java writes this subsequence as is. Next, we found a format specifier. Remember, format specifier starts with "%" and ends with a convertor.
the format specifier is "%n". This specifier is a line-separator specifier. This specifier is platform-specific and we should use this instead of "\n". There are java platforms that don't recognize "\n" as newline. Some platforms prefer "\r\n" as newline. This specifier doesn't expect an argument.
Next, that "%n" is followed by "%s" which is a string specifier. This specifier formats an argument as string. This specifier expects an argument and will throw MissingFormatArgumentException if there's no argument found.
This specifier is our first specifier that requires an argument so, this specifier formats the first argument which is the string "Systemic" in the example above. Next, "%s" is followed by "%n", followed by "%S". "%S" is the same as "%s" the only difference is that "%S" formats argument's letters into uppercase letters whereas "%s" doesn't format the letter case of an argument. This also applies to other convertors except for "%n" and "%%".
"%S" is the second specifier that requires an argument so, it formats the second argument which is the string "Caution" in the example above.
Let's try another example.
Let's examine this format and start off with "%c". "%c" specifier formats an argument as a character. We use this convertor if we have character argument like unicode sequence and other character sequences.
Next, "%d" formats an argument to a decimal integer. Don't be confuse between decimal integer and decimal number. Decimal integers refers to base-10 integer whereas decimal number refers to a number with fractional part or refers to the fractional part of a number. In US and some other countries, fractional part is separated by dot(.).
Next is this format specifier "%2$o". This specifier contains argument index and an "o" converter. "2$" refers to the second argument which is an integer. "o" formats the second argument to an octal(base-8) integer. In the example above, there are only two arguments. If we remove the argument index, we will encounter an error 'cause "o" expects a third argument.
By using argument index, the second argument is reused and "o" formats the argument to an octal(base-8) integer.
Next, Let's examine this specifier "%<X". "<" refers to the previous argument that has been used. "%2$o" precedes "%<X" and "%2$o" refers to the second argument so, "%<" also refers to the second argument. "X" formats argument to a hexadecimeal(base-16) integer.
"%%" puts a literal percent(%) in the format string. "%" alone indicates a format specifier in format string whereas "%%" indicates a percent(%) literal in format string.
In the example above, UnknownFormatConversionException and IllegalFormatConversionException can be caught. try changing this specifier "%2$o%n" into "%1$o%n" and you will encounter an IllegalFormatConversionException. It throws an exception 'cause "1$" refers to the first argument which is a character and the character can't be formatted to octal integer.
Try changing this specifier "%<X" into "%&X" and you will encounter an UnknownFormatConversionException. It throws an exception 'cause "&" is not a valid formatting element.
Those exceptions are usually encountered when formatting string. There more exceptions that can be encountered. Also, notice that we use System.err instead of System.out to display the error messages on the console. err and out are the same though by convention, use err when displaying error messages in console-based applications.
Let's try one more example.
".1" is a precision element. When applied in floating-point number, precision restricts the floating-point's decimal places and round off the last decimal place. "f" is a converter that formats floating-point argument to a floating-point number.
Next, let's examine this specifier "%.4s". ".4" is a precision element. When applied in a string, precision restricts the maximum character that is gonna be included in the formatted result. "s" is convertor that formats argument to a string.
Next, this specifer "%010d". "0" is a flag that pads 0 from left to right. This flag requires width otherwise, an exception will be thrown. Also, there are flags are not allowed to be concatenated with "0" flag like the "-" flag. "10" is the width. It specifiy the maximum character length of the formatted result. If the argument length is less than the width, and "0" flag is set then, excess length will be padded with 0. If "0" flag is not set, whitespace will be used for padding. "d" is a converter that formats an integer argument to a decimal integer.
Last, this specifier "[%-10d]". "[]" are literals. "-" is a flag that formats the alignment of an argument to left justified. "10" is the width and "d" is the decimal integer converter.
We use NumberFormat class to format or parse numbers. NumberFormat is an abstract class so we can't instantiate it directly. Though, there are static NumberFormat's methods that return a NumberFormat instance like getInstance() and other getXXXInstance() methods.
Use setGroupingUsed() to disable number grouping. By default, setGroupingUsed() is set to true.
We can limit the number of decimal places in the formatted result using the setMinimumFractionDigits() and setMaximumFractionDigits(). Zeroes will fill up unused space if the argument digits count is less than the minimum fraction digits. If the digits are greater than the maximum fraction digits then, the argument will be rounded.
We can format percentage and currency. Also, we can assign different types of rounding when formatting decimal numbers.
Note: It's recommended to use BigDecimal When handling currencies.
Read the Currency class documentation to know more about currencies in java. Read the RoundingMode class documentation to know more about rounding modes in java.
If we want a string to be converted to a number then, we can use the parse() method.
Read the parse() method description in the NumberFormat class to know more about the parse() method.
We can use DecimalFormat to format decimal integers and numbers with more control and flexibility. DecimalFormat uses patterns to format decimal numbers. Patterns contain characters, some of them are normal literals, some have special meaning. Here's the pattern syntax of DecimalFormat.
Prefix and suffix are any Unicode characters except for \uFFFE, \uFFFF, and some special characters.
Number is a number of set of numbers that we wanna format.
Here are some characters that we can use in a pattern for DecimalFormat.
0: represents a digit and sets the minimum integer or decimal digits of a pattern. 0 digit substitutes for absence of digits. Applicable in the "Number" part of the pattern.
#: represents a digit. absence of digits is ignored. Should be placed in the "Number" part of the pattern.
dot(.): Decimal separator or monetary decimal separator. Should be placed in the "Number" part of the pattern.
comma(,): Grouping separator or monetary grouping separator. Should be placed in the "Number" part of the pattern.
semicolon(;): Separates positive and negative patterns
%: Multiply by 100 and show as percentage. Should be placed in "prefix" and "suffix" part of the pattern.
': Used to quote special characters in a prefix or suffix. Should be placed in "prefix" and "suffix" part of the pattern.
More information can be found in the DecimalFormat class documentation.
Let's create an example to demonstrate DecimalFormat.
if the given decimal integer is less than the pattern's expected digits, absence of digits is ignored(if # is used) or replaced by "0" digit(if 0 is used) and numbers are grouped based on the last group in the pattern. Otherwise, numbers are just grouped based on the last group in the pattern.
Next, we can mix "#" and "0" in a single pattern. Let's create an example.
Next, let's try a pattern with a subpattern.
Note: Explicit subpattern still follows the number of digits, minimal digits, and other characteristics of a positive pattern. The purpose of subpattern is to apply prefix and suffix to negative pattern that are distinct from the positive pattern.
So,
We already have seen in the example above that we can add literals as part of DecimalFormat's pattern. We can put letters and other acceptable characters in the pattern as prefixes/suffixes.
Let's try other characters that have special meaning in DecimalFormat.
Pattern3 and pattern4 formats numbers to scientific notation. Pattern3
According to DecimalFormat's documentation: "If the maximum number of integer digits is greater than their minimum number and greater than 1, it forces the exponent to be a multiple of the maximum number of integer digits, and the minimum number of integer digits to be interpreted as 1"
In my intuition, "0" denotes the minimum integer or decimal digits. The number of "#" and "0" in the pattern denotes that maximum integer or decimal digits. So, the pattern
In pattern3, maximum integer digits are graater than minimum integer digits. So, the given digits
In patern4, the exponents of the results that are formatted by pattern4 are not the multiple of maximum integer digits because the maximum integer digits are not greater than the minimum. Instead, they are equal. So, the quoted statement above is not applicable to pattern4.
In pattern4, "0" is replaced with with non-zero digits that are supplied by the given digits. If the given digits are not enough to supply the minimum integer or decimal digits, zeroes will be substituted to meet the minimum digit requirement.
We can change the decimal and grouping sepators of a DecimaFormat instance by using the DecimalFormatSymbols class.
We can use NumberFormat's getInstance() or getNumberInstance() method to get a NumberFormat type with a specified locale that we want.
A ChoiceFormat allows you to attach a format to a range of numbers. It is generally used in a MessageFormat for handling plurals. The choice is specified with an ascending list of doubles, where each item specifies a half-open interval up to the next item:
This example demonstrates simple usage of ChoiceFormat.
Then,
We can create a pattern for
We can format date/time by using DateFormat or SimpleDateFormat class. These two classes have distinct features that we can use in certain situations.
DateFormat class is an abstract class that provides methods and fields which help us to format date/time. DateFormat has fields that control the format of the formatted result. These fields are called "Style Patterns".
SimpleDateFormat class is a concrete class that can format date/time by using a pattern. Though, methods like getTimeInstance(), getDateInstance(), or getDateTimeInstance() and parse() can still be used here.
We can see in the patterns above that some letters are repeated. We repeat letters in order to set the minimum number that those letters represent. For example, "mm" denotes seconds with minimum of two digits. If the given numberis less than the minimum number, additional zero will be added to meet the minimum number. For example, if the given second is "1" it will be formatted to "01".
Some characters have special formats when repeated. For example, "y", which represents year, has different formats when repeated two or four times. For example, the given year is "2021". "yy" formats "2021" to "21", "yyyy" formats "2021" to "2021".
Another examples are the "z","Z" and "X". "z","zz" and "zzz" formats general timezone to an abbreviation. For example, "z" formats "Central Standard Time" to "CST". "zzzz" formats general time zone to its full name. Any repetitions of "z"'s that are more than four have the same result as "zzzz". "Z" formats timezone using "RFC 822 4-digit time zone format". Any repetitions of "Z"'s that are more than two have the same result as "Z".
"X" formats timezone using "ISO 8601 Time zone format". "X" can be repeated up to 3 times. each repitition increases the characters of the formatted timezone. An exception will be thrown if "X" is repeated more than 4 times.
Letters from A-Z(case insensitive) are reserved for SimpleDateFormat. It means, we can't directly use them as literals in SimpleDateFormat's pattern. Though, there's a way to use letters as literals in the pattern. Use the single quote(') and enclose the string that you wanna be used as literals. Pattern3 demonstrated this concept.
Check the documentation of SimpleDateFormat for more information.
DateTimeFormatter is a class used for formatting date and time in some date-time-related classes in
This topic assumes that readers are knowledgeable about
For example, "M" and "L" characters are the symbols for
Also, don't be confused with
CE starts at 1 and moves forward as moving forward to the present time. BCE starts at 1 and moves backward as moving forward to the past time.
methods with 'ofLocalized' prefix returns a locale specific date format for the ISO chronology. In the example above I used two overloaded forms of
The first form uses
Actual formatting happens if the formatted text is requested. If
Take note that
Next, we can use
The first form has a
The second form has a
The third form has a
Parsing is implemented as a two-phase operation. First, the text is parsed using the layout defined by the formatter, producing a Map of field to value, a ZoneId and a Chronology. Second, the parsed data is resolved, by validating, combining and simplifying the various fields into more useful ones. More information can be found in the documentation.
In this topic, we're gonna be discussing
This is valid because in java, a combination of
These constants can be found in the ResolverStyle enum.
In strict mode, an exception is thrown because 32 is outside the range 1 to 31 of month of march of ISO calendar. In smart mode, the result is
DateTimeFormatterBuilder allows a
In the example above, the literal
During formatting, the minute will only be output if its value can be obtained from the date-time. During parsing, the input will be successfully parsed whether the minute is present or not.
This pattern
Let's do one more example.
For other fields, use this form:
In my opinion, to get the number of digits of full value of reduced value with two or more digits, full value digits should be two digits higher than the reduced value digits. For example, the number of digits of full value of reduced value with two digits is 4 digits. To get the full value range, add 9*10 to the given full value if the reduced value has two digits, 9*100 is the reduced value has three digits and so on.
For example, the given full value is 1940 and the reduced value is two digits such as 40. Add 9*10 to 1940 and we get 2030. Thus, full value range starts from 1940 to 2030. In the example above,
During formatting,
Examples:
#1: Given field value is out-of-range and number of digits is greater than
Examples:
#1: parse reduced year value to full year value.
- Formatting Specifiers
- Formatting Strings and Numbers
- NumberFormat Class
- DecimalFormat Class
- "#" and "0" Characters
- Mixing "#" and "0" in a Pattern
- Subpattern
- Letters and Other Characters as Prefix/Suffix
- Formatting With Percent(%), Single Quote(') and Scientific Notation
- Changing Numbers Separator Symbols
- Changing DecimalFormat's Locale
- MessageFormat Class
- ChoiceFormat Class
- Formatting Date&Time
Format Specifiers
If we want more control of how our string is formatted, we can use format specifiers to do just that. Some methods support the use of format specifiers like the printf() and format() methods. Format specifiers start with "%" symbol, followed by characters(converters) that do the formatting and optionally, other formatting elements like flags. These converters indicate the type to be formatted.
Format specifiers for general, character, and numeric types %[argument_index$][flags][width][.precision]conversion e.g. String str = String.format("PI: %f, %1$+010.5f",Math.PI); Result PI: 3.141593, +003.14159The elements enclosed with brackets[] are optional elements. "%" and conversion elements are the essential parts of the formatter. Elements must be placed according to the order of the format specifiers above.
argument_index is a decimal integer indicating the position of the argument in the argument list. The first argument is referenced by "1$", the second by "2$", etc. Use the
<
flag to refer to previous argument that is used.
flags is a set of characters that modify the output format. The set of valid flags depends on the conversion. In other words, some flag may not work with some converters.
e.g. #,+,-,0
width is a positive decimal integer indicating the minimum number of characters to be written to the output.
precision is a non-negative decimal integer preceded by dot(.) and it's usually used to restrict the number of characters. The specific behavior depends on the conversion.
conversion or converter indicates the type to be formatted.
e.g. d,f,h
Conversion elements are divided into different categories as follows:
General: may be applied to any argument type.
Character: may be applied to basic types which represent Unicode characters like char, byte, Character,etc. This conversion may also be applied to the types int and Integer when
Character.isValidCodePoint(int)
returns true.
Numeric: may be applied to integral and floating-point types like int, float, Double, etc.
Date/Time: may be applied to Java types which are capable of encoding a date or time like long, Calendar, Date, etc.
Percent: produces a literal '%' ('\u0025')
Line Separator: produces the platform-specific line separator.
Conversion elements, except for percent(%%) and line separator(%n), expect arguments. This tutorial is just a summary of the formatting tutorial in java. Visit the Formatter documentation to read the full, informative and insightful documentation about formatting in java.
Formatting Strings and Numbers
We can format strings and numbers by using the printf() method or the format() method. These two methods are present in some streams like PrintStream. format() method can also be seen in String class.
Let's try formatting strings and display them on the console.
public class SampleClass{ public static void main(String[]args){ String str1 = "Systemic"; String str2 = "Caution"; //we can use printf() and format() //to format an output of a //stream //printf(String format, Object... args) System.out.printf("Sample#1: %n%s%n%S", str1,str2); System.out.println(); //Formatting doesn't change //the argument's original value System.out.println(str2); } } Result Sample#1: Systemic CAUTION CautionFirst off, let's examine Sample#1. So, the first argument of prinf() and format() is the format string. This string will be displayed on the console. The second arugment is an array of objects. These objects are called arguments of the format string. When evaluating the format string, we start from left to right position.
So, this is the format string:
"Sample#1: %n%s%n%S%n"
Let's evaluate this string starting from the left side. There's no specifier in this sebsequence:
Sample#1:
So, java writes this subsequence as is. Next, we found a format specifier. Remember, format specifier starts with "%" and ends with a convertor.
the format specifier is "%n". This specifier is a line-separator specifier. This specifier is platform-specific and we should use this instead of "\n". There are java platforms that don't recognize "\n" as newline. Some platforms prefer "\r\n" as newline. This specifier doesn't expect an argument.
Next, that "%n" is followed by "%s" which is a string specifier. This specifier formats an argument as string. This specifier expects an argument and will throw MissingFormatArgumentException if there's no argument found.
This specifier is our first specifier that requires an argument so, this specifier formats the first argument which is the string "Systemic" in the example above. Next, "%s" is followed by "%n", followed by "%S". "%S" is the same as "%s" the only difference is that "%S" formats argument's letters into uppercase letters whereas "%s" doesn't format the letter case of an argument. This also applies to other convertors except for "%n" and "%%".
"%S" is the second specifier that requires an argument so, it formats the second argument which is the string "Caution" in the example above.
Let's try another example.
import java.util.UnknownFormatConversionException; import java.util.IllegalFormatConversionException; public class SampleClass{ public static void main(String[]args){ try{ //format() is equivalent to printf(). //according to java documentation, printf() //is more of a convenience method System.out.format("Sample#2:%nchar: %c%n"+ "dec: %d%noct: %2$o%n"+ "hex: %<X%npercent: %<d%%", '\u0042',1325); } catch(UnknownFormatConversionException e){ System.err.printf("%nUnknown Format!%nMessage: %s%n"+ "Conversion char: %s", e.getMessage(),e.getConversion()); } catch(IllegalFormatConversionException e){ System.err.printf("%nIllegal Format!%nConverstion char:"+ " %c", e.getConversion()); } } } Result Sample#2: char: B dec: 1325 oct: 2455 hex: 52D percent: 1325%So, this is the format String
"Sample#2:%nchar: %c%ndec: %d%noct: %2$o%nhex: %<X%npercent: %<d%%"
Let's examine this format and start off with "%c". "%c" specifier formats an argument as a character. We use this convertor if we have character argument like unicode sequence and other character sequences.
Next, "%d" formats an argument to a decimal integer. Don't be confuse between decimal integer and decimal number. Decimal integers refers to base-10 integer whereas decimal number refers to a number with fractional part or refers to the fractional part of a number. In US and some other countries, fractional part is separated by dot(.).
Next is this format specifier "%2$o". This specifier contains argument index and an "o" converter. "2$" refers to the second argument which is an integer. "o" formats the second argument to an octal(base-8) integer. In the example above, there are only two arguments. If we remove the argument index, we will encounter an error 'cause "o" expects a third argument.
By using argument index, the second argument is reused and "o" formats the argument to an octal(base-8) integer.
Next, Let's examine this specifier "%<X". "<" refers to the previous argument that has been used. "%2$o" precedes "%<X" and "%2$o" refers to the second argument so, "%<" also refers to the second argument. "X" formats argument to a hexadecimeal(base-16) integer.
"%%" puts a literal percent(%) in the format string. "%" alone indicates a format specifier in format string whereas "%%" indicates a percent(%) literal in format string.
In the example above, UnknownFormatConversionException and IllegalFormatConversionException can be caught. try changing this specifier "%2$o%n" into "%1$o%n" and you will encounter an IllegalFormatConversionException. It throws an exception 'cause "1$" refers to the first argument which is a character and the character can't be formatted to octal integer.
Try changing this specifier "%<X" into "%&X" and you will encounter an UnknownFormatConversionException. It throws an exception 'cause "&" is not a valid formatting element.
Those exceptions are usually encountered when formatting string. There more exceptions that can be encountered. Also, notice that we use System.err instead of System.out to display the error messages on the console. err and out are the same though by convention, use err when displaying error messages in console-based applications.
Let's try one more example.
import java.util.Locale; public class SampleClass{ public static void main(String[]args){ //there's a format() method in String //class that we can use. This method //is helpful if we want to format //a string and put it somewhere //else aside from streams //like in a textbox for example String str = String.format(Locale.US, "%,.1f", 1234567.55); System.out.println(str); str = String.format("%.4s","Hello Everyone!"); System.out.println(str); str = String.format("%010d",150); System.out.println(str); str = String.format("[%-10d]",150); System.out.println(str); } } Result 1,234,567.6 Hell 0000000150 [150 ]First off, let's examine this specifier "%,.1f". "," groups numbers using a locale-specific grouping separators. In US, comma(,) is used to group numbers to represent thousands and beyond. Try changing the locale to
Locale.FRENCH
and you will get a different grouping separators.
".1" is a precision element. When applied in floating-point number, precision restricts the floating-point's decimal places and round off the last decimal place. "f" is a converter that formats floating-point argument to a floating-point number.
Next, let's examine this specifier "%.4s". ".4" is a precision element. When applied in a string, precision restricts the maximum character that is gonna be included in the formatted result. "s" is convertor that formats argument to a string.
Next, this specifer "%010d". "0" is a flag that pads 0 from left to right. This flag requires width otherwise, an exception will be thrown. Also, there are flags are not allowed to be concatenated with "0" flag like the "-" flag. "10" is the width. It specifiy the maximum character length of the formatted result. If the argument length is less than the width, and "0" flag is set then, excess length will be padded with 0. If "0" flag is not set, whitespace will be used for padding. "d" is a converter that formats an integer argument to a decimal integer.
Last, this specifier "[%-10d]". "[]" are literals. "-" is a flag that formats the alignment of an argument to left justified. "10" is the width and "d" is the decimal integer converter.
NumberFormat Class
We use NumberFormat class to format or parse numbers. NumberFormat is an abstract class so we can't instantiate it directly. Though, there are static NumberFormat's methods that return a NumberFormat instance like getInstance() and other getXXXInstance() methods.
import java.text.NumberFormat; import java.util.Locale; public class SampleClass{ public static void main(String[]args){ //Use getInstance() or getNumberInstance() //to get the normal number format. NumberFormat nf = NumberFormat.getInstance(); float f = 12345.44f; System.out.println("Default: " + nf.format(f)); nf = NumberFormat.getInstance(Locale.US); String str = nf.format(f); System.out.println("US: " + str); nf = NumberFormat.getInstance(Locale.ITALY); System.out.println("ITALY: " + nf.format(f)); nf = NumberFormat.getInstance(Locale.US); nf.setGroupingUsed(false); System.out.println("US: " + nf.format(f)); } } Result Default: 12,345.44 US: 12,345.44 ITALY: 12.345,44 US: 12345.44In the example above, getInstance() without argument returns a NumberFormat type with default locale. Default locale is platform-specific. As the time of this writing, my default locale is Locale.US. That's why the first and second result are equal. Yours might not be. Italy groups numbers differently, That's why it's not equal to the previous results.
Use setGroupingUsed() to disable number grouping. By default, setGroupingUsed() is set to true.
Limiting Fractional Digits
We can limit the number of decimal places in the formatted result using the setMinimumFractionDigits() and setMaximumFractionDigits(). Zeroes will fill up unused space if the argument digits count is less than the minimum fraction digits. If the digits are greater than the maximum fraction digits then, the argument will be rounded.
import java.text.NumberFormat; import java.util.Locale; public class SampleClass{ public static void main(String[]args){ NumberFormat nf = NumberFormat.getInstance(Locale.US); float f1 = 3.3f; float f2 = 3.43588f; nf.setMinimumFractionDigits(3); nf.setMinimumFractionDigits(4); System.out.println(nf.format(f1)); System.out.println(nf.format(f2)); } } Result 3.3000 3.43588
Formatting of Percentage, Currency and Rounding Numbers
We can format percentage and currency. Also, we can assign different types of rounding when formatting decimal numbers.
import java.text.NumberFormat; import java.util.Locale; import java.util.Currency; import java.math.RoundingMode; public class SampleClass{ public static void main(String[]args){ float f1 = 0.5f; float f2 = 12300.55f; //use getPercentInstance() get a //percentage format NumberFormat nf = NumberFormat. getPercentInstance(Locale.US); System.out.println(nf.format(f1)); //use getCurrencyInstance() get a //currency format nf = NumberFormat. getCurrencyInstance(Locale.JAPAN); System.out.println(nf.format(f2)); //To set new Currency, use setCurrency() method //or invoke getCurrencyInstance() nf.setCurrency(Currency.getInstance(Locale.US)); System.out.println(nf.format(f2)); nf = NumberFormat.getNumberInstance(Locale.US); nf.setMaximumFractionDigits(1); //use getRoundingMode() to get the current //RoundingMode. Use setRoundingMode to assign //new RoundingMode to the NumberFormat. System.out.println("Default Rounding Mode: " + nf.getRoundingMode()); System.out.println(nf.format(f2)); nf.setRoundingMode(RoundingMode.DOWN); System.out.println("New Rounding Mode: " + nf.getRoundingMode()); System.out.println(nf.format(f2)); nf.setRoundingMode(RoundingMode.UP); System.out.println("New Rounding Mode: " + nf.getRoundingMode()); System.out.println(nf.format(f2)); } } Result 50% ?12,301 $12,301 Default Rounding Mode: HALF_EVEN 12,301.5 New Rounding Mode: DOWN 12,301.5 New Rounding Mode: UP 12,301.6The first result is "50%" 'cause 0.5 multiplied by 100 is equal to 50 or 50%. The second result is "?12,301" 'cause my default charset of my java platform doesn't support japanese characters. If my default charset supports japanese characters then, the result could be this: "¥12,301". Also, notice that the result is rounded off, discarding decimals in the process. The third result is "$12,301" 'cause I used US locale in this formatted result and my default charset supports "$" character.
Note: It's recommended to use BigDecimal When handling currencies.
Read the Currency class documentation to know more about currencies in java. Read the RoundingMode class documentation to know more about rounding modes in java.
Parsing
If we want a string to be converted to a number then, we can use the parse() method.
import java.text.NumberFormat; import java.util.Locale; import java.text.ParseException; public class SampleClass{ public static void main(String[]args) throws ParseException{ NumberFormat nf = NumberFormat. getInstance(Locale.ITALY); Number num = nf.parse("1.234.567"); System.out.println(num.intValue()); } } Result 1234567In the example above, we successfully parse all the characters in "1.234.567" string as a number 'cause we use the Locale.ITALY as locale in the formatted result and dot(.) is equivalent to comma(,) in US locale when grouping digits. Try changing the locale to Locale.US and the result will be different.
Read the parse() method description in the NumberFormat class to know more about the parse() method.
DecimalFormat Class
We can use DecimalFormat to format decimal integers and numbers with more control and flexibility. DecimalFormat uses patterns to format decimal numbers. Patterns contain characters, some of them are normal literals, some have special meaning. Here's the pattern syntax of DecimalFormat.
Prefix(opt) Number Suffix(opt)
Prefix and suffix are any Unicode characters except for \uFFFE, \uFFFF, and some special characters.
Number is a number of set of numbers that we wanna format.
Here are some characters that we can use in a pattern for DecimalFormat.
0: represents a digit and sets the minimum integer or decimal digits of a pattern. 0 digit substitutes for absence of digits. Applicable in the "Number" part of the pattern.
#: represents a digit. absence of digits is ignored. Should be placed in the "Number" part of the pattern.
dot(.): Decimal separator or monetary decimal separator. Should be placed in the "Number" part of the pattern.
comma(,): Grouping separator or monetary grouping separator. Should be placed in the "Number" part of the pattern.
semicolon(;): Separates positive and negative patterns
%: Multiply by 100 and show as percentage. Should be placed in "prefix" and "suffix" part of the pattern.
': Used to quote special characters in a prefix or suffix. Should be placed in "prefix" and "suffix" part of the pattern.
More information can be found in the DecimalFormat class documentation.
"#" and "0" characters
Let's create an example to demonstrate DecimalFormat.
import java.text.DecimalFormat; public class SampleClass{ public static void main(String[]args){ String pattern1 = "##,###.##"; String pattern2 = "00,000.00"; double decimal = 1234567.567; DecimalFormat df = new DecimalFormat(pattern1); System.out.println("Pattern1: " + df.format(decimal)); df = new DecimalFormat(pattern2); System.out.println("Pattern2: " + df.format(decimal)); } } Result Pattern1: 1,234,567.57 Pattern2: 1,234,567.57First off, we use
DecimalFormat(String pattern)
constructor. This constructor creates a DecimalFormat using the given pattern and the symbols for the default FORMAT locale. This is a convenient way to obtain a DecimalFormat when internationalization is not the main concern. "##,###.##" and "00,000.00" expect 5 decimal integers and 2 decimal numbers.
if the given decimal integer is less than the pattern's expected digits, absence of digits is ignored(if # is used) or replaced by "0" digit(if 0 is used) and numbers are grouped based on the last group in the pattern. Otherwise, numbers are just grouped based on the last group in the pattern.
Example Pattern: ##,#### Given: 123456789 Result: 1,2345,6789 Given: 12345678 Result: 1234,5678 Given: 1234567 Result: 123,4567 Given: 123456 Result: 12,3456 Pattern: #,### Given: 1234 Result: 1,234 Given: 12345 Result: 12,345 Given: 123456 Result: 123,456 Given: 1234567 Result: 1,234,567As we can see from the results above, numbers are grouped based on the last group in the pattern. For example, in "#,###" pattern, the last group is "###" so, if possible, the given digits are grouped into three digits. The given digits above are greater than the Patterns' expected digits. Let's try given digits that are less than the expected digits of patterns.
Example Pattern: ##,### Given: 1234 Result: 1,234 Given: 123 Result: 123 Given: 12 Result: 12 Pattern: 00,000 Given: 1234 Result: 01,234 Given: 123 Result: 00,123 Given: 12 Result: 00,012 Pattern: ##,####,### Given: 123456789 Result: 123,456,789 Given: 12345678 Result: 12,345,678 Given: 1234567 Result: 1,234,567 Given: 123456 Result: 123,456 Pattern: 00,0000,000 Given: 123456789 Result: 123,456,789 Given: 12345678 Result: 012,345,678 Given: 1234567 Result: 001,234,567 Given: 123456 Result: 000,123,456For the decimal part, If the given decimal number is greater than the pattern's expected digits then, the given decimal numbers are rounded off, removing the unnecessary decimal numbers in the process. Otherwise, absence of digits is ignored(if # is used) or replaced by "0" digit(if 0 is used) and no rounding off will happen. If the pattern doesn't expect decimal numbers but the given digit has decimal numbers, decimal integers will be rounded off, removing the decimal numbers in the process.
Example Pattern: #,###.## Given: 1234.556 Result: 1234.56 Given: 1234.55 Result: 1234.55 Given: 1234.5 Result: 1234.5 Given: 1234 Result: 1234 Pattern: 0,000.00 Given: 1234.556 Result: 1234.56 Given: 1234.55 Result: 1234.55 Given: 1234.5 Result: 1234.50 Given: 1234 Result: 1234.00 Pattern: #,### Given: 1234.56 Result: 1235
Mixing "#" and "0" in a Pattern
Next, we can mix "#" and "0" in a single pattern. Let's create an example.
import java.text.DecimalFormat; public class SampleClass{ public static void main(String[]args){ //Note, don't put "0" before or in-between "#" //You will encounter a runtime exception //e.g. 0,###,### // //put "0" after "#". This only applies in the //decimal integer part of the pattern. For //the decimal number part, don't put "0" //after or in-between "#" or you will //encounter a runtime exception //e.g. //#,#00.00# //legal //0,#0#.#0 //Illegal String pattern = "##,000.00#"; DecimalFormat df = new DecimalFormat(pattern); System.out.println(df.format(12345.246)); System.out.println(df.format(1234.24)); System.out.println(df.format(123.2)); System.out.println(df.format(12)); } } Result 12,345.246 1,234.24 123.20 012.00
Subpattern
Next, let's try a pattern with a subpattern.
import java.text.DecimalFormat; public class SampleClass{ public static void main(String[]args){ String pattern1 = "#,000.00;(-0,000.00)"; String pattern2 = "#,000.00"; double digits = 12345.56; DecimalFormat df = new DecimalFormat(pattern1); System.out.println("p1: " + df.format(digits)); System.out.println("p1: " + df.format(-digits)); df = new DecimalFormat(pattern2); System.out.println("p2: " + df.format(digits)); System.out.println("p2: " + df.format(-digits)); } } Result p1: 12,345.56 p1: (-12,345.56) p2: 12,345.56 p2: -12,345.56A pattern, can have a positive and negative parts. By default, a pattern without a subpattern is a positive pattern. If a pattern without subpattern formats a negative number, a minus sign(-) will be added as a prefix in the formatted result. Otherwise, a subpattern will be used as a format for negative numbers. The "()" in the subpattern above are normal literals.
Note: Explicit subpattern still follows the number of digits, minimal digits, and other characteristics of a positive pattern. The purpose of subpattern is to apply prefix and suffix to negative pattern that are distinct from the positive pattern.
So,
"0,000.0;(0)" == "0,000.0;(0,000.0)"
Letters and Other Characters as Prefix/Suffix
We already have seen in the example above that we can add literals as part of DecimalFormat's pattern. We can put letters and other acceptable characters in the pattern as prefixes/suffixes.
import java.text.DecimalFormat; public class SampleClass{ public static void main(String[]args){ String pattern1 = "Postive: |#,000.00|;"+ "Negative: (-0,000.00)"; double digits = 12345.56; DecimalFormat df = new DecimalFormat(pattern1); System.out.println(df.format(digits)); System.out.println(df.format(-digits)); } } Result: Positive: |12,345.56| Negative: (-12,345.56)If we misplace prefix/suffix characters and put them in the "Number" part of a pattern, java may make those misplaced characters as suffixes or an exception may be thrown. For example, "#,(##)0" is equivalent to "#,##0()".
Formatting With Percent(%), Single Quote(') and Scientific Notation
Let's try other characters that have special meaning in DecimalFormat.
import java.text.DecimalFormat; public class SampleClass{ public static void main(String[]args){ String pattern1 = "0.00#%"; String pattern2 = "'#': #,###"; String pattern3 = "##0.##E0"; String pattern4 = "000.00E0"; DecimalFormat df = new DecimalFormat(pattern1); System.out.println("p1 " + df.format(.009)); System.out.println(); df = new DecimalFormat(pattern2); System.out.println("p2 " + df.format(1955)); System.out.println(); df = new DecimalFormat(pattern3); System.out.println("p3 " + df.format(1234)); System.out.println("p3 " + df.format(123456)); System.out.println("p3 " + df.format(0.00123)); System.out.println("p3 " + df.format(0.001)); System.out.println(); df = new DecimalFormat(pattern4); System.out.println("p4 " + df.format(1234)); System.out.println("p4 " + df.format(123456)); System.out.println("p4 " + df.format(0.00123)); System.out.println("p4 " + df.format(0.001)); System.out.println(); } } Result p1 0.90% p2 #: 1,955 p3 1.234E3 p3 123.46E3 p3 1.23E-3 p3 1E-3 p4 123.40E1 p4 123.46E3 p4 123.00E-5 p4 100.00E-5So, pattern1 formats 0.009 to percent by multiplying by 100 and the result is 0.90%. Pattern2 uses the single quote('). The purpose of this character is to format special characters of DecimalFormat to literally literals; disregarding their special meanings.
Pattern3 and pattern4 formats numbers to scientific notation. Pattern3
"##0.##E0"
formats 1234
to 1.234E3
. Let's discuss how java yielded the result. First off, let's examine the pattern "##0.##E0"
. Let's focus on the decimal integer part "##0"
.
According to DecimalFormat's documentation: "If the maximum number of integer digits is greater than their minimum number and greater than 1, it forces the exponent to be a multiple of the maximum number of integer digits, and the minimum number of integer digits to be interpreted as 1"
In my intuition, "0" denotes the minimum integer or decimal digits. The number of "#" and "0" in the pattern denotes that maximum integer or decimal digits. So, the pattern
"##0.##E0"
has a minimum of one and a maximum of 3 decimal integers. For the decimal part, the pattern doesn't have minimum and a has maximum of 2 decimal numbers.
In pattern3, maximum integer digits are graater than minimum integer digits. So, the given digits
1234
is formatted 1.234E3
. As you can see, the exponent is multiple of 3 which is the maximum integer digits. We can also see the other results that are formatted by pattern3 have exponents that are multiple of 3.
In patern4, the exponents of the results that are formatted by pattern4 are not the multiple of maximum integer digits because the maximum integer digits are not greater than the minimum. Instead, they are equal. So, the quoted statement above is not applicable to pattern4.
In pattern4, "0" is replaced with with non-zero digits that are supplied by the given digits. If the given digits are not enough to supply the minimum integer or decimal digits, zeroes will be substituted to meet the minimum digit requirement.
Changing Grouping Separator Symbols of Numbers
We can change the decimal and grouping sepators of a DecimaFormat instance by using the DecimalFormatSymbols class.
import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; public class SampleClass{ public static void main(String[]args){ DecimalFormatSymbols dfs = new DecimalFormatSymbols(); //This method sets new character as a decimal point dfs.setDecimalSeparator('|'); //This method sets new character as a grouping //separator. In US and some other countries, //comma(,) is used as grouping separator. dfs.setGroupingSeparator('-'); String pattern = "#,##0.00"; //We use the third form of DecimalFormat's constructor DecimalFormat df = new DecimalFormat(pattern, dfs); String result = df.format(1234567); System.out.println(result); } } Result 1-234-567|00Check the DecimalFormatSymbols documentation for more information.
Changing DecimalFormat's Locale
We can use NumberFormat's getInstance() or getNumberInstance() method to get a NumberFormat type with a specified locale that we want.
import java.text.NumberFormat; import java.text.DecimalFormat; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ NumberFormat nf = NumberFormat. getInstance(Locale.ITALY); if(nf instanceof DecimalFormat){ DecimalFormat df = (DecimalFormat)nf; //This method uses the notation of the //default locale of our platform. //Though, Symbols like decimal and //grouping separators in the //formatted result are going to be //based on a specified locale, if //there's one. df.applyPattern("#,###,##0.00"); //This method uses the localized notation. //In other words, the locale that we //specified when we invoked getInstace() //If there's no specified locale, the //default locale is used //df.applyLocalizedPattern("#.###.##0,00"); System.out.println(df.format(1234567)); } } } Result 1.234.567,00Note: DecimalFormat inherits the parse() method in NumberFormat so, we can still do parsing in DecimalFormat.
MessageFormat Class
MessageFormat provides a means to produce concatenated messages in a language-neutral way. Unlike other
A format pattern of
Note: MessageFormat differs from the other Format classes in that you create a MessageFormat object with one of its constructors (not with a getInstance style factory method). The factory methods aren't necessary because MessageFormat itself doesn't implement locale specific behavior. Any locale specific behavior is defined by the pattern that you provide as well as the subformats used for inserted arguments.
This means that MessageFormat doesn't have any implementations that handles locale specific behavior. Instead, it delagates the formatting to implementations with locale specific behavior from
If that's the case then MessageFormat will invoke
This example demonstrates MessageFormat.
If you want to quickly format a pattern without locale, you can use
Take note that the message that is going to be parsed must have the characters of the pattern that are not in pair of brackets({}). Otherwise,
Brackets have special meaning in MessageFormat. If we want to denote a bracket as a literal in a pattern, we need to enclose it within single quotes. Take a look at this example.
In the
Format
classes such as NumberFormat
and DecimalFormat
, this class doesn't have getInstance
factory methods.
A format pattern of
MessageFormat
consists of String and Format Element. A Format element can be one of these forms:
{ ArgumentIndex } { ArgumentIndex , FormatType } { ArgumentIndex , FormatType , FormatStyle }
ArgumentIndex
is an index that is used for mapping the String to be formatted. FormatType
defines the type of the format. FormatStyle
defines the styles of the format. Values of FormatType
and FormatStyle
can be found in the documentation.
Note: MessageFormat differs from the other Format classes in that you create a MessageFormat object with one of its constructors (not with a getInstance style factory method). The factory methods aren't necessary because MessageFormat itself doesn't implement locale specific behavior. Any locale specific behavior is defined by the pattern that you provide as well as the subformats used for inserted arguments.
This means that MessageFormat doesn't have any implementations that handles locale specific behavior. Instead, it delagates the formatting to implementations with locale specific behavior from
Format
classes. For example, you instantiate a MessageFormat with Locale.US
and then format a string using this pattern "{0, number, percent}".
If that's the case then MessageFormat will invoke
NumberFormat.getPercentInstance(getLocale())
and delegate the formatting to it.
This example demonstrates MessageFormat.
import java.text.MessageFormat; import java.util.Date; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ String message = "My {0} increased in {1, date} {1, time} by "+ "{2, number, percent}."; MessageFormat mf = new MessageFormat(message, Locale.US); System.out.println(mf.format( new Object[]{"salary",new Date(), .15})); } } Result My salary increased in May 20, 2022 5:12:09 PM by 15%.In the example above, objects are converted to String objects.
MessageFormat(String pattern, Locale locale)
constructs a MessageFormat
instance with the specified pattern and locale.
format(Object obj)
is a method from Format class that formats an object to produce a string with the specified object. This method prefers an Object array as its argument.
If you want to quickly format a pattern without locale, you can use
format(String pattern, Object... arguments)
static method. For example:
... String message = "My {0} increased in {1, date} {1, time} by "+ "{2, number, percent}."; System.out.println(MessageFormat.format (message, "salary", new Date(), .15)); ...We can also use
MessageFormat
to convert String objects to objects. Take a look at this example.
import java.text.MessageFormat; import java.text.ParsePosition; import java.text.ParseException; public class SampleClass{ public static void main(String[] args) throws ParseException{ String pattern = "{0} happened in {1, date} in my {2}."; String message = "Olympics happened in May 20, 2012 in my country."; MessageFormat mf = new MessageFormat(pattern); Object obj[] = mf.parse(message, new ParsePosition(0)); for(Object o : obj) System.out.println(o); } } Result Olympics Sun May 20 00:00:00 CST 2012 country
parse(String source, ParsePosition pos)
parses the source
starting from the specified ParsePosition. The index that is used by ParsePosition
is based on the ArgumentIndex
in pair of brackets.
Take note that the message that is going to be parsed must have the characters of the pattern that are not in pair of brackets({}). Otherwise,
parse
method will return null. More information about this method can be found in this documentation.
Brackets have special meaning in MessageFormat. If we want to denote a bracket as a literal in a pattern, we need to enclose it within single quotes. Take a look at this example.
import java.text.MessageFormat; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ String message = "''Objects'': '{'{0}, {1}, {2}'}'"; MessageFormat mf = new MessageFormat(message, Locale.US); System.out.println(mf.format( new Object[] {"Object1","Object2","Object3"})); } } Result 'Objects': {Object1, Object2, Object3}To denote a single quote as a literal in a pattern, use double single quotes just like in the example above.
In the
FormatStyle
of FormatElement
, we can use a pattern that is used by other Format
classes. This pattern in the FormatElement is called SubformatPattern
. Take a look at this example.
import java.text.MessageFormat; import java.util.Locale; import java.util.Date; public class SampleClass{ public static void main(String[] args){ String message = "Date: {0, date, dd-MM-yyyy}\n" + "Number: {1, number, #,###,###}"; MessageFormat mf = new MessageFormat(message); System.out.println( mf.format(new Object[]{new Date(), 1234567})); } } Result Date: 20-05-2022 Number: 1,234,567"dd-MM-yyyy" pattern comes from SimpleDateFormat. "#,###,###" pattern comes from DecimalFormat
ChoiceFormat Class
A ChoiceFormat allows you to attach a format to a range of numbers. It is generally used in a MessageFormat for handling plurals. The choice is specified with an ascending list of doubles, where each item specifies a half-open interval up to the next item:
X matches j if and only if limit[j] ≤ X < limit[j+1]If there is no match, then either the first or last index is used, depending on whether the number (X) is too low or too high. If the limit array is not in ascending order, the results of formatting will be incorrect. ChoiceFormat also accepts \u221E as equivalent to infinity(INF).
This example demonstrates simple usage of ChoiceFormat.
import java.text.ChoiceFormat; public class SampleClass{ public static void main(String[] args){ double[] limits = {1, 10, 100, 1000}; String[] formats = {"Ones", "Tens", "Hundredths", "Thousandths"}; ChoiceFormat cf = new ChoiceFormat(limits, formats); System.out.println(cf.format (Double.NEGATIVE_INFINITY)); System.out.println(cf.format(1)); System.out.println(cf.format(10)); System.out.println(cf.format(100)); System.out.println(cf.format(150)); System.out.println(cf.format(160)); System.out.println(cf.format(1000)); System.out.println(cf.format (Double.POSITIVE_INFINITY)); } } Result Ones Ones Hundredths Hundredths Hundredths Thousandths Thousandths
ChoiceFormat(double[] limits, String[] formats)
constructs with the limits and the corresponding formats. Remember that the length of limits
must be the same as the length of formats
. Take note that Double.NEGATIVE_INFINITY
and Double.POSITIVE_INFINITY
are not real numbers.
ChoiceFormat
is generally used in conjunction with MessageFormat
. Take a look at this example.
import java.text.ChoiceFormat; import java.text.MessageFormat; import java.text.NumberFormat; import java.text.Format; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ double[] limits = {.25, .5, .9}; String[] formats = {"Low", "Average", "Above Average"}; ChoiceFormat cf = new ChoiceFormat(limits, formats); MessageFormat mf = new MessageFormat ("{0} is {0}.", Locale.US); Format[] mfFormats = { NumberFormat.getPercentInstance(Locale.US), cf}; mf.setFormats(mfFormats); System.out.println(mf.format(new Object[]{.25})); System.out.println(mf.format(new Object[]{.2})); System.out.println(mf.format(new Object[]{.5})); System.out.println(mf.format(new Object[]{.9})); System.out.println(mf.format(new Object[]{.99})); } } Result 25% is Low. 20% is Low. 50% is Average. 90% is Above Average. 99% is Above Average.
setFormats(Format[] newFormats)
sets the formats to use for the format elements in the pattern of MessageFormat
instance. The order of formats in newFormats corresponds to the order of format elements in the pattern string. In the example above NumberFormat.getPercentInstance
formats the first "{0}" argument index in the pattern.
Then,
cf
formats the second "{0}" argument index. More information can be found in the documentation.
We can create a pattern for
ChoiceFormat
. These are the available symbols that we can use in the pattern.
import java.text.ChoiceFormat; public class SampleClass{ public static void main(String[] args){ ChoiceFormat cf = new ChoiceFormat( "-1#is negative| 0#is zero or fraction "+ "| 1#is one |1.0<is 1+ |2#is two "+ "|2<is more than 2."); System.out.println(cf.format(-1)); System.out.println(cf.format(1.5)); System.out.println(cf.format(0.9)); System.out.println(cf.format(1)); } } Result is negative is 1+ is zero or fraction is oneLet's examine the pattern and I'm gonna explain the pattern symbols. "|" is an OR symbol. We put this symbol after we're done writing a limit and format. OR symbol functions as an OR operator. "#" and "<" symbols are limiter symbols. "#" is typically used to create a limit. "<" is typically used to create a limit in-between limits created by "#". For example:
import java.text.ChoiceFormat; public class SampleClass{ public static void main(String[] args){ ChoiceFormat cf = new ChoiceFormat( "| 1#is one |1.5<is 1+ |2#is two "+ "|2<is more than 2."); System.out.println(cf.format(1)); System.out.println(cf.format(1.4)); System.out.println(cf.format(1.5)); System.out.println(cf.format(1.9)); System.out.println(cf.format(2)); } } Result is one is one is one is 1+ is two"\u2264" or "≤" symbol is similar to "<" symbol. However, "≤" symbol is inclusive. It means that the specified number after the symbol is included in the limit. For example:
import java.text.ChoiceFormat; public class SampleClass{ public static void main(String[] args){ ChoiceFormat cf = new ChoiceFormat( "| 1#is one |1.5\u2264is 1+ |2#is two "+ "|2<is more than 2."); System.out.println(cf.format(1)); System.out.println(cf.format(1.4)); System.out.println(cf.format(1.5)); System.out.println(cf.format(1.9)); System.out.println(cf.format(2)); } } Result is one is one is 1+ is 1+ is twoWe can also parse a limit number from an input text using
parse
method. Take a look at this example.
import java.text.ChoiceFormat; import java.text.ParsePosition; public class SampleClass{ public static void main(String[] args){ ChoiceFormat cf = new ChoiceFormat( "| 1#is one |1.5<is 1+ |2#is two "+ "|2<is more than 2."); System.out.println(cf.parse( "is one ", new ParsePosition(0))); } } Result 1.0Make sure that the string in the parse method matches the string, excluding the limit number and symbol, in one of the choices in the ChoiceFormat.
Formatting Date&Time
We can format date/time by using DateFormat or SimpleDateFormat class. These two classes have distinct features that we can use in certain situations.
DateFormat Class
DateFormat class is an abstract class that provides methods and fields which help us to format date/time. DateFormat has fields that control the format of the formatted result. These fields are called "Style Patterns".
- SHORT is completely numeric, such as 12.13.52 or 3:30pm
- MEDIUM is longer, such as Jan 12, 1952
- LONG is longer, such as January 12, 1952 or 3:30:32pm
- FULL is pretty completely specified, such as Tuesday, April 12, 1952 AD or 3:30:42pm PST.
import java.util.Date; import java.util.Locale; import java.text.DateFormat; import java.text.ParseException; public class SampleClass{ public static void main(String[]args){ //This Date constroctor allocates //a Date object and initializes it //so that it represents the time at //which it was allocated, measured //to the nearest millisecond Date date = new Date(); System.out.println("Date/Time Default Format: "+ date.toString()); //getInstance() gets a default date/time //formatter that uses the SHORT style for //both the date and the time. DateFormat df = DateFormat.getInstance(); System.out.println("getInstance(): " + df.format(date)); //getDateInstance() gets the date formatter //with the default formatting style for the //default FORMAT locale. df = DateFormat.getDateInstance(); System.out.println("getDateInstance(): "+ df.format(date)); //This form of getDateInstance() gets the time //formatter with the given formatting style //for the given locale. df = DateFormat.getDateInstance(DateFormat.LONG, Locale.US); System.out.println("getDateInstance(): "+ df.format(date)); //getTimeInstance() gets the time formatter //with the default formatting style for the //default FORMAT locale. df = DateFormat.getTimeInstance(); System.out.println("getTimeInstance(): "+ df.format(date)); //getDateTimeInstance() gets the date/time //formatter with the default formatting style //for the default FORMAT locale. df = DateFormat.getDateTimeInstance(); System.out.println("getDateTimeInstance(): "+ df.format(date)); try{ df = DateFormat.getDateInstance(DateFormat.LONG, Locale.US); //parse() Parses text from the beginning of //the given string to produce a date. //The method may not use the entire text //of the given string due to some reasons //like for example, some characters in the //given string can't be parsed Date pDate = df.parse("September 1, 1994"); System.out.println(pDate.toString()); }catch(ParseException e){ e.printStackTrace(); } } } Note Some parts of the results below are based on the system's current date/time where the example is executed Result Date/Time Default Format: Thu Jul 29 07:02:31 CST 2021 getInstance(): 7/29/21, 7:02 AM getDateInstance(): Jul 29, 2021 getDateInstance(): July 29, 2021 getTimeInstance(): 7:02:31 AM getDateTimeInstance(): Jul 29, 2021, 7:02:31 AM Thu Sep 01 00:00:00 CST 1994
SimpleDateFormat Class
SimpleDateFormat class is a concrete class that can format date/time by using a pattern. Though, methods like getTimeInstance(), getDateInstance(), or getDateTimeInstance() and parse() can still be used here.
import java.text.SimpleDateFormat; import java.util.Date; public class SampleClass{ public static void main(String[]args){ String pattern1 = "MM/dd/yyyy hh:mm:ss a"; String pattern2 = "hh:mm:ss a | MM-dd-yy"; String pattern3 = "'hh': hh"; Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat(pattern1); String result = sdf.format(date); System.out.println("Pattern1: " + result); sdf = new SimpleDateFormat(pattern2); result = sdf.format(date); System.out.println("Pattern2: " + result); sdf = new SimpleDateFormat(pattern3); result = sdf.format(date); System.out.println("Pattern3: " + result); } } Note the results below are based on the system's current date/time where the example is executed Result Pattern1: 07/29/2021 11:34:51 AM Pattern2: 11:34:51 AM | 07-29-21 Pattern3: hh: 11First off, let's examine pattern1. Some letters have special meanings in SimpleDateFormat's pattern. For example, "M" represents month in year(context sensitive), "m" represents minute in hour, "s" represents second in minute, etc.
We can see in the patterns above that some letters are repeated. We repeat letters in order to set the minimum number that those letters represent. For example, "mm" denotes seconds with minimum of two digits. If the given numberis less than the minimum number, additional zero will be added to meet the minimum number. For example, if the given second is "1" it will be formatted to "01".
Some characters have special formats when repeated. For example, "y", which represents year, has different formats when repeated two or four times. For example, the given year is "2021". "yy" formats "2021" to "21", "yyyy" formats "2021" to "2021".
Another examples are the "z","Z" and "X". "z","zz" and "zzz" formats general timezone to an abbreviation. For example, "z" formats "Central Standard Time" to "CST". "zzzz" formats general time zone to its full name. Any repetitions of "z"'s that are more than four have the same result as "zzzz". "Z" formats timezone using "RFC 822 4-digit time zone format". Any repetitions of "Z"'s that are more than two have the same result as "Z".
"X" formats timezone using "ISO 8601 Time zone format". "X" can be repeated up to 3 times. each repitition increases the characters of the formatted timezone. An exception will be thrown if "X" is repeated more than 4 times.
Letters from A-Z(case insensitive) are reserved for SimpleDateFormat. It means, we can't directly use them as literals in SimpleDateFormat's pattern. Though, there's a way to use letters as literals in the pattern. Use the single quote(') and enclose the string that you wanna be used as literals. Pattern3 demonstrated this concept.
Check the documentation of SimpleDateFormat for more information.
DateTimeFormatter Class
DateTimeFormatter is a class used for formatting date and time in some date-time-related classes in
java.time
like LocalDate
, LocalDateTime
, etc. This formatter has predefined constants that contain predefined formats. Additionally, this formatter allows patterns and localized styles.
This topic assumes that readers are knowledgeable about
java.time
package. If you're not familiar with java.time
package, you may wanna read this blogpost I created before reading this topic. This example demonstrates DateTimeFormatter
class.
import java.time.*; import java.time.format.*; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ LocalDateTime date = LocalDateTime.of(2021, 9, 20, 16, 35, 25); System.out.println("LocalDate Default Format"); System.out.println(date); System.out.println("With ISO_WEEK_DATE Format"); System.out.println( date.format(DateTimeFormatter.ISO_WEEK_DATE)); System.out.println(); System.out.println("Using Pattern"); System.out.println(date.format( DateTimeFormatter. ofPattern("MM/dd/uu hh:mm:ss", Locale.US))); System.out.println(); System.out.println("Using Localized Style"); DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM).withLocale(Locale.US); System.out.println("Style #1: " + date.format(dtf)); dtf = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG, FormatStyle.MEDIUM). withLocale(Locale.US); System.out.println("Style #2: " + date.format(dtf)); } } Result LocalDate Default Format 2020-09-20T16:35:25 With ISO_WEEK_DATE Format 2020-W38-1 Using Pattern 09/20/21 04:35:25 Using Localized Style Style #1: Sep 20, 2021, 4:35:25 PM Style #2: September 20, 2021, 4:35:25 PMFirst off,
format
method can be found in some date-time-related classes in java.time
like LocalDate
, LocalDateTime
, etc. format
method returns a formatted text of date and time information of a date-time-related class.
DateTimeFormatter
has predefined constants that we can use. Most of these constants are used by some date-time-related classes. For example, LocalDateTime.toString
uses DateTimeFormatter.ISO_LOCAL_DATE_TIME as its default formatter. More information about constant formatters can be found in the documentation.
DateTimeFormatter
can use patterns as formatters. There are characters that have special meaning in a pattern like 'm', 'h', 'M', etc. DateTimeFormater.ofPattern
is used to create a formatter via pattern. Some characters have the same meaning but different presentation.
For example, "M" and "L" characters are the symbols for
month-of-year
. However, "M" presents month-of-year
as a number e.g. 7
or 07
. "L" presents month-of-year
as a text e.g. Jul
or July
. More information about pattern and characters with special meaning can be found in the documentation.
Also, don't be confused with
year
and year-of-era
. year-of-era
represents the concept of the year within the era. There are two eras: BC(Before Christ)
or BCE(Before Common Era)
and AD(Anno Domini)
or CE(Common Era)
.
CE starts at 1 and moves forward as moving forward to the present time. BCE starts at 1 and moves backward as moving forward to the past time.
year
, starting at 1, is equivalent to years in CE. Year 0 is equal to 1 BCE. Proleptic year is commonly used when handling standard dates. year-of-era
is commonly used in historical timeline.
methods with 'ofLocalized' prefix returns a locale specific date format for the ISO chronology. In the example above I used two overloaded forms of
ofLocalizedDateTime
:ofLocalizedDate(FormatStyle dateStyle)
ofLocalizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle)
The first form uses
dateStyle
to format both date and time. The second form uses dateStyle
to format date and timeStyle
to format time. FormatStyle is an enumeration of the style of a localized date, time or date-time formatter.
withLocale
returns a DateTimeFormatter
instance with the given locale. Note that the localized pattern is looked up lazily. In the example above, DateTimeFormatter
holds FormatStyle
given by method with 'ofLocalized' prefix and the locale given by withLocale
.
Actual formatting happens if the formatted text is requested. If
withLocale
is not invoked, your system's default locale is used. If the locale you're using has Unicode Extensions, you may wanna use localizedBy().
Take note that
format
method may throw an exception if the given FormatStyle
can't find necessary information it needs. For example, FormatStyle.FULL
may throw an exception when used as formatter in LocalDateTime
because FormatStyle.FULL
may look for offset and zone id which LocalDateTime
don't have.
Parsing
Next, we can use
DateTimeFormatter
to parse text based on its formatter. This example demonstrates DateTimeFormatter.parse
and its overloaded forms.
import java.text.ParsePosition; import java.time.*; import java.time.format.*; import java.time.temporal.TemporalAccessor; import java.time.temporal.ChronoField; import java.time.temporal.TemporalQueries; import java.util.Locale; public class SampleClass{ public static void main(String[] args){ TemporalAccessor ta = DateTimeFormatter. ofPattern("MM/dd/uu hh:mm:ss", Locale.US). parse("09/20/21 04:35:25"); if(ta.isSupported(ChronoField.DAY_OF_MONTH)) System.out.println("Day of month: "+ ta.get(ChronoField.DAY_OF_MONTH)); ta = DateTimeFormatter. ofPattern("hh:mm:ss", Locale.US). parse("09/20/21 04:35:25", new ParsePosition(9)); System.out.println("Hour of AM/PM: " + ta.get(ChronoField.HOUR_OF_AMPM)); System.out.println("Minutes of hour: " + ta.get(ChronoField.MINUTE_OF_HOUR)); System.out.println("Seconds of minute: " + ta.get(ChronoField.SECOND_OF_MINUTE)); LocalDate ld = DateTimeFormatter. ofPattern("MM/dd/uu hh:mm:ss", Locale.US). parse("09/20/21 04:35:25", TemporalQueries.localDate()); System.out.println("LocalDate: " + ld); } } Result Day of month: 20 Hour of AM/PM: 4 Minutes of hour: 35 Seconds of minute: 25 LocalDate: 2021-09-20
parse
has three overloaded forms:parse(CharSequence text)
parse(CharSequence text, ParsePosition position)
parse(CharSequence text, TemporalQuery<T> query)
The first form has a
text
parameter that contains the text to be parsed. This form returns a TemporalAccessor instance.
isSupported
returns true if a TemporalField instance is supported by the instance that calls this method. Otherwise, returns false. ChronoField implements TemporalField
thus its constant fields are compatible with TemporalField
.
The second form has a
text
parameter that contains the text to be parsed and position
parameter that contains the position or index of where the parsing starts. ParsePosition is a simple class used by Format and its subclasses to keep track of the current position during parsing.
The third form has a
text
parameter that contains the text to be parsed and a query
that contains a query command. TemporalQuery is a functional interface used for querying a temporal object. Queries are a key tool for extracting information from temporal objects. TemporalQueries provides common implementations of TemporalQuery
.
Resolving
Parsing is implemented as a two-phase operation. First, the text is parsed using the layout defined by the formatter, producing a Map of field to value, a ZoneId and a Chronology. Second, the parsed data is resolved, by validating, combining and simplifying the various fields into more useful ones. More information can be found in the documentation.
In this topic, we're gonna be discussing
withResolverFields
and withResolverStyle
methods. This example demonstrates the two methods.
import java.time.*; import java.time.format.*; import java.time.temporal.*; public class SampleClass{ public static void main(String[] args){ TemporalAccessor ta = DateTimeFormatter. ofPattern("MM/dd/uu DDD", Locale.US). withResolverFields(ChronoField.YEAR, ChronoField.DAY_OF_YEAR). withResolverStyle(ResolverStyle.LENIENT). parse("09/20/21 489"); System.out.println(ta); } } Result {}, ISO resolved to 2022-05-04
withResolverFields
acts as a filter between phase 1 and phase 2 of parsing. The fields in withResolverFields
are the fields that are going to phase 2. If the fields are possible to be resolved to a date, the new date will form. In the example above, the year(2021) and day-of-year(489) are extracted, resolved and form the new date(2022-05-04).
This is valid because in java, a combination of
YEAR
and DAY_OF_YEAR
can be resolved and consequently produces a date. More information on how java resolves fields can be found in this
documentation.
withResolverStyle
sets a new resolve style. There are three resolver styles:LENIENT
SMART
STRICT
These constants can be found in the ResolverStyle enum.
Lenient
mode is the least strict mode of the resolver styles. It accepts out-of-range values. For example, lenient mode allows the month in the ISO calendar system to be outside the range 1 to 12. For example, month 15 is treated as being 3 months after month 12.
Strict
mode is the most strict mode of the resolver styles. all field values must not be out-of-range, otherwise, it will be treated as invalid value. For example, month 15 is invalid because it's outside of the range 1 to 12. In the example above, day-of-year is outside the range 1 to 365 in standard years and 1 to 366 in leap years. However, the resolver mode is lenient that's why 489 is still a valid value. Try changing the resolver mode to strict mode and you will encounter an exception.
Smart
mode is a mix between lenient and strict mode with some modifications. Some fields may accept out-of-range values, some may not and the resolving process of this mode may be different from lenient and strict modes. For example, let's parse 2019-03-32
using the resolver modes. In lenient mode, the result is 2019-04-01
. As you can see the month moved to 4 and the day is set to 1.
In strict mode, an exception is thrown because 32 is outside the range 1 to 31 of month of march of ISO calendar. In smart mode, the result is
2019-03-01
. As you can see, the month didn't move and the day is reset to 1. Take note the the smart mode is the default mode of DateTimeFormatter
class.
DateTimeFormatterBuilder
DateTimeFormatterBuilder allows a
DateTimeFormatter
to be created. All DateTimeFormatter
are created ultimately using this builder. This example demonstrates some methods of DateTimeFormatterBuilder
.
import java.time.*; import java.time.format.*; import java.time.temporal.ChronoField; public class SampleClass{ public static void main(String[] args){ DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); DateTimeFormatter formatter = builder. optionalStart(). appendLiteral("Date: "). optionalEnd(). appendValue(ChronoField.YEAR). appendLiteral("/"). appendValue(ChronoField.MONTH_OF_YEAR). appendLiteral("/"). appendValue(ChronoField.DAY_OF_MONTH, 2). parseStrict(). toFormatter(java.util.Locale.US); LocalDate ld = LocalDate.of(2020, 4, 5); System.out.println("Formatting"); System.out.println(ld.format(formatter)); System.out.println(); System.out.println("Parsing"); ld = LocalDate.parse("2015/10/02", formatter); System.out.println("Date: " + ld); } } Result Formatting Date: 2020/4/05 Parsing Date: 2015-10-02
appendLiteral(String literal)
appends plain text to the formatter. appendValue(TemporalField field)
appends the value of a date-time field to the formatter using a normal output style.
appendValue(TemporalField field, int width)
appends the value of a date-time field to the formatter with fixed width. If the value of the date-time field is less than the fixed width, zeroes will be added to compensate.
parseStrict
changes the parse style to strict mode for the remainder of the formatter. optionalStart
marks the start of an optional section and optionalEnd
ends the optional section. All elements in the optional section are treated as optional that may affect formatting or parsing.
In the example above, the literal
"Date: "
without quotes will be output during formatting. During parsing, the literal "Date: "
without quotes doesn't need to be an input. Another example, consider this builder setup:optionalStart().appendValue(MINUTE_OF_HOUR,2).optionalEnd()
During formatting, the minute will only be output if its value can be obtained from the date-time. During parsing, the input will be successfully parsed whether the minute is present or not.
toFormatter(Locale locale)
completes this builder by creating the DateTimeFormatter using the specified locale. Let's try another example.
import java.time.*; import java.time.format.*; import java.time.temporal.ChronoField; public class SampleClass{ public static void main(String[] args){ DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); DateTimeFormatter formatter = builder. appendPattern("[yyyy][yyyyMMdd]"). optionalStart(). parseDefaulting( ChronoField.MONTH_OF_YEAR, 1). parseDefaulting( ChronoField.DAY_OF_MONTH, 1). optionalEnd(). toFormatter(java.util.Locale.US); System.out.println("Parsing"); LocalDate ld = LocalDate.parse( "2015", formatter); System.out.println("Date: " + ld); } } Parsing Date: 2015-01-01
parseDefaulting
appends a default value for a field to the formatter for use in parsing. As the name implies, this method affects the parsing process of the formatter. In the example above, we only put year in the parse
method. Because there are no day and month in the parse
method, the formatter uses the values assigned in the parseDefaulting
method.
appendPattern
method appends the elements defined by the specified pattern to the builder. The "[" and "]" characters are similar to optionalStart
and optionalEnd
methods.
This pattern
"[yyyy][yyyyMMdd]"
accepts four-digit year or four-digit year, two-digit month and two-digit day. More details about reserved characters for date-time patterns can be found in this documentation.
Let's do one more example.
import java.time.*; import java.time.format.*; import java.time.temporal.ChronoField; public class SampleClass{ public static void main(String[] args){ DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); DateTimeFormatter formatter = builder. appendText( ChronoField.MONTH_OF_YEAR, TextStyle.SHORT_STANDALONE). appendLiteral(" "). appendValue(ChronoField.DAY_OF_MONTH). appendLiteral(", "). appendValueReduced(ChronoField.YEAR, 2, 2, LocalDate.of(1940,1,1)). toFormatter(java.util.Locale.US); LocalDate ld = LocalDate.of(1990, 5, 10); System.out.println("Formatting"); System.out.println(ld.format(formatter)); System.out.println(); System.out.println("Parsing"); ld = LocalDate.parse("Feb 20, 90", formatter); System.out.println(ld); } } Result Formatting May 10, 90 Parsing 1990-02-20
appendText(TemporalField field, TextStyle textStyle)
appends the text of a date-time field to the formatter. TextStyle is an enumeration of the style of text formatting and parsing.
appendValueReduced
appends the reduced value of a date-time field to the formatter. This method is typically used for formatting and parsing a two-digit year. When formatting or parsing a year such as YEAR
or YEAR_OR_ERA
, It's recommended to use this form:appendValueReduced(TemporalField field, int width, int maxWidth, ChronoLocalDate baseDate)
For other fields, use this form:
appendValueReduced(TemporalField field, int width, int maxWidth, int baseValue)
field
is a date-time field that we wanna format or parse. width
and maxWidth
determines the number of characters to be parsed or formatted. baseDate
or baseValue
determines the range of where to get the full value of reduced value.
In my opinion, to get the number of digits of full value of reduced value with two or more digits, full value digits should be two digits higher than the reduced value digits. For example, the number of digits of full value of reduced value with two digits is 4 digits. To get the full value range, add 9*10 to the given full value if the reduced value has two digits, 9*100 is the reduced value has three digits and so on.
For example, the given full value is 1940 and the reduced value is two digits such as 40. Add 9*10 to 1940 and we get 2030. Thus, full value range starts from 1940 to 2030. In the example above,
appendValueReduced
only use the YEAR
field. Fields like MONT_OF_YEAR
and DAY_OF_MONTH
in LocalDate
are not used.
During formatting,
width
and maxWidth
determines the number of characters to format. If they are equal then the format is fixed width. If the value of the given field is within the full value range then the reduced value is formatted. If the value of the given field is out-of-range and number of digits is greater than maxWidth
, the value of the given field is truncated to fit maxWidth
. The rightmost characters are output to match the value of the ginve field, left padding with zero.
Examples:
#1: Given field value is out-of-range and number of digits is greater than
maxWidth
.
... appendValueReduced(ChronoField.YEAR, 2, 2, LocalDate.of(1940,1,1)) ... LocalDate ld = LocalDate.of(2040, 5, 10); System.out.println(ld.format(formatter)); Result: May 10, 40 ... appendValueReduced(ChronoField.YEAR, 2, 2, LocalDate.of(1940,1,1)) ... LocalDate ld = LocalDate.of(1910, 5, 10); System.out.println(ld.format(formatter)); Result: May 10, 10#2: Given field value is within full value range.
... appendValueReduced(ChronoField.YEAR, 2, 2, LocalDate.of(1940,1,1)) ... LocalDate ld = LocalDate.of(2020, 5, 10); System.out.println(ld.format(formatter)); Result: May 10, 20#3: The rightmost characters are output to match the value of the ginve field, left padding with zero.
... appendValueReduced(ChronoField.YEAR, 2, 2, LocalDate.of(1940,1,1)) ... LocalDate ld = LocalDate.of(9, 5, 10); System.out.println(ld.format(formatter)); Result: May 10, 09#4: Given field value is out-of-range and number of digits is equal or less than
maxWidth
.
... appendValueReduced(ChronoField.YEAR, 2, 4, LocalDate.of(1940,1,1)) ... LocalDate ld = LocalDate.of(1900, 5, 10); System.out.println(ld.format(formatter)); Result: May 10, 1900During parsing, the created
DateTimeFormatter
gets the value; check if the value is within width
and maxWidth
; and check if the value has a full value match in the full value range.
Examples:
#1: parse reduced year value to full year value.
... appendValueReduced(ChronoField.YEAR, 2, 2, LocalDate.of(1940,1,1)) ... LocalDate ld = LocalDate. parse("Feb 20, 90", formatter); System.out.println(ld); Result: 1990-02-20#2: given value can't be matched with full value range.
... appendValueReduced(ChronoField.YEAR, 2, 4, LocalDate.of(1940,1,1)) ... LocalDate ld = LocalDate. parse("Feb 20, 1900", formatter); System.out.println(ld); Result: 1900-02-20
No comments:
Post a Comment