/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (RelatedGregorianYearRule.java) is part of project Time4J. * * Time4J is free software: You can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * Time4J is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Time4J. If not, see <http://www.gnu.org/licenses/>. * ----------------------------------------------------------------------- */ package net.time4j.calendar; import net.time4j.base.MathUtils; import net.time4j.engine.CalendarSystem; import net.time4j.engine.CalendarVariant; import net.time4j.engine.ChronoElement; import net.time4j.engine.ChronoEntity; import net.time4j.engine.ElementRule; import net.time4j.engine.EpochDays; import java.util.Collections; import java.util.Map; /** * <p>Defines a rule for the related gregorian year. </p> * * @author Meno Hochschild * @since 3.20/4.16 */ final class RelatedGregorianYearRule<T extends ChronoEntity<T>> implements ElementRule<T, Integer> { //~ Statische Felder/Initialisierungen -------------------------------- private static final String KEY_CALENDRICAL = "calendrical"; //~ Instanzvariablen -------------------------------------------------- private final Map<String, ? extends CalendarSystem<T>> map; private final ChronoElement<Integer> dayOfYear; //~ Konstruktoren ----------------------------------------------------- RelatedGregorianYearRule( CalendarSystem<T> calsys, ChronoElement<Integer> dayOfYear ) { super(); this.map = Collections.singletonMap(KEY_CALENDRICAL, calsys); this.dayOfYear = dayOfYear; } RelatedGregorianYearRule( Map<String, ? extends CalendarSystem<T>> map, ChronoElement<Integer> dayOfYear ) { super(); this.map = map; this.dayOfYear = dayOfYear; } //~ Methoden ---------------------------------------------------------- @Override public Integer getValue(T context) { CalendarSystem<T> calsys = this.getCalendarSystem(context); T start = context.with(this.dayOfYear, 1); return toGregorianYear(calsys.transform(start)); } @Override public Integer getMinimum(T context) { CalendarSystem<T> calsys = this.getCalendarSystem(context); long utc = calsys.getMinimumSinceUTC(); T start = calsys.transform(utc).with(this.dayOfYear, 1); return toGregorianYear(calsys.transform(start)); } @Override public Integer getMaximum(T context) { CalendarSystem<T> calsys = this.getCalendarSystem(context); long utc = calsys.getMaximumSinceUTC(); T start = calsys.transform(utc).with(this.dayOfYear, 1); return toGregorianYear(calsys.transform(start)); } @Override public boolean isValid( T context, Integer value ) { return this.getValue(context).equals(value); } @Override public T withValue( T context, Integer value, boolean lenient ) { if (this.isValid(context, value)) { return context; } else { throw new IllegalArgumentException("The related gregorian year is read-only."); } } @Override public ChronoElement<?> getChildAtFloor(T context) { return null; } @Override public ChronoElement<?> getChildAtCeiling(T context) { return null; } private CalendarSystem<T> getCalendarSystem(T context) { if (context instanceof CalendarVariant) { return this.map.get(CalendarVariant.class.cast(context).getVariant()); } else { return this.map.get(KEY_CALENDRICAL); } } private static Integer toGregorianYear(long utc) { long mjd = EpochDays.MODIFIED_JULIAN_DATE.transform(utc, EpochDays.UTC); long days = MathUtils.safeAdd(mjd, 719468 - 40587); long q400 = MathUtils.floorDivide(days, 146097); int r400 = MathUtils.floorModulo(days, 146097); long y; if (r400 == 146096) { y = (q400 + 1) * 400; } else { int q100 = (r400 / 36524); int r100 = (r400 % 36524); int q4 = (r100 / 1461); int r4 = (r100 % 1461); if (r4 == 1460) { y = (q400 * 400 + q100 * 100 + (q4 + 1) * 4); } else { int q1 = (r4 / 365); int r1 = (r4 % 365); y = (q400 * 400 + q100 * 100 + q4 * 4 + q1); int m = (((r1 + 31) * 5) / 153) + 2; if (m > 12) { y++; } } } return Integer.valueOf(MathUtils.safeCast(y)); } }