Java Date/Time Introduction

There are many Java classes available for date/time and it can becomes pretty confusing. This article try to give an overview of which classes or libraries for working with Date/Time in Java, and how you should use it. So, hopefully you doesn't need to look into the source codes to understand the salient features.

Prior to Java 8

If you using Java prior Java 8, you must be familiar with following classes:

ClassDescription
java.util.Daterepresents a specific instant in time, with millisecond precision.
java.util.Calendaris an abstract class that provides methods for converting between a specific instant in time and a set of calendar fields such as YEAR, MONTH, DAY_OF_MONTH, HOUR, and so on, and for manipulating the calendar fields, such as getting the date of the next week. An instant in time can be represented by a millisecond value that is an offset from the Epoch, January 1, 1970 00:00:00.000 GMT (Gregorian).
java.util.GregorianCalendaris a concrete subclass of Calendar and provides the standard calendar system used by most of the world.
java.util.TimeZonerepresents a time zone offset, and also figures out daylight savings.
java.sql.Dateextends extends java.util.Date, a thin wrapper around a millisecond value that allows JDBC to identify this as an SQL DATE value. A milliseconds value represents the number of milliseconds that have passed since January 1, 1970 00:00:00.000 GMT.
java.sql.Timeextends java.util.Date, a thin wrapper around the java.util.Date class that allows the JDBC API to identify this as an SQL TIME value. The Time class adds formatting and parsing operations to support the JDBC escape syntax for time values.
java.sql.Timestampextends java.util.Date, a thin wrapper around java.util.Date that allows the JDBC API to identify this as an SQL TIMESTAMP value. It adds the ability to hold the SQL TIMESTAMP fractional seconds value, by allowing the specification of fractional seconds to a precision of nanoseconds. A Timestamp also provides formatting and parsing operations to support the JDBC escape syntax for timestamp values.

There is also System.currentTimeMillis(), a static method that returns the current date and time as milliseconds since January 1st 1970. We can use the returned long value to initialize classes like Date, Timestamp, etc. Let's check below brief example for Java Date/Time standard library features before Java 8:

DateTimePrior8Example.java
import java.sql.Time;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class DateTimePrior8Example {

    public static void main(String[] args) throws ParseException {
        long now = System.currentTimeMillis();

        Date date = new Date(now);
        System.out.println("java.util.Date: " + date);

        java.sql.Date sqlDate = new java.sql.Date(now);
        System.out.println("java.sql.Date: " + sqlDate);

        Time sqlTime = new Time(now);
        System.out.println("java.sql.Time: " + sqlTime);

        Timestamp ts = new Timestamp(now);
        System.out.println("java.sql.Timestamp: " + ts);

        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(now);
        System.out.println("java.util.Calendar: " + cal.getTime());
        
        GregorianCalendar gCal = new GregorianCalendar();
        gCal.setTimeInMillis(now);
        System.out.println("java.util.GregorianCalendar: " + gCal.getTime());
        
        String strDate = "2019-07-03 08:15:35";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        java.util.Date dt = sdf.parse(strDate);
        System.out.println("Date in SGT: " + dt);
        
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        dt = sdf.parse(strDate);
        System.out.println("Date in UTC: " + dt);        
    }
}
                    

If above code executed, here the result:

java.util.Date: Wed Jul 03 01:29:29 SGT 2019
java.sql.Date: 2019-07-03
java.sql.Time: 01:29:29
java.sql.Timestamp: 2019-07-03 01:29:29.137
java.util.Calendar: Wed Jul 03 01:29:29 SGT 2019
java.util.GregorianCalendar: Wed Jul 03 01:29:29 SGT 2019
Date in SGT: Wed Jul 03 08:15:35 SGT 2019
Date in UTC: Wed Jul 03 16:15:35 SGT 2019

Joda-Time

The standard date and time classes prior to Java SE 8 are poor. By tackling this problem head-on, Joda-Time provides a quality replacement for the Java date and time classes and became the de facto standard date and time library for Java prior to Java SE 8. If you working with Joda-Time, you must aware about following classes:

ClassDescription
org.joda.time.Instantis the standard implementation of a fully immutable instant in time.
org.joda.time.DateTimeis the standard implementation of an unmodifiable datetime class.
org.joda.time.DateTimeZoneis an abstract class that represents a time zone
org.joda.time.LocalDateis an immutable datetime class representing a date without a time zone.
org.joda.time.LocalDateTimeis an unmodifiable datetime class representing a datetime without a time zone.
org.joda.time.LocalTimeis an immutable time class representing a time without a time zone.

Joda-Time also comes with some key features:

  • org.joda.time.Duration: Specifying a length of time in milliseconds. In Duration, there is no concept of fields, such as days or seconds, as these fields can vary in length
  • org.joda.time.Period: Specifying a set of duration field values. Unlike Duration, a time period is divided into a number of fields, such as hours and seconds.
  • org.joda.time.Interval: Represents a period of time between two instants

A comprehensive and flexible formatter-parser:

You need to add Joda-Time dependency, as example if you are using maven:

<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10.2</version>
</dependency>
                    

Following code will showcase Joda-Time library:

