Skip to content

3.6 Dates and Times

Key terms: none introduced

Temporal entities can be represented as collections of primitive values. Three integers, for example, could express a date (day, month, year) or time (hours, minutes, seconds). Additional values could represent time zones or fractions of a second. In principle, temporal calculations — such as computing the day of the week for a given date or the interval between two times — could be performed directly on these component values. Doing so, however, would require explicit handling of technicalities like the varying lengths of months and the rules for leap years.

Java provides a comprehensive Date-Time API with classes that enable programmers to work with temporal entities at a high level of abstraction, focusing on the relationships between them rather than their internal representations. Programmers express their intent through method calls, knowing only what each method requires as input and what it produces as output, while the API accounts for the rules and conventions governing temporal data.

3.6.1 LocalDates and Periods

Listing 3.6.1 introduces LocalDate, which models a date in the ISO-8601 calendar system. The program creates instances representing the user's birthdate and the current date using the factory methods LocalDate.of and LocalDate.now, then outputs the user's age and the number of days lived.

Listing 3.6.1 - DaysAlive.java

DaysAlive.java
package chap03.sect6;

import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Scanner;

/**
 * Outputs the current date and the elapsed time since the user's birthdate.
 * 
 * @author Drue Coles
 */
public class DaysAlive {

   public static void main(String[] args) {
      // read user's birthdate
      Scanner in = new Scanner(System.in);
      System.out.print("Enter date of birth in MM DD YYYY format: ");
      int month = in.nextInt();
      int day = in.nextInt();
      int year = in.nextInt();
      LocalDate birthday = LocalDate.of(year, month, day);

      // output today's date
      LocalDate today = LocalDate.now();
      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE, MMMM dd, yyyy");
      System.out.printf("Today is %s. %n", today.format(formatter));

      // output current age
      Period period = Period.between(birthday, today);
      System.out.printf("You are %d years old. %n", period.getYears());

      // calculate total number of days since birth
      long daysAlive = ChronoUnit.DAYS.between(birthday, today);
      System.out.printf("You have been alive for %,d days. %n", daysAlive);
   }
}
Output 3.6.1
Enter date of birth in MM DD YYYY format: 10 17 2006
Today is Friday, December 12, 2025.
You are 19 years old.
You have been alive for 6,996 days.

The dates are formatted using a DateTimeFormatter created with the factory method ofPattern. This method takes a string with letters and symbols specifying a date format. The class documentation describes a wide range of formatting options; the pattern used here has the following components:

  • EEEE (day of the week as a full word)
  • MMMM (month as a full word)
  • dd (two-digit day number)
  • yyyy (four-digit year)

The Period class represents a span of time in years, months, and days. The static Period.between method takes two dates and returns the period between them; the program extracts the number of years from this object to determine the user's age.

Finally, ChronoUnit.DAYS is a predefined object with a between method for determining the number of days between two dates.

3.6.2 LocalTimes and Durations

Listing 3.6.2 introduces LocalTime and uses the Duration class to represent the interval between the current moment and a user-entered time.

Listing 3.6.2 - FavoriteTime.java

FavoriteTime.java
package chap03.sect6;

import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Scanner;

/**
 * Prompts the user for a favorite time of day and calculates how long the user must wait until it
 * arrives.
 *
 * @author Drue Coles
 */
public class FavoriteTime {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter your favorite time of day (HH:MM) in 24-hour format: ");
        String input = scanner.next();

        // parse user input
        LocalTime futureTime = LocalTime.parse(input); // assumes two-digit hour such as 05
        LocalTime now = LocalTime.now();

        // display current and future times in hh:mm:ss (24-hour format)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        System.out.printf("%13s %s %n", "Current time:", now.format(formatter));
        System.out.printf("%13s %s %n", "Future time:", futureTime.format(formatter));

        // compute number of seconds until future time
        int secondsInDay = 24 * 60 * 60;
        int nowSecs = now.toSecondOfDay();
        int futureSecs = futureTime.toSecondOfDay();
        int waitingSeconds = Math.floorMod(futureSecs - nowSecs, secondsInDay); 

        Duration waitingTime = Duration.ofSeconds(waitingSeconds);

        // display waiting time in HH:MM:SS format
        long totalSeconds = waitingTime.getSeconds();
        long hours = totalSeconds / 3600;
        long minutes = (totalSeconds % 3600) / 60;
        long seconds = totalSeconds % 60;
        System.out.printf("%13s %02d:%02d:%02d %n", "Waiting time:", hours, minutes, seconds);
    }
}
Output 3.6.2
Enter your favorite time of day (HH:MM) in 24-hour format: 05:30
Current time: 18:54:47
 Future time: 05:30:00
Waiting time: 10:35:13

Both times are converted to the number of seconds since midnight, and the current value is subtracted from the future value. If the difference is negative, the next occurrence of the entered time is on the following day, so the waiting time is obtained by adding one full day (86,400 seconds) to that difference.

For example, if the current time is 1000 seconds after midnight and the future time is 1500 seconds, the waiting time is 500 seconds. If the future time is 750 seconds, subtracting yields -250, so the waiting time is 86,400 - 250 = 86,150 seconds.

This wrap-around behavior can be computed using modular arithmetic. However, the remainder operator (%) preserves the sign of the dividend, so it does not provide the desired behavior when the left operand is negative. The program instead uses Math.floorMod, which performs true modulo arithmetic, as shown by the following code:

int waitingSeconds = -250 % 86400; // -250
int waitingSeconds = Math.floorMod(-250, 86400); // 86150

The Date-Time API also provides LocalDateTime, which pairs a LocalDate with a LocalTime, enabling client code to treat the combination as a single entity.