Package javax.time.i18n

Source Code of javax.time.i18n.HistoricDate$Rule

/*
* Copyright (c) 2010, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*  * Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
*
*  * Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
*  * Neither the name of JSR-310 nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package javax.time.i18n;

import java.io.Serializable;

import javax.time.calendar.Calendrical;
import javax.time.calendar.CalendricalMerger;
import javax.time.calendar.CalendricalRule;
import javax.time.calendar.DateProvider;
import javax.time.calendar.DayOfWeek;
import javax.time.calendar.IllegalCalendarFieldValueException;
import javax.time.calendar.InvalidCalendarFieldException;
import javax.time.calendar.LocalDate;
import javax.time.calendar.MonthOfYear;
import javax.time.calendar.UnsupportedRuleException;

/**
* A date in the Historic calendar system.
* <p>
* HistoricDate is an immutable class that represents a date in the Historic calendar system.
* The rules of the calendar system are described in {@link HistoricChronology}.
* The date has a precision of one day and a range within the era from
* year 1 to year 999,999,999 (inclusive).
* <p>
* Instances of this class may be created from any other object that implements
* {@link DateProvider} including {@link LocalDate}. Similarly, instances of
* this class may be passed into the factory method of any other implementation
* of {@code DateProvider}.
* <p>
* HistoricDate is immutable and thread-safe.
*
* @author Michael Nascimento Santos
* @author Stephen Colebourne
*/
public final class HistoricDate
        implements DateProvider, Calendrical, Comparable<HistoricDate>, Serializable {

    /**
     * The standard cutover date between the Julian and Gregorian calendar system of 1582-10-15.
     */
    public static final LocalDate STANDARD_CUTOVER = LocalDate.of(1582, 10, 15);
    /**
     * The maximum valid year of era.
     * This is currently set to 999,999,999 but may be changed to increase
     * the valid range in a future version of the specification.
     */
    public static final int MAX_YEAR = 999999999;
    /**
     * A serialization identifier for this class.
     */
    private static final long serialVersionUID = 1L;

    /**
     * The cutover between the Julian and Gregorian calendar systems.
     */
    private final HistoricChronology chrono;
//    /**
//     * The backing date.
//     */
//    private final LocalDate date;
//    /**
//     * The modified julian day count.
//     */
//    private final long mjDays;
    /**
     * The historic year.
     */
    private final transient int year;
    /**
     * The historic month.
     */
    private final transient MonthOfYear month;
    /**
     * The historic day-of-month.
     */
    private final transient int day;

    //-----------------------------------------------------------------------
    /**
     * Obtains an instance of {@code LocalDate} from a year, month and day
     * using the standard cutover of 1582-10-15.
     * <p>
     * The day must be valid for the year and month or an exception will be thrown.
     *
     * @param historicYear  the year to represent, from -(MAX_YEAR-1) to MAX_YEAR
     * @param monthOfYear  the month-of-year to represent, not null
     * @param dayOfMonth  the day-of-month to represent, from 1 to 31
     * @return the local date, never null
     * @throws IllegalCalendarFieldValueException if the value of any field is out of range
     * @throws InvalidCalendarFieldException if the day-of-month is invalid for the month-year
     */
    public static HistoricDate of(int historicYear, MonthOfYear monthOfYear, int dayOfMonth) {
        return of(STANDARD_CUTOVER, historicYear, monthOfYear, dayOfMonth);
    }

    /**
     * Obtains an instance of {@code LocalDate} from a year, month and day
     * specifying the cutover date to use.
     * <p>
     * The day must be valid for the year and month or an exception will be thrown.
     *
     * @param historicYear  the year to represent, from -(MAX_YEAR-1) to MAX_YEAR
     * @param monthOfYear  the month-of-year to represent, not null
     * @param dayOfMonth  the day-of-month to represent, from 1 to 31
     * @return the local date, never null
     * @throws IllegalCalendarFieldValueException if the value of any field is out of range
     * @throws InvalidCalendarFieldException if the day-of-month is invalid for the month-year
     */
    public static HistoricDate of(LocalDate cutover, int historicYear, MonthOfYear monthOfYear, int dayOfMonth) {
        HistoricChronology.checkNotNull(cutover, "Cutover date must not be null");
        HistoricChronology chrono = HistoricChronology.cutoverAt(cutover);
        chrono.yearRule().checkValue(historicYear);
        HistoricChronology.checkNotNull(monthOfYear, "MonthOfYear must not be null");
        chrono.dayOfMonthRule().checkValue(dayOfMonth);
        return new HistoricDate(chrono, historicYear, monthOfYear, dayOfMonth);
    }

    /**
     * Obtains an instance of {@code HistoricDate} from a calendrical.
     * <p>
     * This can be used extract the date directly from any implementation
     * of {@code Calendrical}, including those in other calendar systems.
     *
     * @param calendrical  the calendrical to extract from, not null
     * @return the Historic date, never null
     * @throws UnsupportedRuleException if the day-of-week cannot be obtained
     */
    public static HistoricDate from(Calendrical calendrical) {
        return rule().getValueChecked(calendrical);
    }

//    /**
//     * Obtains an instance of {@code HistoricDate} from a number of epoch days.
//     *
//     * @param epochDays  the epoch days to use, not null
//     * @return a HistoricDate object, never null
//     * @throws IllegalCalendarFieldValueException if the year range is exceeded
//     */
//    private static HistoricDate historicDateFromEpochDays(int epochDays) {
//        if (epochDays < MIN_EPOCH_DAY || epochDays > MAX_EPOCH_DAY) {
//            throw new IllegalCalendarFieldValueException(
//                    "Date exceeds supported range for HistoricDate", HistoricChronology.yearRule());
//        }
//        int year = ((epochDays * 4) + 1463) / 1461;
//        int startYearEpochDays = (year - 1) * 365 + (year / 4);
//        int doy0 = epochDays - startYearEpochDays;
//        int month = doy0 / 30 + 1;
//        int day = doy0 % 30 + 1;
//        return new HistoricDate(epochDays, year, month, day);
//    }

    //-----------------------------------------------------------------------
    /**
     * Constructs an instance with the specified date.
     *
     * @param chono  the chronology, not null
     * @param year  the year to represent, valid
     * @param month  the month-of-year to represent, not null
     * @param day  the day-of-month to represent, valid
     */
    HistoricDate(HistoricChronology chrono, int year, MonthOfYear month, int day) {
        this.chrono = chrono;
        this.year = year;
        this.month = month;
        this.day = day;
    }

//    /**
//     * Replaces the date instance from the stream with a valid one.
//     *
//     * @return the resolved date, never null
//     */
//    private Object readResolve() {
//        return historicDateFromEpochDays(epochDays);
//    }

    /**
     * Obtains an instance of {@code HistoricDate} using the previous valid algorithm.
     *
     * @param historicYear  the year to represent
     * @param monthOfYear  the month-of-year to represent, not null
     * @param dayOfMonth  the day-of-month to represent, from 1 to 31
     * @return the historic date, never null
     */
    private HistoricDate previousValid(int historicYear, MonthOfYear monthOfYear, int dayOfMonth) {
        chrono.yearRule().checkValue(historicYear);
        HistoricChronology.checkNotNull(monthOfYear, "MonthOfYear must not be null");
        chrono.dayOfMonthRule().checkValue(dayOfMonth);
        int lastDay = monthOfYear.getLastDayOfMonth(chrono.isLeapYear(year));
        if (dayOfMonth > lastDay) {
            dayOfMonth = lastDay;
        }
        // TODO: Handle cutover gap
        return new HistoricDate(chrono, year, monthOfYear, dayOfMonth);
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the chronology that this date uses, which is the historic calendar system.
     *
     * @return the historic chronology, never null
     */
    public HistoricChronology getChronology() {
        return chrono;
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the value of the specified calendar field.
     * <p>
     * This method queries the value of the specified calendar field.
     * If the calendar field is not supported then an exception is thrown.
     *
     * @param rule  the field to query, not null
     * @return the value for the field
     * @throws UnsupportedRuleException if no value for the field is found
     */
    public <T> T get(CalendricalRule<T> rule) {
        if (rule.equals(LocalDate.rule())) {  // NPE check
            return rule.reify(toLocalDate());
        }
        return rule().deriveValueFor(rule, this, this);
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the historic era.
     * <p>
     * The era provides a context for the year-of-era.
     * This calendar system defines two eras, BCE and CE.
     *
     * @return the era, never null
     */
    public HistoricEra getEra() {
        return (year < 1 ? HistoricEra.BCE : HistoricEra.CE);
    }

    /**
     * Gets the historic year-of-era value.
     * <p>
     * The year-of-era is a value that matches the historic definition.
     * Thus, both 1 AD and 1 BCE are represented as year-of-era 1.
     *
     * @return the year, from 1 to MAX_YEAR
     */
    public int getYearOfEra() {
        return (year < 1 ? -(year - 1) : year);
    }

    /**
     * Gets the historic year value, which can be negative.
     * <p>
     * The year is value that is continuous.
     * Thus, 1 AD is represented as year 1, and 1 BCE is represented as year 0.
     *
     * @return the year, from -(MAX_YEAR-1) to MAX_YEAR
     */
    public int getYear() {
        return year;
    }

    /**
     * Gets the historic month-of-year value.
     *
     * @return the month-of-year, never null
     */
    public MonthOfYear getMonthOfYear() {
        return month;
    }

    /**
     * Gets the historic day-of-month value.
     *
     * @return the day-of-month, from 1 to 30
     */
    public int getDayOfMonth() {
        return day;
    }

    /**
     * Gets the historic day-of-year value.
     *
     * @return the day-of-year, from 1 to 366
     */
    public int getDayOfYear() {
        return chrono.getDayOfYear(this);
    }

    /**
     * Gets the historic day-of-week.
     *
     * @return the day-of-week, never null
     */
    public DayOfWeek getDayOfWeek() {
        return toLocalDate().getDayOfWeek();
//        return DayOfWeek.of((int) ((mjDays + 4) % 7) + 1);
    }

    //-----------------------------------------------------------------------
    /**
     * Checks if the date represented is a leap year.
     *
     * @return true if this date is in a leap year
     */
    public boolean isLeapYear() {
        return chrono.isLeapYear(getYear());
    }

    /**
     * Checks if the date represented is the leap day in a leap year.
     * <p>
     * The leap day is when the year is a leap year, the month is February and
     * the day is 29.
     *
     * @return true if this date is the leap day in a leap year
     */
    public boolean isLeapDay() {
        // no need to check leap year, as date always valid
        return getMonthOfYear().isFebruary() && getDayOfMonth() == 29;
    }

    //-----------------------------------------------------------------------
    /**
     * Returns a copy of this date with the year value altered.
     * <p>
     * The result of setting the year may leave the day-of-month invalid.
     * To avoid this, the day-of-month is changed to the largest valid value.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param historicYear  the year to represent, from MIN_YEAR to MAX_YEAR
     * @return a {@code HistoricDate} based on this date with the specified year, never null
     * @throws IllegalCalendarFieldValueException if the year is out of range
     */
    public HistoricDate withYear(int historicYear) {
        return previousValid(historicYear, getMonthOfYear(), getDayOfMonth());
    }

    /**
     * Returns a copy of this date with the month-of-year value altered.
     * <p>
     * The result of setting the month may leave the day-of-month invalid.
     * To avoid this, the day-of-month is changed to the largest valid value.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param monthOfYear  the month-of-year to represent, from 1 to 12
     * @return a {@code HistoricDate} based on this date with the specified month, never null
     */
    public HistoricDate withMonthOfYear(MonthOfYear monthOfYear) {
        return previousValid(getYear(), monthOfYear, getDayOfMonth());
    }

    /**
     * Returns a copy of this date with the day-of-month value altered.
     * <p>
     * The specified day-of-month must be valid for the month and year.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param dayOfMonth  the day-of-month to represent, from 1 to 31
     * @return a {@code HistoricDate} based on this date with the specified day, never null
     * @throws IllegalCalendarFieldValueException if the day is out of range
     * @throws InvalidCalendarFieldException if the day-of-month is invalid for the year and month
     */
    public HistoricDate withDayOfMonth(int dayOfMonth) {
        return of(getYear(), getMonthOfYear(), dayOfMonth);
    }

    /**
     * Returns a copy of this date with the day-of-year value altered.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param dayOfYear  the day-of-year to represent, from 1 to 366
     * @return a {@code HistoricDate} based on this date with the specified day, never null
     * @throws IllegalCalendarFieldValueException if the day-of-year is out of range
     * @throws InvalidCalendarFieldException if the day-of-year is invalid for the year
     */
    public HistoricDate withDayOfYear(int dayOfYear) {
        chrono.dayOfYearRule().checkValue(dayOfYear);
        return chrono.getDateFromDayOfYear(year, dayOfYear);
    }

    //-----------------------------------------------------------------------
    /**
     * Returns a copy of this date with the specified number of years added.
     * <p>
     * The result of changing the year may leave the day-of-month invalid.
     * To avoid this, the day-of-month is changed to the largest valid value.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param years  the years to add, positive or negative
     * @return a {@code HistoricDate} based on this date with the specified years added, never null
     * @throws IllegalCalendarFieldValueException if the year range is exceeded
     */
    public HistoricDate plusYears(int years) {
        int newYear = getYear() + years;  // TODO: check overflow
        return previousValid(newYear, month, day);
    }

    /**
     * Returns a copy of this date with the specified number of months added.
     * <p>
     * The result of changing the month may leave the day-of-month invalid.
     * To avoid this, the day-of-month is changed to the largest valid value.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param months  the months to add, positive or negative
     * @return a {@code HistoricDate} based on this date with the specified months added, never null
     * @throws IllegalCalendarFieldValueException if the year range is exceeded
     */
    public HistoricDate plusMonths(int months) {
        int month0 = (months % 12) + 12 + getMonthOfYear().ordinal();
        int years = (months / 12) - 1 + (month0 / 12);
        month0 %= 12;
        int newYear = getYear() + years;  // TODO: check overflow
        return previousValid(newYear, MonthOfYear.of(month0 + 1), day);
    }

    /**
     * Returns a copy of this date with the specified number of days added.
     * <p>
     * This instance is immutable and unaffected by this method call.
     *
     * @param days  the days to add, positive or negative
     * @return a {@code HistoricDate} based on this date with the specified days added, never null
     * @throws IllegalCalendarFieldValueException if the year range is exceeded
     */
    public HistoricDate plusDays(int days) {
        return from(toLocalDate().plusDays(days))// TODO: better
    }

    //-----------------------------------------------------------------------
    /**
     * Converts this date to an ISO-8601 calendar system {@code LocalDate}.
     *
     * @return the equivalent date in the ISO-8601 calendar system, never null
     */
    public LocalDate toLocalDate() {
        LocalDate possible = LocalDate.of(year, month, day);
        if (possible.isBefore(chrono.getCutover())) {
            long julYear1Days = (year - 1) * 365 + (year / 4) + chrono.getDayOfYear(this) - 1;
            return LocalDate.fromModifiedJulianDays(julYear1Days + 0)// TODO
        } else {
            return possible;
        }
    }

    //-----------------------------------------------------------------------
    /**
     * Compares this date to the specified date.
     * <p>
     * The comparison is based on the year, month, day and cutover date.
     *
     * @param otherDate  the other date instance to compare to, not null
     * @return the comparator value, negative if less, positive if greater
     */
    public int compareTo(HistoricDate otherDate) {
        int cmp = toLocalDate().compareTo(otherDate.toLocalDate());
        if (cmp == 0 ) {
            cmp = chrono.getCutover().compareTo(otherDate.chrono.getCutover());
        }
        return cmp;
    }

    /**
     * Checks is this date is after the specified date.
     * <p>
     * The comparison is based on the year, month, day and cutover date.
     *
     * @param otherDate  the other date instance to compare to, not null
     * @return true if this day is after the specified day
     */
    public boolean isAfter(HistoricDate otherDate) {
        return compareTo(otherDate) > 0;
    }

    /**
     * Checks is this date is before the specified date.
     * <p>
     * The comparison is based on the year, month, day and cutover date.
     *
     * @param otherDate  the other date instance to compare to, not null
     * @return true if this day is before the specified day
     */
    public boolean isBefore(HistoricDate otherDate) {
        return compareTo(otherDate) < 0;
    }

    //-----------------------------------------------------------------------
    /**
     * Checks is this date is equal to the specified date.
     * <p>
     * The comparison is based on the year, month, day and cutover date.
     *
     * @param otherDate  the other date instance to compare to, null returns false
     * @return true if this day is equal to the specified day
     */
    @Override
    public boolean equals(Object otherDate) {
        if (this == otherDate) {
            return true;
        }
        if (otherDate instanceof HistoricDate) {
            HistoricDate other = (HistoricDate) otherDate;
            return year == other.year &&
                    month == other.month &&
                    day == other.day &&
                    chrono.getCutover().equals(other.chrono.getCutover());
        }
        return false;
    }

    /**
     * A hash code for this date.
     *
     * @return a suitable hash code
     */
    @Override
    public int hashCode() {
        int yearValue = year;
        int monthValue = month.getValue();
        int dayValue = day;
        return (yearValue & 0xFFFFF800) ^ ((yearValue << 11) + (monthValue << 6) + (dayValue)) +
                chrono.getCutover().hashCode();
    }

    //-----------------------------------------------------------------------
    /**
     * Outputs the date as a {@code String}, such as '1723-13-01 (Historic 2010-10-15)'.
     * <p>
     * The output will be in the format 'yyyy-MM-dd (Historic 2010-10-15)' where
     * 2010-10-15 is the cutover date.
     *
     * @return the formatted date string, never null
     */
    @Override
    public String toString() {
        int yearValue = getYear();
        int monthValue = getMonthOfYear().getValue();
        int dayValue = getDayOfMonth();
        int absYear = Math.abs(yearValue);
        StringBuilder buf = new StringBuilder(12);
        if (absYear < 1000) {
            buf.append(yearValue + 10000).deleteCharAt(0);
        } else {
            buf.append(yearValue);
        }
        return buf.append(monthValue < 10 ? "-0" : "-")
            .append(monthValue)
            .append(dayValue < 10 ? "-0" : "-")
            .append(dayValue)
            .append(" (" + chrono.getName() + ")")
            .toString();
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the field rule for {@code HistoricDate}.
     *
     * @return the field rule for the date, never null
     */
    public static CalendricalRule<HistoricDate> rule() {
        return Rule.INSTANCE;
    }

    //-----------------------------------------------------------------------
    /**
     * Rule implementation.
     */
    static final class Rule extends CalendricalRule<HistoricDate> implements Serializable {
        private static final CalendricalRule<HistoricDate> INSTANCE = new Rule();
        private static final long serialVersionUID = 1L;
        private Rule() {
            // TODO
            super(HistoricDate.class, HistoricChronology.standardCutover(), "HistoricDate", HistoricChronology.periodDays(), null);
        }
        private Object readResolve() {
            return INSTANCE;
        }
        @Override
        protected HistoricDate derive(Calendrical calendrical) {
            LocalDate ld = calendrical.get(LocalDate.rule());
            if (ld == null) {
                return null;
            }
//            long epochDays = ld.toModifiedJulianDays() + MJD_TO_historic;
//            return historicDateFromEpochDays((int) epochDays);
            return null; // TODO
        }
        @Override
        protected void merge(CalendricalMerger merger) {
            HistoricDate cd = merger.getValue(this);
            merger.storeMerged(LocalDate.rule(), cd.toLocalDate());
            merger.removeProcessed(this);
        }
    }

}
TOP

Related Classes of javax.time.i18n.HistoricDate$Rule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.