1 /* 2 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * * Neither the name of JSR-310 nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.threeten.bp.chrono; 33 34 import java.io.Serializable; 35 36 import org.threeten.bp.DateTimeException; 37 import org.threeten.bp.LocalDate; 38 import org.threeten.bp.LocalTime; 39 import org.threeten.bp.jdk8.Jdk8Methods; 40 import org.threeten.bp.temporal.ChronoUnit; 41 import org.threeten.bp.temporal.Temporal; 42 import org.threeten.bp.temporal.TemporalAdjuster; 43 import org.threeten.bp.temporal.TemporalUnit; 44 45 /** 46 * A date expressed in terms of a standard year-month-day calendar system. 47 * <p> 48 * This class is used by applications seeking to handle dates in non-ISO calendar systems. 49 * For example, the Japanese, Minguo, Thai Buddhist and others. 50 * <p> 51 * {@code ChronoLocalDate} is built on the generic concepts of year, month and day. 52 * The calendar system, represented by a {@link Chronology}, expresses the relationship between 53 * the fields and this class allows the resulting date to be manipulated. 54 * <p> 55 * Note that not all calendar systems are suitable for use with this class. 56 * For example, the Mayan calendar uses a system that bears no relation to years, months and days. 57 * <p> 58 * The API design encourages the use of {@code LocalDate} for the majority of the application. 59 * This includes code to read and write from a persistent data store, such as a database, 60 * and to send dates and times across a network. The {@code ChronoLocalDate} instance is then used 61 * at the user interface level to deal with localized input/output. 62 * 63 * <P>Example: </p> 64 * <pre> 65 * System.out.printf("Example()%n"); 66 * // Enumerate the list of available calendars and print today for each 67 * Set<Chrono> chronos = Chrono.getAvailableChronologies(); 68 * for (Chrono chrono : chronos) { 69 * ChronoLocalDate date = chrono.dateNow(); 70 * System.out.printf(" %20s: %s%n", chrono.getID(), date.toString()); 71 * } 72 * 73 * // Print the Hijrah date and calendar 74 * ChronoLocalDate date = Chrono.of("Hijrah").dateNow(); 75 * int day = date.get(ChronoField.DAY_OF_MONTH); 76 * int dow = date.get(ChronoField.DAY_OF_WEEK); 77 * int month = date.get(ChronoField.MONTH_OF_YEAR); 78 * int year = date.get(ChronoField.YEAR); 79 * System.out.printf(" Today is %s %s %d-%s-%d%n", date.getChrono().getID(), 80 * dow, day, month, year); 81 82 * // Print today's date and the last day of the year 83 * ChronoLocalDate now1 = Chrono.of("Hijrah").dateNow(); 84 * ChronoLocalDate first = now1.with(ChronoField.DAY_OF_MONTH, 1) 85 * .with(ChronoField.MONTH_OF_YEAR, 1); 86 * ChronoLocalDate last = first.plus(1, ChronoUnit.YEARS) 87 * .minus(1, ChronoUnit.DAYS); 88 * System.out.printf(" Today is %s: start: %s; end: %s%n", last.getChrono().getID(), 89 * first, last); 90 * </pre> 91 * 92 * <h4>Adding Calendars</h4> 93 * <p> The set of calendars is extensible by defining a subclass of {@link ChronoLocalDate} 94 * to represent a date instance and an implementation of {@code Chronology} 95 * to be the factory for the ChronoLocalDate subclass. 96 * </p> 97 * <p> To permit the discovery of the additional calendar types the implementation of 98 * {@code Chronology} must be registered as a Service implementing the {@code Chronology} interface 99 * in the {@code META-INF/Services} file as per the specification of {@link java.util.ServiceLoader}. 100 * The subclass must function according to the {@code Chronology} class description and must provide its 101 * {@link Chronology#getID calendar name} and 102 * {@link Chronology#getCalendarType() calendar type}. </p> 103 * 104 * <h3>Specification for implementors</h3> 105 * This abstract class must be implemented with care to ensure other classes operate correctly. 106 * All implementations that can be instantiated must be final, immutable and thread-safe. 107 * Subclasses should be Serializable wherever possible. 108 * 109 * @param <D> the date type 110 */ 111 abstract class ChronoDateImpl<D extends ChronoLocalDate> 112 extends ChronoLocalDate 113 implements Temporal, TemporalAdjuster, Serializable { 114 115 /** 116 * Serialization version. 117 */ 118 private static final long serialVersionUID = 6282433883239719096L; 119 120 /** 121 * Creates an instance. 122 */ ChronoDateImpl()123 ChronoDateImpl() { 124 } 125 126 //----------------------------------------------------------------------- 127 @SuppressWarnings("unchecked") 128 @Override plus(long amountToAdd, TemporalUnit unit)129 public ChronoDateImpl<D> plus(long amountToAdd, TemporalUnit unit) { 130 if (unit instanceof ChronoUnit) { 131 ChronoUnit f = (ChronoUnit) unit; 132 switch (f) { 133 case DAYS: return plusDays(amountToAdd); 134 case WEEKS: return plusDays(Jdk8Methods.safeMultiply(amountToAdd, 7)); 135 case MONTHS: return plusMonths(amountToAdd); 136 case YEARS: return plusYears(amountToAdd); 137 case DECADES: return plusYears(Jdk8Methods.safeMultiply(amountToAdd, 10)); 138 case CENTURIES: return plusYears(Jdk8Methods.safeMultiply(amountToAdd, 100)); 139 case MILLENNIA: return plusYears(Jdk8Methods.safeMultiply(amountToAdd, 1000)); 140 // case ERAS: throw new DateTimeException("Unable to add era, standard calendar system only has one era"); 141 // case FOREVER: return (period == 0 ? this : (period > 0 ? LocalDate.MAX_DATE : LocalDate.MIN_DATE)); 142 } 143 throw new DateTimeException(unit + " not valid for chronology " + getChronology().getId()); 144 } 145 return (ChronoDateImpl<D>) getChronology().ensureChronoLocalDate(unit.addTo(this, amountToAdd)); 146 } 147 148 //----------------------------------------------------------------------- 149 /** 150 * Returns a copy of this date with the specified period in years added. 151 * <p> 152 * This adds the specified period in years to the date. 153 * In some cases, adding years can cause the resulting date to become invalid. 154 * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure 155 * that the result is valid. Typically this will select the last valid day of the month. 156 * <p> 157 * This instance is immutable and unaffected by this method call. 158 * 159 * @param yearsToAdd the years to add, may be negative 160 * @return a date based on this one with the years added, not null 161 * @throws DateTimeException if the result exceeds the supported date range 162 */ plusYears(long yearsToAdd)163 abstract ChronoDateImpl<D> plusYears(long yearsToAdd); 164 165 /** 166 * Returns a copy of this date with the specified period in months added. 167 * <p> 168 * This adds the specified period in months to the date. 169 * In some cases, adding months can cause the resulting date to become invalid. 170 * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure 171 * that the result is valid. Typically this will select the last valid day of the month. 172 * <p> 173 * This instance is immutable and unaffected by this method call. 174 * 175 * @param monthsToAdd the months to add, may be negative 176 * @return a date based on this one with the months added, not null 177 * @throws DateTimeException if the result exceeds the supported date range 178 */ plusMonths(long monthsToAdd)179 abstract ChronoDateImpl<D> plusMonths(long monthsToAdd); 180 181 /** 182 * Returns a copy of this date with the specified period in weeks added. 183 * <p> 184 * This adds the specified period in weeks to the date. 185 * In some cases, adding weeks can cause the resulting date to become invalid. 186 * If this occurs, then other fields will be adjusted to ensure that the result is valid. 187 * <p> 188 * The default implementation uses {@link #plusDays(long)} using a 7 day week. 189 * <p> 190 * This instance is immutable and unaffected by this method call. 191 * 192 * @param weeksToAdd the weeks to add, may be negative 193 * @return a date based on this one with the weeks added, not null 194 * @throws DateTimeException if the result exceeds the supported date range 195 */ plusWeeks(long weeksToAdd)196 ChronoDateImpl<D> plusWeeks(long weeksToAdd) { 197 return plusDays(Jdk8Methods.safeMultiply(weeksToAdd, 7)); 198 } 199 200 /** 201 * Returns a copy of this date with the specified number of days added. 202 * <p> 203 * This adds the specified period in days to the date. 204 * <p> 205 * This instance is immutable and unaffected by this method call. 206 * 207 * @param daysToAdd the days to add, may be negative 208 * @return a date based on this one with the days added, not null 209 * @throws DateTimeException if the result exceeds the supported date range 210 */ plusDays(long daysToAdd)211 abstract ChronoDateImpl<D> plusDays(long daysToAdd); 212 213 //----------------------------------------------------------------------- 214 /** 215 * Returns a copy of this date with the specified period in years subtracted. 216 * <p> 217 * This subtracts the specified period in years to the date. 218 * In some cases, subtracting years can cause the resulting date to become invalid. 219 * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure 220 * that the result is valid. Typically this will select the last valid day of the month. 221 * <p> 222 * The default implementation uses {@link #plusYears(long)}. 223 * <p> 224 * This instance is immutable and unaffected by this method call. 225 * 226 * @param yearsToSubtract the years to subtract, may be negative 227 * @return a date based on this one with the years subtracted, not null 228 * @throws DateTimeException if the result exceeds the supported date range 229 */ minusYears(long yearsToSubtract)230 ChronoDateImpl<D> minusYears(long yearsToSubtract) { 231 return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract)); 232 } 233 234 /** 235 * Returns a copy of this date with the specified period in months subtracted. 236 * <p> 237 * This subtracts the specified period in months to the date. 238 * In some cases, subtracting months can cause the resulting date to become invalid. 239 * If this occurs, then other fields, typically the day-of-month, will be adjusted to ensure 240 * that the result is valid. Typically this will select the last valid day of the month. 241 * <p> 242 * The default implementation uses {@link #plusMonths(long)}. 243 * <p> 244 * This instance is immutable and unaffected by this method call. 245 * 246 * @param monthsToSubtract the months to subtract, may be negative 247 * @return a date based on this one with the months subtracted, not null 248 * @throws DateTimeException if the result exceeds the supported date range 249 */ minusMonths(long monthsToSubtract)250 ChronoDateImpl<D> minusMonths(long monthsToSubtract) { 251 return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract)); 252 } 253 254 /** 255 * Returns a copy of this date with the specified period in weeks subtracted. 256 * <p> 257 * This subtracts the specified period in weeks to the date. 258 * In some cases, subtracting weeks can cause the resulting date to become invalid. 259 * If this occurs, then other fields will be adjusted to ensure that the result is valid. 260 * <p> 261 * The default implementation uses {@link #plusWeeks(long)}. 262 * <p> 263 * This instance is immutable and unaffected by this method call. 264 * 265 * @param weeksToSubtract the weeks to subtract, may be negative 266 * @return a date based on this one with the weeks subtracted, not null 267 * @throws DateTimeException if the result exceeds the supported date range 268 */ minusWeeks(long weeksToSubtract)269 ChronoDateImpl<D> minusWeeks(long weeksToSubtract) { 270 return (weeksToSubtract == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeksToSubtract)); 271 } 272 273 /** 274 * Returns a copy of this date with the specified number of days subtracted. 275 * <p> 276 * This subtracts the specified period in days to the date. 277 * <p> 278 * The default implementation uses {@link #plusDays(long)}. 279 * <p> 280 * This instance is immutable and unaffected by this method call. 281 * 282 * @param daysToSubtract the days to subtract, may be negative 283 * @return a date based on this one with the days subtracted, not null 284 * @throws DateTimeException if the result exceeds the supported date range 285 */ minusDays(long daysToSubtract)286 ChronoDateImpl<D> minusDays(long daysToSubtract) { 287 return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract)); 288 } 289 290 @Override atTime(LocalTime localTime)291 public ChronoLocalDateTime<?> atTime(LocalTime localTime) { 292 return ChronoLocalDateTimeImpl.of(this, localTime); 293 } 294 295 //----------------------------------------------------------------------- 296 @Override until(Temporal endExclusive, TemporalUnit unit)297 public long until(Temporal endExclusive, TemporalUnit unit) { 298 ChronoLocalDate end = getChronology().date(endExclusive); 299 if (unit instanceof ChronoUnit) { 300 return LocalDate.from(this).until(end, unit); // TODO: this is wrong 301 } 302 return unit.between(this, end); 303 } 304 305 @Override until(ChronoLocalDate endDate)306 public ChronoPeriod until(ChronoLocalDate endDate) { 307 throw new UnsupportedOperationException("Not supported in ThreeTen backport"); 308 } 309 310 } 311