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