Skip to content

Java Date & Time

Handling dates and times is a common requirement in many applications. Java provides powerful APIs for this, but it’s important to use the modern API introduced in Java 8.

Introduced in Java 8, the java.time package provides a comprehensive, immutable, and much-improved API for handling dates, times, time zones, durations, and periods. This is the recommended API for all new development.

  • Immutability: Objects in java.time (like LocalDate, LocalTime) are immutable, meaning their state cannot be changed after creation. This makes them inherently thread-safe and easier to reason about.
  • Clarity: Clear separation between concepts like date, time, date-time, timestamp, duration, period, and time zones.
  • Fluent API: Methods are designed for clear and readable chaining (e.g., dateTime.plusDays(5).minusHours(2)).
  • Better Time Zone Handling: Robust support for time zones (ZoneId, ZonedDateTime).
  • ISO 8601 Standard: Based on the ISO standard for date and time representation.
  • LocalDate: Represents a date without time or time zone (e.g., 2023-10-27).
  • LocalTime: Represents a time without date or time zone (e.g., 14:30:55.123).
  • LocalDateTime: Represents both date and time, without a time zone (e.g., 2023-10-27T14:30:55).
  • ZonedDateTime: Represents a date and time with a specific time zone (e.g., 2023-10-27T14:30:55+01:00[Europe/London]).
  • Instant: Represents a specific point on the timeline (nanosecond precision), typically relative to the UTC epoch (January 1, 1970). Useful for timestamps.
  • Duration: Represents a time-based amount of time (seconds, nanoseconds). Useful for measuring machine time (e.g., difference between two Instants).
  • Period: Represents a date-based amount of time (years, months, days). Useful for human-readable time differences (e.g., age).
  • DateTimeFormatter: Used for formatting date/time objects into strings and parsing strings into date/time objects.
  • ZoneId: Represents a time zone identifier (e.g., Europe/Paris, Asia/Tokyo).
  • ZoneOffset: Represents a fixed offset from UTC (e.g., +02:00).

Use the static now() methods:

import java.time.*;
public class CurrentDateTime {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("Today's Date: " + today); // e.g., 2023-10-27
LocalTime currentTime = LocalTime.now();
System.out.println("Current Time: " + currentTime); // e.g., 10:15:30.123456789
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("Current Date and Time: " + currentDateTime);
// Get current time in a specific time zone
ZoneId londonZone = ZoneId.of("Europe/London");
ZonedDateTime londonTime = ZonedDateTime.now(londonZone);
System.out.println("Current London Time: " + londonTime);
Instant timestamp = Instant.now();
System.out.println("Current Instant (UTC): " + timestamp);
}
}

Use the static of() factory methods:

import java.time.*;
public class SpecificDateTime {
public static void main(String[] args) {
LocalDate specificDate = LocalDate.of(2024, Month.JANUARY, 15); // Year, Month, Day
System.out.println("Specific Date: " + specificDate);
LocalDate dateFromYMD = LocalDate.of(2024, 1, 15); // Year, Month (1-12), Day
System.out.println("Date from YMD: " + dateFromYMD);
LocalTime specificTime = LocalTime.of(18, 30, 0); // Hour, Minute, Second
System.out.println("Specific Time: " + specificTime);
LocalDateTime specificDateTime = LocalDateTime.of(2024, 1, 15, 18, 30);
System.out.println("Specific DateTime: " + specificDateTime);
ZoneId newYorkZone = ZoneId.of("America/New_York");
ZonedDateTime newYorkMeeting = ZonedDateTime.of(specificDateTime, newYorkZone);
System.out.println("New York Meeting: " + newYorkMeeting);
}
}

