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 * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos 28 * 29 * All rights reserved. 30 * 31 * Redistribution and use in source and binary forms, with or without 32 * modification, are permitted provided that the following conditions are met: 33 * 34 * * Redistributions of source code must retain the above copyright notice, 35 * this list of conditions and the following disclaimer. 36 * 37 * * Redistributions in binary form must reproduce the above copyright notice, 38 * this list of conditions and the following disclaimer in the documentation 39 * and/or other materials provided with the distribution. 40 * 41 * * Neither the name of JSR-310 nor the names of its contributors 42 * may be used to endorse or promote products derived from this software 43 * without specific prior written permission. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 46 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 47 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 48 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 49 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 50 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 52 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 53 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 */ 57 package java.time.chrono; 58 59 import static java.time.chrono.ThaiBuddhistChronology.YEARS_DIFFERENCE; 60 import static java.time.temporal.ChronoField.DAY_OF_MONTH; 61 import static java.time.temporal.ChronoField.MONTH_OF_YEAR; 62 import static java.time.temporal.ChronoField.YEAR; 63 64 import java.io.DataInput; 65 import java.io.DataOutput; 66 import java.io.IOException; 67 import java.io.Serializable; 68 import java.time.Clock; 69 import java.time.DateTimeException; 70 import java.time.LocalDate; 71 import java.time.LocalTime; 72 import java.time.Period; 73 import java.time.ZoneId; 74 import java.time.temporal.ChronoField; 75 import java.time.temporal.TemporalQuery; 76 import java.time.temporal.TemporalAccessor; 77 import java.time.temporal.TemporalAdjuster; 78 import java.time.temporal.TemporalAmount; 79 import java.time.temporal.TemporalField; 80 import java.time.temporal.TemporalUnit; 81 import java.time.temporal.ValueRange; 82 import java.util.Objects; 83 84 /** 85 * A date in the Thai Buddhist calendar system. 86 * <p> 87 * This date operates using the {@linkplain ThaiBuddhistChronology Thai Buddhist calendar}. 88 * This calendar system is primarily used in Thailand. 89 * Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}. 90 * 91 * <h3>Specification for implementors</h3> 92 * This class is immutable and thread-safe. 93 * 94 * @since 1.8 95 */ 96 public final class ThaiBuddhistDate 97 extends ChronoDateImpl<ThaiBuddhistDate> 98 implements ChronoLocalDate<ThaiBuddhistDate>, Serializable { 99 100 /** 101 * Serialization version. 102 */ 103 private static final long serialVersionUID = -8722293800195731463L; 104 105 /** 106 * The underlying date. 107 */ 108 private final LocalDate isoDate; 109 110 //----------------------------------------------------------------------- 111 /** 112 * Obtains the current {@code ThaiBuddhistDate} from the system clock in the default time-zone. 113 * <p> 114 * This will query the {@link Clock#systemDefaultZone() system clock} in the default 115 * time-zone to obtain the current date. 116 * <p> 117 * Using this method will prevent the ability to use an alternate clock for testing 118 * because the clock is hard-coded. 119 * 120 * @return the current date using the system clock and default time-zone, not null 121 */ 122 public static ThaiBuddhistDate now() { 123 return now(Clock.systemDefaultZone()); 124 } 125 126 /** 127 * Obtains the current {@code ThaiBuddhistDate} from the system clock in the specified time-zone. 128 * <p> 129 * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. 130 * Specifying the time-zone avoids dependence on the default time-zone. 131 * <p> 132 * Using this method will prevent the ability to use an alternate clock for testing 133 * because the clock is hard-coded. 134 * 135 * @param zone the zone ID to use, not null 136 * @return the current date using the system clock, not null 137 */ 138 public static ThaiBuddhistDate now(ZoneId zone) { 139 return now(Clock.system(zone)); 140 } 141 142 /** 143 * Obtains the current {@code ThaiBuddhistDate} from the specified clock. 144 * <p> 145 * This will query the specified clock to obtain the current date - today. 146 * Using this method allows the use of an alternate clock for testing. 147 * The alternate clock may be introduced using {@linkplain Clock dependency injection}. 148 * 149 * @param clock the clock to use, not null 150 * @return the current date, not null 151 * @throws DateTimeException if the current date cannot be obtained 152 */ 153 public static ThaiBuddhistDate now(Clock clock) { 154 return ThaiBuddhistChronology.INSTANCE.date(LocalDate.now(clock)); 155 } 156 157 /** 158 * Obtains a {@code ThaiBuddhistDate} representing a date in the Thai Buddhist calendar 159 * system from the proleptic-year, month-of-year and day-of-month fields. 160 * <p> 161 * This returns a {@code ThaiBuddhistDate} with the specified fields. 162 * The day must be valid for the year and month, otherwise an exception will be thrown. 163 * 164 * @param prolepticYear the Thai Buddhist proleptic-year 165 * @param month the Thai Buddhist month-of-year, from 1 to 12 166 * @param dayOfMonth the Thai Buddhist day-of-month, from 1 to 31 167 * @return the date in Thai Buddhist calendar system, not null 168 * @throws DateTimeException if the value of any field is out of range, 169 * or if the day-of-month is invalid for the month-year 170 */ 171 public static ThaiBuddhistDate of(int prolepticYear, int month, int dayOfMonth) { 172 return new ThaiBuddhistDate(LocalDate.of(prolepticYear - YEARS_DIFFERENCE, month, dayOfMonth)); 173 } 174 175 /** 176 * Obtains a {@code ThaiBuddhistDate} from a temporal object. 177 * <p> 178 * This obtains a date in the Thai Buddhist calendar system based on the specified temporal. 179 * A {@code TemporalAccessor} represents an arbitrary set of date and time information, 180 * which this factory converts to an instance of {@code ThaiBuddhistDate}. 181 * <p> 182 * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY} 183 * field, which is standardized across calendar systems. 184 * <p> 185 * This method matches the signature of the functional interface {@link TemporalQuery} 186 * allowing it to be used as a query via method reference, {@code ThaiBuddhistDate::from}. 187 * 188 * @param temporal the temporal object to convert, not null 189 * @return the date in Thai Buddhist calendar system, not null 190 * @throws DateTimeException if unable to convert to a {@code ThaiBuddhistDate} 191 */ 192 public static ThaiBuddhistDate from(TemporalAccessor temporal) { 193 return ThaiBuddhistChronology.INSTANCE.date(temporal); 194 } 195 196 //----------------------------------------------------------------------- 197 /** 198 * Creates an instance from an ISO date. 199 * 200 * @param isoDate the standard local date, validated not null 201 */ 202 ThaiBuddhistDate(LocalDate isoDate) { 203 Objects.requireNonNull(isoDate, "isoDate"); 204 this.isoDate = isoDate; 205 } 206 207 //----------------------------------------------------------------------- 208 @Override 209 public ThaiBuddhistChronology getChronology() { 210 return ThaiBuddhistChronology.INSTANCE; 211 } 212 213 @Override 214 public int lengthOfMonth() { 215 return isoDate.lengthOfMonth(); 216 } 217 218 @Override 219 public ValueRange range(TemporalField field) { 220 if (field instanceof ChronoField) { 221 if (isSupported(field)) { 222 ChronoField f = (ChronoField) field; 223 switch (f) { 224 case DAY_OF_MONTH: 225 case DAY_OF_YEAR: 226 case ALIGNED_WEEK_OF_MONTH: 227 return isoDate.range(field); 228 case YEAR_OF_ERA: { 229 ValueRange range = YEAR.range(); 230 long max = (getProlepticYear() <= 0 ? -(range.getMinimum() + YEARS_DIFFERENCE) + 1 : range.getMaximum() + YEARS_DIFFERENCE); 231 return ValueRange.of(1, max); 232 } 233 } 234 return getChronology().range(f); 235 } 236 throw new DateTimeException("Unsupported field: " + field.getName()); 237 } 238 return field.rangeRefinedBy(this); 239 } 240 241 @Override 242 public long getLong(TemporalField field) { 243 if (field instanceof ChronoField) { 244 switch ((ChronoField) field) { 245 case YEAR_OF_ERA: { 246 int prolepticYear = getProlepticYear(); 247 return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear); 248 } 249 case YEAR: 250 return getProlepticYear(); 251 case ERA: 252 return (getProlepticYear() >= 1 ? 1 : 0); 253 } 254 return isoDate.getLong(field); 255 } 256 return field.getFrom(this); 257 } 258 259 private int getProlepticYear() { 260 return isoDate.getYear() + YEARS_DIFFERENCE; 261 } 262 263 //----------------------------------------------------------------------- 264 @Override 265 public ThaiBuddhistDate with(TemporalField field, long newValue) { 266 if (field instanceof ChronoField) { 267 ChronoField f = (ChronoField) field; 268 if (getLong(f) == newValue) { 269 return this; 270 } 271 switch (f) { 272 case YEAR_OF_ERA: 273 case YEAR: 274 case ERA: { 275 f.checkValidValue(newValue); 276 int nvalue = (int) newValue; 277 switch (f) { 278 case YEAR_OF_ERA: 279 return with(isoDate.withYear((getProlepticYear() >= 1 ? nvalue : 1 - nvalue) - YEARS_DIFFERENCE)); 280 case YEAR: 281 return with(isoDate.withYear(nvalue - YEARS_DIFFERENCE)); 282 case ERA: 283 return with(isoDate.withYear((1 - getProlepticYear()) - YEARS_DIFFERENCE)); 284 } 285 } 286 } 287 return with(isoDate.with(field, newValue)); 288 } 289 return (ThaiBuddhistDate) ChronoLocalDate.super.with(field, newValue); 290 } 291 292 /** 293 * {@inheritDoc} 294 * @throws DateTimeException {@inheritDoc} 295 * @throws ArithmeticException {@inheritDoc} 296 */ 297 @Override 298 public ThaiBuddhistDate with(TemporalAdjuster adjuster) { 299 return (ThaiBuddhistDate)super.with(adjuster); 300 } 301 302 /** 303 * {@inheritDoc} 304 * @throws DateTimeException {@inheritDoc} 305 * @throws ArithmeticException {@inheritDoc} 306 */ 307 @Override 308 public ThaiBuddhistDate plus(TemporalAmount amount) { 309 return (ThaiBuddhistDate)super.plus(amount); 310 } 311 312 /** 313 * {@inheritDoc} 314 * @throws DateTimeException {@inheritDoc} 315 * @throws ArithmeticException {@inheritDoc} 316 */ 317 @Override 318 public ThaiBuddhistDate minus(TemporalAmount amount) { 319 return (ThaiBuddhistDate)super.minus(amount); 320 } 321 322 //----------------------------------------------------------------------- 323 @Override 324 ThaiBuddhistDate plusYears(long years) { 325 return with(isoDate.plusYears(years)); 326 } 327 328 @Override 329 ThaiBuddhistDate plusMonths(long months) { 330 return with(isoDate.plusMonths(months)); 331 } 332 333 @Override 334 ThaiBuddhistDate plusWeeks(long weeksToAdd) { 335 return (ThaiBuddhistDate)super.plusWeeks(weeksToAdd); 336 } 337 338 @Override 339 ThaiBuddhistDate plusDays(long days) { 340 return with(isoDate.plusDays(days)); 341 } 342 343 @Override 344 public ThaiBuddhistDate plus(long amountToAdd, TemporalUnit unit) { 345 return (ThaiBuddhistDate)super.plus(amountToAdd, unit); 346 } 347 348 @Override 349 public ThaiBuddhistDate minus(long amountToAdd, TemporalUnit unit) { 350 return (ThaiBuddhistDate)super.minus(amountToAdd, unit); 351 } 352 353 @Override 354 ThaiBuddhistDate minusYears(long yearsToSubtract) { 355 return (ThaiBuddhistDate)super.minusYears(yearsToSubtract); 356 } 357 358 @Override 359 ThaiBuddhistDate minusMonths(long monthsToSubtract) { 360 return (ThaiBuddhistDate)super.minusMonths(monthsToSubtract); 361 } 362 363 @Override 364 ThaiBuddhistDate minusWeeks(long weeksToSubtract) { 365 return (ThaiBuddhistDate)super.minusWeeks(weeksToSubtract); 366 } 367 368 @Override 369 ThaiBuddhistDate minusDays(long daysToSubtract) { 370 return (ThaiBuddhistDate)super.minusDays(daysToSubtract); 371 } 372 373 private ThaiBuddhistDate with(LocalDate newDate) { 374 return (newDate.equals(isoDate) ? this : new ThaiBuddhistDate(newDate)); 375 } 376 377 @Override // for javadoc and covariant return type 378 public final ChronoLocalDateTime<ThaiBuddhistDate> atTime(LocalTime localTime) { 379 return (ChronoLocalDateTime<ThaiBuddhistDate>)super.atTime(localTime); 380 } 381 382 @Override 383 public Period periodUntil(ChronoLocalDate<?> endDate) { 384 return isoDate.periodUntil(endDate); 385 } 386 387 @Override // override for performance 388 public long toEpochDay() { 389 return isoDate.toEpochDay(); 390 } 391 392 //------------------------------------------------------------------------- 393 @Override // override for performance 394 public boolean equals(Object obj) { 395 if (this == obj) { 396 return true; 397 } 398 if (obj instanceof ThaiBuddhistDate) { 399 ThaiBuddhistDate otherDate = (ThaiBuddhistDate) obj; 400 return this.isoDate.equals(otherDate.isoDate); 401 } 402 return false; 403 } 404 405 @Override // override for performance 406 public int hashCode() { 407 return getChronology().getId().hashCode() ^ isoDate.hashCode(); 408 } 409 410 //----------------------------------------------------------------------- 411 private Object writeReplace() { 412 return new Ser(Ser.THAIBUDDHIST_DATE_TYPE, this); 413 } 414 415 void writeExternal(DataOutput out) throws IOException { 416 // ThaiBuddhistChronology is implicit in the THAIBUDDHIST_DATE_TYPE 417 out.writeInt(this.get(YEAR)); 418 out.writeByte(this.get(MONTH_OF_YEAR)); 419 out.writeByte(this.get(DAY_OF_MONTH)); 420 } 421 422 static ThaiBuddhistDate readExternal(DataInput in) throws IOException { 423 int year = in.readInt(); 424 int month = in.readByte(); 425 int dayOfMonth = in.readByte(); 426 return ThaiBuddhistChronology.INSTANCE.date(year, month, dayOfMonth); 427 } 428 429 }