dcsimg
September 29, 2016
Hot Topics:

Working with the New Java Date and Java Time APIs

  • January 18, 2016
  • By Manoj Debnath
  • Send Email »
  • More Articles »

One of the significant API improvements in Java version 8 is the introduction of new Date and Time APIs into the core library. Prior to this version, date and time processing had been under constant criticism due to its inherent weakness of handling internationalization. This gap, however, was filled by the add-on library component called Joda Time, created by Stephen Colebourne. But, the core API was devoid of the much needed lustre of its own. Java 8 addressed this issue and provided a built-in mechanism rather than rely on an add-on component into the core library group via the Java Community Process (JCP). This article mainly demonstrates a specific example to illustrate one of its usability.

Pitfalls of Java Date and Time

As of its earliest release (JDK 1.0), the only support for date and time processing was through the java.util.Date class. This class represented a specific instance of time in milliseconds, wrapped around a long value. The precision is set in relation to the time elapsed since Jan 1, 1970 00:00:00 GMT (midnight) called "the epoch." The interpretation of date, month, year, hour, minute, and second values and formatting and parsing of date and time strings were cumbersome, with discrepant internationalization support. Then came JDK 1.1, the Calendar class, along with DateFormat, provided a respite from inscrutable parsing and formatting mechanism; yet, the problem was far from over. Perhaps the biggest stumbling block of the Date class is its mutability and its being not associated with any time zone. Even the subsequent introduction of the Calendar class did not fix it. Moreover, like many of the old Java classes, the Date class is not cohesive; rather, it's a complex admixture of multiple concepts (date, time, and a combination of date and time) that clearly reciprocates a loose design. Joda Time addressed these issues head-on and became the de facto standard until Java version 8 came into the picture. In fact, Joda Time is streamlined into the core library, rather than being just an add-on.

Refer to the rationale behind the creation of Joda Time that gives a glimpse of what programmers expect and what Java date and time lacked in its earlier releases.

What the New API Offers

The most visible improvements are as follows:

  • APIs designed with cohesive classes that represent concept singularity
  • A dedicated root package, java.time, for date and time APIs
  • Organised for thread-safety, mainly achieved through the use of immutable classes
  • More intuitive class names, such as LocalTime, LocalDate, and so forth
  • Time fraction is fine-grained to nanosecond granularity (older classes supported millisecond granularity)

Overview of JSR 310 Content **[1]

As per JSR 310 Final Release, the contents of the following five packages with their primary function are:

  • java.time contains the main API based on the ISO-8601 standard. The classes defined here represent the principal date-time concepts, including instants, durations, dates, times, time zones, and periods. They are based on the ISO calendar system, which is the de facto world calendar following the proleptic Gregorian rules. All the classes are immutable and thread safe.
  • java.time.temporal contains the API for accessing the fields and units of date-time. Units are measurable, such as years, months, and hours. For example, the expression "2 hours later" uses the hours unit. By contrast, fields are mini-calculations, defining a value. For example, month-of-year, day-of-week, and hour-of-day are all fields. The set of supported units and fields can be extended by applications if desired.
  • java.time.format contains the API to print and parse fields into date-time objects and to customize parsing and printing. Formatters can be created in a variety of ways, including constants, patterns, localized styles, and a builder. Formatters are immutable and thread safe.
  • java.time.zone contains the API to handle time zones. Detailed information is made available about the rules of each time zone.
  • The java.time.chrono package contains the calendar-neutral API. This is intended for use by applications that need to use localized calendars. Support is provided for the Hijrah, Japanese, Minguo, and ThaiBuddhist calendars.

**[1] Excerpt from JSR 310 Final Release document. Reference: https://jcp.org/aboutJava/communityprocess/final/jsr310/index.html.

Java documentation is replete with code snippets and examples. And, by the way, the Joda Time documentation also provides interesting insights. So, without further elaboration, let's take on an interesting example of creating a time-zoned, analog clock with the help of the new Java date and time API.

An Example

The following example shows the creation of an analog clock with different time zones using the new Java date and time API. I have commented out some of the older APIs that can be used to achieve the same effect. Further, the comparison between the old and new APIs clearly demarcates the convenience of coding in favour of new APIs.

Note: The mathematics involved in the clock animation is explained in a similar code example.

AnalogClock.java

This class creates the clock and is primarily responsible for clock animation such as rotating the hour, minute, and second hands. The hour, minute, and second hands are adjusted according to the local date and time values through a parameterized constructor. Apart from graphical elements and Swing components, the uses of the actual date and time API are very few in the code. The idea is to provide a simple yet interesting example through the new APIs.

