Java Date and Time API (java.time)
Question
Explain Java's Date and Time API (java.time). What are the key classes and their purposes? How do you work with dates, times, and durations? What are the best practices for handling time zones and formatting?
Answer
Java 8 introduced the new Date and Time API (java.time) to address the shortcomings of the old java.util.Date and java.util.Calendar classes. The new API is immutable, thread-safe, and provides a more intuitive way to work with dates and times.
Key Classes
-
Basic Date and Time Classes
public class DateTimeBasics { public static void basicDateTime() { // LocalDate - Date without time LocalDate date = LocalDate.now(); LocalDate specificDate = LocalDate.of(2024, 3, 15); // LocalTime - Time without date LocalTime time = LocalTime.now(); LocalTime specificTime = LocalTime.of(14, 30); // LocalDateTime - Date and time LocalDateTime dateTime = LocalDateTime.now(); LocalDateTime specificDateTime = LocalDateTime.of(2024, 3, 15, 14, 30); // ZonedDateTime - Date and time with time zone ZonedDateTime zonedDateTime = ZonedDateTime.now(); ZonedDateTime specificZoned = ZonedDateTime.of( LocalDateTime.of(2024, 3, 15, 14, 30), ZoneId.of("America/New_York") ); } }
-
Duration and Period
public class DurationAndPeriod { public static void timeIntervals() { // Duration - Time-based amount Duration duration = Duration.ofHours(2); Duration betweenTimes = Duration.between( LocalTime.of(10, 0), LocalTime.of(12, 0) ); // Period - Date-based amount Period period = Period.ofDays(5); Period betweenDates = Period.between( LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 6) ); } }
Date and Time Operations
-
Manipulation
public class DateTimeManipulation { public static void manipulation() { LocalDate date = LocalDate.now(); // Add/subtract days LocalDate tomorrow = date.plusDays(1); LocalDate yesterday = date.minusDays(1); // Add/subtract months LocalDate nextMonth = date.plusMonths(1); // Add/subtract years LocalDate nextYear = date.plusYears(1); // With specific values LocalDate specificDay = date.withDayOfMonth(15); LocalDate specificMonth = date.withMonth(6); } }
-
Comparison
public class DateTimeComparison { public static void comparison() { LocalDate date1 = LocalDate.of(2024, 3, 15); LocalDate date2 = LocalDate.of(2024, 3, 20); // Compare dates boolean isBefore = date1.isBefore(date2); boolean isAfter = date1.isAfter(date2); boolean isEqual = date1.isEqual(date2); // Compare with current date boolean isToday = date1.equals(LocalDate.now()); // Compare with specific date boolean isLeapYear = date1.isLeapYear(); } }
Time Zone Handling
-
Working with Time Zones
public class TimeZoneHandling { public static void timeZones() { // Get current time in different zones ZonedDateTime newYork = ZonedDateTime.now(ZoneId.of("America/New_York")); ZonedDateTime london = ZonedDateTime.now(ZoneId.of("Europe/London")); // Convert between zones ZonedDateTime converted = newYork.withZoneSameInstant(ZoneId.of("Asia/Tokyo")); // Get all available zones Set<String> allZones = ZoneId.getAvailableZoneIds(); // Get system default zone ZoneId systemZone = ZoneId.systemDefault(); } }
-
Offset Handling
public class OffsetHandling { public static void offsets() { // OffsetDateTime - Date and time with offset OffsetDateTime offsetDateTime = OffsetDateTime.now(); // Create with specific offset OffsetDateTime specificOffset = OffsetDateTime.of( LocalDateTime.now(), ZoneOffset.ofHours(-5) ); // Convert between offsets OffsetDateTime converted = specificOffset.withOffsetSameInstant( ZoneOffset.ofHours(0) ); } }
Formatting and Parsing
- DateTimeFormatter
public class DateTimeFormatting { public static void formatting() { LocalDateTime dateTime = LocalDateTime.now(); // Basic formatting DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String formatted = dateTime.format(formatter); // Predefined formatters String isoDate = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); String isoDateOnly = dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE); // Parsing LocalDateTime parsed = LocalDateTime.parse( "2024-03-15 14:30:00", formatter ); // Localized formatting DateTimeFormatter localizedFormatter = DateTimeFormatter .ofLocalizedDateTime(FormatStyle.FULL) .withLocale(Locale.US); String localized = dateTime.format(localizedFormatter); } }
Best Practices
-
Immutability and Thread Safety
public class DateTimeBestPractices { // Good - Immutable fields private final LocalDateTime creationTime; public DateTimeBestPractices() { this.creationTime = LocalDateTime.now(); } // Good - Return new instance public LocalDateTime getCreationTime() { return creationTime; // Safe to return immutable object } // Bad - Mutable field private Date oldDate; // Avoid using old Date class }
-
Time Zone Handling
public class TimeZoneBestPractices { // Good - Store in UTC private final ZonedDateTime utcTime = ZonedDateTime.now(ZoneOffset.UTC); // Good - Convert for display public String getDisplayTime(ZoneId userZone) { return utcTime.withZoneSameInstant(userZone) .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); } // Bad - Store in local time private final LocalDateTime localTime = LocalDateTime.now(); // Avoid for storage }
Common Use Cases
-
Event Scheduling
public class EventScheduler { private final List<Event> events = new ArrayList<>(); public void scheduleEvent(String name, LocalDateTime time) { events.add(new Event(name, time)); } public List<Event> getUpcomingEvents() { LocalDateTime now = LocalDateTime.now(); return events.stream() .filter(event -> event.getTime().isAfter(now)) .sorted(Comparator.comparing(Event::getTime)) .collect(Collectors.toList()); } }
-
Duration Calculations
public class DurationCalculator { public Duration calculateWorkHours( LocalDateTime start, LocalDateTime end ) { return Duration.between(start, end) .minusHours(1) // Lunch break .withSeconds(0); // Ignore seconds } public boolean isOvertime(Duration workHours) { return workHours.toHours() > 8; } }
Testing
- Testing Date and Time Operations
@Test public void testDateTimeOperations() { // Test date manipulation LocalDate date = LocalDate.of(2024, 3, 15); assertEquals(LocalDate.of(2024, 3, 16), date.plusDays(1)); assertEquals(LocalDate.of(2024, 3, 14), date.minusDays(1)); // Test time zone conversion ZonedDateTime nyTime = ZonedDateTime.of( LocalDateTime.of(2024, 3, 15, 12, 0), ZoneId.of("America/New_York") ); ZonedDateTime londonTime = nyTime.withZoneSameInstant( ZoneId.of("Europe/London") ); assertEquals(17, londonTime.getHour()); // 5 hours ahead // Test duration Duration duration = Duration.ofHours(2); assertEquals(7200, duration.toSeconds()); }
Common Pitfalls
-
Time Zone Confusion
public class TimeZonePitfalls { // Bad - Mixing LocalDateTime and ZonedDateTime public void badTimeZone() { LocalDateTime local = LocalDateTime.now(); ZonedDateTime zoned = ZonedDateTime.now(); // Comparing without time zone consideration boolean isEqual = local.equals(zoned.toLocalDateTime()); } // Good - Proper time zone handling public void goodTimeZone() { ZonedDateTime zoned1 = ZonedDateTime.now(ZoneId.of("America/New_York")); ZonedDateTime zoned2 = ZonedDateTime.now(ZoneId.of("Europe/London")); // Compare with time zone consideration boolean isEqual = zoned1.toInstant().equals(zoned2.toInstant()); } }
-
Leap Year Handling
public class LeapYearPitfalls { // Bad - Incorrect leap year check public boolean badLeapYear(int year) { return year % 4 == 0; // Incorrect } // Good - Using Year class public boolean goodLeapYear(int year) { return Year.of(year).isLeap(); } }
-
Duration vs Period
public class DurationPeriodPitfalls { // Bad - Using Duration for date-based calculations public Duration badDateCalculation() { return Duration.ofDays(30); // Incorrect for months } // Good - Using Period for date-based calculations public Period goodDateCalculation() { return Period.ofMonths(1); // Correct for months } }