Fix a bug with mutable state.
[aquarium] / src / main / scala / gr / grnet / aquarium / util / date / DateCalculator.scala
1 package gr.grnet.aquarium.util.date
2
3 import org.joda.time.{MutableDateTime, DateMidnight}
4 import java.util.{Date, Calendar}
5 import java.text.DateFormat
6
7
8 /**
9  * Date calculator.
10  *
11  * Utility class for date manipulations. Not that this is mutable.
12  *
13  * @author Christos KK Loverdos <loverdos@gmail.com>
14  */
15
16 class DateCalculator private(private[this] var dateTime: MutableDateTime) extends Cloneable {
17   def this(millis: Long)  = this(new MutableDateTime(millis))
18   def this(date: Date)    = this(new MutableDateTime(date))
19   def this(cal: Calendar) = this(new MutableDateTime(cal))
20
21   def this(year: Int, monthOfYear: Int, dayOfMonth: Int) =
22     this(new MutableDateTime(year, monthOfYear, dayOfMonth, 0, 0, 0, 0))
23
24   def this(year: Int, monthOfYear: Int) =
25     this(year, monthOfYear, 1)
26
27
28   override def clone(): DateCalculator = new DateCalculator(this.dateTime)
29
30   def copy: DateCalculator = clone()
31
32   def plusMonths(n: Int): this.type = {
33     dateTime.addMonths(n)
34
35     this
36   }
37
38   def minusMonths(n: Int): this.type = {
39     dateTime.addMonths(-n)
40
41     this
42   }
43
44   def nextMonth: this.type = {
45     plusMonths(1)
46   }
47
48   def previousMonth: this.type = {
49     minusMonths(1)
50   }
51
52   def plusDays(n: Int): this.type = {
53     dateTime.addDays(n)
54     this
55   }
56
57   def minusDays(n: Int): this.type = {
58     dateTime.addDays(n)
59     this
60   }
61   
62   def nextDay: this.type = {
63     plusDays(1)
64   }
65   
66   def previousDay: this.type = {
67     minusDays(1)
68   }
69
70   def plusSeconds(n: Int): this.type = {
71     dateTime.addSeconds(n)
72     this
73   }
74
75   def minusSeconds(n: Int): this.type = {
76     plusSeconds(-n)
77   }
78
79   def plusHours(n: Int): this.type = {
80     dateTime.addHours(n)
81     this
82   }
83
84   def minusHours(n: Int): this.type = {
85     plusHours(-n)
86   }
87
88
89   def plusMillis(n: Long): this.type = {
90     dateTime.add(n)
91     this
92   }
93
94  def minusMillis(n: Long): this.type = {
95    plusMillis(-n)
96  }
97
98  def nextMilli: this.type = {
99    plusMillis(1L)
100  }
101
102  def previousMilli: this.type = {
103    minusMillis(1L)
104  }
105
106   def year: Int = {
107     dateTime.getYear
108   }
109
110   /**
111    * Months range from 1 to 12.
112    */
113   def monthOfYear: Int = {
114     dateTime.getMonthOfYear
115   }
116
117   /**
118    * Month days start from 1
119    */
120   def dayOfMonth: Int = {
121     dateTime.getDayOfMonth
122   }
123
124   /**
125    * Year days start from 1
126    */
127   def dayOfYear: Int = {
128     dateTime.getDayOfYear
129   }
130
131   /**
132    * Week days start from 1, which is Sunday
133    */
134   def dayOfWeek: Int = {
135     dateTime.getDayOfWeek
136   }
137
138   def midnight: this.type = {
139     this.dateTime = new DateMidnight(dateTime).toMutableDateTime
140     this
141   }
142
143   /**
144    * At the first millisecond of this month. This month is the month indicated by the
145    * state of the [[gr.grnet.aquarium.util.date.DateCalculator]] and not the real-life month.
146    */
147   def startOfThisMonth: this.type = {
148     midnight
149     while(dayOfMonth > 1) {
150       previousDay
151     }
152     this
153   }
154
155   /**
156    * At the first millisecond of the next month.
157    */
158   def startOfNextMonth: this.type = {
159     nextMonth.startOfThisMonth
160   }
161
162   /**
163    * At the last millisecond of the month
164    */
165   def endOfThisMonth: this.type = {
166     startOfNextMonth.previousMilli
167   }
168   
169   def isSameYearAndMonthAs(other: Long): Boolean = {
170     isSameYearAndMonthAs(new DateCalculator(other))
171   }
172
173   def isSameYearAndMonthAs(otherDate: DateCalculator): Boolean = {
174     this.year == otherDate.year && this.monthOfYear == otherDate.monthOfYear
175   }
176   
177   def toMillis: Long = {
178     dateTime.getMillis
179   }
180
181   def toDate: Date = {
182     dateTime.toDate
183   }
184   
185   def beforeMillis(millis: Long): Boolean = {
186     toMillis < millis
187   }
188
189   def afterMillis(millis: Long): Boolean = {
190     toMillis > millis
191   }
192
193   def beforeEqMillis(millis: Long): Boolean = {
194     toMillis <= millis
195   }
196
197   def afterEqMillis(millis: Long): Boolean = {
198     toMillis >= millis
199   }
200
201   def format(fmt: String) = {
202     dateTime.formatted(fmt)
203   }
204
205   override def toString = {
206     dateTime.toString("yyyy-MM-dd HH:mm:ss.SSS")
207   }
208 }