package org.mano.example;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class AnalogClock extends JPanel
      implements Runnable {
   private static final long serialVersionUID = 1L;
   private int xs, ys, xm, ym, xh, yh;
   private String zoneId;
   private LocalDateTime localDateTime;
   private Thread localThread = new Thread(this);

   public AnalogClock(String zoneId) {
      this.zoneId = zoneId;
      this.setDoubleBuffered(true);
      localThread.start();
   }

   public String getZoneId() {
      return zoneId;
   }

   public void setZoneId(String zoneId) {
      this.zoneId = zoneId;
   }

   public void paintComponent(Graphics g) {
      Graphics2D g2d = (Graphics2D) g;
      g2d.translate(getWidth() / 2, getHeight() / 2);
      int side = getWidth() > getHeight() ?
         getHeight() : getWidth();
      g2d.scale(side / 250, side / 250);
      setAllRenderingHints(g2d);
      /* Toggle between drawClockFace(g2d) and loadClockFace(g2d)
       * method first method draws the clock face with java 2d
       * graphics component second one loads a clockface.png image
       */
      // drawClockFace(g2d);
      loadClockFace(g2d);
      drawClockHands(g2d);
   }

   @Override
   public void run() {
      while (true) {
         try {

            localDateTime = LocalDateTime.now(ZoneId.of(zoneId));

            // Calendar.getInstance().get(Calendar.SECOND);
            int s = localDateTime.getSecond();
            xs = (int) (Math.cos(s * Math.PI / 30 - Math.PI / 2)
               * 80 + 0);
            ys = (int) (Math.sin(s * Math.PI / 30 - Math.PI / 2)
               * 80 + 0);

             // Calendar.getInstance().get(Calendar.MINUTE);
             int m = localDateTime.getMinute();
             xm = (int) (Math.cos(m * Math.PI / 30 - Math.PI / 2)
                * 75 + 0);
             ym = (int) (Math.sin(m * Math.PI / 30 - Math.PI / 2)
                * 75 + 0);

            // Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
            int h = localDateTime.getHour();
            xh = (int) (Math.cos((h * 30 + m / 2) * Math.PI / 180 -
               Math.PI / 2) * 60 + 0);
            yh = (int) (Math.sin((h * 30 + m / 2) * Math.PI / 180 -
               Math.PI / 2) * 60 + 0);
            repaint();
            Thread.sleep(500);

         } catch (InterruptedException ie) {
            ie.printStackTrace();
         }
      }

   }

   private void setAllRenderingHints(Graphics2D g2d) {
      g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
         RenderingHints.VALUE_TEXT_ANTIALIAS_ON)
      g2d.setRenderingHint(RenderingHints.KEY_DITHERING,
         RenderingHints.VALUE_DITHER_ENABLE);
      g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
         RenderingHints.VALUE_RENDER_QUALITY);
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
         RenderingHints.VALUE_ANTIALIAS_ON);
      g2d.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST, 100);
      g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
         RenderingHints.VALUE_FRACTIONALMETRICS_ON);
      g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
         RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
      g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
         RenderingHints.VALUE_COLOR_RENDER_QUALITY);
      g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
         RenderingHints.VALUE_STROKE_PURE);

   }

   private void loadClockFace(Graphics2D g2d){
      BufferedImage image = null;
      try{
         image = ImageIO.read(new File("../img/clockface.png"));
      }catch(IOException e){
         e.printStackTrace();
      }
      g2d.drawImage(image, null, -125, -130);

   }
 
   /*private void drawClockFace(Graphics2D g2d) {
      g2d.setPaint(new Color(255, 255, 153));
      g2d.fill(new Arc2D.Double(-110, -110, 220, 220, 0, 360,
         Arc2D.CHORD));
      g2d.setColor(new Color(255, 102, 0));
      g2d.setStroke(new BasicStroke(4.0f));
      g2d.draw(new Arc2D.Double(-110, -110, 220, 220, 0, 360,
         Arc2D.CHORD));

      for (int i = 0; i < 60; i++) {
         if ((i % 5) != 0) {
            g2d.setStroke(new BasicStroke(1.0f));
            g2d.setColor(new Color(255, 102, 0));
            g2d.drawLine(92, 0, 96, 0);
         } else {
            g2d.setColor(new Color(0, 0, 0));
            g2d.setStroke(new BasicStroke(2.0f));
            g2d.drawLine(88, 0, 96, 0);
         }
         g2d.rotate((Math.PI / 180.0) * 6.0);
      }
   }*/

   private void drawClockHands(Graphics2D g2d) {
      g2d.setColor(new Color(255, 0, 0));
      g2d.setStroke(new BasicStroke(5.0f));
      g2d.drawLine(0, 0, xh, yh);
      g2d.setStroke(new BasicStroke(3.0f));
      g2d.drawLine(0, 0, xm, ym);
      g2d.setColor(Color.black);
      g2d.setStroke(new BasicStroke(2.0f));
      g2d.drawLine(0, 0, xs, ys);
      g2d.setColor(Color.black);
      g2d.fillOval(-5, -5, 10, 10);
      g2d.setColor(Color.white);
      g2d.fillOval(-2, -2, 4, 4);
   }
}