DateTimeJodaTimeExample.java
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Duration;
import org.joda.time.Instant;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class DateTimeJodaTimeExample {

    public static void main(String[] args) {
        LocalDate localDate = LocalDate.now();
        System.out.println("org.joda.time.LocalDate: " + localDate);
        
        LocalTime localTime = LocalTime.now();
        System.out.println("org.joda.time.LocalTime: " + localTime);

        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println("org.joda.time.LocalDateTime: " + localDateTime);
        
        Instant instant = new Instant();
        System.out.println("org.joda.time.Instant: " + instant);
        
        DateTime dateTime = instant.toDateTime();
        System.out.println("org.joda.time.DateTime: " + dateTime);
        
        long now = System.currentTimeMillis();
        long plus24Hours = now + 24*24*60*1000;  // in miliseconds
        Duration duration = new Duration(now, plus24Hours);
        System.out.println("org.joda.time.Duration: " + duration);
        Instant instantPlus24Hours = instant.plus(duration);
        System.out.println("Instant plus Duration: " + instantPlus24Hours);
        
        Period period = new Period().withMonths(1);
        System.out.println("org.joda.time.Period: " + period);
        DateTime datePeriod = dateTime.plus(period);
        System.out.println("Date plus Period: " + datePeriod);
        
        Interval interval = new Interval(instant, instantPlus24Hours);
        System.out.println("org.joda.time.Interval: " + interval);
        
        DateTimeFormatter fmt = DateTimeFormat.forPattern("dd-MM-yyyy HH:mm:ss:SSSZZ ZZZ");
        
        DateTime dt1 = new DateTime();
        System.out.println(fmt.print(dt1));

        DateTimeZone dtz2 = DateTimeZone.forID("UTC");
        DateTime dt2 = dt1.withZone(dtz2);
        System.out.println(fmt.print(dt2));

        DateTimeZone dtz3 = DateTimeZone.forID("America/New_York");
        DateTime dt3 = dt1.withZone(dtz3);
        System.out.println(fmt.print(dt3));
    }
}
                    

Will resulting:

org.joda.time.LocalDate: 2019-07-04
org.joda.time.LocalTime: 05:26:47.263
org.joda.time.LocalDateTime: 2019-07-04T05:26:47.265
org.joda.time.Instant: 2019-07-03T21:26:47.266Z
org.joda.time.DateTime: 2019-07-04T05:26:47.266+08:00
org.joda.time.Duration: PT34560S
Instant plus Duration: 2019-07-04T07:02:47.266Z
org.joda.time.Period: P1M
Date plus Period: 2019-08-04T05:26:47.266+08:00
org.joda.time.Interval: 2019-07-03T21:26:47.266Z/2019-07-04T07:02:47.266Z
04-07-2019 05:26:47:314+08:00 Asia/Singapore
03-07-2019 21:26:47:314+00:00 UTC
03-07-2019 17:26:47:314-04:00 America/New_York

Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).

Java 8 Date/Time API

In Java 8 a whole new set of date time API was added as an effort to revamp older date time API. This is done with the introduction of a whole new set of classes under located in the package java.time which is part of the standard Java 8 class library. Here some classes that mostly we use in Java 8 (and going forward) Date/Time API:

ClassDescription
java.time.InstantAn instantaneous point on the time-line.
java.time.LocalDateA date without a time-zone in the ISO-8601 calendar system, such as 2007-12-03.
java.time.LocalDateTimeA date-time without a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.
java.time.LocalTimeA time without a time-zone in the ISO-8601 calendar system, such as 10:15:30.
java.time.OffsetDateTimeA date-time with an offset from UTC/Greenwich in the ISO-8601 calendar system, such as 2007-12-03T10:15:30+01:00.
java.time.OffsetTimeA time with an offset from UTC/Greenwich in the ISO-8601 calendar system, such as 10:15:30+01:00.
java.time.ZonedDateTimeA date-time with a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30+01:00 Europe/Paris.

And some highlighted classes:

Find similarity between Java 8 classes and Joda-Time? In fact JSR 310: Date and Time API is submitted by by the author of Joda-Time library (Stephen Colebourne) and led jointly by Michael Nascimento Santos and Roger Riggs (Oracle).

Without further ado, here code example for Java 8 Date/Time standard library:

DateTimeJava8Example.java
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeJava8Example {

    public static void main(String[] args) {
        LocalDate localDate = LocalDate.now();
        System.out.println("java.time.LocalDate: " + localDate);
        
        LocalTime localTime = LocalTime.now();
        System.out.println("java.time.LocalTime: " + localTime);

        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println("java.time.LocalDateTime: " + localDateTime);
        
        Instant instant = Instant.now();
        System.out.println("java.time.Instant: " + instant);
        
        Instant instantPlus24Hours = instant.plusSeconds(24*24*60);
        
        Duration duration = Duration.between(instant, instantPlus24Hours);
        System.out.println("java.time.Duration: " + duration);
        
        LocalDate nextMonth = localDate.plusMonths(1);
        Period period = Period.between(localDate, nextMonth);
        System.out.println("java.time.Period: " + period);
        
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss:SSSZZ VV");
        
        ZonedDateTime dtz1 = localDateTime.atZone(ZoneId.systemDefault());
        System.out.println(fmt.format(dtz1));
        
        ZonedDateTime dtz2 = dtz1.withZoneSameInstant(ZoneId.of("UTC"));
        System.out.println(fmt.format(dtz2));

        ZonedDateTime dtz3 = dtz1.withZoneSameInstant(ZoneId.of("America/New_York"));
        System.out.println(fmt.format(dtz3));
    }
}
                    

with result:

java.time.LocalDate: 2019-07-04
java.time.LocalTime: 05:34:46.909
java.time.LocalDateTime: 2019-07-04T05:34:46.909
java.time.Instant: 2019-07-03T21:34:46.909Z
java.time.Duration: PT9H36M
java.time.Period: P1M
04-07-2019 05:34:46:909+0800 Asia/Singapore
03-07-2019 21:34:46:909+0000 UTC
03-07-2019 17:34:46:909-0400 America/New_York

Conclusion

If you working with Java version prior of Java 8, and you feel that the standard library are limited, you can use Joda-Time library as complement or alternative library. But if you already use Java 8 and above, use Date/Time standard library that comes in package java.time.