import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Collectors; /** * Represents an event that occurs on a specific date or a range of dates. *
* The event has a time interval and a description. *
* The event can be recurring on specific days of the week.
*
* @author Yuri Tatishchev
* @version 0.1 2025-02-08
*/
public class Event implements Comparable
* Two events overlap if their dates overlap and their time intervals overlap.
*
* @param other the other event
* @return true if the events overlap, false otherwise
*/
public boolean overlapsWith(Event other) {
// Dates do not overlap
if (this.endDate.isBefore(other.startDate) || this.startDate.isAfter(other.endDate)) {
return false;
// Dates overlap, but time intervals do not
} else if (!this.timeInterval.overlapsWith(other.timeInterval)) {
return false;
// Dates and time intervals overlap, but possibly not on the same day
} else if (this.isRecurring()) {
// this is recurring, but other is not
if (!other.isRecurring()) {
return this.recurs.contains(other.getStartDate().getDayOfWeek());
}
// Both are recurring, check if any of the recurring days overlap
// TODO: check if this is correct.
// I suspect there are edge cases where the days that overlap are not in the overlapping date range.
return this.recurs.stream().anyMatch(other.recurs::contains);
// other is recurring, but this is not
} else if (other.isRecurring()) {
return other.recurs.contains(this.startDate.getDayOfWeek());
}
return true;
}
public int compareTo(Event other) {
if (this.startDate.isBefore(other.startDate)) {
return -1;
} else if (this.startDate.isAfter(other.startDate)) {
return 1;
} else {
return this.timeInterval.getStart().compareTo(other.timeInterval.getStart());
}
}
/**
* Returns a string representation of the event.
*
* @return a string representation of the event
*
* Example output:
* Example output:
* Example schedule strings:
*
*
One event name
*
1/23/2025 9:00 10:15
*
*
Recurring event name
*
TR 10:30 11:45 1/27/2025 5/12/2025
*/
public String toString() {
return String.format("%s\n%s\n", name, scheduleString());
}
/**
* Returns a string representation of the event's schedule.
*
* @return a string representation of the event's schedule
*
*
1/23/2025 9:00 10:15
*
TR 10:30 11:45 1/27/2025 5/12/2025
*/
public String scheduleString() {
if (recurs == null) return String.format("%s %s", dateFormat.format(startDate), timeInterval);
String recursString = recurs.stream().map(dayOfWeekToChar::get).map(String::valueOf).collect(Collectors.joining());
return String.format("%s %s %s %s", recursString, timeInterval, dateFormat.format(startDate), dateFormat.format(endDate));
}
/**
* Parses a schedule string and returns an event.
*
* @param name the name of the event
* @param schedule the schedule string
* @return an event
*
TR 9:00 10:15 1/23/2025 5/8/2025
*
MW 10:30 11:45 1/27/2025 5/12/2025
*
4/18/2025 9:30 11:30
*
6/3/2025 16:15 17:00
*
F 18:30 20:30 1/24/2025 5/9/2025
*/
public static Event fromScheduleString(String name, String schedule) {
String[] parts = schedule.split("\\s+");
if (parts.length == 3) {
LocalDate date = LocalDate.parse(parts[0], dateFormat);
LocalTime start = LocalTime.parse(parts[1], timeFormat);
LocalTime end = LocalTime.parse(parts[2], timeFormat);
TimeInterval timeInterval = new TimeInterval(start, end);
return new Event(name, date, timeInterval);
} else {
LocalTime start = LocalTime.parse(parts[1], timeFormat);
LocalTime end = LocalTime.parse(parts[2], timeFormat);
LocalDate startDate = LocalDate.parse(parts[3], dateFormat);
LocalDate endDate = LocalDate.parse(parts[4], dateFormat);
TimeInterval timeInterval = new TimeInterval(start, end);
SortedSet