ZonedClockPanel.java

This class creates a panel to accommodate the analog clock. The combo box within the panel contains a list of zone IDs that can manipulate the clock within the panel by selecting a specific zone ID at runtime.

package org.mano.example;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

public class ZonedClockPanel extends JPanel {

   private static final long serialVersionUID = 1L;
   private JComboBox<String> comboBox;
   private AnalogClock clock;
   private static JLabel dateLabel;

   public ZonedClockPanel() {
      List<String> zones = new ArrayList<String>
         (ZoneId.getAvailableZoneIds());
      Collections.sort(zones);
      comboBox = new JComboBox<String>();
      comboBox.setFont(new Font("Serif",
         Font.BOLD, 12));
      for (String s : zones)
         comboBox.addItem(s);
      Random random=new Random();
      int rand=random.nextInt(zones.size());
      comboBox.setSelectedIndex(rand);
      dateLabel = new JLabel(LocalDateTime.now
         (ZoneId.of(comboBox.getItemAt(rand))).format
         (DateTimeFormatter.ofPattern("MMMM d',' yyyy a")));
      dateLabel.setFont(new Font("Serif",
         Font.BOLD, 14));
      dateLabel.setHorizontalAlignment(SwingConstants.CENTER);
      clock = new AnalogClock(comboBox.getItemAt(rand));

      comboBox.addItemListener(new ItemListener() {

         @Override
         public void itemStateChanged(ItemEvent e) {
            if (e.getStateChange() == ItemEvent.SELECTED) {
               String zoneId = comboBox.getItemAt
                  (comboBox.getSelectedIndex());
               dateLabel.setText(LocalDateTime.now
                  (ZoneId.of(zoneId)).format
                  (DateTimeFormatter.ofPattern("MMMM d','
                  yyyy a")));
               clock.setZoneId(zoneId)
            }
         }
      });

      setLayout(new BorderLayout());
      setBorder(BorderFactory.createLineBorder(Color.gray));
      add(comboBox, BorderLayout.NORTH);
      add(clock, BorderLayout.CENTER);
      add(dateLabel, BorderLayout.SOUTH);
   }
}

AnalogTimeZoneClock.java

This is the frame class responsible for accommodating more than one time-zoned analog clock for display and ultimately running the application.

package org.mano.example;

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class AnalogTimeZoneClock extends JFrame {
   private static final long serialVersionUID = 1L;
   public AnalogTimeZoneClock() {
      super("Timezoned Analog Clock");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setSize(800, 800);
      JMenuBar menubar = new JMenuBar();
      JMenu helpMenu = new JMenu("Help");
      JMenuItem aboutItem = new JMenuItem("About");
      helpMenu.add(aboutItem);
      aboutItem.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            if (e.getActionCommand().equals("About"))
            JOptionPane.showMessageDialog(null,
            "Copyright © 2015. www.developer.com |
               developer md | manojdebnath@fastmail.fm",
            "About", JOptionPane.INFORMATION_MESSAGE);
         }
      });
      menubar.add(helpMenu);
      setJMenuBar(menubar);

      ZonedClockPanel c1 = new ZonedClockPanel();
      ZonedClockPanel c2 = new ZonedClockPanel();
      ZonedClockPanel c3 = new ZonedClockPanel();
      ZonedClockPanel c4 = new ZonedClockPanel();
      ZonedClockPanel c5 = new ZonedClockPanel();
      ZonedClockPanel c6 = new ZonedClockPanel();

      setLayout(new GridLayout(2, 4, 5, 5));
      add(c1);
      add(c2);
      add(c3);
      add(c4);
      add(c5);
      add(c6);
      setVisible(true);
   }

   public static void main(String[] args) {
      try {UIManager.setLookAndFeel
         (UIManager.getSystemLookAndFeelClassName());
   } catch (ClassNotFoundException | InstantiationException |
      IllegalAccessException | UnsupportedLookAndFeelException ex) {

   }
   new AnalogTimeZoneClock();
}

Output

Date and time, with the help of an analog clock, shows six customizable random time zones.

Date
Figure 1: The finished clocks, showing different time zones

Conclusion

This article just glimpsed into the library with more emphasis on giving an example.

Date and Time processing had been one of the weakest cornerstones of the API library. Java 8 brought exactly what developers needed. The new API will undoubtedly make it highly productive and easy to write code and maintain applications that require the heavy use of date and time functions.


Tags: API, JDK, time zone, JCP, Java 8, java community process, Swing, date and time, Joda Time, JSR 310, analog clock, Java Date, Java Time




Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date
Rocket Fuel