DateTimeFormatter is used to convert date/time objects to strings (formatting) and strings back to date/time objects (parsing).

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class PredefinedFormatting {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
// Predefined ISO formats (default for toString())
System.out.println("ISO DATE TIME: " + now.format(DateTimeFormatter.ISO_DATE_TIME));
System.out.println("ISO DATE: " + now.format(DateTimeFormatter.ISO_DATE));
// Locale-specific formats
DateTimeFormatter shortFormat = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
DateTimeFormatter mediumFormat = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
System.out.println("Short format: " + now.format(shortFormat)); // e.g., 10/27/23, 11:30 AM (US locale)
System.out.println("Medium format: " + now.format(mediumFormat)); // e.g., Oct 27, 2023, 11:30:00 AM
}
}
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class CustomFormattingParsing {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
// Define a custom pattern
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("EEEE, MMMM dd, yyyy 'at' hh:mm a");
// Formatting
String formattedString = now.format(customFormatter);
System.out.println("Custom format: " + formattedString);
// e.g., Friday, October 27, 2023 at 11:35 AM
// Parsing
String dateString = "Monday, June 05, 2023 at 02:30 PM";
try {
LocalDateTime parsedDateTime = LocalDateTime.parse(dateString, customFormatter);
System.out.println("Parsed DateTime: " + parsedDateTime);
} catch (DateTimeParseException e) {
System.err.println("Error parsing date string: " + e.getMessage());
}
// Parsing simpler format
String simpleDate = "2023-01-20";
try {
// ISO_DATE formatter is suitable here
LocalDate parsedDate = LocalDate.parse(simpleDate, DateTimeFormatter.ISO_DATE);
System.out.println("Parsed simple date: " + parsedDate);
} catch (DateTimeParseException e) {
System.err.println("Error parsing simple date: " + e.getMessage());
}
}
}
LetterComponentExamples
yYearyy (03), yyyy (2003)
MMonthM (7), MM (07), MMM (Jul), MMMM (July)
dDay of monthd (5), dd (05)
EDay of weekE/EE/EEE (Tue), EEEE (Tuesday)
HHour (0-23)H (9), HH (09)
hHour (1-12)h (9), hh (09)
mMinutem (5), mm (05)
sSeconds (8), ss (08)
aAM/PM markera (AM/PM)
zTime zone namez (PDT)
Z / O / XTime zone offsetZ (+0100), O (GMT+1), X / XX / XXX (-08 / -0800 / -08:00)
'Escape for text'Date:' yyyy

Since java.time objects are immutable, manipulation methods return a new object with the modified value.

import java.time.*;
public class DateTimeManipulation {
public static void main(String[] args) {
LocalDateTime meetingTime = LocalDateTime.of(2023, 11, 10, 14, 0); // Nov 10, 2023, 2 PM
System.out.println("Original Meeting Time: " + meetingTime);
// Add/Subtract time
LocalDateTime rescheduledTime = meetingTime.plusDays(3).minusHours(1);
System.out.println("Rescheduled Time: " + rescheduledTime); // Nov 13, 2023, 1 PM
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plusWeeks(1);
System.out.println("Next Week: " + nextWeek);
// Get parts of date/time
int year = meetingTime.getYear(); // 2023
Month month = meetingTime.getMonth(); // NOVEMBER
int day = meetingTime.getDayOfMonth(); // 10
DayOfWeek dayOfWeek = meetingTime.getDayOfWeek(); // FRIDAY
int hour = meetingTime.getHour(); // 14
System.out.println("Meeting details: " + dayOfWeek + ", " + month + " " + day + ", " + year);
// Modify specific fields using 'with'
LocalDateTime startOfDay = meetingTime.withHour(9).withMinute(0).withSecond(0);
System.out.println("Start of Business Day: " + startOfDay);
}
}

Period for date-based differences, Duration for time-based differences.

