Friday, February 18, 2011

Chapter 36: Using Dates, Numbers, and Currency

Dates, Numbers and Currencies are commonly used in all java programs and if you are a java developer you’ll end up using them eventually, sooner or later.
The Java API provides an extensive set of classes to help you work with dates, numbers, and currency. The exam will test your knowledge of the basic classes and methods you’ll use to work with dates and such.

So, let’s get started!!!

Working with Dates, Numbers, and Currencies

If you want to work with dates from around the world, you’ll need to be familiar with at least four classes from the java.text and java.util packages. Here are the four date related classes you’ll need to understand:

• java.util.Date Most of this class’s methods have been deprecated, but you can use this class to bridge between the Calendar and DateFormat class. An instance of Date represents a mutable date and time, to a millisecond.
• java.util.Calendar This class provides a huge variety of methods that help you convert and manipulate dates and times. For instance, if you want to add a month to a given date, or find out what day of the week January 1, 3000 falls on, the methods in the Calendar class will save your bacon.
• java.text.DateFormat This class is used to format dates not only providing various styles such as “01/01/70” or “January 1, 1970,” but also to format dates for numerous locales around the world.
• java.text.NumberFormat This class is used to format numbers and currencies for locales around the world.
• java.util.Locale This class is used in conjunction with DateFormat and NumberFormat to format dates, numbers and currency for specific locales. With the help of the Locale class you’ll be able to convert a date like “10/10/2005” to “Segunda-feira, 10 de Outubro de 2005” in no time. If you want to manipulate dates without producing formatted output, you can use the Locale class directly with the Calendar class.

Using Date and Number Related Classes

When you work with dates and numbers, you’ll often use several classes together. It’s important to understand how the classes we described above relate to each other, and when to use which classes in combination. For instance, you’ll need to know that if you want to do date formatting for a specific locale, you need to create your Locale object before your DateFormat object, because you’ll need your Locale object as an argument to your DateFormat factory method.
Before we delve deep into dates and numbers lets look at some common examples that would help us understand the usage of these classes better.

1. Get the Current date and time
Date d = new Date();
String s = d.toString(); //the s variable has the current date and time correct to a millisecond
2. Get an Object that lets you perform date and time calculations in your local time zone
Calendar c = Calendar.getInstance();
Use c.add() or c.roll() to perform the date manipulations
3. Get an Object that lets you perform date and time calculations in a different time zone
Locale loc = new Locale(language, country);
Calendar c = Calendar.getInstance(loc);
Again - use c.add() or c.roll() to perform the date and time manipulations
4. Get an Object that lets you perform date and time calculations, and then format the output in different locales with different date styles
Calendar c = Calendar.getInstance();
Locale loc = new Locale(…);
Date d = c.getTime();
DateFormat df = DateFormat.getDateInstance(style, loc);
String s = df.format(d);
5. Get an Object that lets you format numbers or currencies across many different locales
Locale loc = new Locale(…);
NumberFormat nf = NumberFormat.getCurrencyInstance(loc);
String s = nf.format(…);

The Date Class

The Date class has a long history. Its API design didn’t do a good job of handling internationalization and localization situations. In its current state, most of its methods have been deprecated, and for most purposes you’ll want to use the Calendar class instead of the Date class. The Date class is on the exam for several reasons: you might find it used in legacy code, it’s really easy if all you want is a quick and easy way to get the current date and time, it’s good when you want a universal time that is not affected by time zones, and finally, you’ll use it as a temporary bridge to format a Calendar object using the DateFormat class.

As mentioned above, an instance of the Date class represents a single date and time. Internally, the date and time is stored as a primitive long. Specifically, the long holds the number of milliseconds, between the date being represented and January 1, 1970.

Although most of Date’s methods have been deprecated, it’s still acceptable to use the getTime and setTime methods, although as we’ll soon see, it’s a bit painful. Let’s add an hour to our Date instance, d1, from the previous example:

