|
1 /** |
|
2 * Copyright (c) 2012, Ben Fortuna |
|
3 * All rights reserved. |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions |
|
7 * are met: |
|
8 * |
|
9 * o Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * o Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * o Neither the name of Ben Fortuna nor the names of any other contributors |
|
17 * may be used to endorse or promote products derived from this software |
|
18 * without specific prior written permission. |
|
19 * |
|
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
31 */ |
|
32 package net.fortuna.ical4j.util; |
|
33 |
|
34 import java.text.MessageFormat; |
|
35 import java.util.ArrayList; |
|
36 import java.util.Calendar; |
|
37 import java.util.List; |
|
38 import java.util.TimeZone; |
|
39 |
|
40 import net.fortuna.ical4j.model.Date; |
|
41 import net.fortuna.ical4j.model.DateTime; |
|
42 import net.fortuna.ical4j.model.parameter.Value; |
|
43 |
|
44 /** |
|
45 * $Id$ |
|
46 * |
|
47 * Created on 26/06/2005 |
|
48 * |
|
49 * Implements a collection of utility methods relevant to date processing. |
|
50 * |
|
51 * @author Ben Fortuna |
|
52 */ |
|
53 public final class Dates { |
|
54 |
|
55 /** |
|
56 * Number of milliseconds in one second. |
|
57 */ |
|
58 public static final long MILLIS_PER_SECOND = 1000; |
|
59 |
|
60 /** |
|
61 * Number of milliseconds in one minute. |
|
62 */ |
|
63 public static final long MILLIS_PER_MINUTE = 60000; |
|
64 |
|
65 /** |
|
66 * Number of milliseconds in one hour. |
|
67 */ |
|
68 public static final long MILLIS_PER_HOUR = 3600000; |
|
69 |
|
70 /** |
|
71 * Number of milliseconds in one day. |
|
72 */ |
|
73 public static final long MILLIS_PER_DAY = 86400000; |
|
74 |
|
75 /** |
|
76 * Number of milliseconds in one week. |
|
77 */ |
|
78 public static final long MILLIS_PER_WEEK = 604800000; |
|
79 |
|
80 /** |
|
81 * Number of days in one week. |
|
82 */ |
|
83 public static final int DAYS_PER_WEEK = 7; |
|
84 |
|
85 /** |
|
86 * Constant indicating precision to the second. |
|
87 */ |
|
88 public static final int PRECISION_SECOND = 0; |
|
89 |
|
90 /** |
|
91 * Constant indicating precision to the day. |
|
92 */ |
|
93 public static final int PRECISION_DAY = 1; |
|
94 |
|
95 /** |
|
96 * Maximum number of weeks per year. |
|
97 */ |
|
98 public static final int MAX_WEEKS_PER_YEAR = 53; |
|
99 |
|
100 /** |
|
101 * Maximum number of days per year. |
|
102 */ |
|
103 public static final int MAX_DAYS_PER_YEAR = 366; |
|
104 |
|
105 /** |
|
106 * Maximum number of days per month. |
|
107 */ |
|
108 public static final int MAX_DAYS_PER_MONTH = 31; |
|
109 |
|
110 private static final String INVALID_WEEK_MESSAGE = "Invalid week number [{0}]"; |
|
111 |
|
112 private static final String INVALID_YEAR_DAY_MESSAGE = "Invalid year day [{0}]"; |
|
113 |
|
114 private static final String INVALID_MONTH_DAY_MESSAGE = "Invalid month day [{0}]"; |
|
115 |
|
116 /** |
|
117 * Constructor made private to prevent instantiation. |
|
118 */ |
|
119 private Dates() { |
|
120 } |
|
121 |
|
122 /** |
|
123 * Returns the absolute week number for the year specified by the |
|
124 * supplied date. Note that a value of zero (0) is invalid for the |
|
125 * weekNo parameter and an <code>IllegalArgumentException</code> |
|
126 * will be thrown. |
|
127 * @param date a date instance representing a week of the year |
|
128 * @param weekNo a week number offset |
|
129 * @return the absolute week of the year for the specified offset |
|
130 */ |
|
131 public static int getAbsWeekNo(final java.util.Date date, final int weekNo) { |
|
132 if (weekNo == 0 || weekNo < -MAX_WEEKS_PER_YEAR || weekNo > MAX_WEEKS_PER_YEAR) { |
|
133 throw new IllegalArgumentException(MessageFormat.format(INVALID_WEEK_MESSAGE, |
|
134 new Object[] {new Integer(weekNo)})); |
|
135 } |
|
136 if (weekNo > 0) { |
|
137 return weekNo; |
|
138 } |
|
139 final Calendar cal = Calendar.getInstance(); |
|
140 cal.setTime(date); |
|
141 final int year = cal.get(Calendar.YEAR); |
|
142 // construct a list of possible week numbers.. |
|
143 final List weeks = new ArrayList(); |
|
144 cal.set(Calendar.WEEK_OF_YEAR, 1); |
|
145 while (cal.get(Calendar.YEAR) == year) { |
|
146 weeks.add(new Integer(cal.get(Calendar.WEEK_OF_YEAR))); |
|
147 cal.add(Calendar.WEEK_OF_YEAR, 1); |
|
148 } |
|
149 return ((Integer) weeks.get(weeks.size() + weekNo)).intValue(); |
|
150 } |
|
151 |
|
152 /** |
|
153 * Returns the absolute year day for the year specified by the |
|
154 * supplied date. Note that a value of zero (0) is invalid for the |
|
155 * yearDay parameter and an <code>IllegalArgumentException</code> |
|
156 * will be thrown. |
|
157 * @param date a date instance representing a day of the year |
|
158 * @param yearDay a day of year offset |
|
159 * @return the absolute day of month for the specified offset |
|
160 */ |
|
161 public static int getAbsYearDay(final java.util.Date date, final int yearDay) { |
|
162 if (yearDay == 0 || yearDay < -MAX_DAYS_PER_YEAR || yearDay > MAX_DAYS_PER_YEAR) { |
|
163 throw new IllegalArgumentException(MessageFormat.format(INVALID_YEAR_DAY_MESSAGE, |
|
164 new Object[] {new Integer(yearDay)})); |
|
165 } |
|
166 if (yearDay > 0) { |
|
167 return yearDay; |
|
168 } |
|
169 final Calendar cal = Calendar.getInstance(); |
|
170 cal.setTime(date); |
|
171 final int year = cal.get(Calendar.YEAR); |
|
172 // construct a list of possible year days.. |
|
173 final List days = new ArrayList(); |
|
174 cal.set(Calendar.DAY_OF_YEAR, 1); |
|
175 while (cal.get(Calendar.YEAR) == year) { |
|
176 days.add(new Integer(cal.get(Calendar.DAY_OF_YEAR))); |
|
177 cal.add(Calendar.DAY_OF_YEAR, 1); |
|
178 } |
|
179 return ((Integer) days.get(days.size() + yearDay)).intValue(); |
|
180 } |
|
181 |
|
182 /** |
|
183 * Returns the absolute month day for the month specified by the |
|
184 * supplied date. Note that a value of zero (0) is invalid for the |
|
185 * monthDay parameter and an <code>IllegalArgumentException</code> |
|
186 * will be thrown. |
|
187 * @param date a date instance representing a day of the month |
|
188 * @param monthDay a day of month offset |
|
189 * @return the absolute day of month for the specified offset |
|
190 */ |
|
191 public static int getAbsMonthDay(final java.util.Date date, final int monthDay) { |
|
192 if (monthDay == 0 || monthDay < -MAX_DAYS_PER_MONTH || monthDay > MAX_DAYS_PER_MONTH) { |
|
193 throw new IllegalArgumentException(MessageFormat.format(INVALID_MONTH_DAY_MESSAGE, |
|
194 new Object[] {new Integer(monthDay)})); |
|
195 } |
|
196 if (monthDay > 0) { |
|
197 return monthDay; |
|
198 } |
|
199 final Calendar cal = Calendar.getInstance(); |
|
200 cal.setTime(date); |
|
201 final int month = cal.get(Calendar.MONTH); |
|
202 // construct a list of possible month days.. |
|
203 final List days = new ArrayList(); |
|
204 cal.set(Calendar.DAY_OF_MONTH, 1); |
|
205 while (cal.get(Calendar.MONTH) == month) { |
|
206 days.add(new Integer(cal.get(Calendar.DAY_OF_MONTH))); |
|
207 cal.add(Calendar.DAY_OF_MONTH, 1); |
|
208 } |
|
209 return ((Integer) days.get(days.size() + monthDay)).intValue(); |
|
210 } |
|
211 |
|
212 /** |
|
213 * Returns a new date instance of the specified type. If no type is |
|
214 * specified a DateTime instance is returned. |
|
215 * @param date a seed Java date instance |
|
216 * @param type the type of date instance |
|
217 * @return an instance of <code>net.fortuna.ical4j.model.Date</code> |
|
218 */ |
|
219 public static Date getInstance(final java.util.Date date, final Value type) { |
|
220 if (Value.DATE.equals(type)) { |
|
221 return new Date(date); |
|
222 } |
|
223 return new DateTime(date); |
|
224 } |
|
225 |
|
226 /** |
|
227 * Returns an instance of <code>java.util.Calendar</code> that is suitably |
|
228 * initialised for working with the specified date. |
|
229 * @param date a date instance |
|
230 * @return a <code>java.util.Calendar</code> |
|
231 */ |
|
232 public static Calendar getCalendarInstance(final Date date) { |
|
233 Calendar instance = null; |
|
234 if (date instanceof DateTime) { |
|
235 final DateTime dateTime = (DateTime) date; |
|
236 if (dateTime.getTimeZone() != null) { |
|
237 instance = Calendar.getInstance(dateTime.getTimeZone()); |
|
238 } |
|
239 else if (dateTime.isUtc()) { |
|
240 instance = Calendar.getInstance(TimeZones.getUtcTimeZone()); |
|
241 } |
|
242 else { |
|
243 // a date-time without a timezone but not UTC is floating |
|
244 instance = Calendar.getInstance(); |
|
245 } |
|
246 } |
|
247 else { |
|
248 instance = Calendar.getInstance(TimeZones.getDateTimeZone()); |
|
249 } |
|
250 return instance; |
|
251 } |
|
252 |
|
253 /** |
|
254 * @param time the time value to round |
|
255 * @param precision the rounding precision |
|
256 * @return a round time value |
|
257 * @deprecated It is not all that useful to perform rounding without specifying an |
|
258 * explicit timezone. |
|
259 */ |
|
260 public static long round(final long time, final int precision) { |
|
261 return round(time, precision, TimeZone.getDefault()); |
|
262 // return round(time, precision, TimeZone.getTimeZone(TimeZones.UTC_ID)); |
|
263 /* |
|
264 long newTime = time; |
|
265 if (precision == PRECISION_DAY) { |
|
266 long remainder = newTime%(1000*60*60); // get the mod remainder using milliseconds*seconds*min |
|
267 newTime = newTime-remainder; |
|
268 // remove the remainder from the time to clear the milliseconds, seconds and minutes |
|
269 } |
|
270 else if (precision == PRECISION_SECOND) { |
|
271 long remainder = newTime%(1000); // get the mod remainder using milliseconds |
|
272 newTime = newTime-remainder; // remove the remainder from the time to clear the milliseconds |
|
273 } |
|
274 return newTime; |
|
275 */ |
|
276 } |
|
277 |
|
278 /** |
|
279 * Rounds a time value to remove any precision smaller than specified. |
|
280 * @param time the time value to round |
|
281 * @param precision the rounding precision |
|
282 * @param tz the timezone of the rounded value |
|
283 * @return a round time value |
|
284 */ |
|
285 public static long round(final long time, final int precision, final TimeZone tz) { |
|
286 if ((precision == PRECISION_SECOND) && ((time % Dates.MILLIS_PER_SECOND) == 0)) { |
|
287 return time; |
|
288 } |
|
289 final Calendar cal = Calendar.getInstance(tz); |
|
290 cal.setTimeInMillis(time); |
|
291 if (precision == PRECISION_DAY) { |
|
292 // return (long) Math.floor(time / (double) Dates.MILLIS_PER_DAY) * Dates.MILLIS_PER_DAY; |
|
293 cal.set(Calendar.HOUR_OF_DAY, 0); |
|
294 cal.clear(Calendar.MINUTE); |
|
295 cal.clear(Calendar.SECOND); |
|
296 cal.clear(Calendar.MILLISECOND); |
|
297 } |
|
298 else if (precision == PRECISION_SECOND) { |
|
299 // return (long) Math.floor(time / (double) Dates.MILLIS_PER_SECOND) * Dates.MILLIS_PER_SECOND; |
|
300 cal.clear(Calendar.MILLISECOND); |
|
301 } |
|
302 // unrecognised precision.. |
|
303 return cal.getTimeInMillis(); |
|
304 } |
|
305 |
|
306 /** |
|
307 * Returns the {@code System.currentTimeMillis()}, rounded to the second. |
|
308 * <p>By doing a rough rounding here, we avoid an expensive java.util.Calendar based |
|
309 * rounding later on.</p> |
|
310 * @return the current time in millisec. |
|
311 */ |
|
312 public static long getCurrentTimeRounded() { |
|
313 return (long) Math.floor(System.currentTimeMillis() / (double) Dates.MILLIS_PER_SECOND) * Dates.MILLIS_PER_SECOND; |
|
314 } |
|
315 } |