1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file: 31 * 32 * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos 33 * 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions are met: 38 * 39 * * Redistributions of source code must retain the above copyright notice, 40 * this list of conditions and the following disclaimer. 41 * 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package java.time.temporal; 63 64 import java.io.IOException; 65 import java.io.InvalidObjectException; 66 import java.io.ObjectInput; 67 import java.io.ObjectOutput; 68 import java.io.ObjectStreamException; 69 import java.io.Serializable; 70 import java.time.DateTimeException; 71 import java.util.Objects; 72 73 /** 74 * A period of time, measured as an amount of a single unit, such as '3 Months'. 75 * <p> 76 * A {@code SimplePeriod} represents an amount of time measured in terms of a 77 * single {@link TemporalUnit unit}. Any unit may be used with this class. 78 * An alternative period implementation is {@link java.time.Period Period}, which 79 * allows a combination of date and time units. 80 * <p> 81 * This class is the return type from {@link TemporalUnit#between}. 82 * It can be used more generally, but is designed to enable the following code: 83 * <pre> 84 * date = date.minus(MONTHS.between(start, end)); 85 * </pre> 86 * The unit determines which calendar systems it can be added to. 87 * <p> 88 * The period is modeled as a directed amount of time, meaning that the period may 89 * be negative. See {@link #abs()} to ensure the period is positive. 90 * 91 * <h3>Specification for implementors</h3> 92 * This class is immutable and thread-safe, providing that the unit is immutable, 93 * which it is required to be. 94 * 95 * @since 1.8 96 */ 97 public final class SimplePeriod 98 implements TemporalAdder, TemporalSubtractor, Comparable<SimplePeriod>, Serializable { 99 100 /** 101 * Serialization version. 102 */ 103 private static final long serialVersionUID = 3752975649629L; 104 105 /** 106 * The amount of the unit. 107 */ 108 private final long amount; 109 /** 110 * The unit. 111 */ 112 private final TemporalUnit unit; 113 114 //----------------------------------------------------------------------- 115 /** 116 * Obtains an instance of {@code SimplePeriod} from a period in the specified unit. 117 * <p> 118 * The parameters represent the two parts of a phrase like '6 Days'. For example: 119 * <pre> 120 * SimplePeriod.of(3, SECONDS); 121 * SimplePeriod.of(5, YEARS); 122 * </pre> 123 * 124 * @param amount the amount of the period, measured in terms of the unit, positive or negative 125 * @param unit the unit that the period is measured in, not null 126 * @return the period, not null 127 */ 128 public static SimplePeriod of(long amount, TemporalUnit unit) { 129 Objects.requireNonNull(unit, "unit"); 130 return new SimplePeriod(amount, unit); 131 } 132 133 //----------------------------------------------------------------------- 134 /** 135 * Constructor. 136 * 137 * @param amount the amount of the period, measured in terms of the unit, positive or negative 138 * @param unit the unit that the period is measured in, not null 139 */ 140 SimplePeriod(long amount, TemporalUnit unit) { 141 this.amount = amount; 142 this.unit = unit; 143 } 144 145 //----------------------------------------------------------------------- 146 /** 147 * Gets the amount of this period. 148 * <p> 149 * In the phrase "2 Months", the amount is 2. 150 * 151 * @return the amount of the period, may be negative 152 */ 153 public long getAmount() { 154 return amount; 155 } 156 157 /** 158 * Gets the unit of this period. 159 * <p> 160 * In the phrase "2 Months", the unit is "Months". 161 * 162 * @return the unit of the period, not null 163 */ 164 public TemporalUnit getUnit() { 165 return unit; 166 } 167 168 //------------------------------------------------------------------------- 169 /** 170 * Adds this period to the specified temporal object. 171 * <p> 172 * This returns a temporal object of the same observable type as the input 173 * with this period added. 174 * <p> 175 * In most cases, it is clearer to reverse the calling pattern by using 176 * {@link Temporal#plus(TemporalAdder)}. 177 * <pre> 178 * // these two lines are equivalent, but the second approach is recommended 179 * dateTime = thisPeriod.addTo(dateTime); 180 * dateTime = dateTime.plus(thisPeriod); 181 * </pre> 182 * <p> 183 * The calculation is equivalent to invoking {@link Temporal#plus(long, TemporalUnit)}. 184 * <p> 185 * This instance is immutable and unaffected by this method call. 186 * 187 * @param temporal the temporal object to adjust, not null 188 * @return an object of the same type with the adjustment made, not null 189 * @throws DateTimeException if unable to add 190 * @throws ArithmeticException if numeric overflow occurs 191 */ 192 @Override 193 public Temporal addTo(Temporal temporal) { 194 return temporal.plus(amount, unit); 195 } 196 197 /** 198 * Subtracts this period to the specified temporal object. 199 * <p> 200 * This returns a temporal object of the same observable type as the input 201 * with this period subtracted. 202 * <p> 203 * In most cases, it is clearer to reverse the calling pattern by using 204 * {@link Temporal#plus(TemporalAdder)}. 205 * <pre> 206 * // these two lines are equivalent, but the second approach is recommended 207 * dateTime = thisPeriod.subtractFrom(dateTime); 208 * dateTime = dateTime.minus(thisPeriod); 209 * </pre> 210 * <p> 211 * The calculation is equivalent to invoking {@link Temporal#minus(long, TemporalUnit)}. 212 * <p> 213 * This instance is immutable and unaffected by this method call. 214 * 215 * @param temporal the temporal object to adjust, not null 216 * @return an object of the same type with the adjustment made, not null 217 * @throws DateTimeException if unable to subtract 218 * @throws ArithmeticException if numeric overflow occurs 219 */ 220 @Override 221 public Temporal subtractFrom(Temporal temporal) { 222 return temporal.minus(amount, unit); 223 } 224 225 //----------------------------------------------------------------------- 226 /** 227 * Returns a copy of this period with a positive amount. 228 * <p> 229 * This returns a period with the absolute value of the amount and the same unit. 230 * If the amount of this period is positive or zero, then this period is returned. 231 * If the amount of this period is negative, then a period with the negated 232 * amount is returned. If the amount equals {@code Long.MIN_VALUE}, 233 * an {@code ArithmeticException} is thrown 234 * <p> 235 * This is useful to convert the result of {@link TemporalUnit#between} to 236 * a positive amount when you do not know which date is the earlier and 237 * which is the later. 238 * 239 * @return a period with a positive amount and the same unit, not null 240 * @throws ArithmeticException if the amount is {@code Long.MIN_VALUE} 241 */ 242 public SimplePeriod abs() { 243 if (amount == Long.MIN_VALUE) { 244 throw new ArithmeticException("Unable to call abs() on MIN_VALUE"); 245 } 246 return (amount >= 0 ? this : new SimplePeriod(-amount, unit)); 247 } 248 249 //----------------------------------------------------------------------- 250 /** 251 * Compares this {@code SimplePeriod} to another period. 252 * <p> 253 * The comparison is based on the amount within the unit. 254 * Only two periods with the same unit can be compared. 255 * 256 * @param other the other period to compare to, not null 257 * @return the comparator value, negative if less, positive if greater 258 * @throws IllegalArgumentException if the units do not match 259 */ 260 @Override 261 public int compareTo(SimplePeriod other) { 262 if (unit.equals(other.unit) == false) { 263 throw new IllegalArgumentException("Unable to compare periods with different units"); 264 } 265 return Long.compare(amount, other.amount); 266 } 267 268 //----------------------------------------------------------------------- 269 /** 270 * Checks if this period is equal to another period. 271 * <p> 272 * The comparison is based on the amount and unit. 273 * 274 * @param obj the object to check, null returns false 275 * @return true if this is equal to the other period 276 */ 277 @Override 278 public boolean equals(Object obj) { 279 if (this == obj) { 280 return true; 281 } 282 if (obj instanceof SimplePeriod) { 283 SimplePeriod other = (SimplePeriod) obj; 284 return amount == other.amount && unit.equals(other.unit); 285 } 286 return false; 287 } 288 289 /** 290 * A hash code for this period. 291 * 292 * @return a suitable hash code 293 */ 294 @Override 295 public int hashCode() { 296 return unit.hashCode() ^ (int) (amount ^ (amount >>> 32)); 297 } 298 299 //----------------------------------------------------------------------- 300 /** 301 * Outputs this period as a {@code String}, such as {@code 2 Months}. 302 * <p> 303 * The string consists of the amount, then a space, then the unit name. 304 * 305 * @return a string representation of this period, not null 306 */ 307 @Override 308 public String toString() { 309 return amount + " " + unit.getName(); 310 } 311 312 //----------------------------------------------------------------------- 313 /** 314 * Writes the object using a 315 * <a href="../../../serialized-form.html#java.time.temporal.Ser">dedicated serialized form</a>. 316 * <pre> 317 * out.writeByte(10); // identifies this as a SimplePeriod 318 * out.writeLong(amount); 319 * out.writeObject(unit); 320 * </pre> 321 * 322 * @return the instance of {@code Ser}, not null 323 */ 324 private Object writeReplace() { 325 return new Ser(Ser.SIMPLE_PERIOD_TYPE, this); 326 } 327 328 /** 329 * Defend against malicious streams. 330 * @return never 331 * @throws InvalidObjectException always 332 */ 333 private Object readResolve() throws ObjectStreamException { 334 throw new InvalidObjectException("Deserialization via serialization delegate"); 335 } 336 337 void writeExternal(ObjectOutput out) throws IOException { 338 out.writeLong(amount); 339 out.writeObject(unit); 340 } 341 342 static SimplePeriod readExternal(ObjectInput in) throws IOException, ClassNotFoundException { 343 long amount = in.readLong(); 344 TemporalUnit unit = (TemporalUnit) in.readObject(); 345 return SimplePeriod.of(amount, unit); 346 } 347 348 }