Internationalize your software, Part 3

Learn how to develop software for the global marketplace

1 2 3 4 5 Page 2
Page 2 of 5

Here are the results of running a Java application that creates a new time zone. Why would you want to create another time zone? Your code might come across a locale where Java doesn't provide time zone support. The source code to this application is located in example9.java.

Time zone ID: XYZ
Raw offset (hours): -6
Daylight savings time not in use.
Daylight savings time not in effect
Daylight savings time in use.
Daylight savings time not in effect

The preceding application results show that a new XYZ time zone object was created. The following code fragment shows how this object was created. As you can see, this time zone is a combination of a GMT offset and an ID, and is created by calling the SimpleTimeZone (int, String) constructor. Also, this time zone does not yet support daylight savings time.

// Create a time zone -6 hours from GMT (same as CST). // Name this time zone XYZ. There is no daylight savings // time.

SimpleTimeZone stz = new SimpleTimeZone (-6 * millisInHour, "XYZ");

Although I have not done so, it would be a good idea first to check if the XYZ ID has been assigned to the specified offset before using this ID. Call SimpleTimeZone's getAvailableIDs (int) method to return an array of IDs that are assigned to a specified offset. Scan this list to see if XYZ has already been assigned.

At some point, we decide this time zone should enforce daylight savings time behavior. How do we do this? Check out the following code fragment. It calls the setStartRule (int, int, int, int) and setEndRule (int, int, int, int) methods to set the rules for when daylight savings time starts and ends within this time zone.

// Set the start rule for daylight savings time. // This rule states that daylight savings begins at // 2 AM standard time on the first Sunday in April.

stz.setStartRule (Calendar.APRIL, 1, Calendar.SUNDAY, 2 * millisInHour);

// Set the end rule for daylight savings time. // This rule states that daylight savings ends at // 2 AM standard time on the last Sunday in October.

stz.setEndRule (Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * millisInHour);

The setStartRule (int, int, int, int) method sets the starting rule for determining daylight savings time within a time zone object and has the following prototype:

public void setStartRule (int month, int dayOfWeekInMonth, int dayOfWeek, int time)

The setEndRule (int, int, int, int) method sets the ending rule for determining daylight savings time within a time zone object and has the following prototype:

public void setEndRule (int month, int dayOfWeekInMonth, int dayOfWeek, int time)

What rules can be defined? Check out the following table for a list of rules. These rules apply equally apply to both methods.

RuleDescription
1

If dayOfWeekInMonth is x (a positive number) and dayOfWeek is y (a positive number), this indicates the xth occurrence of y from the start of month.

// DST starts at 2 AM standard time on the first Sunday in April.

stz.setStartRule (Calendar.APRIL, 1, Calendar.SUNDAY, 2 * millisInHour);

2

If dayOfWeekInMonth is -x (a negative number) and dayOfWeek is y (a positive number) then this indicates the xth occurrence of y from the end of month.

// DST starts at 3 AM standard time on the last Sunday in April.

stz.setStartRule (Calendar.APRIL, -1, Calendar.SUNDAY, 3 * millisInHour);

3

If dayOfWeek is 0 then this specifies an exact day within month.

// DST starts at 5 AM standard time on June 5.

stz.setStartRule (Calendar.JUNE, 5, 0, 5 * millisInHour);

4

If dayOfWeekInMonth is x (a positive number) and dayOfWeek is -y (a negative number), this indicates the first occurrence of y on or after the xth of month.

// DST starts at 1 PM standard time on the first WEDNESDAY on or after the 15th // of August.

stz.setStartRule (Calendar.AUGUST, 15, -Calendar.WEDNESDAY, 13 * millisInHour);

5

If dayOfWeekInMonth is -x (a negative number) and dayOfWeek is -y (a negative number), this indicates the last occurrence of y on or before the xth of month.

// DST starts at 6 AM standard time on the last FRIDAY on or before the 21st // of February.

stz.setStartRule (Calendar.FEBRUARY, -21, -Calendar.FRIDAY, 6 * millisInHour);

