I am facing a validation that get's my head smoking quite a bit.
I have an Object which I call Downtime
for now and it looks like this:
public class Downtime {
/** The start date of the downtime. */
private ZonedDateTime downtimeFrom;
/** The end date of the downtime. */
private ZonedDateTime downtimeTo;
/**
* Gets the downtime from.
*
* @return the downtime from
*/
public ZonedDateTime getDowntimeFrom()
{
return downtimeFrom;
}
/**
* Gets the downtime to.
*
* @return the downtime to
*/
public ZonedDateTime getDowntimeTo()
{
return downtimeTo;
}
/**
* Sets the downtime from.
*
* @param downtimeFrom the new downtime from
*/
protected void setDowntimeFrom( ZonedDateTime downtimeFrom )
{
this.downtimeFrom = downtimeFrom;
}
/**
* Sets the downtime to.
*
* @param downtimeTo the new downtime to
*/
protected void setDowntimeTo( ZonedDateTime downtimeTo )
{
this.downtimeTo = downtimeTo;
}
}
When I create a new downtime over a CRUD implementation, I already validate that the start time is actually before the end time and such things.
Now I have to add the validation that when I create a new Downtime, it doesn't interfere with the already created downtimes. Meaning the start date of the new downtime is not already existent and is not in another downtime. ( between the start and end of an already created downtime ).
So my way of doing this right now, since I am terrible at date/time oriented things when it comes to localization, would be something like this:
private boolean isNewDowntimeValid(Downtime newDowntime, List<Downtime> createdDowntimes){
// let's just assume I already filtered out that the list only contains the same day. That's actually pretty easy.
List<ZonedDateTime> dateRange = new LinkedList<>();
ZonedDateTime newTime = newDowntime.getDowntimeFrom();
for(Downtime downtime : createdDowntimes){
ZonedDateTime downtimeStart = downtime.getDowntimeFrom();
ZonedDateTime downtimeEnd = downtime.getDowntimeTo();
for(ZonedDateTime start = downtimeStart; !start.isAfter(downtimeEnd); start = start.plusHours(1)){
dateRange.add(start);
}
}
if(dateRange.contains(newTime)){
return false;
}
return true;
}
The code is written out of my head in here, so there might be syntax errors but I think you can get the idea of what I want.
And now to my question.
This code above seems like such an overhead and I would like to know how I can validate it faster and with less code.
EDIT: Let me provide a clear Example
I have a List of Downtimes like this:
List<Downtime> createdDowntimes = [
{
start:2015-01-10T00:00Z,
end:2015-01-10T02:00Z
},
{
start:2015-01-10T04:00Z,
end:2015-01-10T06:00Z
},
{
start:2015-01-10T07:00Z,
end:2015-01-10T09:00Z
}
]
and then I have the new Downtime I want to create:
Downtime newDowntime =
{
start:2015-01-10T05:00Z,
end:2015-01-10T05:30Z
}
In this example the new downtime is not valid since it actually is during a period that is already in another already created downtime.
Hopefully it makes things more clear.
EDIT 2: While the marked duplicate consists of the reason and offers a solution I also want to give credits to Hugo who provided a good answer with considering my criterias.
Here is another solution I prepared which offers a lot of more detailed exception and information handling
/*
* Collision 1 = the new downtime starts before the created ones but ends in their span
* Collision 2 = the new downtime starts after created ones and also ends after their span
* Collision 3 = the new downtime starts after created ones and ends in their span
*/
List<Downtime> collision1 = createdDowntimes.stream().filter( e -> e.getDowntimeFrom().isAfter( newTimeStart ) )
.filter( e -> e.getDowntimeTo().isAfter( newTimeEnd ) ).collect( Collectors.toList() );
List<Downtime> collision2 = createdDowntimes.stream().filter( e -> e.getDowntimeFrom().isBefore( newTimeStart ) )
.filter( e -> e.getDowntimeTo().isBefore( newTimeEnd ) ).collect( Collectors.toList() );
List<Downtime> collision3 = createdDowntimes.stream().filter( e -> e.getDowntimeFrom().isBefore( newTimeStart ) )
.filter( e -> e.getDowntimeTo().isAfter( newTimeEnd ) ).collect( Collectors.toList() );
Keep in mind that my "solution" is one of many and also pretty intensive in terms of performance since streams are heavy operations. So if you don't need to know exactly how many collided and why they collided consider Hugo's answer.