Applications often need to obtain date and time. Java provides a system-independent encapsulation of date and time in the java.util.Date class; it also provides java.util.TimeZone for dealing with time zones, and java.util.Calendar for extracting detailed information from Date . Different locales have different conventions for displaying date and time. Should the year, month, or day be displayed first? Should slashes , periods, or colons be used to separate fields of the date? What are the names of the months in the language? The java.text.DateFormat class can be used to format date and time in a locale-sensitive way for display to the user . The Date class was introduced in §7.5.1, "The Date Class," and the Calendar class and its subclass GregorianCalendar were introduced in §10.3, "The Calendar and GregorianCalendar Classes."
TimeZone represents a time zone offset and also figures out daylight savings. To get a TimeZone object for a specified time zone ID, use TimeZone.getTimeZone(id) . To set a time zone in a Calendar object, use the setTimeZone method with a time zone ID. For example, cal.setTimeZone(TimeZone.getTimeZone("CST")) sets the time zone to Central Standard Time. To find all the available time zones supported in Java, use the static method getAvailableIDs() in the TimeZone class. In general, the international time zone ID is a string in the form of continent /city like Europe/Berlin, Asia/Taipei, and America/Washington. You can also use the static method getDefault() in the TimeZone class to obtain the default time zone on the host machine.
The DateFormat class can be used to format date and time in a number of styles. The DateFormat class supports several standard formatting styles. To format date and time, simply create an instance of DateFormat using one of the three static methods getDateInstance , getTimeInstance , and getDateTimeInstance and apply the format(Date) method on the instance, as shown in Figure 26.2.
The dateStyle and timeStyle are one of the following constants: DateFormat.SHORT , DateFormat.MEDIUM , DateFormat.LONG , DateFormat.FULL . The exact result depends on the locale, but generally ,
SHORT is completely numeric, such as 7/24/98 (for date) and 4:49 PM (for time).
MEDIUM is longer, such as 24-Jul-98 (for date) and 4:52:09 PM (for time).
LONG is even longer, such as July 24, 1998 (for date) and 4:53:16 PM EST (for time).
FULL is completely specified, such as Friday, July 24, 1998 (for date) and 4:54:13 o'clock PM EST (for time).
The statements given below display current time with a specified time zone (CST), formatting style (full date and full time), and locale (US).
GregorianCalendar calendar = new GregorianCalendar(); DateFormat formatter = DateFormat.getDateTimeInstance( DateFormat.FULL, DateFormat.FULL, Locale.US); TimeZone timeZone = TimeZone.getTimeZone( "CST" ); formatter.setTimeZone(timeZone); System.out.println( "The local time is " + formatter.format(calendar.getTime()));
The date and time formatting subclass, SimpleDateFormat , enables you to choose any user-defined pattern for date and time formatting. The constructor shown below can be used to create a SimpleDateFormat object, and the object can be used to convert a Date object into a string with the desired format.
public SimpleDateFormat(String pattern)
The parameter pattern is a string consisting of characters with special meanings. For example, y means year, M means month, d means day of the month, G is for era designator, h means hour , m means minute of the hour, s means second of the minute, and z means time zone. Therefore, the following code will display a string like "Current time is 1997.11.12 AD at 04:10:18 PST" because the pattern is "yyyy.MM.dd G 'at' hh:mm:ss z":
SimpleDateFormat formatter = new SimpleDateFormat( "yyyy.MM.dd G 'at' hh:mm:ss z" ); date currentTime = new Date(); String dateString = formatter.format(currentTime); System.out.println( "Current time is " + dateString);
The DateFormatSymbols class encapsulates localizable date-time formatting data, such as the names of the months, and the names of the days of the week, as shown in Figure 26.3.
For example, the following statement displays the month names and weekday names for the default locale:
DateFormatSymbols symbols = new DateFormatSymbols(); String[] monthNames = symbols.getMonths(); for ( int i = 0; i < monthNames.length; i++) { System.out.println(monthNames[i]); } String[] weekdayNames = symbols.getWeekdays(); for ( int i = ; i < weekdayNames.length; i++) { System.out.println(weekdayNames[i]); }
The following two examples demonstrate how to display date, time, and calendar based on locale. The first example creates a clock and displays date and time in locale-sensitive format. The second example displays several different calendars with the names of the days shown in the appropriate local language.
Write a program that displays a clock to show the current time based on the specified locale and time zone. The locale and time zone are selected from the combo boxes that contain the available locales and time zones in the system, as shown in Figure 26.4.
Here are the major steps in the program:
Create a subclass of JPanel named WorldClock (Listing 26.1) to contain an instance of the StillClock class (developed in §13.12, "Case Study: The StillClock Class"), and place it in the center. Create a JLabel to display the digit time, and place it in the south. Use the GreogorianCalendar class to obtain the current time for a specific locale and time zone.
Create a subclass of JPanel named WorldClockControl (Listing 26.2) to contain an instance of WorldClock and two instances of JComboBox for selecting locales and time zones.
Create an applet named WorldClockApp (Listing 26.3) to contain an instance of WorldClockControl and enable the applet to run standalone.
The relationship among these classes is shown in Figure 26.5.
1 import javax.swing.*; 2 import java.awt.*; 3 import java.awt.event.*; 4 import java.util.Calendar; 5 import java.util.TimeZone; 6 import java.util.GregorianCalendar; 7 import java.text.*; 8 9 public class WorldClock extends JPanel { 10 private TimeZone timeZone = TimeZone.getTimeZone( "EST" ); 11 private Timer timer = new Timer( 1000 , new TimerListener()); 12 private StillClock clock = new StillClock(); 13 private JLabel jlblDigitTime = new JLabel( "" , JLabel.CENTER); 14 15 public WorldClock() { 16 setLayout( new BorderLayout()); 17 add(clock, BorderLayout.CENTER); 18 add(jlblDigitTime, BorderLayout.SOUTH); 19 timer.start(); 20 } 21 22 public void setTimeZone(TimeZone timeZone) { 23 this .timeZone = timeZone; 24 } 25 26 private class TimerListener implements ActionListener { 27 public void actionPerformed(ActionEvent e) { 28 Calendar calendar = new GregorianCalendar(timeZone, getLocale()); 29 clock.setHour(calendar.get(Calendar.HOUR)); 30 clock.setMinute(calendar.get(Calendar.MINUTE)); 31 clock.setSecond(calendar.get(Calendar.SECOND)); 32 33 // Display digit time on the label 34 DateFormat formatter = DateFormat.getDateTimeInstance 35 (DateFormat.MEDIUM, DateFormat.LONG, getLocale()); 36 formatter.setTimeZone(timeZone); 37 jlblDigitTime.setText( formatter.format(calendar.getTime()) ); 38 } 39 } 40 } |
1 import javax.swing.*; 2 import java.awt.*; 3 import java.awt.event.*; 4 import java.util.*; 5 6 public class WorldClockControl extends JPanel { 7 // Obtain all available locales and time zone ids 8 private Locale[] availableLocales = Locale.getAvailableLocales(); 9 private String[] availableTimeZones = TimeZone.getAvailableIDs(); 10 11 // Comboxes to display available locales and time zones 12 private JComboBox jcbLocales = new JComboBox(); 13 private JComboBox jcbTimeZones = new JComboBox(); 14 15 // Create a clock 16 private WorldClock clock = new WorldClock(); 17 18 public WorldClockControl() { 19 // Initialize jcbLocales with all available locales 20 setAvailableLocales(); 21 22 // Initialize jcbTimeZones with all available time zones 23 setAvailableTimeZones(); 24 25 // Initialize locale and time zone 26 clock.setLocale( 27 availableLocales[jcbLocales.getSelectedIndex()]); 28 clock.setTimeZone(TimeZone.getTimeZone( 29 availableTimeZones[jcbTimeZones.getSelectedIndex()])); 30 31 JPanel panel1 = new JPanel(); 32 panel1.setLayout( new GridLayout( 2 , 1 )); 33 panel1.add( new JLabel( "Locale" )); 34 panel1.add( new JLabel( "Time Zone" )); 35 JPanel panel2 = new JPanel(); 36 37 panel2.setLayout( new GridLayout( 2 , 1 )); 38 panel2.add(jcbLocales, BorderLayout.CENTER); 39 panel2.add(jcbTimeZones, BorderLayout.CENTER); 40 41 JPanel panel3 = new JPanel(); 42 panel3.setLayout( new BorderLayout()); 43 panel3.add(panel1, BorderLayout.WEST); 44 panel3.add(panel2, BorderLayout.CENTER); 45 46 setLayout( new BorderLayout()); 47 add(panel3, BorderLayout.NORTH); 48 add(clock, BorderLayout.CENTER); 49 50 jcbLocales.addActionListener( new ActionListener() { 51 public void actionPerformed(ActionEvent e) { 52 clock.setLocale( 53 availableLocales[jcbLocales.getSelectedIndex()]); 54 } 55 }); 56 jcbTimeZones.addActionListener( new ActionListener() { 57 public void actionPerformed(ActionEvent e) { 58 clock.setTimeZone(TimeZone.getTimeZone( 59 availableTimeZones[jcbTimeZones.getSelectedIndex()])); 60 } 61 }); 62 } 63 64 private void setAvailableLocales() { 65 for ( int i = ; i < availableLocales.length; i++) { 66 jcbLocales.addItem(availableLocales[i].getDisplayName() + " " 67 + availableLocales[i].toString()); 68 } 69 } 70 71 private void setAvailableTimeZones() { 72 // Sort time zones 73 Arrays.sort(availableTimeZones); 74 for ( int i = ; i < availableTimeZones.length; i++) { 75 jcbTimeZones.addItem(availableTimeZones[i]); 76 } 77 } 78 } |
1 import javax.swing.*; 2 3 public class WorldClockApp extends JApplet { 4 /** Construct the applet */ 5 public WorldClockApp() { 6 add( new WorldClockControl()); 7 } 8 } |
The WorldClock class uses GregorianCalendar to obtain a Calendar object for the specified locale and time zone (line 28). Since WorldClock extends JPanel , and every GUI component has the locale property, the locale for the calendar is obtained from the WorldClock using getLocale() (line 35).
An instance of StillClock is created (line 12) and placed in the panel (line 17). The clock time is updated every one second using the current Calendar object in lines 28 “31.
An instance of DateFormat is created (lines 34 “35) and is used to format the date in accordance with the locale (line 37).
The WorldClockControl class contains an instance of WorldClock and two combo boxes. The combo boxes store all the available locales and time zones (lines 64 “77). The newly selected locale and time zone are set in the clock (lines 56 “61) and used to display a new time based on the current locale and time zone.
Write a program that displays a calendar based on the specified locale, as shown in Figure 26.6. The user can specify a locale from a combo box that consists of a list of all the available locales supported by the system. When the program starts, the calendar for the current month of the year is displayed. The user can use the Prior and Next buttons to browse the calendar.
Here are the major steps in the program:
Create a subclass of JPanel named CalendarPanel (Listing 26.4) to display the calendar for the given year and month based on the specified locale and time zone.
Create an applet named CalendarApp (Listing 26.5). Create a panel to hold an instance of CalendarPanel and two buttons, Prior and Next . Place the panel in the center of the applet. Create a combo box and place it in the south of the applet. The relationships among these classes are shown in Figure 26.7.
1 import java.awt.*; 2 import javax.swing.*; 3 import javax.swing.border.LineBorder; 4 import java.util.*; 5 import java.text.*; 6 7 public class CalendarPanel extends JPanel { 8 // The header label 9 private JLabel jlblHeader = new JLabel( " " , JLabel.CENTER); 10 11 // Labels to display day names and days 12 private JLabel[] jlblDay = new JLabel[ 49 ]; 13 14 private Calendar calendar; 15 private int month; // The specified month 16 private int year; // The specified year 17 18 public CalendarPanel() { 19 // Panel jpDays to hold day names and days 20 JPanel jpDays = new JPanel(); 21 jpDays.setLayout( new GridLayout( 7 , 1 )); 22 for ( int i = ; i < 49 ; i++) { 23 jpDays.add(jlblDay[i] = new JLabel()); 24 jlblDay[i].setBorder( new LineBorder(Color.black, 1 )); 25 jlblDay[i].setHorizontalAlignment(JLabel.RIGHT); 26 jlblDay[i].setVerticalAlignment(JLabel.TOP); 27 } 28 29 // Place header and calendar body in the panel 30 this .setLayout( new BorderLayout()); 31 this .add(jlblHeader, BorderLayout.NORTH); 32 this .add(jpDays, BorderLayout.CENTER); 33 34 // Set current month, and year 35 calendar = new GregorianCalendar(); 36 month = calendar.get(Calendar.MONTH) + 1 ; 37 year = calendar.get(Calendar.YEAR); 38 39 // Show calendar 40 showHeader(); 41 showDayNames(); 42 showDays(); 43 } 44 45 /** Update the header based on locale */ 46 private void showHeader() { 47 SimpleDateFormat sdf = 48 new SimpleDateFormat( "MMMM yyyy" , getLocale()); 49 String header = sdf.format(calendar.getTime()); 50 jlblHeader.setText(header); 51 } 52 53 /** Update the day names based on locale */ 54 private void showDayNames() { 55 DateFormatSymbols dfs = new DateFormatSymbols(getLocale()); 56 String dayNames[] = dfs.getWeekdays(); 57 58 // Set calendar days 59 for ( int i = ; i < 7 ; i++) { 60 jlblDay[i].setText(dayNames[i + 1 ]); 61 jlblDay[i].setHorizontalAlignment(JLabel.CENTER); 62 } 63 } 64 65 /** Display days */ 66 public void showDays() { 67 // Set the calendar to the first day of the 68 // specified month and year 69 calendar.set(Calendar.YEAR, year); 70 calendar.set(Calendar.MONTH, month - 1 ); 71 calendar.set(Calendar.DATE, 1 ); 72 73 // Get the day of the first day in a month 74 int startingDayOfMonth = calendar.get(Calendar.DAY_OF_WEEK) ; 75 76 // Fill the calendar with the days before this month 77 Calendar cloneCalendar = (Calendar)calendar.clone(); 78 cloneCalendar.add(Calendar.DATE, -1 ); 79 int daysInMonth = cloneCalendar.getActualMaximum( 80 Calendar.DAY_OF_MONTH); 81 82 for ( int i = ; i < startingDayOfMonth - 1 ; i++) { 83 jlblDay[i + 7 ].setForeground(Color.yellow); 84 jlblDay[i + 7 ].setText(daysInMonth - 85 startingDayOfMonth + 2 + i + "" ); 86 } 87 88 // Display days of this month 89 for ( int i = 1 ; i <= daysInMonth; i++) { 90 jlblDay[i - 2 + startingDayOfMonth + 7 ]. 91 setForeground(Color.black); 92 jlblDay[i - 2 + startingDayOfMonth + 7 ].setText(i + "" ); 93 } 94 95 // Fill the calendar with the days after this month 96 int j = 1 ; 97 for ( int i = daysInMonth - 1 + startingDayOfMonth + 7 ; 98 i < 49 ; i++) { 99 jlblDay[i].setForeground(Color.yellow); 100 jlblDay[i].setText(j++ + "" ); 101 } 102 103 showHeader(); 104 } 105 106 /** Return month */ 107 public int getMonth() { 108 return month; 109 } 110 111 /** Set a new month */ 112 public void setMonth( int newMonth) { 113 month = newMonth; 114 showDays(); 115 } 116 117 /** Return year */ 118 public int getYear() { 119 return year; 120 } 121 122 /** Set a new year */ 123 public void setYear( int newYear) { 124 year = newYear; 125 showDays(); 126 } 127 128 /** Set a new locale */ 129 public void changeLocale(Locale newLocale) { 130 setLocale(newLocale); 131 showHeader(); 132 showDayNames(); 133 } 134 } |
CalendarPanel is created to control and display the calendar. It displays the month and year in the header, and the day names and days in the calendar body. The header and day names are locale-sensitive.
The showHeader method (lines 46 “51) displays the calendar title in a form like "MMMM yyyy". The SimpleDateFormat class used in the showHeader method is a subclass of DateFormat . SimpleDateFormat allows you to customize the date format to display the date in various nonstandard styles.
The showDayNames method (lines 54 “63) displays the day names in the calendar. The DateFormatSymbols class used in the showDayNames method is a class for encapsulating localizable date-time formatting data, such as the names of the months, the names of the days of the week, and the time zone data. The getWeekdays method is used to get an array of day names.
The showDays method (lines 66 “104) displays the days for the specified month of the year. As you can see in Figure 26.6, the labels before the current month are filled with the last few days of the preceding month, and the labels after the current month are filled with the first few days of the next month.
To fill the calendar with the days before the current month, a clone of calendar , named cloneCalendar , is created to obtain the days for the preceding month (line 77). cloneCalendar is a copy of calendar with separate memory space. Thus you can change the properties of cloneCalendar without corrupting the calendar object. The clone() method is defined in the Object class, which was introduced in §9.13.4, "The clone Method." You can clone any object as long as its defining class implements the Cloneable interface. The Calendar class implements Cloneable .
The cloneCalendar.getActualMaximum(Calendar.DAY_OF_MONTH) method (lines 79 “80) returns the number of days in the month for the specified calendar.
1 import java.awt.*; 2 import java.awt.event.*; 3 import javax.swing.*; 4 import javax.swing.border.*; 5 import java.util.*; 6 7 public class CalendarApp extends JApplet { 8 // Create a CalendarPanel for showing calendars 9 private CalendarPanel calendarPanel = new CalendarPanel(); 10 11 // Combo box for selecting available locales 12 private JComboBox jcboLocale = new JComboBox(); 13 14 // Declare locales to store available locales 15 private Locale locales[] = Calendar.getAvailableLocales(); 16 17 // Buttons Prior and Next to displaying prior and next month 18 private JButton jbtPrior = new JButton( "Prior" ); 19 private JButton jbtNext = new JButton( "Next" ); 20 21 /** Initialize the applet */ 22 public void init() { 23 // Panel jpLocale to hold the combo box for selecting locales 24 JPanel jpLocale = new JPanel(); 25 jpLocale.setBorder( new TitledBorder( "Choose a locale" )); 26 jpLocale.setLayout( new FlowLayout()); 27 jpLocale.add(jcboLocale); 28 29 // Initialize the combo box to add locale names 30 for ( int i = ; i < locales.length; i++) 31 jcboLocale.addItem(locales[i].getDisplayName()); 32 33 // Panel jpButtons to hold buttons 34 JPanel jpButtons = new JPanel(); 35 jpButtons.setLayout( new FlowLayout()); 36 jpButtons.add(jbtPrior); 37 jpButtons.add(jbtNext); 38 39 // Panel jpCalendar to hold calendarPanel and buttons 40 JPanel jpCalendar = new JPanel(); 41 jpCalendar.setLayout( new BorderLayout()); 42 jpCalendar.add(calendarPanel, BorderLayout.CENTER); 43 jpCalendar.add(jpButtons, BorderLayout.SOUTH); 44 45 // Place jpCalendar and jpLocale to the applet 46 add(jpCalendar, BorderLayout.CENTER); 47 add(jpLocale, BorderLayout.SOUTH); 48 49 // Register listeners 50 jcboLocale.addActionListener(new ActionListener() { 51 public void actionPerformed(ActionEvent e) { 52 if (e.getSource() == jcboLocale) 53 calendarPanel.changeLocale( 54 locales[jcboLocale.getSelectedIndex()]); 55 } 56 }); 57 58 jbtPrior.addActionListener( new ActionListener() { 59 public void actionPerformed(ActionEvent e) { 60 int currentMonth = calendarPanel.getMonth(); 61 if (currentMonth == 1 ) { 62 calendarPanel.setMonth( 12 ); 63 calendarPanel.setYear(calendarPanel.getYear() - 1 ); 64 } 65 else 66 calendarPanel.setMonth(currentMonth - 1 ); 67 } 68 }); 69 70 jbtNext.addActionListener( new ActionListener() { 71 public void actionPerformed(ActionEvent e) { 72 int currentMonth = calendarPanel.getMonth(); 73 if (currentMonth == 12 ) { 74 calendarPanel.setMonth( 1 ); 75 calendarPanel.setYear(calendarPanel.getYear() + 1 ); 76 } 77 else 78 calendarPanel.setMonth(currentMonth + 1 ); 79 } 80 }); 81 82 calendarPanel.changeLocale( 83 locales[jcboLocale.getSelectedIndex()]); 84 } 85 } |
CalendarApp creates the user interface and handles the button actions and combo box item selections for locales. The Calendar.getAvailableLocales() method (line 15) is used to find all the available locales that have calendars. Its getDisplayName() method returns the name of each locale and adds the name to the combo box (line 31). When the user selects a locale name in the combo box, a new locale is passed to calendarPanel , and a new calendar is displayed based on the new locale (lines 53 “54).