Finally, let's take a look at a code fragment that calls inDaylightTime (Date) to see if a given date, specified by a Date object argument, falls within daylight savings time.

// Check if date is in daylight savings time.

if (stz.inDaylightTime (now)) System.out.println ("Daylight savings time in effect"); else System.out.println ("Daylight savings time not in effect");

Detailed information about SimpleTimeZone is available in the following class reference, located at Sun's Java Web site: http://java.sun.com/products/jdk/1.1/docs/api/java.util.SimpleTimeZone.html.

Calendars

A calendar is a system for setting the beginning, length, and division of a year into days, weeks, and months. There are many different kinds of calendars: lunar (a 13-month calendar based on the phases of the moon), Gregorian, Julian, Mayan, Hebrew, and so on. These days, most of our world recognizes the Gregorian calendar. However, unless I'm mistaken, the lunar calendar is still used in some countries in the Middle East.

Ideally, international code should work with calendars in a generic fashion. For example, if the default locale shows that Java code is running in North America, this code should work with the Gregorian calendar. On the other hand, this code should probably work with the lunar calendar if it detects a Middle Eastern locale. For most applications, this point may not seem to be important.

Why not just use the Gregorian calendar, since it's already used by most of the world? Software that has been internationalized cannot afford to make assumptions. If a given locale uses a non-Gregorian calendar, calendar logic must automatically use that calendar whenever that locale is the default.

Java's Calendar class is used to obtain objects that represent calendars. Because Calendar is an abstract class, you must call one of Calendar's four static factory methods -- getInstance (), getInstance (TimeZone), getInstance (Locale), and getInstance (TimeZone, Locale) -- to return objects that have been instantiated from Calendar's concrete subclasses.

The applet below instantiates a Calendar object for each user-selected locale and then queries this object for date/time information. The source code to this applet is located in example10.java.

You need a Java-enabled browser to view this applet.

Most of the information shown in the preceding applet is pretty straightforward. However, there are some elements to point out.

Zone offset identifies the number of hours that must be added to GMT to achieve local standard time. For example, adding -6 to GMT results in the CST time zone, found in North America and South America.

DST offset represents the number of hours that must be subtracted from local time to obtain standard time (when daylight savings time is in effect). For example, one hour is added to CST on the first Sunday in April to begin daylight savings time. CST is then known as Central Daylight Savings Time (CDT). This extra hour is then subtracted from CDT on the last Sunday in October to return to CST.

First day of week is a one-based integer that represents the first day of a new week. A value of 1 represents Sunday.

In our applet, Sunday is shown as the first day of the week in Japan and the United States, while Monday marks the first day of the week in France. This is yet another example of a cultural difference that internationalized software must handle.

Let's look at a code fragment to see how Calendar subclass objects are instantiated.

if (locale.equals ("Default")) c = Calendar.getInstance (); else if (locale.equals ("France")) c = Calendar.getInstance (TimeZone.getTimeZone ("ECT"), new Locale ("fr", "FR")); else if (locale.equals ("Japan")) c = Calendar.getInstance (TimeZone.getTimeZone ("JST"), new Locale ("ja", "JP")); else c = Calendar.getInstance (TimeZone.getTimeZone ("CST"), new Locale ("en", "US"));

// Update the text area to show calendar information.

populate (c);

In the preceding code fragment, two of Calendar's factory methods are called to instantiate calendar objects from concrete subclasses: getInstance () and getInstance (TimeZone, Locale). You would call the getInstance () method whenever you want to work with a calendar that's associated with the default time zone and default locale. When working with other locales, you need to obtain appropriate time zones. This is done by calling TimeZone's getTimeZone (String) factory method with an appropriate time zone ID. As shown in the preceding code fragment, ECT is the ID for French standard time while JST is the ID for Japanese standard time.

Once a Calendar subclass object has been created, we can query this object to obtain the current values of its time fields. Calendar's get (int) method is used to obtain these values. The argument passed to get (int) identifies a time field that is stored within the calendar object. Calendar defines suitable field name constants for these time fields.

StringBuffer sb = new StringBuffer ();

