/*
- * Copyright 2011 GRNET S.A. All rights reserved.
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
import java.util.Date
import scala.collection.mutable
import annotation.tailrec
+import gr.grnet.aquarium.util.date.MutableDateCalc
/**
* A representation of a timeslot with a start and end date.
*
* @author Georgios Gousios <gousiosg@gmail.com>
*/
-final case class Timeslot(from: Date, to: Date)
- extends DSLItem with Ordered[Timeslot] {
+final case class Timeslot(from: Date, to: Date) extends Ordered[Timeslot] {
- /* Preconditions to ensure correct object creation */
- assert(from != null, "From cannot be null")
- assert(to != null, "From cannot be null")
- assert(from.before(to), "Timeslot to is before from")
+ /* Preconditions to ensure correct object creations */
+ assert(from != null)
+ assert(to != null)
+ assert(start <= end, "from = %s, to = %s".format(new MutableDateCalc(from), new MutableDateCalc(to)))
- def startsBefore(t: Timeslot) : Boolean = this.from.before(t.from)
+ def startsBefore(t: Timeslot) : Boolean = start < t.start
- def startsAfter(t: Timeslot) : Boolean = this.from.after(t.from)
+ def startsAfter(t: Timeslot) : Boolean = start > t.start
- def endsBefore(t: Timeslot) : Boolean = this.to.before(t.to)
+ def endsBefore(t: Timeslot) : Boolean = end < t.end
- def endsAfter(t: Timeslot) : Boolean = this.to.after(t.to)
+ def endsAfter(t: Timeslot) : Boolean = end > t.end
- def after(t: Timeslot): Boolean = if (this.from.after(t.to)) true else false
+ def after(t: Timeslot): Boolean = start > t.end
- def before(t: Timeslot): Boolean = if (this.to.before(t.from)) true else false
+ def before(t: Timeslot): Boolean = end < t.start
+
+ def start : Long = this.from.getTime
+
+ def end : Long = this.to.getTime
/**
* Check whether this time slot fully contains the provided one.
*/
- def contains(t: Timeslot) : Boolean = t.startsAfter(this) && t.endsBefore(this)
+ def contains(t: Timeslot) : Boolean = this.start <= t.start && this.end >= t.end
+
+ def weakIncludes(t: Date) : Boolean = start < t.getTime && t.getTime < end
+ def weakOverlaps(t: Timeslot) : Boolean =
+ contains(t) || t.contains(this) || this.weakIncludes(t.from) || this.weakIncludes(t.to)
+
+
+ def containsTimeInMillis(millis: Long) = start <= millis && millis <= end
+
/**
* Check whether this timeslot contains the provided time instant.
*/
- def includes(t: Date) : Boolean =
- if (from.before(t) && to.after(t)) true else false
+ def includes(t: Date) : Boolean = start <= t.getTime && t.getTime <= end
+
/**
* Check whether this timeslot overlaps with the provided one.
*/
- def overlaps(t: Timeslot) : Boolean = {
- if (contains(t) || t.contains(this))
- return true
+ def overlaps(t: Timeslot) : Boolean =
+ contains(t) || t.contains(this) || this.includes(t.from) || this.includes(t.to)
- if (this.includes(t.from) || this.includes(t.to))
- return true
-
- false
- }
/**
* Merges this timeslot with the provided one. If the timeslots overlap,
* a list with the resulting merge is returned. If the timeslots do not
- * overlap, the returned list contains both timeslots in increasing start
+ * overlap, the returned list contains both timeslots in increasing start
* date order.
*/
- def merge(t: Timeslot) : List[Timeslot] = {
- if (overlaps(t)) {
- val nfrom = if (from.before(t.from)) from else t.from
- val nto = if (to.after(t.to)) to else t.to
- List(Timeslot(nfrom, nto))
- } else
- if (this.from.before(t.from))
- List(this, t)
- else
- List(t, this)
+ def merge(t: Timeslot) : Timeslot = {
+ assert(overlaps(t),this +" has no overlap with " + t)
+ val nfrom = if (start < t.start) from else t.from
+ val nto = if (end > t.end) to else t.to
+ Timeslot(nfrom, nto)
}
/**
* Split the timeslot in two parts at the provided timestamp, if the
* timestamp falls within the timeslot boundaries.
*/
- def slice(d: Date) : List[Timeslot] =
- if (includes(d))
+ def slice(d: Date) : List[Timeslot] =
+ if (includes(d) && d.getTime != start && d.getTime != end)
List(Timeslot(from, d), Timeslot(d,to))
else
List(this)
+
/**
* Find and return the timeslots within which this Timeslot overrides
* with the provided list of timeslots. For example if:
*
* the result will be: `List(Timeslot(7,8), Timeslot(11,15))`
*/
- def overlappingTimeslots(list: List[Timeslot]) : List[Timeslot] = {
+ def overlappingTimeslots(list: List[Timeslot]) : List[Timeslot] =
+ list.foldLeft(List[Timeslot]()) { (ret,t) =>
+ if (t.contains(this)) this :: ret
+ else if (this.contains(t)) t :: ret
+ else if (t.overlaps(this) && t.startsBefore(this)) slice(t.to).head :: ret
+ else if (t.overlaps(this) && t.startsAfter(this)) slice(t.from).last :: ret
+ else ret
+ }.reverse
- val result = new mutable.ListBuffer[Timeslot]()
-
- list.foreach {
- t =>
- if (t.contains(this)) result += this
- else if (this.contains(t)) result += t
- else if (t.overlaps(this) && t.startsBefore(this)) result += this.slice(t.to).head
- else if (t.overlaps(this) && t.startsAfter(this)) result += this.slice(t.from).last
- }
- result.toList
- }
/**
* Find and return the timeslots whithin which this Timeslot does not
*
* the result will be: `List(Timeslot(9,10), Timeslot(15,20))`
*/
- def nonOverlappingTimeslots(list: List[Timeslot]): List[Timeslot] = {
-
- val overlaps = list.filter(t => this.overlaps(t))
-
- if (overlaps.isEmpty)
- return List(this)
-
- def build(acc: List[Timeslot], listPart: List[Timeslot]): List[Timeslot] = {
-
- listPart match {
- case Nil => acc
- case x :: Nil => build(acc, List())
- case x :: y :: rest =>
- build(acc ++ List(Timeslot(x.to, y.from)), y :: rest)
- }
+ def nonOverlappingTimeslots(list: List[Timeslot]): List[Timeslot] =
+ overlappingTimeslots(list) sortWith {_.start < _.start} match {
+ case Nil => List(this)
+ case over =>
+ val (head,last) = (over.head,over.last)
+ val hd = if (head.start > this.start) List(Timeslot(this.from, head.from)) else List()
+ val tl = if (last.end < this.end) List(Timeslot(last.to, this.to)) else List()
+ hd ++ over.tail.foldLeft((List[Timeslot](),over.head)) {
+ case ((l,x),y) => (l ++ List(Timeslot(x.to, y.from)),y)
+ }._1 ++ tl
}
- val head = overlaps.head
- val last = overlaps.reverse.head
-
- val start = if (head.startsAfter(this)) List(Timeslot(this.from, head.from)) else List()
- val end = if (last.endsBefore(this)) List(Timeslot(last.to, this.to)) else List()
-
- start ++ build(List(), overlaps) ++ end
- }
-
/**
* Align a list of consecutive timeslots to the boundaries
* defined by this timeslot. Elements that do not overlap
def align(l: List[Timeslot]): List[Timeslot] = {
if (l.isEmpty) return List()
- val result =
- if (!this.overlaps(l.head)) List()
- else if (this.contains(l.head)) List(l.head)
- else if (l.head.startsBefore(this)) List(Timeslot(this.from, l.head.to))
- else if (l.head.endsAfter(this)) List(Timeslot(l.head.from, this.to))
- else List(this)
+ val result : Option[Timeslot] =
+ if (!this.overlaps(l.head)) None
+ else if (l.head.contains(this)) Some(this)
+ else if (l.head.startsBefore(this)) Some(Timeslot(this.from, l.head.to))
+ else if (l.head.endsAfter(this)) Some(Timeslot(l.head.from, this.to))
+ else Some(this)
- if (!result.isEmpty)
- result.head :: align(l.tail)
- else
- align(l.tail)
+ result match {
+ case Some(x) => x :: align(l.tail)
+ case None => align(l.tail)
+ }
+ }
+
+ /* align a time slot in "bound_size" boundaries so that
+ * start0 <= start and end0 >= end */
+ def align(bound_size : Long) : Timeslot = {
+ val start0 = (start / bound_size) * bound_size
+ val add_one = if (end % bound_size == 0) 0 else 1
+ val end0 = (end / bound_size + add_one) * bound_size
+ Timeslot(start0,end0)
}
+ /* returns true when the start and end address are
+ * multiples of bound_size*/
+ def isAligned(bound_size : Long) : Boolean =
+ start % bound_size == 0 && end % bound_size == 0
+
+
/**
* Compares the starting times of two timeslots.
*/
*/
def hours: Double = (to.getTime - from.getTime).toDouble / 1000.0 / 60.0 / 60.0
- override def toString() = "from: %d, to: %d".format(from.getTime, to.getTime)
+ def deltaMillis = to.getTime - from.getTime
+
+
+ def myString : String = "Timeslot(" + this.start + "," + this.end + ")"
+ //override def toString() = myString
+ override def toString() =
+ toDateString
+
+ def toDateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from), new MutableDateCalc(to))
+ def toISODateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from).toISOString, new MutableDateCalc(to).toISOString)
+}
+
+object Timeslot {
+ def apply(x: Long, y: Long): Timeslot =
+ new Timeslot(new Date(x), new Date(y))
+
+ def apply(x: Int, y: Int): Timeslot =
+ new Timeslot(new Date(x), new Date(y))
+
+ def mergeOverlaps(list: List[Timeslot]): List[Timeslot] = {
+ def sorter(x: Timeslot, y: Timeslot) : Boolean = y.from after x.from
+ (list sortWith sorter).foldLeft(List[Timeslot]()) {
+ case (Nil,b) =>
+ List(b)
+ case (hd::Nil,b) =>
+ if (hd overlaps b) (hd merge b)::Nil
+ else b::hd::Nil
+ case (a @ hd::tl,b) =>
+ if(hd overlaps b) (hd merge b)::tl
+ else b :: a
+ }.reverse
+ }
}
\ No newline at end of file