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 static org.threeten.bp.chrono.ThaiBuddhistChronology.YEARS_DIFFERENCE; 35 import static org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH; 36 import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR; 37 import static org.threeten.bp.temporal.ChronoField.YEAR; 38 39 import java.io.DataInput; 40 import java.io.DataOutput; 41 import java.io.IOException; 42 import java.io.Serializable; 43 44 import org.threeten.bp.Clock; 45 import org.threeten.bp.DateTimeException; 46 import org.threeten.bp.LocalDate; 47 import org.threeten.bp.LocalTime; 48 import org.threeten.bp.Period; 49 import org.threeten.bp.ZoneId; 50 import org.threeten.bp.jdk8.Jdk8Methods; 51 import org.threeten.bp.temporal.ChronoField; 52 import org.threeten.bp.temporal.TemporalAccessor; 53 import org.threeten.bp.temporal.TemporalAdjuster; 54 import org.threeten.bp.temporal.TemporalAmount; 55 import org.threeten.bp.temporal.TemporalField; 56 import org.threeten.bp.temporal.TemporalQuery; 57 import org.threeten.bp.temporal.TemporalUnit; 58 import org.threeten.bp.temporal.UnsupportedTemporalTypeException; 59 import org.threeten.bp.temporal.ValueRange; 60 61 /** 62 * A date in the Thai Buddhist calendar system. 63 * <p> 64 * This date operates using the {@linkplain ThaiBuddhistChronology Thai Buddhist calendar}. 65 * This calendar system is primarily used in Thailand. 66 * Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}. 67 * 68 * <h3>Specification for implementors</h3> 69 * This class is immutable and thread-safe. 70 */ 71 public final class ThaiBuddhistDate 72 extends ChronoDateImpl<ThaiBuddhistDate> 73 implements Serializable { 74 75 /** 76 * Serialization version. 77 */ 78 private static final long serialVersionUID = -8722293800195731463L; 79 80 /** 81 * The underlying date. 82 */ 83 private final LocalDate isoDate; 84 85 //----------------------------------------------------------------------- 86 /** 87 * Obtains the current {@code ThaiBuddhistDate} from the system clock in the default time-zone. 88 * <p> 89 * This will query the {@link Clock#systemDefaultZone() system clock} in the default 90 * time-zone to obtain the current date. 91 * <p> 92 * Using this method will prevent the ability to use an alternate clock for testing 93 * because the clock is hard-coded. 94 * 95 * @return the current date using the system clock and default time-zone, not null 96 */ now()97 public static ThaiBuddhistDate now() { 98 return now(Clock.systemDefaultZone()); 99 } 100 101 /** 102 * Obtains the current {@code ThaiBuddhistDate} from the system clock in the specified time-zone. 103 * <p> 104 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. 105 * Specifying the time-zone avoids dependence on the default time-zone. 106 * <p> 107 * Using this method will prevent the ability to use an alternate clock for testing 108 * because the clock is hard-coded. 109 * 110 * @param zone the zone ID to use, not null 111 * @return the current date using the system clock, not null 112 */ now(ZoneId zone)113 public static ThaiBuddhistDate now(ZoneId zone) { 114 return now(Clock.system(zone)); 115 } 116 117 /** 118 * Obtains the current {@code ThaiBuddhistDate} from the specified clock. 119 * <p> 120 * This will query the specified clock to obtain the current date - today. 121 * Using this method allows the use of an alternate clock for testing. 122 * The alternate clock may be introduced using {@linkplain Clock dependency injection}. 123 * 124 * @param clock the clock to use, not null 125 * @return the current date, not null 126 * @throws DateTimeException if the current date cannot be obtained 127 */ now(Clock clock)128 public static ThaiBuddhistDate now(Clock clock) { 129 return new ThaiBuddhistDate(LocalDate.now(clock)); 130 } 131 132 /** 133 * Obtains a {@code ThaiBuddhistDate} representing a date in the Thai Buddhist calendar 134 * system from the proleptic-year, month-of-year and day-of-month fields. 135 * <p> 136 * This returns a {@code ThaiBuddhistDate} with the specified fields. 137 * The day must be valid for the year and month, otherwise an exception will be thrown. 138 * 139 * @param prolepticYear the Thai Buddhist proleptic-year 140 * @param month the Thai Buddhist month-of-year, from 1 to 12 141 * @param dayOfMonth the Thai Buddhist day-of-month, from 1 to 31 142 * @return the date in Thai Buddhist calendar system, not null 143 * @throws DateTimeException if the value of any field is out of range, 144 * or if the day-of-month is invalid for the month-year 145 */ of(int prolepticYear, int month, int dayOfMonth)146 public static ThaiBuddhistDate of(int prolepticYear, int month, int dayOfMonth) { 147 return ThaiBuddhistChronology.INSTANCE.date(prolepticYear, month, dayOfMonth); 148 } 149 150 /** 151 * Obtains a {@code ThaiBuddhistDate} from a temporal object. 152 * <p> 153 * This obtains a date in the Thai Buddhist calendar system based on the specified temporal. 154 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 155 * which this factory converts to an instance of {@code ThaiBuddhistDate}. 156 * <p> 157 * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} 158 * field, which is standardized across calendar systems. 159 * <p> 160 * This method matches the signature of the functional interface {@link TemporalQuery} 161 * allowing it to be used as a query via method reference, {@code ThaiBuddhistDate::from}. 162 * 163 * @param temporal the temporal object to convert, not null 164 * @return the date in Thai Buddhist calendar system, not null 165 * @throws DateTimeException if unable to convert to a {@code ThaiBuddhistDate} 166 */ from(TemporalAccessor temporal)167 public static ThaiBuddhistDate from(TemporalAccessor temporal) { 168 return ThaiBuddhistChronology.INSTANCE.date(temporal); 169 } 170 171 //----------------------------------------------------------------------- 172 /** 173 * Creates an instance from an ISO date. 174 * 175 * @param isoDate the standard local date, validated not null 176 */ ThaiBuddhistDate(LocalDate date)177 ThaiBuddhistDate(LocalDate date) { 178 Jdk8Methods.requireNonNull(date, "date"); 179 this.isoDate = date; 180 } 181 182 //----------------------------------------------------------------------- 183 @Override getChronology()184 public ThaiBuddhistChronology getChronology() { 185 return ThaiBuddhistChronology.INSTANCE; 186 } 187 188 @Override getEra()189 public ThaiBuddhistEra getEra() { 190 return (ThaiBuddhistEra) super.getEra(); 191 } 192 193 @Override lengthOfMonth()194 public int lengthOfMonth() { 195 return isoDate.lengthOfMonth(); 196 } 197 198 @Override range(TemporalField field)199 public ValueRange range(TemporalField field) { 200 if (field instanceof ChronoField) { 201 if (isSupported(field)) { 202 ChronoField f = (ChronoField) field; 203 switch (f) { 204 case DAY_OF_MONTH: 205 case DAY_OF_YEAR: 206 case ALIGNED_WEEK_OF_MONTH: 207 return isoDate.range(field); 208 case YEAR_OF_ERA: { 209 ValueRange range = YEAR.range(); 210 long max = (getProlepticYear() <= 0 ? -(range.getMinimum() + YEARS_DIFFERENCE) + 1 : range.getMaximum() + YEARS_DIFFERENCE); 211 return ValueRange.of(1, max); 212 } 213 } 214 return getChronology().range(f); 215 } 216 throw new UnsupportedTemporalTypeException("Unsupported field: " + field); 217 } 218 return field.rangeRefinedBy(this); 219 } 220 221 @Override getLong(TemporalField field)222 public long getLong(TemporalField field) { 223 if (field instanceof ChronoField) { 224 switch ((ChronoField) field) { 225 case PROLEPTIC_MONTH: 226 return getProlepticMonth(); 227 case YEAR_OF_ERA: { 228 int prolepticYear = getProlepticYear(); 229 return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear); 230 } 231 case YEAR: 232 return getProlepticYear(); 233 case ERA: 234 return (getProlepticYear() >= 1 ? 1 : 0); 235 } 236 return isoDate.getLong(field); 237 } 238 return field.getFrom(this); 239 } 240 getProlepticMonth()241 private long getProlepticMonth() { 242 return getProlepticYear() * 12L + isoDate.getMonthValue() - 1; 243 } 244 getProlepticYear()245 private int getProlepticYear() { 246 return isoDate.getYear() + YEARS_DIFFERENCE; 247 } 248 249 //----------------------------------------------------------------------- 250 @Override with(TemporalAdjuster adjuster)251 public ThaiBuddhistDate with(TemporalAdjuster adjuster) { 252 return (ThaiBuddhistDate) super.with(adjuster); 253 } 254 255 @Override with(TemporalField field, long newValue)256 public ThaiBuddhistDate with(TemporalField field, long newValue) { 257 if (field instanceof ChronoField) { 258 ChronoField f = (ChronoField) field; 259 if (getLong(f) == newValue) { 260 return this; 261 } 262 switch (f) { 263 case PROLEPTIC_MONTH: 264 getChronology().range(f).checkValidValue(newValue, f); 265 return plusMonths(newValue - getProlepticMonth()); 266 case YEAR_OF_ERA: 267 case YEAR: 268 case ERA: { 269 int nvalue = getChronology().range(f).checkValidIntValue(newValue, f); 270 switch (f) { 271 case YEAR_OF_ERA: 272 return with(isoDate.withYear((getProlepticYear() >= 1 ? nvalue : 1 - nvalue) - YEARS_DIFFERENCE)); 273 case YEAR: 274 return with(isoDate.withYear(nvalue - YEARS_DIFFERENCE)); 275 case ERA: 276 return with(isoDate.withYear((1 - getProlepticYear()) - YEARS_DIFFERENCE)); 277 } 278 } 279 } 280 return with(isoDate.with(field, newValue)); 281 } 282 return field.adjustInto(this, newValue); 283 } 284 285 @Override plus(TemporalAmount amount)286 public ThaiBuddhistDate plus(TemporalAmount amount) { 287 return (ThaiBuddhistDate) super.plus(amount); 288 } 289 290 @Override plus(long amountToAdd, TemporalUnit unit)291 public ThaiBuddhistDate plus(long amountToAdd, TemporalUnit unit) { 292 return (ThaiBuddhistDate) super.plus(amountToAdd, unit); 293 } 294 295 @Override minus(TemporalAmount amount)296 public ThaiBuddhistDate minus(TemporalAmount amount) { 297 return (ThaiBuddhistDate) super.minus(amount); 298 } 299 300 @Override minus(long amountToAdd, TemporalUnit unit)301 public ThaiBuddhistDate minus(long amountToAdd, TemporalUnit unit) { 302 return (ThaiBuddhistDate) super.minus(amountToAdd, unit); 303 } 304 305 //----------------------------------------------------------------------- 306 @Override plusYears(long years)307 ThaiBuddhistDate plusYears(long years) { 308 return with(isoDate.plusYears(years)); 309 } 310 311 @Override plusMonths(long months)312 ThaiBuddhistDate plusMonths(long months) { 313 return with(isoDate.plusMonths(months)); 314 } 315 316 @Override plusDays(long days)317 ThaiBuddhistDate plusDays(long days) { 318 return with(isoDate.plusDays(days)); 319 } 320 with(LocalDate newDate)321 private ThaiBuddhistDate with(LocalDate newDate) { 322 return (newDate.equals(isoDate) ? this : new ThaiBuddhistDate(newDate)); 323 } 324 325 @Override 326 @SuppressWarnings("unchecked") atTime(LocalTime localTime)327 public final ChronoLocalDateTime<ThaiBuddhistDate> atTime(LocalTime localTime) { 328 return (ChronoLocalDateTime<ThaiBuddhistDate>) super.atTime(localTime); 329 } 330 331 @Override until(ChronoLocalDate endDate)332 public ChronoPeriod until(ChronoLocalDate endDate) { 333 Period period = isoDate.until(endDate); 334 return getChronology().period(period.getYears(), period.getMonths(), period.getDays()); 335 } 336 337 @Override // override for performance toEpochDay()338 public long toEpochDay() { 339 return isoDate.toEpochDay(); 340 } 341 342 //------------------------------------------------------------------------- 343 @Override // override for performance equals(Object obj)344 public boolean equals(Object obj) { 345 if (this == obj) { 346 return true; 347 } 348 if (obj instanceof ThaiBuddhistDate) { 349 ThaiBuddhistDate otherDate = (ThaiBuddhistDate) obj; 350 return this.isoDate.equals(otherDate.isoDate); 351 } 352 return false; 353 } 354 355 @Override // override for performance hashCode()356 public int hashCode() { 357 return getChronology().getId().hashCode() ^ isoDate.hashCode(); 358 } 359 360 //----------------------------------------------------------------------- writeReplace()361 private Object writeReplace() { 362 return new Ser(Ser.THAIBUDDHIST_DATE_TYPE, this); 363 } 364 writeExternal(DataOutput out)365 void writeExternal(DataOutput out) throws IOException { 366 // MinguoChrono is implicit in the THAIBUDDHIST_DATE_TYPE 367 out.writeInt(this.get(YEAR)); 368 out.writeByte(this.get(MONTH_OF_YEAR)); 369 out.writeByte(this.get(DAY_OF_MONTH)); 370 } 371 readExternal(DataInput in)372 static ChronoLocalDate readExternal(DataInput in) throws IOException { 373 int year = in.readInt(); 374 int month = in.readByte(); 375 int dayOfMonth = in.readByte(); 376 return ThaiBuddhistChronology.INSTANCE.date(year, month, dayOfMonth); 377 } 378 379 } 380