1 /*
2 * Copyright 2001-2010 Stephen Colebourne
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.joda.time;
17
18 import org.joda.convert.FromString;
19 import org.joda.convert.ToString;
20 import org.joda.time.base.BaseSingleFieldPeriod;
21 import org.joda.time.field.FieldUtils;
22 import org.joda.time.format.ISOPeriodFormat;
23 import org.joda.time.format.PeriodFormatter;
24
25 /**
26 * An immutable time period representing a number of days.
27 * <p>
28 * <code>Days</code> is an immutable period that can only store days.
29 * It does not store years, months or hours for example. As such it is a
30 * type-safe way of representing a number of days in an application.
31 * <p>
32 * The number of days is set in the constructor, and may be queried using
33 * <code>getDays()</code>. Basic mathematical operations are provided -
34 * <code>plus()</code>, <code>minus()</code>, <code>multipliedBy()</code> and
35 * <code>dividedBy()</code>.
36 * <p>
37 * <code>Days</code> is thread-safe and immutable.
38 *
39 * @author Stephen Colebourne
40 * @since 1.4
41 */
42 public final class Days extends BaseSingleFieldPeriod {
43
44 /** Constant representing zero days. */
45 public static final Days ZERO = new Days(0);
46 /** Constant representing one day. */
47 public static final Days ONE = new Days(1);
48 /** Constant representing two days. */
49 public static final Days TWO = new Days(2);
50 /** Constant representing three days. */
51 public static final Days THREE = new Days(3);
52 /** Constant representing four days. */
53 public static final Days FOUR = new Days(4);
54 /** Constant representing five days. */
55 public static final Days FIVE = new Days(5);
56 /** Constant representing six days. */
57 public static final Days SIX = new Days(6);
58 /** Constant representing seven days. */
59 public static final Days SEVEN = new Days(7);
60 /** Constant representing the maximum number of days that can be stored in this object. */
61 public static final Days MAX_VALUE = new Days(Integer.MAX_VALUE);
62 /** Constant representing the minimum number of days that can be stored in this object. */
63 public static final Days MIN_VALUE = new Days(Integer.MIN_VALUE);
64
65 /** The paser to use for this class. */
66 private static final PeriodFormatter PARSER = ISOPeriodFormat.standard().withParseType(PeriodType.days());
67 /** Serialization version. */
68 private static final long serialVersionUID = 87525275727380865L;
69
70 //-----------------------------------------------------------------------
71 /**
72 * Obtains an instance of <code>Days</code> that may be cached.
73 * <code>Days</code> is immutable, so instances can be cached and shared.
74 * This factory method provides access to shared instances.
75 *
76 * @param days the number of days to obtain an instance for
77 * @return the instance of Days
78 */
79 public static Days days(int days) {
80 switch (days) {
81 case 0:
82 return ZERO;
83 case 1:
84 return ONE;
85 case 2:
86 return TWO;
87 case 3:
88 return THREE;
89 case 4:
90 return FOUR;
91 case 5:
92 return FIVE;
93 case 6:
94 return SIX;
95 case 7:
96 return SEVEN;
97 case Integer.MAX_VALUE:
98 return MAX_VALUE;
99 case Integer.MIN_VALUE:
100 return MIN_VALUE;
101 default:
102 return new Days(days);
103 }
104 }
105
106 //-----------------------------------------------------------------------
107 /**
108 * Creates a <code>Days</code> representing the number of whole days
109 * between the two specified datetimes. This method corectly handles
110 * any daylight savings time changes that may occur during the interval.
111 *
112 * @param start the start instant, must not be null
113 * @param end the end instant, must not be null
114 * @return the period in days
115 * @throws IllegalArgumentException if the instants are null or invalid
116 */
117 public static Days daysBetween(ReadableInstant start, ReadableInstant end) {
118 int amount = BaseSingleFieldPeriod.between(start, end, DurationFieldType.days());
119 return Days.days(amount);
120 }
121
122 /**
123 * Creates a <code>Days</code> representing the number of whole days
124 * between the two specified partial datetimes.
125 * <p>
126 * The two partials must contain the same fields, for example you can specify
127 * two <code>LocalDate</code> objects.
128 *
129 * @param start the start partial date, must not be null
130 * @param end the end partial date, must not be null
131 * @return the period in days
132 * @throws IllegalArgumentException if the partials are null or invalid
133 */
134 public static Days daysBetween(ReadablePartial start, ReadablePartial end) {
135 if (start instanceof LocalDate && end instanceof LocalDate) {
136 Chronology chrono = DateTimeUtils.getChronology(start.getChronology());
137 int days = chrono.days().getDifference(
138 ((LocalDate) end).getLocalMillis(), ((LocalDate) start).getLocalMillis());
139 return Days.days(days);
140 }
141 int amount = BaseSingleFieldPeriod.between(start, end, ZERO);
142 return Days.days(amount);
143 }
144
145 /**
146 * Creates a <code>Days</code> representing the number of whole days
147 * in the specified interval. This method corectly handles any daylight
148 * savings time changes that may occur during the interval.
149 *
150 * @param interval the interval to extract days from, null returns zero
151 * @return the period in days
152 * @throws IllegalArgumentException if the partials are null or invalid
153 */
154 public static Days daysIn(ReadableInterval interval) {
155 if (interval == null) {
156 return Days.ZERO;
157 }
158 int amount = BaseSingleFieldPeriod.between(interval.getStart(), interval.getEnd(), DurationFieldType.days());
159 return Days.days(amount);
160 }
161
162 /**
163 * Creates a new <code>Days</code> representing the number of complete
164 * standard length days in the specified period.
165 * <p>
166 * This factory method converts all fields from the period to hours using standardised
167 * durations for each field. Only those fields which have a precise duration in
168 * the ISO UTC chronology can be converted.
169 * <ul>
170 * <li>One week consists of 7 days.
171 * <li>One day consists of 24 hours.
172 * <li>One hour consists of 60 minutes.
173 * <li>One minute consists of 60 seconds.
174 * <li>One second consists of 1000 milliseconds.
175 * </ul>
176 * Months and Years are imprecise and periods containing these values cannot be converted.
177 *
178 * @param period the period to get the number of hours from, null returns zero
179 * @return the period in days
180 * @throws IllegalArgumentException if the period contains imprecise duration values
181 */
182 public static Days standardDaysIn(ReadablePeriod period) {
183 int amount = BaseSingleFieldPeriod.standardPeriodIn(period, DateTimeConstants.MILLIS_PER_DAY);
184 return Days.days(amount);
185 }
186
187 /**
188 * Creates a new <code>Days</code> by parsing a string in the ISO8601 format 'PnD'.
189 * <p>
190 * The parse will accept the full ISO syntax of PnYnMnWnDTnHnMnS however only the
191 * days component may be non-zero. If any other component is non-zero, an exception
192 * will be thrown.
193 *
194 * @param periodStr the period string, null returns zero
195 * @return the period in days
196 * @throws IllegalArgumentException if the string format is invalid
197 */
198 @FromString
199 public static Days parseDays(String periodStr) {
200 if (periodStr == null) {
201 return Days.ZERO;
202 }
203 Period p = PARSER.parsePeriod(periodStr);
204 return Days.days(p.getDays());
205 }
206
207 //-----------------------------------------------------------------------
208 /**
209 * Creates a new instance representing a number of days.
210 * You should consider using the factory method {@link #days(int)}
211 * instead of the constructor.
212 *
213 * @param days the number of days to represent
214 */
215 private Days(int days) {
216 super(days);
217 }
218
219 /**
220 * Resolves singletons.
221 *
222 * @return the singleton instance
223 */
224 private Object readResolve() {
225 return Days.days(getValue());
226 }
227
228 //-----------------------------------------------------------------------
229 /**
230 * Gets the duration field type, which is <code>days</code>.
231 *
232 * @return the period type
233 */
234 public DurationFieldType getFieldType() {
235 return DurationFieldType.days();
236 }
237
238 /**
239 * Gets the period type, which is <code>days</code>.
240 *
241 * @return the period type
242 */
243 public PeriodType getPeriodType() {
244 return PeriodType.days();
245 }
246
247 //-----------------------------------------------------------------------
248 /**
249 * Converts this period in days to a period in weeks assuming a
250 * 7 day week.
251 * <p>
252 * This method allows you to convert between different types of period.
253 * However to achieve this it makes the assumption that all weeks are
254 * 7 days long.
255 * This may not be true for some unusual chronologies. However, it is included
256 * as it is a useful operation for many applications and business rules.
257 *
258 * @return a period representing the number of weeks for this number of days
259 */
260 public Weeks toStandardWeeks() {
261 return Weeks.weeks(getValue() / DateTimeConstants.DAYS_PER_WEEK);
262 }
263
264 /**
265 * Converts this period in days to a period in hours assuming a
266 * 24 hour day.
267 * <p>
268 * This method allows you to convert between different types of period.
269 * However to achieve this it makes the assumption that all days are 24 hours long.
270 * This is not true when daylight savings is considered and may also not
271 * be true for some unusual chronologies. However, it is included
272 * as it is a useful operation for many applications and business rules.
273 *
274 * @return a period representing the number of hours for this number of days
275 * @throws ArithmeticException if the number of hours is too large to be represented
276 */
277 public Hours toStandardHours() {
278 return Hours.hours(FieldUtils.safeMultiply(getValue(), DateTimeConstants.HOURS_PER_DAY));
279 }
280
281 /**
282 * Converts this period in days to a period in minutes assuming a
283 * 24 hour day and 60 minute hour.
284 * <p>
285 * This method allows you to convert between different types of period.
286 * However to achieve this it makes the assumption that all days are 24 hours
287 * long and all hours are 60 minutes long.
288 * This is not true when daylight savings is considered and may also not
289 * be true for some unusual chronologies. However, it is included
290 * as it is a useful operation for many applications and business rules.
291 *
292 * @return a period representing the number of minutes for this number of days
293 * @throws ArithmeticException if the number of minutes is too large to be represented
294 */
295 public Minutes toStandardMinutes() {
296 return Minutes.minutes(FieldUtils.safeMultiply(getValue(), DateTimeConstants.MINUTES_PER_DAY));
297 }
298
299 /**
300 * Converts this period in days to a period in seconds assuming a
301 * 24 hour day, 60 minute hour and 60 second minute.
302 * <p>
303 * This method allows you to convert between different types of period.
304 * However to achieve this it makes the assumption that all days are 24 hours
305 * long, all hours are 60 minutes long and all minutes are 60 seconds long.
306 * This is not true when daylight savings is considered and may also not
307 * be true for some unusual chronologies. However, it is included
308 * as it is a useful operation for many applications and business rules.
309 *
310 * @return a period representing the number of seconds for this number of days
311 * @throws ArithmeticException if the number of seconds is too large to be represented
312 */
313 public Seconds toStandardSeconds() {
314 return Seconds.seconds(FieldUtils.safeMultiply(getValue(), DateTimeConstants.SECONDS_PER_DAY));
315 }
316
317 //-----------------------------------------------------------------------
318 /**
319 * Converts this period in days to a duration in milliseconds assuming a
320 * 24 hour day, 60 minute hour and 60 second minute.
321 * <p>
322 * This method allows you to convert from a period to a duration.
323 * However to achieve this it makes the assumption that all days are 24 hours
324 * long, all hours are 60 minutes and all minutes are 60 seconds.
325 * This is not true when daylight savings time is considered, and may also
326 * not be true for some unusual chronologies. However, it is included as it
327 * is a useful operation for many applications and business rules.
328 *
329 * @return a duration equivalent to this number of days
330 */
331 public Duration toStandardDuration() {
332 long days = getValue(); // assign to a long
333 return new Duration(days * DateTimeConstants.MILLIS_PER_DAY);
334 }
335
336 //-----------------------------------------------------------------------
337 /**
338 * Gets the number of days that this period represents.
339 *
340 * @return the number of days in the period
341 */
342 public int getDays() {
343 return getValue();
344 }
345
346 //-----------------------------------------------------------------------
347 /**
348 * Returns a new instance with the specified number of days added.
349 * <p>
350 * This instance is immutable and unaffected by this method call.
351 *
352 * @param days the amount of days to add, may be negative
353 * @return the new period plus the specified number of days
354 * @throws ArithmeticException if the result overflows an int
355 */
356 public Days plus(int days) {
357 if (days == 0) {
358 return this;
359 }
360 return Days.days(FieldUtils.safeAdd(getValue(), days));
361 }
362
363 /**
364 * Returns a new instance with the specified number of days added.
365 * <p>
366 * This instance is immutable and unaffected by this method call.
367 *
368 * @param days the amount of days to add, may be negative, null means zero
369 * @return the new period plus the specified number of days
370 * @throws ArithmeticException if the result overflows an int
371 */
372 public Days plus(Days days) {
373 if (days == null) {
374 return this;
375 }
376 return plus(days.getValue());
377 }
378
379 //-----------------------------------------------------------------------
380 /**
381 * Returns a new instance with the specified number of days taken away.
382 * <p>
383 * This instance is immutable and unaffected by this method call.
384 *
385 * @param days the amount of days to take away, may be negative
386 * @return the new period minus the specified number of days
387 * @throws ArithmeticException if the result overflows an int
388 */
389 public Days minus(int days) {
390 return plus(FieldUtils.safeNegate(days));
391 }
392
393 /**
394 * Returns a new instance with the specified number of days taken away.
395 * <p>
396 * This instance is immutable and unaffected by this method call.
397 *
398 * @param days the amount of days to take away, may be negative, null means zero
399 * @return the new period minus the specified number of days
400 * @throws ArithmeticException if the result overflows an int
401 */
402 public Days minus(Days days) {
403 if (days == null) {
404 return this;
405 }
406 return minus(days.getValue());
407 }
408
409 //-----------------------------------------------------------------------
410 /**
411 * Returns a new instance with the days multiplied by the specified scalar.
412 * <p>
413 * This instance is immutable and unaffected by this method call.
414 *
415 * @param scalar the amount to multiply by, may be negative
416 * @return the new period multiplied by the specified scalar
417 * @throws ArithmeticException if the result overflows an int
418 */
419 public Days multipliedBy(int scalar) {
420 return Days.days(FieldUtils.safeMultiply(getValue(), scalar));
421 }
422
423 /**
424 * Returns a new instance with the days divided by the specified divisor.
425 * The calculation uses integer division, thus 3 divided by 2 is 1.
426 * <p>
427 * This instance is immutable and unaffected by this method call.
428 *
429 * @param divisor the amount to divide by, may be negative
430 * @return the new period divided by the specified divisor
431 * @throws ArithmeticException if the divisor is zero
432 */
433 public Days dividedBy(int divisor) {
434 if (divisor == 1) {
435 return this;
436 }
437 return Days.days(getValue() / divisor);
438 }
439
440 //-----------------------------------------------------------------------
441 /**
442 * Returns a new instance with the days value negated.
443 *
444 * @return the new period with a negated value
445 * @throws ArithmeticException if the result overflows an int
446 */
447 public Days negated() {
448 return Days.days(FieldUtils.safeNegate(getValue()));
449 }
450
451 //-----------------------------------------------------------------------
452 /**
453 * Is this days instance greater than the specified number of days.
454 *
455 * @param other the other period, null means zero
456 * @return true if this days instance is greater than the specified one
457 */
458 public boolean isGreaterThan(Days other) {
459 if (other == null) {
460 return getValue() > 0;
461 }
462 return getValue() > other.getValue();
463 }
464
465 /**
466 * Is this days instance less than the specified number of days.
467 *
468 * @param other the other period, null means zero
469 * @return true if this days instance is less than the specified one
470 */
471 public boolean isLessThan(Days other) {
472 if (other == null) {
473 return getValue() < 0;
474 }
475 return getValue() < other.getValue();
476 }
477
478 //-----------------------------------------------------------------------
479 /**
480 * Gets this instance as a String in the ISO8601 duration format.
481 * <p>
482 * For example, "P4D" represents 4 days.
483 *
484 * @return the value as an ISO8601 string
485 */
486 @ToString
487 public String toString() {
488 return "P" + String.valueOf(getValue()) + "D";
489 }
490
491 }