2 * Copyright 2011 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)
49 extends DSLItem with Ordered[Timeslot] {
51 /* Preconditions to ensure correct object creations */
54 assert(from.before(to), "from = %s, to = %s".format(new MutableDateCalc(from), new MutableDateCalc(to)))
56 def startsBefore(t: Timeslot) : Boolean = this.from.before(t.from)
58 def startsAfter(t: Timeslot) : Boolean = this.from.after(t.from)
60 def endsBefore(t: Timeslot) : Boolean = this.to.before(t.to)
62 def endsAfter(t: Timeslot) : Boolean = this.to.after(t.to)
64 def after(t: Timeslot): Boolean = if (this.from.after(t.to)) true else false
66 def before(t: Timeslot): Boolean = if (this.to.before(t.from)) true else false
69 * Check whether this time slot fully contains the provided one.
71 def contains(t: Timeslot) : Boolean = {
72 if (this.from.getTime <= t.from.getTime &&
73 this.to.getTime >= t.to.getTime)
79 * Check whether this timeslot contains the provided time instant.
81 def includes(t: Date) : Boolean =
82 if (from.before(t) && to.after(t)) true else false
85 * Check whether this timeslot overlaps with the provided one.
87 def overlaps(t: Timeslot) : Boolean = {
88 if (contains(t) || t.contains(this))
91 if (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) : List[Timeslot] = {
105 val nfrom = if (from.before(t.from)) from else t.from
106 val nto = if (to.after(t.to)) to else t.to
107 List(Timeslot(nfrom, nto))
109 if (this.from.before(t.from))
116 * Split the timeslot in two parts at the provided timestamp, if the
117 * timestamp falls within the timeslot boundaries.
119 def slice(d: Date) : List[Timeslot] =
121 List(Timeslot(from, d), Timeslot(d,to))
126 * Find and return the timeslots within which this Timeslot overrides
127 * with the provided list of timeslots. For example if:
129 * - `this == Timeslot(7,20)`
130 * - `list == List(Timeslot(1,3), Timeslot(6,8), Timeslot(11,15))`
132 * the result will be: `List(Timeslot(7,8), Timeslot(11,15))`
134 def overlappingTimeslots(list: List[Timeslot]) : List[Timeslot] = {
136 val result = new mutable.ListBuffer[Timeslot]()
140 if (t.contains(this)) result += this
141 else if (this.contains(t)) result += t
142 else if (t.overlaps(this) && t.startsBefore(this)) result += this.slice(t.to).head
143 else if (t.overlaps(this) && t.startsAfter(this)) result += this.slice(t.from).last
149 * Find and return the timeslots whithin which this Timeslot does not
150 * override with the provided list of timeslots. For example if:
152 * - `this == Timeslot(7,20)`
153 * - `list == List(Timeslot(1,3), Timeslot(6,8), Timeslot(11,15))`
155 * the result will be: `List(Timeslot(9,10), Timeslot(15,20))`
157 def nonOverlappingTimeslots(list: List[Timeslot]): List[Timeslot] = {
159 val overlaps = list.filter(t => this.overlaps(t))
161 if (overlaps.isEmpty)
164 def build(acc: List[Timeslot], listPart: List[Timeslot]): List[Timeslot] = {
168 case x :: Nil => build(acc, List())
169 case x :: y :: rest =>
170 build(acc ++ List(Timeslot(x.to, y.from)), y :: rest)
174 val head = overlaps.head
175 val last = overlaps.reverse.head
177 val start = if (head.startsAfter(this)) List(Timeslot(this.from, head.from)) else List()
178 val end = if (last.endsBefore(this)) List(Timeslot(last.to, this.to)) else List()
180 start ++ build(List(), overlaps) ++ end
184 * Align a list of consecutive timeslots to the boundaries
185 * defined by this timeslot. Elements that do not overlap
186 * with this timeslot are rejected, while elements not
187 * contained in the timeslot are trimmed to this timeslot's
188 * start and end time.
190 def align(l: List[Timeslot]): List[Timeslot] = {
191 if (l.isEmpty) return List()
194 if (!this.overlaps(l.head)) List()
195 else if (this.contains(l.head)) List(l.head)
196 else if (l.head.startsBefore(this)) List(Timeslot(this.from, l.head.to))
197 else if (l.head.endsAfter(this)) List(Timeslot(l.head.from, this.to))
201 result.head :: align(l.tail)
207 * Compares the starting times of two timeslots.
209 def compare(that: Timeslot): Int = {
210 if (this.startsBefore(that)) -1
211 else if (this.startsAfter(that)) 1
216 * Converts the timeslot to the amount of hours it represents
218 def hours: Double = (to.getTime - from.getTime).toDouble / 1000.0 / 60.0 / 60.0
220 def deltaMillis = to.getTime - from.getTime
222 override def toString() = toDateString
224 def toDateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from), new MutableDateCalc(to))
225 def toISODateString = "Timeslot(%s, %s)".format(new MutableDateCalc(from).toISOString, new MutableDateCalc(to).toISOString)