LocalDate
, LocalDateTime
Java Streams work seamlessly with the java.time
package, allowing powerful manipulation and analysis of date and time data. Two of the most commonly used types are LocalDate
(date without time) and LocalDateTime
(date with time).
Streams can be used to sort, filter, group, and transform collections of date/time objects. Common use cases include parsing string-based dates, computing time differences, or filtering based on a date range.
LocalDate
and Sortingimport java.time.LocalDate;
import java.util.*;
import java.util.stream.*;
public class SortDates {
public static void main(String[] args) {
List<String> dateStrings = List.of("2024-03-15", "2025-01-01", "2023-12-31");
List<LocalDate> sortedDates = dateStrings.stream()
.map(LocalDate::parse) // Convert to LocalDate
.sorted() // Natural order (chronological)
.collect(Collectors.toList());
sortedDates.forEach(System.out::println);
}
}
Output:
2023-12-31
2024-03-15
2025-01-01
import java.time.LocalDate;
import java.util.*;
import java.util.stream.*;
public class FilterPastDates {
public static void main(String[] args) {
List<LocalDate> dates = List.of(
LocalDate.of(2023, 5, 1),
LocalDate.now().plusDays(1),
LocalDate.now().minusDays(10)
);
LocalDate today = LocalDate.now();
List<LocalDate> pastDates = dates.stream()
.filter(d -> d.isBefore(today)) // Keep only past dates
.collect(Collectors.toList());
pastDates.forEach(System.out::println);
}
}
import java.time.LocalDate;
import java.util.*;
import java.util.stream.*;
public class AddDays {
public static void main(String[] args) {
List<LocalDate> dates = List.of(
LocalDate.of(2024, 10, 10),
LocalDate.of(2024, 12, 25)
);
List<LocalDate> futureDates = dates.stream()
.map(date -> date.plusDays(7)) // Add 7 days to each date
.collect(Collectors.toList());
futureDates.forEach(System.out::println);
}
}
Java's LocalDate
and LocalDateTime
integrate cleanly with streams. Use cases include:
isBefore()
, isAfter()
, and equals()
.plusDays()
, minusMonths()
, etc..sorted()
.These operations enable expressive, readable pipelines for time-based logic in domains like scheduling, logging, and event processing.
Java Streams, in combination with LocalDate
and LocalDateTime
, make it simple to filter and group data based on time components like year, month, or day of week. This is useful in scenarios such as grouping events by month, filtering records from a specific year, or analyzing weekly patterns.
We’ll use methods like getYear()
, getMonth()
, and getDayOfWeek()
and combine them with Collectors.groupingBy()
to build structured date-based groupings.
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
public class FilterByYear {
public static void main(String[] args) {
List<LocalDate> events = List.of(
LocalDate.of(2023, 6, 10),
LocalDate.of(2024, 1, 5),
LocalDate.of(2024, 7, 20),
LocalDate.of(2025, 2, 14)
);
List<LocalDate> events2024 = events.stream()
.filter(date -> date.getYear() == 2024)
.collect(Collectors.toList());
events2024.forEach(System.out::println);
}
}
Output:
2024-01-05
2024-07-20
import java.time.LocalDate;
import java.time.Month;
import java.util.*;
import java.util.stream.Collectors;
public class GroupByMonth {
public static void main(String[] args) {
Map<String, LocalDate> tasks = Map.of(
"Submit Report", LocalDate.of(2024, 3, 15),
"Doctor Visit", LocalDate.of(2024, 3, 28),
"Vacation", LocalDate.of(2024, 7, 10),
"Conference", LocalDate.of(2024, 7, 25),
"New Year Prep", LocalDate.of(2024, 12, 29)
);
Map<Month, List<String>> tasksByMonth = tasks.entrySet().stream()
.collect(Collectors.groupingBy(
entry -> entry.getValue().getMonth(),
Collectors.mapping(Map.Entry::getKey, Collectors.toList())
));
tasksByMonth.forEach((month, taskList) -> {
System.out.println(month + ": " + taskList);
});
}
}
Output:
MARCH: [Submit Report, Doctor Visit]
JULY: [Vacation, Conference]
DECEMBER: [New Year Prep]
import java.time.LocalDate;
import java.time.DayOfWeek;
import java.util.*;
import java.util.stream.Collectors;
public class GroupByDayOfWeek {
public static void main(String[] args) {
List<LocalDate> dates = List.of(
LocalDate.of(2024, 6, 24), // MONDAY
LocalDate.of(2024, 6, 25), // TUESDAY
LocalDate.of(2024, 6, 26), // WEDNESDAY
LocalDate.of(2024, 7, 1), // MONDAY
LocalDate.of(2024, 7, 2) // TUESDAY
);
Map<DayOfWeek, List<LocalDate>> grouped = dates.stream()
.collect(Collectors.groupingBy(LocalDate::getDayOfWeek));
grouped.forEach((day, dateList) -> {
System.out.println(day + ": " + dateList);
});
}
}
Sample Output:
MONDAY: [2024-06-24, 2024-07-01]
TUESDAY: [2024-06-25, 2024-07-02]
WEDNESDAY: [2024-06-26]
getYear()
, getMonth()
, or getDayOfWeek()
on LocalDate
or LocalDateTime
to filter or classify dates.Collectors.groupingBy()
to create maps organized by these time components.These patterns are commonly used in reporting, scheduling systems, and analytics dashboards.
Event processing is a common use case for Java Streams, especially when working with timestamps. With LocalDateTime
, we can filter, group, and summarize events efficiently and expressively.
In this section, we’ll walk through two real-world examples using streams:
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
class Event {
String title;
LocalDateTime timestamp;
Event(String title, LocalDateTime timestamp) {
this.title = title;
this.timestamp = timestamp;
}
@Override
public String toString() {
return title + " @ " + timestamp;
}
}
public class ScheduleProcessor {
public static void main(String[] args) {
List<Event> events = List.of(
new Event("Team Meeting", LocalDateTime.now().plusDays(1)),
new Event("Project Deadline", LocalDateTime.now().plusDays(3)),
new Event("Code Review", LocalDateTime.now().minusDays(1)),
new Event("Client Call", LocalDateTime.now().plusDays(1)),
new Event("System Maintenance", LocalDateTime.now().plusDays(5))
);
LocalDateTime now = LocalDateTime.now();
// Filter future events and group by event date
Map<LocalDate, List<Event>> futureEventsByDate = events.stream()
.filter(e -> e.timestamp.isAfter(now))
.collect(Collectors.groupingBy(e -> e.timestamp.toLocalDate()));
futureEventsByDate.forEach((date, evts) -> {
System.out.println("Date: " + date);
evts.forEach(e -> System.out.println(" " + e));
});
}
}
Output (example):
Date: 2025-06-24
Team Meeting @ 2025-06-24T08:30
Client Call @ 2025-06-24T14:00
Date: 2025-06-26
Project Deadline @ 2025-06-26T09:00
Date: 2025-06-28
System Maintenance @ 2025-06-28T23:00
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
class LogEntry {
LocalDateTime timestamp;
String level;
LogEntry(LocalDateTime timestamp, String level) {
this.timestamp = timestamp;
this.level = level;
}
}
public class LogAnalyzer {
public static void main(String[] args) {
List<LogEntry> logs = List.of(
new LogEntry(LocalDateTime.of(2024, 6, 23, 10, 15), "INFO"),
new LogEntry(LocalDateTime.of(2024, 6, 23, 12, 40), "ERROR"),
new LogEntry(LocalDateTime.of(2024, 6, 24, 9, 0), "INFO"),
new LogEntry(LocalDateTime.of(2024, 6, 24, 16, 30), "WARN"),
new LogEntry(LocalDateTime.of(2024, 6, 25, 11, 20), "INFO")
);
Map<LocalDate, Long> logCountPerDay = logs.stream()
.collect(Collectors.groupingBy(
log -> log.timestamp.toLocalDate(),
Collectors.counting()
));
logCountPerDay.forEach((date, count) ->
System.out.println(date + ": " + count + " log entries")
);
}
}
Output:
2024-06-23: 2 log entries
2024-06-24: 2 log entries
2024-06-25: 1 log entries
LocalDateTime
for timestamps and convert to LocalDate
using .toLocalDate()
for grouping.These techniques scale well even for large event datasets and are widely used in production applications such as schedulers, log analyzers, and activity trackers.