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.calendar;
  58 
  59 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
  60 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
  61 import static java.time.temporal.ChronoField.YEAR;
  62 
  63 import java.io.DataInput;
  64 import java.io.DataOutput;
  65 import java.io.IOException;
  66 import java.io.ObjectInputStream;
  67 import java.io.Serializable;
  68 import java.time.DateTimeException;
  69 import java.time.LocalDate;
  70 import java.time.temporal.ChronoField;
  71 import java.time.temporal.ChronoLocalDate;
  72 import java.time.temporal.TemporalField;
  73 import java.time.temporal.ValueRange;
  74 import java.util.Calendar;
  75 import java.util.Objects;
  76 
  77 import sun.util.calendar.LocalGregorianCalendar;
  78 
  79 /**
  80  * A date in the Japanese Imperial calendar system.
  81  * <p>
  82  * This implements {@code ChronoLocalDate} for the
  83  * {@linkplain JapaneseChrono Japanese Imperial calendar}.
  84  *
  85  * <h3>Specification for implementors</h3>
  86  * This class is immutable and thread-safe.
  87  *
  88  * @since 1.8
  89  */
  90 final class JapaneseDate
  91         extends ChronoDateImpl<JapaneseChrono>
  92         implements ChronoLocalDate<JapaneseChrono>, Serializable {
  93     // this class is package-scoped so that future conversion to public
  94     // would not change serialization
  95 
  96     /**
  97      * Serialization version.
  98      */
  99     private static final long serialVersionUID = -305327627230580483L;
 100 
 101     /**
 102      * The underlying ISO local date.
 103      */
 104     private transient final LocalDate isoDate;
 105     /**
 106      * The JapaneseEra of this date.
 107      */
 108     private transient JapaneseEra era;
 109     /**
 110      * The Japanese imperial calendar year of this date.
 111      */
 112     private transient int yearOfEra;
 113 
 114     /**
 115      * Obtains an instance of {@code JapaneseDate} from the era, year-of-era,
 116      * month-of-year and day-of-month.
 117      *
 118      * @param era  the era to represent, not null
 119      * @param yearOfEra  the year-of-era to represent
 120      * @param month  the month-of-year to represent
 121      * @param dayOfMonth  the day-of-month to represent, from 1 to 31
 122      * @return the Japanese date, never null
 123      * @throws DateTimeException if the value of any field is out of range, or
 124      *                           if the day-of-month is invalid for the month-year
 125      */
 126     static JapaneseDate of(JapaneseEra era, int yearOfEra, int month, int dayOfMonth) {
 127         Objects.requireNonNull(era, "era");
 128         LocalGregorianCalendar.Date jdate = JapaneseChrono.JCAL.newCalendarDate(null);
 129         jdate.setEra(era.getPrivateEra()).setDate(yearOfEra, month, dayOfMonth);
 130         if (!JapaneseChrono.JCAL.validate(jdate)) {
 131             throw new IllegalArgumentException();
 132         }
 133         LocalDate date = LocalDate.of(jdate.getNormalizedYear(), month, dayOfMonth);
 134         return new JapaneseDate(era, yearOfEra, date);
 135     }
 136 
 137     //-----------------------------------------------------------------------
 138     /**
 139      * Creates an instance from an ISO date.
 140      *
 141      * @param isoDate  the standard local date, validated not null
 142      */
 143     JapaneseDate(LocalDate isoDate) {
 144         LocalGregorianCalendar.Date jdate = toPrivateJapaneseDate(isoDate);
 145         this.era = JapaneseEra.toJapaneseEra(jdate.getEra());
 146         this.yearOfEra = jdate.getYear();
 147         this.isoDate = isoDate;
 148     }
 149 
 150     /**
 151      * Constructs a {@code JapaneseDate}. This constructor does NOT validate the given parameters,
 152      * and {@code era} and {@code year} must agree with {@code isoDate}.
 153      *
 154      * @param era  the era, validated not null
 155      * @param year  the year-of-era, validated
 156      * @param isoDate  the standard local date, validated not null
 157      */
 158     JapaneseDate(JapaneseEra era, int year, LocalDate isoDate) {
 159         this.era = era;
 160         this.yearOfEra = year;
 161         this.isoDate = isoDate;
 162     }
 163 
 164     //-----------------------------------------------------------------------
 165     @Override
 166     public JapaneseChrono getChrono() {
 167         return JapaneseChrono.INSTANCE;
 168     }
 169 
 170     @Override
 171     public int lengthOfMonth() {
 172         return isoDate.lengthOfMonth();
 173     }
 174 
 175     @Override
 176     public ValueRange range(TemporalField field) {
 177         if (field instanceof ChronoField) {
 178             if (isSupported(field)) {
 179                 ChronoField f = (ChronoField) field;
 180                 switch (f) {
 181                     case DAY_OF_YEAR:
 182                         return actualRange(Calendar.DAY_OF_YEAR);
 183                     case YEAR_OF_ERA:
 184                         return actualRange(Calendar.YEAR);
 185                 }
 186                 return getChrono().range(f);
 187             }
 188             throw new DateTimeException("Unsupported field: " + field.getName());
 189         }
 190         return field.doRange(this);
 191     }
 192 
 193     private ValueRange actualRange(int calendarField) {
 194         Calendar jcal = Calendar.getInstance(JapaneseChrono.LOCALE);
 195         jcal.set(Calendar.ERA, era.getValue() + JapaneseEra.ERA_OFFSET);
 196         jcal.set(yearOfEra, isoDate.getMonthValue() - 1, isoDate.getDayOfMonth());
 197         return ValueRange.of(jcal.getActualMinimum(calendarField),
 198                 jcal.getActualMaximum(calendarField));
 199     }
 200 
 201     @Override
 202     public long getLong(TemporalField field) {
 203         if (field instanceof ChronoField) {
 204             switch ((ChronoField) field) {
 205                 case YEAR_OF_ERA:
 206                     return yearOfEra;
 207                 case ERA:
 208                     return era.getValue();
 209                 case DAY_OF_YEAR: {
 210                     LocalGregorianCalendar.Date jdate = toPrivateJapaneseDate(isoDate);
 211                     return JapaneseChrono.JCAL.getDayOfYear(jdate);
 212                 }
 213             }
 214             // TODO: review other fields
 215             return isoDate.getLong(field);
 216         }
 217         return field.doGet(this);
 218     }
 219 
 220     /**
 221      * Returns a {@code LocalGregorianCalendar.Date} converted from the given {@code isoDate}.
 222      *
 223      * @param isoDate  the local date, not null
 224      * @return a {@code LocalGregorianCalendar.Date}, not null
 225      */
 226     private static LocalGregorianCalendar.Date toPrivateJapaneseDate(LocalDate isoDate) {
 227         LocalGregorianCalendar.Date jdate = JapaneseChrono.JCAL.newCalendarDate(null);
 228         sun.util.calendar.Era sunEra = JapaneseEra.privateEraFrom(isoDate);
 229         int year = isoDate.getYear();
 230         if (sunEra != null) {
 231             year -= sunEra.getSinceDate().getYear() - 1;
 232         }
 233         jdate.setEra(sunEra).setYear(year).setMonth(isoDate.getMonthValue()).setDayOfMonth(isoDate.getDayOfMonth());
 234         JapaneseChrono.JCAL.normalize(jdate);
 235         return jdate;
 236     }
 237 
 238     //-----------------------------------------------------------------------
 239     @Override
 240     public JapaneseDate with(TemporalField field, long newValue) {
 241         if (field instanceof ChronoField) {
 242             ChronoField f = (ChronoField) field;
 243             if (getLong(f) == newValue) {
 244                 return this;
 245             }
 246             switch (f) {
 247                 case YEAR_OF_ERA:
 248                 case YEAR:
 249                 case ERA: {
 250                     f.checkValidValue(newValue);
 251                     int nvalue = (int) newValue;
 252                     switch (f) {
 253                         case YEAR_OF_ERA:
 254                             return this.withYear(nvalue);
 255                         case YEAR:
 256                             return with(isoDate.withYear(nvalue));
 257                         case ERA: {
 258                             return this.withYear(JapaneseEra.of(nvalue), yearOfEra);
 259                         }
 260                     }
 261                 }
 262             }
 263             // TODO: review other fields, such as WEEK_OF_YEAR
 264             return with(isoDate.with(field, newValue));
 265         }
 266         return (JapaneseDate) ChronoLocalDate.super.with(field, newValue);
 267     }
 268 
 269     //-----------------------------------------------------------------------
 270     /**
 271      * Returns a copy of this date with the year altered.
 272      * <p>
 273      * This method changes the year of the date.
 274      * If the month-day is invalid for the year, then the previous valid day
 275      * will be selected instead.
 276      * <p>
 277      * This instance is immutable and unaffected by this method call.
 278      *
 279      * @param era  the era to set in the result, not null
 280      * @param yearOfEra  the year-of-era to set in the returned date
 281      * @return a {@code JapaneseDate} based on this date with the requested year, never null
 282      * @throws DateTimeException if {@code year} is invalid
 283      */
 284     private JapaneseDate withYear(JapaneseEra era, int yearOfEra) {
 285         int year = JapaneseChrono.INSTANCE.prolepticYear(era, yearOfEra);
 286         return with(isoDate.withYear(year));
 287     }
 288 
 289     /**
 290      * Returns a copy of this date with the year-of-era altered.
 291      * <p>
 292      * This method changes the year-of-era of the date.
 293      * If the month-day is invalid for the year, then the previous valid day
 294      * will be selected instead.
 295      * <p>
 296      * This instance is immutable and unaffected by this method call.
 297      *
 298      * @param year  the year to set in the returned date
 299      * @return a {@code JapaneseDate} based on this date with the requested year-of-era, never null
 300      * @throws DateTimeException if {@code year} is invalid
 301      */
 302     private JapaneseDate withYear(int year) {
 303         return withYear((JapaneseEra) getEra(), year);
 304     }
 305 
 306     //-----------------------------------------------------------------------
 307     @Override
 308     JapaneseDate plusYears(long years) {
 309         return with(isoDate.plusYears(years));
 310     }
 311 
 312     @Override
 313     JapaneseDate plusMonths(long months) {
 314         return with(isoDate.plusMonths(months));
 315     }
 316 
 317     @Override
 318     JapaneseDate plusDays(long days) {
 319         return with(isoDate.plusDays(days));
 320     }
 321 
 322     private JapaneseDate with(LocalDate newDate) {
 323         return (newDate.equals(isoDate) ? this : new JapaneseDate(newDate));
 324     }
 325 
 326     @Override  // override for performance
 327     public long toEpochDay() {
 328         return isoDate.toEpochDay();
 329     }
 330 
 331     //-------------------------------------------------------------------------
 332     @Override  // override for performance
 333     public boolean equals(Object obj) {
 334         if (this == obj) {
 335             return true;
 336         }
 337         if (obj instanceof JapaneseDate) {
 338             JapaneseDate otherDate = (JapaneseDate) obj;
 339             return this.isoDate.equals(otherDate.isoDate);
 340         }
 341         return false;
 342     }
 343 
 344     @Override  // override for performance
 345     public int hashCode() {
 346         return getChrono().getId().hashCode() ^ isoDate.hashCode();
 347     }
 348 
 349     @Override
 350     public String toString() {
 351         if (era == JapaneseEra.SEIREKI) {
 352             return getChrono().getId() + " " + isoDate.toString();
 353         }
 354         return super.toString();
 355     }
 356 
 357     //-----------------------------------------------------------------------
 358     private Object writeReplace() {
 359         return new Ser(Ser.JAPANESE_DATE_TYPE, this);
 360     }
 361 
 362     void writeExternal(DataOutput out) throws IOException {
 363         // JapaneseChrono is implicit in the JAPANESE_DATE_TYPE
 364         out.writeInt(get(YEAR));
 365         out.writeByte(get(MONTH_OF_YEAR));
 366         out.writeByte(get(DAY_OF_MONTH));
 367     }
 368 
 369     static ChronoLocalDate<JapaneseChrono> readExternal(DataInput in) throws IOException {
 370         int year = in.readInt();
 371         int month = in.readByte();
 372         int dayOfMonth = in.readByte();
 373         return JapaneseChrono.INSTANCE.date(year, month, dayOfMonth);
 374     }
 375 
 376 
 377 }