sb.append ("Year = " + c.get (c.YEAR) + "\n"); sb.append ("Month = " + c.get (c.MONTH) + "\n"); sb.append ("Week of Year = " + c.get (c.WEEK_OF_YEAR) + "\n"); sb.append ("Week of Month = " + c.get (c.WEEK_OF_MONTH) + "\n"); sb.append ("Day = " + c.get (c.DATE) + "\n"); sb.append ("Day of Month = " + c.get (c.DAY_OF_MONTH) + "\n"); sb.append ("Day of Year = " + c.get (c.DAY_OF_YEAR) + "\n"); sb.append ("Day of Week = " + c.get (c.DAY_OF_WEEK) + "\n"); sb.append ("Day of Week in Month = " + c.get (c.DAY_OF_WEEK_IN_MONTH) + "\n"); sb.append ("AM/PM = " + ((c.get (c.AM_PM) == c.AM) ? "AM" : "PM") + "\n"); sb.append ("Hour = " + c.get (c.HOUR) + "\n"); sb.append ("Hour of day = " + c.get (c.HOUR_OF_DAY) + "\n"); sb.append ("Minute = " + c.get (c.MINUTE) + "\n"); sb.append ("Second = " + c.get (c.SECOND) + "\n"); sb.append ("Millisecond = " + c.get (c.MILLISECOND) + "\n"); sb.append ("Zone Offset = " + c.get (c.ZONE_OFFSET) / millisInHour + "\n"); sb.append ("DST Offset = " + c.get (c.DST_OFFSET) / millisInHour + "\n"); sb.append ("First Day of Week = " + c.getFirstDayOfWeek () + "\n");

// Populate text area control with calendar information.

ta.setText (sb.toString ());

Internally, a Calendar subclass object uses an integer array of time fields. Here are just a handful of the many useful methods Calendar provides:

  • You can call Calendar's add (int, int) method to add an offset (either positive or negative) to a time field.

  • You can call Calendar's roll (int, boolean) method to roll a time field up or down by a single unit of time. For example, roll (Calendar.MINUTE, true) rolls the minute field ahead by one minute.

  • You can call Calendar's getTimeZone () method to obtain the time zone object associated with a calendar, and Calendar's getTime () method to return the time fields as an equivalent Date object.

Please consult the JDK documentation for additional information on these and other methods. Detailed information about Calendar is available in the following class reference, located at Sun's Java Web site: http://java.sun.com/products/jdk/1.1/docs/api/java.util.Calendar.html.

As of JDK 1.1.6, Calendar has only one concrete subclass -- GregorianCalendar. Objects instantiated from this class represent calendars based on the Gregorian calendar. Although your code normally should work with the Calendar umbrella class (for maximum portability, since future versions of Java could introduce support for new calendars), it may need to access the various constants and methods available only in the GregorianCalendar class.

Detailed information about GregorianCalendar can be found in the following class reference, located at Sun's Java Web site: http://java.sun.com/products/jdk/1.1/docs/api/java.util.GregorianCalendar.html.

Formatters

A formatter is an object that arranges data into a locale-specific, user-oriented representation. For example, the United States locale formats numbers that contain comma characters (,) in the "thousands" positions. In contrast, the France locale formats numbers with space characters (" ") in these positions.

There are three categories of formatters: numeric, date, and message. Note that Java's various numeric and date formatters may not handle every possible locale. So prior to attempting to instantiate either formatter, your code should check for locale support by calling the actual numeric or date umbrella class's getAvailableLocales () method -- to obtain a list of all supported locales -- and then examining this list to see if a given locale is supported.

For the sake of simplicity, the various formatter examples do not check to see if a locale is supported. (The locales they work with are pretty standard.) In a production environment, however, these examples should contain logic to check for locale support.

Numeric formatters

A numeric formatter is an object that formats numeric data into a locale-specific representation. Java contains four kinds of numeric formatters: number, currency, percentage, and scientific. For whatever reason, the scientific formatter is not available for use. (Internally, JDK 1.1.6 source code has commented out the public reserved word for each of the scientific formatter's two static factory methods.)

Related:
1 2 3 4 5 Page 2
Page 2 of 5