Unit 5: Java Date and Time API

CSE406 — Advanced Java Programming 9 min read

I. Orientation

The Java Date and Time API, introduced in Java 8 through the java.time package, models dates, times, time zones, and temporal amounts using immutable, thread-safe classes inspired by the ISO-8601 calendar system.

  • Immutability: Classes such as LocalDate, LocalTime, and Instant cannot be modified after creation; methods such as plusDays(1) return new objects.
  • Thread safety: Immutable temporal objects and DateTimeFormatter instances can safely be shared between threads.
  • ISO-8601 default: The API normally uses formats such as 2025-06-18 for dates and 14:30:00 for times.
  • Type-specific modeling: A date without a time is represented by LocalDate, while an exact global moment is represented by Instant.
  • Nanosecond precision: Time classes can represent fractional seconds down to nanoseconds, subject to the precision of the system clock.
  • Clear arithmetic: Methods such as plusMonths, minusHours, and between express temporal calculations directly.
  • Package structure:
    • java.time: Core date-time classes.
    • java.time.format: Parsing and formatting facilities.
    • java.time.temporal: Fields, units, adjusters, and queries.
    • java.time.zone: Time-zone rules and transitions.

II. Date-Based Events

A. Creating and managing date-based events

LocalDate represents a calendar date, such as 18 June 2025, without a time of day or time-zone information.

  • Current date: LocalDate.now() obtains the current date from the system clock and default time zone.
JAVA
LocalDate today = LocalDate.now();
  • Explicit construction: of(year, month, day) creates a validated date; an invalid value such as 30 February causes DateTimeException.
JAVA
LocalDate eventDate = LocalDate.of(2025, Month.JUNE, 18);
  • Text parsing: parse reads an ISO-8601 date by default.
JAVA
LocalDate deadline = LocalDate.parse("2025-12-15");
  • Field access: Methods include getYear(), getMonth(), getMonthValue(), getDayOfMonth(), and getDayOfWeek().
  • Date arithmetic: plusDays, plusWeeks, plusMonths, and plusYears return adjusted copies.
JAVA
LocalDate original = LocalDate.of(2025, 1, 31);
LocalDate result = original.plusMonths(1); // 2025-02-28
  • Comparison: isBefore, isAfter, and isEqual compare dates; compareTo provides chronological ordering.
  • Date properties: isLeapYear() checks leap-year status, while lengthOfMonth() returns values such as 28, 29, 30, or 31.
  • Temporal adjustment: TemporalAdjusters supports rules such as the first Monday or last day of a month.
JAVA
LocalDate lastDay =
    eventDate.with(TemporalAdjusters.lastDayOfMonth());

B. Applications and limitations

LocalDate is appropriate only when an event is defined by a calendar date rather than an exact global moment.

  • Suitable uses: Birthdays, holidays, billing dates, and submission deadlines expressed only as dates.
  • No clock time: A LocalDate cannot distinguish between 09:00 and 17:00.
  • No time zone: The same date may begin at different instants in Tokyo and London.
  • Clock-dependent creation: Prefer LocalDate.now(clock) with a fixed Clock in repeatable tests.

III. Time-Based Events

A. Creating and managing time-based events

LocalTime represents a time of day from 00:00 through 23:59:59.999999999, without a date or time zone.

  • Current time: LocalTime.now() reads the current local time using the default system clock.
  • Explicit construction: Factory methods accept hour, minute, second, and optional nanosecond components.
JAVA
LocalTime start = LocalTime.of(9, 30);
LocalTime precise = LocalTime.of(9, 30, 15, 500_000_000);
  • Parsing: ISO text can be converted directly.
JAVA
LocalTime closingTime = LocalTime.parse("17:45:00");
  • Constants: LocalTime.MIDNIGHT, LocalTime.NOON, LocalTime.MIN, and LocalTime.MAX represent useful boundaries.
  • Time arithmetic: Methods such as plusHours, minusMinutes, and plusSeconds return new values.
  • Midnight wraparound: Time arithmetic does not retain elapsed-day information.
