/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (NewYearRule.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.history;
/**
* <p>Defines a listing of common historic new year rules. </p>
*
* <p>Note that some rules were simultaneously used in any region by different people. </p>
*
* <p>Literature: </p>
*
* <ul>
* <li><a href="http://www.ortelius.de/kalender/jul_en.php">http://www.ortelius.de/kalender/jul_en.php</a></li>
* <li><a href="http://www.newadvent.org/cathen/03738a.htm#beginning">http://www.newadvent.org/cathen/03738a.htm#beginning</a></li>
* <li><a href="https://en.wikipedia.org/wiki/New_Year#Historical_European_new_year_dates">https://en.wikipedia.org/wiki/New_Year#Historical_European_new_year_dates</a></li>
* <li><a href="https://en.wikipedia.org/wiki/Anno_Domini#Change_of_year">https://en.wikipedia.org/wiki/Anno_Domini#Change_of_year</a></li>
* <li><a href="https://en.wikipedia.org/wiki/New_Year's_Day">https://en.wikipedia.org/wiki/New_Year's_Day</a></li>
* <li><a href="https://groups.google.com/forum/#!topic/soc.roots/SYETvljQPYc">https://groups.google.com/forum/#!topic/soc.roots/SYETvljQPYc</a></li>
* </ul>
*
* @author Meno Hochschild
* @since 3.14/4.11
*/
/*[deutsch]
* <p>Definiert eine Aufzählung von in der Geschichte gebräuchlichen Neujahrsregeln. </p>
*
* <p>Zu beachten: Einige Regeln wurden in einer gegebenen Region manchmal gleichzeitig von verschiedenen
* Menschen angewandt. </p>
*
* <p>Literatur: </p>
*
* <ul>
* <li><a href="http://www.ortelius.de/kalender/jul_en.php">http://www.ortelius.de/kalender/jul_en.php</a></li>
* <li><a href="http://www.newadvent.org/cathen/03738a.htm#beginning">http://www.newadvent.org/cathen/03738a.htm#beginning</a></li>
* <li><a href="https://en.wikipedia.org/wiki/New_Year#Historical_European_new_year_dates">https://en.wikipedia.org/wiki/New_Year#Historical_European_new_year_dates</a></li>
* <li><a href="https://en.wikipedia.org/wiki/Anno_Domini#Change_of_year">https://en.wikipedia.org/wiki/Anno_Domini#Change_of_year</a></li>
* <li><a href="https://en.wikipedia.org/wiki/New_Year's_Day">https://en.wikipedia.org/wiki/New_Year's_Day</a></li>
* <li><a href="https://groups.google.com/forum/#!topic/soc.roots/SYETvljQPYc">https://groups.google.com/forum/#!topic/soc.roots/SYETvljQPYc</a></li>
* </ul>
*
* @author Meno Hochschild
* @since 3.14/4.11
*/
public enum NewYearRule {
//~ Statische Felder/Initialisierungen --------------------------------
/**
* <p>The new year starts on January the first. </p>
*
* <p>This rule was and has been widely used until up to now. Some people call it the circumcision style
* according to some efforts of the church to connect that day to any religious events. However, the Romans
* had originally invented this rule celebrating the introduction of new consules in their office. </p>
*
* <p>Although sometimes in history the authorities like the church officially used other styles,
* many people still used to inofficially celebrate New Year on first of January. </p>
*/
/*[deutsch]
* <p>Das neue Jahr beginnt am ersten Tag des Januar. </p>
*
* <p>Diese Regel wurde und wird bis heute am meisten verwendet. Das Beschneidungsfest der Kirche
* erinnert ebenfalls an diese Regel, die jedoch ursprünglich von den Römern stammt, die
* damit die Amtseinführung neuer Konsuln feierten. </p>
*
* <p>Auch wenn manchmal in der Geschichte Autoritäten wie die Kirche offiziell andere Regeln
* anwandten, haben viele Menschen nicht aufgehört, inoffiziell Neujahr am 1. Januar zu feiern. </p>
*/
BEGIN_OF_JANUARY() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
return HistoricDate.of(era, yearOfDisplay, 1, 1);
}
@Override
int displayedYear(
NewYearStrategy strategy,
HistoricDate date
) {
return date.getYearOfEra(); // overridden for performance
}
},
/**
* <p>The new year starts on 1st of March. </p>
*
* <p>This rule was used in the Republic of Venice until 1797. </p>
*/
/*[deutsch]
* <p>Das neue Jahr fängt am ersten Tag des März an. </p>
*
* <p>Diese Regel war in der Republik von Venedig bis 1797 gebrächlich. </p>
*/
BEGIN_OF_MARCH() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
return HistoricDate.of(era, yearOfDisplay, 3, 1);
}
},
/**
* <p>The new year starts on 1st of September. </p>
*
* <p>This rule was used in Russia during midage (byzantine calendar). </p>
*/
/*[deutsch]
* <p>Das neue Jahr fängt am ersten Tag des September an. </p>
*
* <p>Diese Regel war in Rußland im Mittelalter gebrächlich (byzantinischer Kalender). </p>
*/
BEGIN_OF_SEPTEMBER() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
return HistoricDate.of(era, yearOfDisplay - 1, 9, 1);
}
@Override
int displayedYear(
NewYearStrategy strategy,
HistoricDate date
) {
HistoricEra era = date.getEra();
int yearOfEra = date.getYearOfEra();
HistoricDate newYear = strategy.newYear(era, yearOfEra + 1); // use strategy of next year!!!
int yearOfDisplay = yearOfEra;
if (date.compareTo(newYear) >= 0) {
yearOfDisplay++;
}
return yearOfDisplay;
}
},
/**
* <p>The new year starts on 25th of December (yyyy-12-25). </p>
*/
/*[deutsch]
* <p>Das neue Jahr beginnt Weihnachten zum postulierten Datum von Jesu Geburt (yyyy-12-25). </p>
*/
CHRISTMAS_STYLE() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
return HistoricDate.of(era, yearOfDisplay - 1, 12, 25);
}
@Override
int displayedYear(
NewYearStrategy strategy,
HistoricDate date
) { // does not work for era BC
int yearOfEra = date.getYearOfEra();
HistoricDate newYear = strategy.newYear(date.getEra(), yearOfEra + 1); // use strategy of next year!!!
int yearOfDisplay = yearOfEra;
if (date.compareTo(newYear) >= 0) {
yearOfDisplay++;
}
return yearOfDisplay;
}
},
/**
* <p>The new year starts on Holy Saturday (one day before Easter Sunday). </p>
*
* <p>Mainly used in France until AD 1567. Due to the possibility to have two same dates per year,
* both dates were distinguished by the addition "after Easter/before Easter". This rule
* always uses the Julian calendar for determining Easter. </p>
*
* @since 3.16/4.13
*/
/*[deutsch]
* <p>Das neue Jahr startet Samstag vor Ostern. </p>
*
* <p>Hauptsächlich in Frankreich bis AD 1567 verwendet. Wegen der Möglichkeit, das gleiche Datum
* zweimal im Jahr zu haben, wurden die Datumsangaben mit dem Zusatz "nach Ostern/vor Ostern"
* versehen. Diese Regel verwendet immer den julianischen Kalender zur Bestimmung von Ostern. </p>
*
* @since 3.16/4.13
*/
EASTER_STYLE() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
int ad = era.annoDomini(yearOfDisplay);
int dom = Computus.EASTERN.marchDay(ad) - 1;
int month = 3;
if (dom > 31) {
month++;
dom -= 31;
}
return HistoricDate.of(era, yearOfDisplay, month, dom);
}
},
/**
* <p>The new year starts on Good Friday (two days before Easter Sunday). </p>
*
* <p>Due to the possibility to have two same dates per year, both dates were
* distinguished by the addition "after Easter/before Easter". This rule
* always uses the Julian calendar for determining Easter. </p>
*
* @since 3.16/4.13
*/
/*[deutsch]
* <p>Das neue Jahr startet am Karfreitag. </p>
*
* <p>Wegen der Möglichkeit, das gleiche Datum zweimal im Jahr zu haben, wurden die Datumsangaben
* mit dem Zusatz "nach Ostern/vor Ostern" versehen. Diese Regel verwendet immer den julianischen
* Kalender zur Bestimmung von Ostern. </p>
*
* @since 3.16/4.13
*/
GOOD_FRIDAY() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
int ad = era.annoDomini(yearOfDisplay);
int dom = Computus.EASTERN.marchDay(ad) - 2;
int month = 3;
if (dom > 31) {
month++;
dom -= 31;
}
return HistoricDate.of(era, yearOfDisplay, month, dom);
}
},
/**
* <p>The new year starts on 25th of March (yyyy-03-25), also called Lady Day or Calculus Florentinus. </p>
*
* <p>This rule was also called the annunciation style and applied in parts of Europe during midage
* (was officially in effect in England until 1752). A great disadvantage of this reckoning system is
* Easter happening not at all, once or twice per year. </p>
*/
/*[deutsch]
* <p>Das neue Jahr beginnt im März zu Mariä Verkündigung (yyyy-03-25), auch
* Calculus Florentinus genannt. </p>
*
* <p>Diese Regel wurde in Teilen Europas im Mittelalter angewandt (war in England bis 1752 in Kraft).
* Ein großer Nachteil dieser Zählweise ist, da&azlig; Ostern gar nicht, einmal oder zweimal
* im Jahr vorkommen kann. </p>
*/
MARIA_ANUNCIATA() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
return HistoricDate.of(era, yearOfDisplay, 3, 25);
}
},
/**
* <p>The new year starts on 25th of March (yyyy-03-25), but one year earlier than the calculus florentinus. </p>
*
* <p>This rule was used in Pisa/Italy based on the statement that the date of incarnation of Jesus must
* happen before the birth. </p>
*
* @see #MARIA_ANUNCIATA
*/
/*[deutsch]
* <p>Das neue Jahr beginnt im März zu Mariä Verkündigung (yyyy-03-25), aber ein Jahr
* früher als nach dem Calculus Florentinus. </p>
*
* <p>Diese Regel wurde in Pisa/Italien angewandt und fußt darauf, daß das Datum der
* Fleischwerdung Jesu vor seiner Geburt liegen muß. </p>
*
* @see #MARIA_ANUNCIATA
*/
CALCULUS_PISANUS() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
return MARIA_ANUNCIATA.newYear(era, yearOfDisplay + 1);
}
@Override
int displayedYear(
NewYearStrategy strategy,
HistoricDate date
) {
int yearOfEra = date.getYearOfEra() - 1;
HistoricDate newYear = this.newYear(date.getEra(), yearOfEra);
int yearOfDisplay = yearOfEra;
if (date.compareTo(newYear) < 0) {
yearOfDisplay--;
}
return yearOfDisplay;
}
@Override
int standardYear(
boolean earlier,
NewYearStrategy strategy,
HistoricEra era,
int yearOfDisplay,
int month,
int dom
) {
return MARIA_ANUNCIATA.standardYear(earlier, strategy, era, yearOfDisplay + 1, month, dom);
}
},
/**
* <p>The new year starts on 6th of January. </p>
*
* <p>This rule was used in some East European countries during early midage. </p>
*
* @since 3.16/4.13
*/
/*[deutsch]
* <p>Das neue Jahr fängt am sechsten Tag des Januar an (Dreikönigstag). </p>
*
* <p>Diese Regel war vor allem in Osteuropa im frühen Mittelalter gebrächlich. </p>
*
* @since 3.16/4.13
*/
EPIPHANY() {
@Override
HistoricDate newYear(HistoricEra era, int yearOfDisplay) {
return HistoricDate.of(era, yearOfDisplay, 1, 6);
}
};
private static final int COUNCIL_OF_TOURS = 567;
//~ Methoden ----------------------------------------------------------
/**
* <p>Creates a new-year-strategy based on this rule which is valid until given year of era. </p>
*
* <p>Time4J will always use first of January as New Year for all times before the
* <a href="http://www.newadvent.org/cathen/03724b.htm">Council of Tours</a> in AD 567 where
* a first try of the church is documented to move away from the Roman tradition how to celebrate
* New Year. So this method is only relevant for times after the Council of Tours. </p>
*
* @param annoDomini end year of validity range related to era AD (exclusive)
* @return NewYearStrategy
* @throws IllegalArgumentException if given year is not after AD 567 (when the Council of Tours took place)
* @since 3.14/4.11
*/
/*[deutsch]
* <p>Erzeugt eine Neujahrsstrategie, die auf dieser Regel fußt und bis zum angegebenen Jahr
* der Ära gültig ist. </p>
*
* <p>Time4J wird immer den ersten Januar als Neujahr für alle Zeiten vor dem
* <a href="http://www.newadvent.org/cathen/03724b.htm">Konzil von Tours</a> AD 567
* anwenden, zu dem ein erster Versuch der Kirche dokumentiert ist, von der römischen
* Tradition abzuweichen, wann Neujahr gefeiert wird. Somit ist diese Methode nur für
* Zeiten nach dem Konzil von Tours von Bedeutung. </p>
*
* @param annoDomini end year of validity range related to era AD (exclusive)
* @return NewYearStrategy
* @throws IllegalArgumentException if given year is not after AD 567 (when the Council of Tours took place)
* @since 3.14/4.11
*/
public NewYearStrategy until(int annoDomini) {
if (annoDomini <= COUNCIL_OF_TOURS) {
throw new IllegalArgumentException(
"Defining New-Year-strategy is not supported before Council of Tours in AD 567.");
}
NewYearStrategy nys = new NewYearStrategy(this, annoDomini);
if (this != BEGIN_OF_JANUARY){
NewYearStrategy first = new NewYearStrategy(BEGIN_OF_JANUARY, COUNCIL_OF_TOURS);
nys = first.and(nys);
}
return nys;
}
// calculates the new year
abstract HistoricDate newYear(
HistoricEra era,
int yearOfDisplay
);
// also suitable for EASTER_STYLE and GOOD_FRIDAY
int displayedYear(
NewYearStrategy strategy,
HistoricDate date
) {
int yearOfEra = date.getYearOfEra();
HistoricDate newYear = this.newYear(date.getEra(), yearOfEra);
int yearOfDisplay = yearOfEra;
if (date.compareTo(newYear) < 0) {
yearOfDisplay--;
}
return yearOfDisplay;
}
// apply year-definition
int standardYear(
boolean earlier,
NewYearStrategy strategy,
HistoricEra era,
int yearOfDisplay,
int month,
int dom
) {
if (month >= 5 && (month <= 8)) { // may-aug
return yearOfDisplay;
}
HistoricDate currentNY = this.newYear(era, yearOfDisplay);
HistoricDate nextNY = strategy.newYear(era, yearOfDisplay + 1);
HistoricDate testCurrent = HistoricDate.of(era, yearOfDisplay, month, dom);
int minYear;
int maxYear;
if (month <= 4) { // jan-apr
HistoricDate testNext = HistoricDate.of(era, yearOfDisplay + 1, month, dom);
minYear = ((testCurrent.compareTo(currentNY) >= 0) ? yearOfDisplay : yearOfDisplay + 1);
maxYear = ((testNext.compareTo(nextNY) >= 0) ? yearOfDisplay : yearOfDisplay + 1);
} else { // sep-dec
HistoricDate testPrevious = HistoricDate.of(era, yearOfDisplay - 1, month, dom);
minYear = ((testPrevious.compareTo(currentNY) >= 0) ? yearOfDisplay - 1 : yearOfDisplay);
maxYear = ((testCurrent.compareTo(nextNY) >= 0) ? yearOfDisplay - 1 : yearOfDisplay);
}
if (minYear > maxYear) {
throw new IllegalArgumentException(
"Invalid date due to changing new year rule (year too short to cover month and day-of-month): "
+ testCurrent);
} else {
return (earlier ? minYear : maxYear);
}
}
}