• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (c) 2012, 2019, 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.MinguoChronology.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.InvalidObjectException;
68  import java.io.ObjectInputStream;
69  import java.io.Serializable;
70  import java.time.Clock;
71  import java.time.DateTimeException;
72  import java.time.LocalDate;
73  import java.time.LocalTime;
74  import java.time.Period;
75  import java.time.ZoneId;
76  import java.time.temporal.ChronoField;
77  import java.time.temporal.TemporalAccessor;
78  import java.time.temporal.TemporalAdjuster;
79  import java.time.temporal.TemporalAmount;
80  import java.time.temporal.TemporalField;
81  import java.time.temporal.TemporalQuery;
82  import java.time.temporal.TemporalUnit;
83  import java.time.temporal.UnsupportedTemporalTypeException;
84  import java.time.temporal.ValueRange;
85  import java.util.Objects;
86  
87  // Android-changed: removed ValueBased paragraph.
88  /**
89   * A date in the Minguo calendar system.
90   * <p>
91   * This date operates using the {@linkplain MinguoChronology Minguo calendar}.
92   * This calendar system is primarily used in the Republic of China, often known as Taiwan.
93   * Dates are aligned such that {@code 0001-01-01 (Minguo)} is {@code 1912-01-01 (ISO)}.
94   *
95   * @implSpec
96   * This class is immutable and thread-safe.
97   *
98   * @since 1.8
99   */
100  public final class MinguoDate
101          extends ChronoLocalDateImpl<MinguoDate>
102          implements ChronoLocalDate, Serializable {
103  
104      /**
105       * Serialization version.
106       */
107      @java.io.Serial
108      private static final long serialVersionUID = 1300372329181994526L;
109  
110      /**
111       * The underlying date.
112       */
113      private final transient LocalDate isoDate;
114  
115      //-----------------------------------------------------------------------
116      /**
117       * Obtains the current {@code MinguoDate} from the system clock in the default time-zone.
118       * <p>
119       * This will query the {@link Clock#systemDefaultZone() system clock} in the default
120       * time-zone to obtain the current date.
121       * <p>
122       * Using this method will prevent the ability to use an alternate clock for testing
123       * because the clock is hard-coded.
124       *
125       * @return the current date using the system clock and default time-zone, not null
126       */
now()127      public static MinguoDate now() {
128          return now(Clock.systemDefaultZone());
129      }
130  
131      /**
132       * Obtains the current {@code MinguoDate} from the system clock in the specified time-zone.
133       * <p>
134       * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date.
135       * Specifying the time-zone avoids dependence on the default time-zone.
136       * <p>
137       * Using this method will prevent the ability to use an alternate clock for testing
138       * because the clock is hard-coded.
139       *
140       * @param zone  the zone ID to use, not null
141       * @return the current date using the system clock, not null
142       */
now(ZoneId zone)143      public static MinguoDate now(ZoneId zone) {
144          return now(Clock.system(zone));
145      }
146  
147      /**
148       * Obtains the current {@code MinguoDate} from the specified clock.
149       * <p>
150       * This will query the specified clock to obtain the current date - today.
151       * Using this method allows the use of an alternate clock for testing.
152       * The alternate clock may be introduced using {@linkplain Clock dependency injection}.
153       *
154       * @param clock  the clock to use, not null
155       * @return the current date, not null
156       * @throws DateTimeException if the current date cannot be obtained
157       */
now(Clock clock)158      public static MinguoDate now(Clock clock) {
159          return new MinguoDate(LocalDate.now(clock));
160      }
161  
162      /**
163       * Obtains a {@code MinguoDate} representing a date in the Minguo calendar
164       * system from the proleptic-year, month-of-year and day-of-month fields.
165       * <p>
166       * This returns a {@code MinguoDate} with the specified fields.
167       * The day must be valid for the year and month, otherwise an exception will be thrown.
168       *
169       * @param prolepticYear  the Minguo proleptic-year
170       * @param month  the Minguo month-of-year, from 1 to 12
171       * @param dayOfMonth  the Minguo day-of-month, from 1 to 31
172       * @return the date in Minguo calendar system, not null
173       * @throws DateTimeException if the value of any field is out of range,
174       *  or if the day-of-month is invalid for the month-year
175       */
of(int prolepticYear, int month, int dayOfMonth)176      public static MinguoDate of(int prolepticYear, int month, int dayOfMonth) {
177          return new MinguoDate(LocalDate.of(prolepticYear + YEARS_DIFFERENCE, month, dayOfMonth));
178      }
179  
180      /**
181       * Obtains a {@code MinguoDate} from a temporal object.
182       * <p>
183       * This obtains a date in the Minguo calendar system based on the specified temporal.
184       * A {@code TemporalAccessor} represents an arbitrary set of date and time information,
185       * which this factory converts to an instance of {@code MinguoDate}.
186       * <p>
187       * The conversion typically uses the {@link ChronoField#EPOCH_DAY EPOCH_DAY}
188       * field, which is standardized across calendar systems.
189       * <p>
190       * This method matches the signature of the functional interface {@link TemporalQuery}
191       * allowing it to be used as a query via method reference, {@code MinguoDate::from}.
192       *
193       * @param temporal  the temporal object to convert, not null
194       * @return the date in Minguo calendar system, not null
195       * @throws DateTimeException if unable to convert to a {@code MinguoDate}
196       */
from(TemporalAccessor temporal)197      public static MinguoDate from(TemporalAccessor temporal) {
198          return MinguoChronology.INSTANCE.date(temporal);
199      }
200  
201      //-----------------------------------------------------------------------
202      /**
203       * Creates an instance from an ISO date.
204       *
205       * @param isoDate  the standard local date, validated not null
206       */
MinguoDate(LocalDate isoDate)207      MinguoDate(LocalDate isoDate) {
208          Objects.requireNonNull(isoDate, "isoDate");
209          this.isoDate = isoDate;
210      }
211  
212      //-----------------------------------------------------------------------
213      /**
214       * Gets the chronology of this date, which is the Minguo calendar system.
215       * <p>
216       * The {@code Chronology} represents the calendar system in use.
217       * The era and other fields in {@link ChronoField} are defined by the chronology.
218       *
219       * @return the Minguo chronology, not null
220       */
221      @Override
getChronology()222      public MinguoChronology getChronology() {
223          return MinguoChronology.INSTANCE;
224      }
225  
226      /**
227       * Gets the era applicable at this date.
228       * <p>
229       * The Minguo calendar system has two eras, 'ROC' and 'BEFORE_ROC',
230       * defined by {@link MinguoEra}.
231       *
232       * @return the era applicable at this date, not null
233       */
234      @Override
getEra()235      public MinguoEra getEra() {
236          return (getProlepticYear() >= 1 ? MinguoEra.ROC : MinguoEra.BEFORE_ROC);
237      }
238  
239      /**
240       * Returns the length of the month represented by this date.
241       * <p>
242       * This returns the length of the month in days.
243       * Month lengths match those of the ISO calendar system.
244       *
245       * @return the length of the month in days
246       */
247      @Override
lengthOfMonth()248      public int lengthOfMonth() {
249          return isoDate.lengthOfMonth();
250      }
251  
252      //-----------------------------------------------------------------------
253      @Override
range(TemporalField field)254      public ValueRange range(TemporalField field) {
255          if (field instanceof ChronoField) {
256              if (isSupported(field)) {
257                  ChronoField f = (ChronoField) field;
258                  switch (f) {
259                      case DAY_OF_MONTH:
260                      case DAY_OF_YEAR:
261                      case ALIGNED_WEEK_OF_MONTH:
262                          return isoDate.range(field);
263                      case YEAR_OF_ERA: {
264                          ValueRange range = YEAR.range();
265                          long max = (getProlepticYear() <= 0 ? -range.getMinimum() + 1 + YEARS_DIFFERENCE : range.getMaximum() - YEARS_DIFFERENCE);
266                          return ValueRange.of(1, max);
267                      }
268                  }
269                  return getChronology().range(f);
270              }
271              throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
272          }
273          return field.rangeRefinedBy(this);
274      }
275  
276      @Override
getLong(TemporalField field)277      public long getLong(TemporalField field) {
278          if (field instanceof ChronoField) {
279              switch ((ChronoField) field) {
280                  case PROLEPTIC_MONTH:
281                      return getProlepticMonth();
282                  case YEAR_OF_ERA: {
283                      int prolepticYear = getProlepticYear();
284                      return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear);
285                  }
286                  case YEAR:
287                      return getProlepticYear();
288                  case ERA:
289                      return (getProlepticYear() >= 1 ? 1 : 0);
290              }
291              return isoDate.getLong(field);
292          }
293          return field.getFrom(this);
294      }
295  
getProlepticMonth()296      private long getProlepticMonth() {
297          return getProlepticYear() * 12L + isoDate.getMonthValue() - 1;
298      }
299  
getProlepticYear()300      private int getProlepticYear() {
301          return isoDate.getYear() - YEARS_DIFFERENCE;
302      }
303  
304      //-----------------------------------------------------------------------
305      @Override
with(TemporalField field, long newValue)306      public MinguoDate with(TemporalField field, long newValue) {
307          if (field instanceof ChronoField chronoField) {
308              if (getLong(chronoField) == newValue) {
309                  return this;
310              }
311              switch (chronoField) {
312                  case PROLEPTIC_MONTH:
313                      getChronology().range(chronoField).checkValidValue(newValue, chronoField);
314                      return plusMonths(newValue - getProlepticMonth());
315                  case YEAR_OF_ERA:
316                  case YEAR:
317                  case ERA: {
318                      int nvalue = getChronology().range(chronoField).checkValidIntValue(newValue, chronoField);
319                      switch (chronoField) {
320                          case YEAR_OF_ERA:
321                              return with(isoDate.withYear(getProlepticYear() >= 1 ? nvalue + YEARS_DIFFERENCE : (1 - nvalue)  + YEARS_DIFFERENCE));
322                          case YEAR:
323                              return with(isoDate.withYear(nvalue + YEARS_DIFFERENCE));
324                          case ERA:
325                              return with(isoDate.withYear((1 - getProlepticYear()) + YEARS_DIFFERENCE));
326                      }
327                  }
328              }
329              return with(isoDate.with(field, newValue));
330          }
331          return super.with(field, newValue);
332      }
333  
334      /**
335       * {@inheritDoc}
336       * @throws DateTimeException {@inheritDoc}
337       * @throws ArithmeticException {@inheritDoc}
338       */
339      @Override
with(TemporalAdjuster adjuster)340      public  MinguoDate with(TemporalAdjuster adjuster) {
341          return super.with(adjuster);
342      }
343  
344      /**
345       * {@inheritDoc}
346       * @throws DateTimeException {@inheritDoc}
347       * @throws ArithmeticException {@inheritDoc}
348       */
349      @Override
plus(TemporalAmount amount)350      public MinguoDate plus(TemporalAmount amount) {
351          return super.plus(amount);
352      }
353  
354      /**
355       * {@inheritDoc}
356       * @throws DateTimeException {@inheritDoc}
357       * @throws ArithmeticException {@inheritDoc}
358       */
359      @Override
minus(TemporalAmount amount)360      public MinguoDate minus(TemporalAmount amount) {
361          return super.minus(amount);
362      }
363  
364      //-----------------------------------------------------------------------
365      @Override
plusYears(long years)366      MinguoDate plusYears(long years) {
367          return with(isoDate.plusYears(years));
368      }
369  
370      @Override
plusMonths(long months)371      MinguoDate plusMonths(long months) {
372          return with(isoDate.plusMonths(months));
373      }
374  
375      @Override
plusWeeks(long weeksToAdd)376      MinguoDate plusWeeks(long weeksToAdd) {
377          return super.plusWeeks(weeksToAdd);
378      }
379  
380      @Override
plusDays(long days)381      MinguoDate plusDays(long days) {
382          return with(isoDate.plusDays(days));
383      }
384  
385      @Override
plus(long amountToAdd, TemporalUnit unit)386      public MinguoDate plus(long amountToAdd, TemporalUnit unit) {
387          return super.plus(amountToAdd, unit);
388      }
389  
390      @Override
minus(long amountToAdd, TemporalUnit unit)391      public MinguoDate minus(long amountToAdd, TemporalUnit unit) {
392          return super.minus(amountToAdd, unit);
393      }
394  
395      @Override
minusYears(long yearsToSubtract)396      MinguoDate minusYears(long yearsToSubtract) {
397          return super.minusYears(yearsToSubtract);
398      }
399  
400      @Override
minusMonths(long monthsToSubtract)401      MinguoDate minusMonths(long monthsToSubtract) {
402          return super.minusMonths(monthsToSubtract);
403      }
404  
405      @Override
minusWeeks(long weeksToSubtract)406      MinguoDate minusWeeks(long weeksToSubtract) {
407          return super.minusWeeks(weeksToSubtract);
408      }
409  
410      @Override
minusDays(long daysToSubtract)411      MinguoDate minusDays(long daysToSubtract) {
412          return super.minusDays(daysToSubtract);
413      }
414  
with(LocalDate newDate)415      private MinguoDate with(LocalDate newDate) {
416          return (newDate.equals(isoDate) ? this : new MinguoDate(newDate));
417      }
418  
419      @Override        // for javadoc and covariant return type
420      @SuppressWarnings("unchecked")
atTime(LocalTime localTime)421      public final ChronoLocalDateTime<MinguoDate> atTime(LocalTime localTime) {
422          return (ChronoLocalDateTime<MinguoDate>)super.atTime(localTime);
423      }
424  
425      @Override
until(ChronoLocalDate endDate)426      public ChronoPeriod until(ChronoLocalDate endDate) {
427          Period period = isoDate.until(endDate);
428          return getChronology().period(period.getYears(), period.getMonths(), period.getDays());
429      }
430  
431      @Override  // override for performance
toEpochDay()432      public long toEpochDay() {
433          return isoDate.toEpochDay();
434      }
435  
436      //-------------------------------------------------------------------------
437      /**
438       * Compares this date to another date, including the chronology.
439       * <p>
440       * Compares this {@code MinguoDate} with another ensuring that the date is the same.
441       * <p>
442       * Only objects of type {@code MinguoDate} are compared, other types return false.
443       * To compare the dates of two {@code TemporalAccessor} instances, including dates
444       * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator.
445       *
446       * @param obj  the object to check, null returns false
447       * @return true if this is equal to the other date
448       */
449      @Override  // override for performance
equals(Object obj)450      public boolean equals(Object obj) {
451          if (this == obj) {
452              return true;
453          }
454          return (obj instanceof MinguoDate otherDate)
455                  && this.isoDate.equals(otherDate.isoDate);
456      }
457  
458      /**
459       * A hash code for this date.
460       *
461       * @return a suitable hash code based only on the Chronology and the date
462       */
463      @Override  // override for performance
hashCode()464      public int hashCode() {
465          return getChronology().getId().hashCode() ^ isoDate.hashCode();
466      }
467  
468      //-----------------------------------------------------------------------
469      /**
470       * Defend against malicious streams.
471       *
472       * @param s the stream to read
473       * @throws InvalidObjectException always
474       */
475      @java.io.Serial
readObject(ObjectInputStream s)476      private void readObject(ObjectInputStream s) throws InvalidObjectException {
477          throw new InvalidObjectException("Deserialization via serialization delegate");
478      }
479  
480      /**
481       * Writes the object using a
482       * <a href="{@docRoot}/serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
483       * @serialData
484       * <pre>
485       *  out.writeByte(8);                 // identifies a MinguoDate
486       *  out.writeInt(get(YEAR));
487       *  out.writeByte(get(MONTH_OF_YEAR));
488       *  out.writeByte(get(DAY_OF_MONTH));
489       * </pre>
490       *
491       * @return the instance of {@code Ser}, not null
492       */
493      @java.io.Serial
writeReplace()494      private Object writeReplace() {
495          return new Ser(Ser.MINGUO_DATE_TYPE, this);
496      }
497  
writeExternal(DataOutput out)498      void writeExternal(DataOutput out) throws IOException {
499          // MinguoChronology is implicit in the MINGUO_DATE_TYPE
500          out.writeInt(get(YEAR));
501          out.writeByte(get(MONTH_OF_YEAR));
502          out.writeByte(get(DAY_OF_MONTH));
503      }
504  
readExternal(DataInput in)505      static MinguoDate readExternal(DataInput in) throws IOException {
506          int year = in.readInt();
507          int month = in.readByte();
508          int dayOfMonth = in.readByte();
509          return MinguoChronology.INSTANCE.date(year, month, dayOfMonth);
510      }
511  
512  }
513