JAVA
LocalTime result = LocalTime.of(23, 30).plusHours(2);
// 01:30; the next-day relationship is not stored
  • Field access: getHour(), getMinute(), getSecond(), and getNano() expose time components.
  • Comparison: isBefore and isAfter compare positions within a day.

B. Applications and limitations

LocalTime models recurring or date-independent clock times but does not identify when they occur globally.

  • Suitable uses: Opening hours, daily alarms, class schedules, and shift start times.
  • No date rollover: 01:30 alone does not reveal whether arithmetic crossed into tomorrow.
  • No zone rules: A local time may be ambiguous or nonexistent during a daylight-saving transition.
  • Precision distinction: Nanosecond capacity does not guarantee that the operating-system clock measures time at nanosecond accuracy.

IV. Combined Local Date-Time Values

A. Combining date and time into a single object

LocalDateTime combines a LocalDate and LocalTime, representing a local calendar date and clock time without a time-zone offset.

  • Direct creation: Components can be supplied through of.
JAVA
LocalDateTime meeting =
    LocalDateTime.of(2025, Month.JUNE, 18, 14, 30);
  • Combining objects: atTime and atDate join existing values.
JAVA
LocalDate date = LocalDate.of(2025, 6, 18);
LocalTime time = LocalTime.of(14, 30);
LocalDateTime appointment = date.atTime(time);
  • Separation: toLocalDate() and toLocalTime() recover the component objects.
  • Arithmetic: Date-based and time-based methods are both available, including plusDays, plusHours, and minusMinutes.
  • Replacement: Methods such as withHour(10) and withDayOfMonth(20) create modified copies.
  • Comparison: isBefore, isAfter, and isEqual compare local date-time fields.
  • Parsing: ISO-8601 uses the letter T between the date and time.
JAVA
LocalDateTime value =
    LocalDateTime.parse("2025-06-18T14:30:00");

B. Applications and limitations

LocalDateTime suits events stated in local civil time but cannot independently represent a unique point on the global timeline.

  • Suitable uses: A provisional appointment at “18 June, 2:30 PM” before its location is known.
  • Ambiguity: 2025-06-18T14:30 could refer to different instants in different zones.
  • Conversion requirement: A ZoneId or ZoneOffset must be supplied before reliable conversion to Instant.
  • Database caution: It should not be used for globally comparable audit timestamps unless zone or offset information is stored separately.

V. Time Zones and Global Events

A. Working with dates and times across time zones

ZonedDateTime combines a local date-time, a region-based ZoneId, and the offset selected from that zone’s historical and daylight-saving rules.

  • Zone identifiers: Region IDs such as Europe/Paris preserve rule information; fixed offsets such as +05:30 do not contain regional transition rules.
JAVA
ZoneId paris = ZoneId.of("Europe/Paris");
ZonedDateTime event =
    ZonedDateTime.of(2025, 6, 18, 14, 30, 0, 0, paris);
  • Current zoned time: ZonedDateTime.now(zone) obtains the current time in a specified region.
  • Same instant conversion: withZoneSameInstant changes the displayed local fields while preserving the global moment.
JAVA
ZonedDateTime london =
    event.withZoneSameInstant(ZoneId.of("Europe/London"));
  • Same local conversion: withZoneSameLocal preserves local fields but usually changes the represented instant.
  • Offsets: OffsetDateTime stores a date-time with an offset such as +05:30, while OffsetTime stores only a time and offset.
  • Daylight-saving gaps: During a spring transition, some local times do not exist and are normally shifted forward when resolved.
  • Daylight-saving overlaps: During an autumn transition, one local time can correspond to two offsets; methods such as withEarlierOffsetAtOverlap() select explicitly.

B. Applications and limitations

Zone-aware classes are required for travel, international communication, and future events whose meaning depends on civil-time rules.

  • Region preference: Use ZoneId.of("America/New_York") instead of a fixed offset when daylight-saving changes matter.
  • Rule changes: Governments may alter future offsets, so installed time-zone database versions can affect future calculations.
  • Instant preservation: Convert between zones by preserving the instant when coordinating one event across locations.
  • Display responsibility: Store an instant for factual occurrence time and retain the zone when the original regional context matters.

VI. Temporal Points and Amounts

A. Defining and creating timestamps, periods, and durations