import java.time.*;
public class PeriodDurationExample {
public static void main(String[] args) {
// Period (Years, Months, Days)
LocalDate startDate = LocalDate.of(2023, 5, 15);
LocalDate endDate = LocalDate.of(2024, 7, 20);
Period periodBetween = Period.between(startDate, endDate);
System.out.println("Period: " + periodBetween); // P1Y2M5D (1 year, 2 months, 5 days)
System.out.printf("Difference: %d years, %d months, %d days%n",
periodBetween.getYears(), periodBetween.getMonths(), periodBetween.getDays());
Period twoWeeks = Period.ofWeeks(2);
LocalDate futureDate = startDate.plus(twoWeeks);
System.out.println("Two weeks later: " + futureDate);
// Duration (Days, Hours, Minutes, Seconds, Nanos)
Instant startInstant = Instant.now();
// Simulate some work
try { Thread.sleep(1500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
Instant endInstant = Instant.now();
Duration timeElapsed = Duration.between(startInstant, endInstant);
System.out.println("\nTime Elapsed (Duration): " + timeElapsed); // e.g., PT1.500123456S
System.out.println("Time Elapsed (Millis): " + timeElapsed.toMillis());
Duration twoHours = Duration.ofHours(2);
LocalTime deadline = LocalTime.of(15, 0).plus(twoHours);
System.out.println("Deadline Time: " + deadline);
}
}

Use Instant and Duration for precise time measurement.

import java.time.Duration;
import java.time.Instant;
public class MeasureTime {
public static void main(String[] args) {
Instant start = Instant.now();
System.out.println("Starting operation at: " + start);
// Simulate a task
try {
Thread.sleep(2000); // Sleep for 2 seconds
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Sleep interrupted");
}
Instant end = Instant.now();
System.out.println("Finished operation at: " + end);
Duration duration = Duration.between(start, end);
System.out.println("Duration: " + duration); // e.g., PT2.001S
System.out.println("Duration in milliseconds: " + duration.toMillis());
}
}

Legacy Date/Time API (java.util.Date, Calendar)

Section titled “Legacy Date/Time API (java.util.Date, Calendar)”

Before Java 8, developers used java.util.Date and java.util.Calendar (often java.util.GregorianCalendar). These classes have several drawbacks:

  • Mutability: Date objects are mutable, leading to potential bugs in concurrent environments or when passed around.
  • Confusing API: Date represents an instant, but its toString() includes date/time components based on the default time zone. Month indexing in Date and Calendar was 0-based (January=0), a common source of errors.
  • Poor Separation: Date mixes the concept of an instant with date/time fields.
  • Limited Time Zone Support: Calendar’s time zone handling was cumbersome.

Avoid using java.util.Date and java.util.Calendar in new code. If you need to interact with older APIs that use them, convert between the legacy and modern APIs:

import java.time.*;
import java.util.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class LegacyConversion {
public static void main(String[] args) {
// Legacy Date to Instant
Date legacyDate = new Date();
Instant instantFromDate = legacyDate.toInstant();
ZonedDateTime zdtFromDate = instantFromDate.atZone(ZoneId.systemDefault());
System.out.println("From legacy Date: " + zdtFromDate);
// Instant to Legacy Date
Instant nowInstant = Instant.now();
Date dateFromInstant = Date.from(nowInstant);
System.out.println("To legacy Date: " + dateFromInstant);
// Legacy Calendar to ZonedDateTime
Calendar legacyCalendar = new GregorianCalendar(); // Represents current time
ZonedDateTime zdtFromCalendar = ((GregorianCalendar) legacyCalendar).toZonedDateTime();
System.out.println("From legacy Calendar: " + zdtFromCalendar);
// ZonedDateTime to Legacy Calendar
ZonedDateTime currentZdt = ZonedDateTime.now();
GregorianCalendar calendarFromZdt = GregorianCalendar.from(currentZdt);
System.out.println("To legacy Calendar (month is 0-based): " + calendarFromZdt.get(Calendar.MONTH));
}
}

Mastering the java.time API is essential for modern Java development. Next, we’ll look at another common data manipulation task: working with sequences of characters using the String class and related concepts like Regular Expressions.