Index

Working with Dates and Times in Streams

Java Streams

16.1 Stream Operations on 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.

Example 1: Mapping Strings to LocalDate and Sorting

import 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

Example 2: Filter Dates in the Past

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);
    }
}

Example 3: Add Days to Each Date

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);
    }
}

Summary

Java's LocalDate and LocalDateTime integrate cleanly with streams. Use cases include:

These operations enable expressive, readable pipelines for time-based logic in domains like scheduling, logging, and event processing.

Index

16.2 Filtering and Grouping by Date Fields

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.

Example 1: Filter Events by Year

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

Example 2: Group Tasks by Month

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]

Example 3: Group Events by Day of Week

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]

Summary

These patterns are commonly used in reporting, scheduling systems, and analytics dashboards.

Index

16.3 Practical Examples: Event Processing

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:

  1. Filtering and grouping scheduled events by date
  2. Counting log events per day

Example 1: Scheduling Events Filter Future Events and Group by Day

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

Example 2: Log Event Count by Day

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

Summary

These techniques scale well even for large event datasets and are widely used in production applications such as schedulers, log analyzers, and activity trackers.

Index