Of course! Working with dates and times in Java has evolved significantly. Here’s a comprehensive guide covering the modern approach (Java 8+), the legacy approach, and how to handle strings in between.

TL;DR: The Modern Way (Java 8+)
For any new project, always use the java.time package. It's immutable, thread-safe, and much easier to use.
Parsing a String to a LocalDateTime
To convert a string into a date-time object, you need to tell Java the format of the string.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class ModernExample {
public static void main(String[] args) {
String dateTimeString = "2025-10-27T10:15:30";
// Define the format of the input string
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// Parse the string into a LocalDateTime object
LocalDateTime dateTime = LocalDateTime.parse(dateTimeString, formatter);
System.out.println("Parsed LocalDateTime: " + dateTime);
System.out.println("Year: " + dateTime.getYear());
System.out.println("Month: " + dateTime.getMonth());
System.out.println("Day: " + dateTime.getDayOfMonth());
System.out.println("Hour: " + dateTime.getHour());
}
}
Output:
Parsed LocalDateTime: 2025-10-27T10:15:30
Year: 2025
Month: OCTOBER
Day: 27
Hour: 10
Formatting a LocalDateTime to a String
To convert a date-time object back into a string, you use the same DateTimeFormatter.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class FormattingExample {
public static void main(String[] args) {
// Get the current date and time
LocalDateTime now = LocalDateTime.now();
// Define a custom format pattern
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// Format the LocalDateTime object into a string
String formattedString = now.format(formatter);
System.out.println("Current LocalDateTime: " + now);
System.out.println("Formatted String: " + formattedString);
}
}
Output (will vary):
Current LocalDateTime: 2025-10-27T10:20:45.123
Formatted String: 2025-10-27 10:20:45
Key Classes in java.time
| Class | Description | Example Format |
|---|---|---|
LocalDate |
A date without a time (year, month, day). | 2025-10-27 |
LocalTime |
A time without a date (hour, minute, second, nanosecond). | 10:15:30 |
LocalDateTime |
A combination of date and time. | 2025-10-27T10:15:30 |
ZonedDateTime |
A date-time with a time zone (e.g., America/New_York). |
2025-10-27T10:15:30-04:00[America/New_York] |
DateTimeFormatter |
The class used for parsing and formatting date-time objects. | yyyy-MM-dd |
Handling Different String Formats
The DateTimeFormatter class has several predefined constants (like ISO_LOCAL_DATE_TIME), but you can also define your own patterns using letters.
| Letter | Meaning | Example |
|---|---|---|
y |
Year | yyyy (2025), yy (23) |
M |
Month in year | MM (01-12), MMM (Jan-Dec), MMMM (January) |
d |
Day in month | dd (01-31) |
H |
Hour in day (0-23) | HH |
h |
Hour in am/pm (1-12) | hh |
m |
Minute in hour | mm |
s |
Second in minute | ss |
a |
Am/pm marker | AM or PM |
Example: Parsing a Custom Format
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class CustomFormatExample {
public static void main(String[] args) {
// A string in a common "user-friendly" format
String userDateString = "27/10/2025 02:30 PM";
// Define the pattern that matches the string
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy hh:mm a");
// Parse the string
LocalDateTime dateTime = LocalDateTime.parse(userDateString, formatter);
System.out.println("Parsed from string: " + userDateString);
System.out.println("To LocalDateTime: " + dateTime);
}
}
Output:
Parsed from string: 27/10/2025 02:30 PM
To LocalDateTime: 2025-10-27T14:30
Notice how the 02:30 PM was correctly parsed into 14:30 (24-hour format).
The Legacy Way (Before Java 8)
You will still encounter this in older codebases. Avoid using it in new code. The main classes are java.util.Date and java.text.SimpleDateFormat.
Parsing a String to a Date
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class LegacyExample {
public static void main(String[] args) {
String dateString = "October 27, 2025";
// 1. Create a SimpleDateFormat object with the pattern
SimpleDateFormat formatter = new SimpleDateFormat("MMMM d, yyyy");
try {
// 2. Parse the string. This is SLOW and NOT THREAD-SAFE.
Date date = formatter.parse(dateString);
System.out.println("Parsed Date: " + date);
// Note: The output includes time and timezone info because java.util.Date has both.
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Output:
Parsed Date: Fri Oct 27 00:00:00 CDT 2025
Formatting a Date to a String
import java.text.SimpleDateFormat;
import java.util.Date;
public class LegacyFormatting {
public static void main(String[] args) {
Date now = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedString = formatter.format(now);
System.out.println("Current Date: " + now);
System.out.println("Formatted String: " + formattedString);
}
}
Why is the Legacy Approach Bad?
- Mutable:
Dateobjects can be changed, which can lead to bugs. - Confusing:
Dateactually represents an instant in time (milliseconds since epoch), but its name andtoString()method make it look like a date-only object. - Not Thread-Safe:
SimpleDateFormatis notoriously not thread-safe. You cannot share a single instance across multiple threads without synchronization, which kills performance. - Awkward API: The API is clumsy and error-prone.
Migration from Legacy to Modern (java.time)
If you have to work with legacy java.util.Date objects, you can easily convert them.
import java.time.*;
import java.util.Date;
public class MigrationExample {
public static void main(String[] args) {
// --- From java.util.Date to java.time ---
Date legacyDate = new Date();
System.out.println("Legacy java.util.Date: " + legacyDate);
// Convert to Instant (an instantaneous point on the timeline)
Instant instant = legacyDate.toInstant();
// Convert Instant to ZonedDateTime (with the system's default timezone)
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
// Get other types from ZonedDateTime
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
LocalDate localDate = zonedDateTime.toLocalDate();
LocalTime localTime = zonedDateTime.toLocalTime();
System.out.println("Converted to ZonedDateTime: " + zonedDateTime);
System.out.println("Converted to LocalDateTime: " + localDateTime);
// --- From java.time to java.util.Date ---
LocalDateTime newLocalDateTime = LocalDateTime.now(ZoneId.of("America/Los_Angeles"));
ZonedDateTime newZonedDateTime = newLocalDateTime.atZone(ZoneId.of("America/Los_Angeles"));
Date newLegacyDate = Date.from(newZonedDateTime.toInstant());
System.out.println("\nNew LocalDateTime: " + newLocalDateTime);
System.out.println("Converted back to Legacy Date: " + newLegacyDate);
}
}
Summary and Best Practices
- For New Code: Always prefer the
java.timepackage (Java 8+). - Parsing: Use
LocalDateTime.parse()orLocalDate.parse()with aDateTimeFormatter. - Formatting: Use
