2 * Copyright 2011-2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and
31 * documentation are those of the authors and should not be
32 * interpreted as representing official policies, either expressed
33 * or implied, of GRNET S.A.
36 package gr.grnet.aquarium.logic.accounting.dsl
39 import scala.collection.mutable
40 import annotation.tailrec
41 import gr.grnet.aquarium.util.date.MutableDateCalc
44 * A representation of a timeslot with a start and end date.
46 * @author Georgios Gousios <gousiosg@gmail.com>
48 final case class Timeslot(from: Date, to: Date) extends Ordered[Timeslot] {
50 /* Preconditions to ensure correct object creations */
53 assert(start <= end, "from = %s, to = %s".format(new MutableDateCalc(from), new MutableDateCalc(to)))
55 def startsBefore(t: Timeslot) : Boolean = start < t.start
57 def startsAfter(t: Timeslot) : Boolean = start > t.start
59 def endsBefore(t: Timeslot) : Boolean = end < t.end
61 def endsAfter(t: Timeslot) : Boolean = end > t.end
63 def after(t: Timeslot): Boolean = start > t.end
65 def before(t: Timeslot): Boolean = end < t.start
67 def start : Long = this.from.getTime
69 def end : Long = this.to.getTime
72 * Check whether this time slot fully contains the provided one.
74 def contains(t: Timeslot) : Boolean = this.start <= t.start && this.end >= t.end
76 def weakIncludes(t: Date) : Boolean = start < t.getTime && t.getTime < end
77 def weakOverlaps(t: Timeslot) : Boolean =
78 contains(t) || t.contains(this) || this.weakIncludes(t.from) || this.weakIncludes(t.to)
81 def containsTimeInMillis(millis: Long) = start <= millis && millis <= end
85 * Check whether this timeslot contains the provided time instant.
87 def includes(t: Date) : Boolean = start <= t.getTime && t.getTime <= end
91 * Check whether this timeslot overlaps with the provided one.
93 def overlaps(t: Timeslot) : Boolean =
94 contains(t) || t.contains(this) || this.includes(t.from) || this.includes(t.to)
98 * Merges this timeslot with the provided one. If the timeslots overlap,
99 * a list with the resulting merge is returned. If the timeslots do not
100 * overlap, the returned list contains both timeslots in increasing start
103 def merge(t: Timeslot) : Timeslot = {
104 assert(overlaps(t),this +" has no overlap with " + t)
105 val nfrom = if (start < t.start) from else t.from
106 val nto = if (end > t.end) to else t.to
111 * Split the timeslot in two parts at the provided timestamp, if the
112 * timestamp falls within the timeslot boundaries.
114 def slice(d: Date) : List[Timeslot] =
115 if (includes(d) && d.getTime != start && d.getTime != end)
116 List(Timeslot(from, d), Timeslot(d,to))
122 * Find and return the timeslots within which this Timeslot overrides
123 * with the provided list of timeslots. For example if:
125 * - `this == Timeslot(7,20)`
126 * - `list == List(Timeslot(1,3), Timeslot(6,8), Timeslot(11,15))`
128 * the result will be: `List(Timeslot(7,8), Timeslot(11,15))`
130 def overlappingTimeslots(list: List[Timeslot]) : List[Timeslot] =
131 list.foldLeft(List[Timeslot]()) { (ret,t) =>
132 if (t.contains(this)) this :: ret
133 else if (this.contains(t)) t :: ret
134 else if (t.overlaps(this) && t.startsBefore(this)) slice(t.to).head :: ret
135 else if (t.overlaps(this) && t.startsAfter(this)) slice(t.from).last :: ret
141 * Find and return the timeslots whithin which this Timeslot does not
142 * override with the provided list of timeslots. For example if:
144 * - `this == Timeslot(7,20)`
145 * - `list == List(Timeslot(1,3), Timeslot(6,8), Timeslot(11,15))`
147 * the result will be: `List(Timeslot(9,10), Timeslot(15,20))`
149 def nonOverlappingTimeslots(list: List[Timeslot]): List[Timeslot] =
150 overlappingTimeslots(list) sortWith {_.start < _.start} match {
151 case Nil => List(this)
153 val (head,last) = (over.head,over.last)
154 val hd = if (head.start > this.start) List(Timeslot(this.from, head.from)) else List()
155 val tl = if (last.end < this.end) List(Timeslot(last.to, this.to)) else List()
156 hd ++ over.tail.foldLeft((List[Timeslot](),over.head)) {
157 case ((l,x),y) => (l ++ List(Timeslot(x.to, y.from)),y)
162 * Align a list of consecutive timeslots to the boundaries
163 * defined by this timeslot. Elements that do not overlap
164 * with this timeslot are rejected, while elements not
165 * contained in the timeslot are trimmed to this timeslot's
166 * start and end time.
168 def align(l: List[Timeslot]): List[Timeslot] = {
169 if (l.isEmpty) return List()
171 val result : Option[Timeslot] =
172 if (!this.overlaps(l.head)) None
173 else if (l.head.contains(this)) Some(this)
174 else if (l.head.startsBefore(this)) Some(Timeslot(this.from, l.head.to))
175 else if (l.head.endsAfter(this)) Some(Timeslot(l.head.from, this.to))
179 case Some(x) => x :: align(l.tail)
180 case None => align(l.tail)
184 /* align a time slot in "bound_size" boundaries so that
185 * start0 <= start and end0 >= end */
186 def align(bound_size : Long) : Timeslot = {
187 val start0 = (start / bound_size) * bound_size
188 val add_one = if (end % bound_size == 0) 0 else 1
189 val end0 = (end / bound_size + add_one) * bound_size
190 Timeslot(start0,end0)
193 /* returns true when the start and end address are
194 * multiples of bound_size*/
195 def isAligned(bound_size : Long) : Boolean =
196 start % bound_size == 0 && end % bound_size == 0
200 * Compares the starting times of two timeslots.
202 def compare(that: Timeslot): Int = {
203 if (this.startsBefore(that)) -1
204 else if (this.startsAfter(that)) 1
209 * Converts the timeslot to the amount of hours it represents
211 def hours: Double = (to.getTime - from.getTime).toDouble / 1000.0 / 60.0 / 60.0
213 def deltaMillis = to.getTime - from.getTime
216 def myString : String = "Timeslot(" + this.start + "," + this.end + ")"
217 //override def toString() = myString
218 override def toString() =
221 def toDateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from), new MutableDateCalc(to))
222 def toISODateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from).toISOString, new MutableDateCalc(to).toISOString)
226 def apply(x: Long, y: Long): Timeslot =
227 new Timeslot(new Date(x), new Date(y))
229 def apply(x: Int, y: Int): Timeslot =
230 new Timeslot(new Date(x), new Date(y))
232 def mergeOverlaps(list: List[Timeslot]): List[Timeslot] = {
233 def sorter(x: Timeslot, y: Timeslot) : Boolean = y.from after x.from
234 (list sortWith sorter).foldLeft(List[Timeslot]()) {
238 if (hd overlaps b) (hd merge b)::Nil
240 case (a @ hd::tl,b) =>
241 if(hd overlaps b) (hd merge b)::tl