import java.util.*;
class TestDates {
public static void main(String[] args) {
Date d1 = new Date();
// Get current date in variable d1
System.out.println("1st date " + d1.toString());
d1.setTime(d1.getTime() + 3600000);
// 3600000 milli seconds (1 hour)
System.out.println("new time " + d1.toString());

which produces:
1st date Fri Feb 18 14:05:58 SGT 2011
new time Fri Feb 18 15:05:58 SGT 2011

Notice that both setTime() and getTime() used the millisecond scale. If you want to manipulate dates using the Date class, that’s our only choice. While that wasn’t too painful, imagine how much fun it would be to add, say, a year to a given date.

The Calendar Class

We’ve just seen that manipulating dates using the Date class is tricky. The Calendar class is designed to make date manipulation easier. While the Calendar class has numerous fields and methods, once you get the hang of a few of them the rest tend to work in a similar fashion.
When you first try to use the Calendar class you might notice that it’s an abstract class. You can’t say

Calendar c = new Calendar(); // illegal, Calendar is abstract

In order to create a Calendar instance, you have to use one of the overloaded getInstance() static factory methods:

Calendar cal = Calendar.getInstance();

When you get a Calendar reference like cal, from above, your Calendar reference variable is actually referring to an instance of a concrete subclass of Calendar. You can’t know for sure what subclass you’ll get (java.util.GregorianCalendar is what you’ll almost certainly get). You’ll be using Calendar’s API. (As Java continues to spread around the world, in order to maintain cohesion, you might find additional, locale-specific subclasses of Calendar.)
Let us have a simple example:

import java.util.*;
class Dates2 {
public static void main(String[] args) {
Date d1 = new Date();
System.out.println("1st date " + d1.toString());

Calendar c = Calendar.getInstance();
Date d2 = c.getTime();
d2.setTime(d1.getTime() + 3600000);
System.out.println("new date " + d2.toString() );

This produces something like

1st date Fri Feb 18 14:13:01 SGT 2011
new date Fri Feb 18 15:13:01 SGT 2011

Let’s take a look at this program:
1. First we create a date object d1
2. We assign the Date d1 to the Calendar instance c.
3. We create another date object d2 using the time from the calendar
4. Then we are incrementing the time by one hour and assigning it as the time for the date variable d1

The other Calendar method you should know for the exam is the roll() method. The roll() method acts like the add() method, except that when a part of a Date gets incremented or decremented, larger parts of the Date will not get incremented or decremented. For instance:

Date d1 = new Date();
System.out.println("1st date " + d1.toString());

Calendar c = Calendar.getInstance();

c.roll(Calendar.MONTH, 11);

Date d2 = c.getTime();
System.out.println("new date " + d2.toString() );

The output would be something like this

1st date Fri Feb 18 14:16:15 SGT 2011
new date Tue Jan 18 14:16:15 SGT 2011

Notice that the year did not change, even though we added 11 months to a February date (As of today Feb 18th 2011). In a similar fashion, invoking roll() with HOUR won’t change the date, the month, or the year.

For the exam, you won’t have to memorize the Calendar class’s fields. If you need them to help answer a question, they will be provided as part of the question.

The DateFormat Class

Having learned how to create dates and manipulate them, let’s find out how to format them. Here’s an example of how a date can be formatted in different ways:
import java.text.*;
import java.util.*;
class Dates3 {
public static void main(String[] args) {
Date d1 = new Date();

DateFormat[] dfa = new DateFormat[6];
dfa[0] = DateFormat.getInstance();
dfa[1] = DateFormat.getDateInstance();
dfa[2] = DateFormat.getDateInstance(DateFormat.SHORT);
dfa[3] = DateFormat.getDateInstance(DateFormat.MEDIUM);
dfa[4] = DateFormat.getDateInstance(DateFormat.LONG);
dfa[5] = DateFormat.getDateInstance(DateFormat.FULL);

for(DateFormat df : dfa)

Which produces the following output
18/02/11 14:18
18 February 2011
18 February 2011

Examining this code we see a couple of things right away. First off, it looks like DateFormat is another abstract class, so we can’t use new to create instances of DateFormat. In this case we used two factory methods, getInstance() and getDateInstance(). Notice that getDateInstance() is overloaded; when we discuss locales, we’ll look at the other version of getDateInstance() that you’ll need to understand for the exam.

Next, we used static fields from the DateFormat class to customize our various instances of DateFormat. Each of these static fields represents a formatting style. In this case it looks like the no-arg version of getDateInstance() gives us the same style as the MEDIUM version of the method, but that’s not a hard and fast rule. Finally, we used the format() method to create Strings representing the properly formatted versions of the Date we’re working with.

The last method you should be familiar with is the parse() method. The parse() method takes a String formatted in the style of the DateFormat instance being used, and converts the String into a Date object. As you might imagine, this is a risky operation because the parse() method could easily receive a badly formatted String. Because of this, parse() can throw a ParseException. The following code creates a Date instance, uses DateFormat.format() to convert it into a String, and then uses DateFormat.parse() to change it back into a Date:

Date d1 = new Date();
System.out.println("d1 = " + d1.toString());

DateFormat df = DateFormat.getDateInstance(
String s = df.format(d1);

try {
Date d2 = df.parse(s);
System.out.println("parsed = " + d2.toString());
} catch (ParseException pe) {
System.out.println("parse exc"); }

which on our JVM produces
d1 = Fri Feb 18 14:19:48 SGT 2011
parsed = Fri Feb 18 00:00:00 SGT 2011

Note: If we’d wanted to retain the time along with the date we could have used the getDateTimeInstance() method, but it’s not on the exam.

Exam Tip:
The API for DateFormat.parse() explains that by default, the parse() method is lenient when parsing dates. Our experience is that parse() isn’t very lenient about the formatting of Strings it will successfully parse into dates; take care when you use this method!

The Locale Class

Remember the Local class I used in the initial examples for date formatting? Here we are. Now lets see what the Locale Class does for us.

Both the DateFormat class and the NumberFormat class (which we’ll cover next) can use an instance of Locale to customize formatted output to be specific to a locale. You might ask how Java defines a locale? The API says a locale is “a specific geographical, political, or cultural region.” The two Locale constructors you’ll need to understand for the exam are

Locale(String language)
Locale(String language, String country)

The language argument represents an ISO 639 Language Code, so for instance if you want to format your dates or numbers in English, you’d use “en” as your language string. Similarly it is “zh” for Chinese and so on. Don't worry, the exam wont ask you what is the locale code for every country.

Let’s get back to how you might use these codes. If you want to represent basic Italian in your application, all you need is the language code. If, on the other hand, you want to represent the Italian used in Switzerland, you’d want to indicate that the country is Switzerland (yes, the country code for Switzerland is “CH”), but that the language is Italian:
Locale locPT = new Locale("it"); // Italian
Locale locBR = new Locale("it", "CH"); // Switzerland

Using these two locales on a date could give us output like this:
sabato 1 ottobre 2005
sabato, 1. ottobre 2005

Now let’s put this all together in some code that creates a Calendar object, sets its date, then converts it to a Date. After that we’ll take that Date object and print it out using locales from around the world:

Calendar c = Calendar.getInstance();
c.set(2011, 01, 18); //Current Days Date

Date d2 = c.getTime();

Locale locIT = new Locale("it", "IT"); // Italy
Locale locIN = new Locale("hi", "IN"); // India
Locale locJA = new Locale("ja"); // Japan

DateFormat dfUS = DateFormat.getInstance();
System.out.println("US " + dfUS.format(d2));

DateFormat dfIT = DateFormat.getDateInstance(
DateFormat.FULL, locIT);
System.out.println("Italy " + dfIT.format(d2));

DateFormat dfIN = DateFormat.getDateInstance(
DateFormat.FULL, locIN);
System.out.println("India " + dfIN.format(d2));

DateFormat dfJA = DateFormat.getDateInstance(
DateFormat.FULL, locJA);
System.out.println("Japan " + dfJA.format(d2));

This, on our JVM, produces
US 18/02/11 14:26
Italy venerdì 18 febbraio 2011
India ????????, ?? ??????, ????
Japan 2011?2?18?

Don't worry about the ??? symbols. That is because my PC doesn't have support for locales from India or Japan properly. Hence the ???’s. But, the point here is, a single date object can be formatted to work for many locales.

There are a couple more methods in Locale (getDisplayCountry() and getDisplayLanguage()) that you’ll have to know for the exam. These methods let you create Strings that represent a given locale’s country and language in terms of both the default locale and any other locale:
Calendar c = Calendar.getInstance();

c.set(2011, 01, 18); //Current Days Date

Date d2 = c.getTime();

Locale locBR = new Locale("pt", "BR"); // Brazil
Locale locDK = new Locale("da", "DK"); // Denmark
Locale locIT = new Locale("it", "IT"); // Italy

System.out.println("def " + locBR.getDisplayCountry());
System.out.println("loc " + locBR.getDisplayCountry(locBR));

System.out.println("def " + locDK.getDisplayLanguage());
System.out.println("loc " + locDK.getDisplayLanguage(locDK));

This produces
def Brazil
loc Brasil
def Danish
loc dansk

Given that our JVM’s locale (the default for us) is US, the default for the country Brazil is “Brazil”, and the default for the Danish language is “Danish”. In Brazil, the country is called “Brasil”, and in Denmark the language is called “dansk”.

Exam Tip:
Remember that both DateFormat and NumberFormat objects can have their locales set only at the time of instantiation. Watch for code that attempts to change the locale of an existing instance—no such methods exist!

The NumberFormat Class

We’ll wrap up this chapter by discussing the NumberFormat class. Like the DateFormat class, NumberFormat is abstract, so you’ll typically use some version of either getInstance() or getCurrencyInstance() to create a NumberFormat object. Not surprisingly, you use this class to format numbers or currency values:

float f1 = 999.999f;
Locale locIt = new Locale("IT"); //Italy
NumberFormat[] nfa = new NumberFormat[4];

nfa[0] = NumberFormat.getInstance();
nfa[1] = NumberFormat.getInstance(locIt);
nfa[2] = NumberFormat.getCurrencyInstance();
nfa[3] = NumberFormat.getCurrencyInstance(locIt);

for(NumberFormat nf : nfa)

This, on our JVM, produces
¤ 1.000,00

Don’t be worried if, your system is not set up to display the symbols for all possible currency codes. You won’t be expected to know the symbols used for currency: if you need one, it will be specified in the question. You might encounter methods other than the format method on the exam. Here’s a little code that uses getMaximumFractionDigits(), setMaximumFractionDigits(), parse(), and setParseIntegerOnly()

float f1 = 999.999f;
NumberFormat nf = NumberFormat.getInstance();
System.out.print(nf.getMaximumFractionDigits() + " ");
System.out.print(nf.format(f1) + " ");

System.out.println(nf.format(f1) + " ");

try {
} catch (ParseException pe) {
System.out.println("parse exc");

This, on our JVM, produces
3 999.999 999.99902

Notice that in this case, the initial number of fractional digits for the default NumberFormat is three: and that the format() method rounds f1’s value, it doesn’t truncate it. After changing nf’s fractional digits, the entire value of f1 is displayed. Next, notice that the parse() method must run in a try/catch block and that the setParseIntegerOnly() method takes a boolean and in this case, causes subsequent calls to parse() to return only the integer part of Strings formatted as floating-point numbers.

As we’ve seen, several of the classes covered in this objective are abstract. In addition, for all of these classes, key functionality for every instance is established at the time of creation.

Summary of Constructors:
new Date() or
new Date(long milliseconds)

Calendar.getInstance() or

Locale.getDefault() or
new Locale(String language) or
new Locale(String language, String country)

DateFormat.getInstance() or
DateFormat.getDateInstance() or
DateFormat.getDateInstance(style) or
DateFormat.getDateInstance(style, locale)

NumberFormat.getInstance() or
NumberFormat.getInstance(Locale) or
NumberFormat.getNumberInstance() or
NumberFormat.getNumberInstance(Locale) or
NumberFormat.getCurrencyInstance() or

Previous Chapter: Chapter 35 - Serialization

Next Chapter: Chapter 37 - Searching, Parsing, Tokenizing and Formatting

No comments:

Post a Comment

© 2013 by All rights reserved. No part of this blog or its contents may be reproduced or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without prior written permission of the Author.