001/*
002 *  Copyright 2001-2010 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 */
016package org.joda.time.base;
017
018import org.joda.convert.ToString;
019import org.joda.time.Duration;
020import org.joda.time.Period;
021import org.joda.time.ReadableDuration;
022import org.joda.time.ReadableInstant;
023import org.joda.time.format.FormatUtils;
024
025/**
026 * AbstractDuration provides the common behaviour for duration classes.
027 * <p>
028 * This class should generally not be used directly by API users. The 
029 * {@link ReadableDuration} interface should be used when different 
030 * kinds of durations are to be referenced.
031 * <p>
032 * AbstractDuration subclasses may be mutable and not thread-safe.
033 *
034 * @author Brian S O'Neill
035 * @author Stephen Colebourne
036 * @since 1.0
037 */
038public abstract class AbstractDuration implements ReadableDuration {
039
040    /**
041     * Constructor.
042     */
043    protected AbstractDuration() {
044        super();
045    }
046
047    //-----------------------------------------------------------------------
048    /**
049     * Get this duration as an immutable <code>Duration</code> object.
050     * 
051     * @return a Duration created using the millisecond duration from this instance
052     */
053    public Duration toDuration() {
054        return new Duration(getMillis());
055    }
056
057    //-----------------------------------------------------------------------
058    /**
059     * Converts this duration to a Period instance using the standard period type
060     * and the ISO chronology.
061     * <p>
062     * Only precise fields in the period type will be used. Thus, only the hour,
063     * minute, second and millisecond fields on the period will be used.
064     * The year, month, week and day fields will not be populated.
065     * <p>
066     * If the duration is small, less than one day, then this method will perform
067     * as you might expect and split the fields evenly.
068     * If the duration is larger than one day then all the remaining duration will
069     * be stored in the largest available field, hours in this case.
070     * <p>
071     * For example, a duration effectively equal to (365 + 60 + 5) days will be
072     * converted to ((365 + 60 + 5) * 24) hours by this constructor.
073     * <p>
074     * For more control over the conversion process, you must pair the duration with
075     * an instant, see {@link Period#Period(ReadableInstant,ReadableDuration)}.
076     * 
077     * @return a Period created using the millisecond duration from this instance
078     */
079    public Period toPeriod() {
080        return new Period(getMillis());
081    }
082
083    //-----------------------------------------------------------------------
084    /**
085     * Compares this duration with the specified duration based on length.
086     *
087     * @param other  a duration to check against
088     * @return negative value if this is less, 0 if equal, or positive value if greater
089     * @throws NullPointerException if the object is null
090     * @throws ClassCastException if the given object is not supported
091     */
092    public int compareTo(ReadableDuration other) {
093        long thisMillis = this.getMillis();
094        long otherMillis = other.getMillis();
095        
096        // cannot do (thisMillis - otherMillis) as it can overflow
097        if (thisMillis < otherMillis) {
098            return -1;
099        }
100        if (thisMillis > otherMillis) {
101            return 1;
102        }
103        return 0;
104    }
105
106    /**
107     * Is the length of this duration equal to the duration passed in.
108     *
109     * @param duration  another duration to compare to, null means zero milliseconds
110     * @return true if this duration is equal to than the duration passed in
111     */
112    public boolean isEqual(ReadableDuration duration) {
113        if (duration == null) {
114            duration = Duration.ZERO;
115        }
116        return compareTo(duration) == 0;
117    }
118
119    /**
120     * Is the length of this duration longer than the duration passed in.
121     *
122     * @param duration  another duration to compare to, null means zero milliseconds
123     * @return true if this duration is equal to than the duration passed in
124     */
125    public boolean isLongerThan(ReadableDuration duration) {
126        if (duration == null) {
127            duration = Duration.ZERO;
128        }
129        return compareTo(duration) > 0;
130    }
131
132    /**
133     * Is the length of this duration shorter than the duration passed in.
134     *
135     * @param duration  another duration to compare to, null means zero milliseconds
136     * @return true if this duration is equal to than the duration passed in
137     */
138    public boolean isShorterThan(ReadableDuration duration) {
139        if (duration == null) {
140            duration = Duration.ZERO;
141        }
142        return compareTo(duration) < 0;
143    }
144
145    //-----------------------------------------------------------------------
146    /**
147     * Compares this object with the specified object for equality based
148     * on the millisecond length. All ReadableDuration instances are accepted.
149     *
150     * @param duration  a readable duration to check against
151     * @return true if the length of the duration is equal
152     */
153    public boolean equals(Object duration) {
154        if (this == duration) {
155            return true;
156        }
157        if (duration instanceof ReadableDuration == false) {
158            return false;
159        }
160        ReadableDuration other = (ReadableDuration) duration;
161        return (getMillis() == other.getMillis());
162    }
163
164    /**
165     * Gets a hash code for the duration that is compatible with the 
166     * equals method.
167     *
168     * @return a hash code
169     */
170    public int hashCode() {
171        long len = getMillis();
172        return (int) (len ^ (len >>> 32));
173    }
174
175    //-----------------------------------------------------------------------
176    /**
177     * Gets the value as a String in the ISO8601 duration format including
178     * only seconds and milliseconds.
179     * <p>
180     * For example, "PT72.345S" represents 1 minute, 12 seconds and 345 milliseconds.
181     * <p>
182     * For more control over the output, see
183     * {@link org.joda.time.format.PeriodFormatterBuilder PeriodFormatterBuilder}.
184     *
185     * @return the value as an ISO8601 string
186     */
187    @ToString
188    public String toString() {
189        long millis = getMillis();
190        StringBuffer buf = new StringBuffer();
191        buf.append("PT");
192        boolean negative = (millis < 0);
193        FormatUtils.appendUnpaddedInteger(buf, millis);
194        while (buf.length() < (negative ? 7 : 6)) {
195            buf.insert(negative ? 3 : 2, "0");
196        }
197        if ((millis / 1000) * 1000 == millis) {
198            buf.setLength(buf.length() - 3);
199        } else {
200            buf.insert(buf.length() - 3, ".");
201        }
202        buf.append('S');
203        return buf.toString();
204    }
205
206}