Instant, Period, and Duration distinguish global timestamps, calendar-based amounts, and exact elapsed-time amounts.

  • Timestamp with Instant: An Instant represents a point on the UTC timeline as seconds and nanoseconds from the epoch 1970-01-01T00:00:00Z.
JAVA
Instant now = Instant.now();
Instant timestamp = Instant.parse("2025-06-18T12:30:00Z");
  • Epoch conversion: Instant.ofEpochSecond(1_750_249_800L) creates an instant from epoch seconds; toEpochMilli() produces epoch milliseconds.
  • Calendar amount with Period: A Period stores years, months, and days, making it suitable for ages or monthly schedules.
JAVA
Period subscription = Period.ofMonths(3);
LocalDate renewal = LocalDate.of(2025, 1, 31)
                             .plus(subscription);
  • Elapsed amount with Duration: A Duration stores seconds and nanoseconds and is suited to machine timeouts or measured intervals.
JAVA
Duration timeout = Duration.ofSeconds(30);
Duration elapsed = Duration.between(
    LocalTime.of(9, 0), LocalTime.of(10, 15));
  • Paired distinction:
    1. Period.ofDays(1) means one calendar day.
    2. Duration.ofHours(24) means exactly 86,400 seconds.
  • Interval calculation: Period.between(startDate, endDate) works with dates, while Duration.between(start, end) works with compatible time-based temporal values.
  • Unit conversion: Methods such as toDays(), toHours(), and toMinutes() return whole-unit totals and may discard remainders.

B. Applications and limitations

Choosing the correct temporal type prevents calendar rules from being confused with elapsed timeline measurement.

  • Audit events: Use Instant for log entries, transaction times, and globally sortable timestamps.
  • Human schedules: Use Period for “three months” or “two years,” where month lengths and leap years matter.
  • Machine intervals: Use Duration for a 500-millisecond timeout or a 90-second execution period.
  • Unsupported combinations: A date alone cannot accept a time-based duration such as one hour without first supplying a time.

VII. Date-Time Presentation

A. Applying formatting to local and zoned dates and times

DateTimeFormatter converts temporal objects to strings and parses compatible strings back into temporal objects.

  • Predefined formatters: Constants include ISO_LOCAL_DATE, ISO_LOCAL_DATE_TIME, ISO_OFFSET_DATE_TIME, and ISO_ZONED_DATE_TIME.
  • Custom patterns: Pattern letters have specific meanings: uuuu is the proleptic year, MM the month, dd the day, HH the 24-hour clock, and mm the minute.
JAVA
DateTimeFormatter formatter =
    DateTimeFormatter.ofPattern("dd MMM uuuu HH:mm", Locale.ENGLISH);

String text = LocalDateTime.of(2025, 6, 18, 14, 30)
                           .format(formatter);
// 18 Jun 2025 14:30
  • Parsing with a pattern: The formatter used for parsing must match the input structure.
JAVA
LocalDate parsed = LocalDate.parse(
    "18/06/2025",
    DateTimeFormatter.ofPattern("dd/MM/uuuu"));
  • Locale sensitivity: Textual months and weekdays depend on Locale; MMM may produce Jun in English and a different abbreviation elsewhere.
  • Zone formatting: Pattern XXX prints an offset such as +02:00, while VV prints a zone ID such as Europe/Paris.
  • Pattern precision: MM means month, whereas mm means minute; HH is a 24-hour field, whereas hh is a 12-hour field requiring a for AM/PM.
  • Formatter immutability: A configured formatter is immutable and thread-safe, so it may be stored as a shared constant.

B. Applications and limitations

Formatting controls presentation and input interpretation but does not alter the underlying temporal value.

  • Separation of concerns: Store structured objects such as Instant or LocalDate; format them only at system boundaries.
  • Zoned output: Include an offset or zone identifier when recipients must understand the represented global time.
  • Input validation: Invalid field values or mismatched patterns cause DateTimeParseException.
  • Year notation: Prefer uuuu for general date processing; yyyy represents year-of-era and may require era information for dates outside the common era.
  • Information loss: Formatting a ZonedDateTime without its offset or zone can produce text that cannot reconstruct the original instant.