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