/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (JulianMath.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; import net.time4j.base.MathUtils; /** * <p>Contains conversion methods for the rules of julian calendar. </p> * * @author Meno Hochschild * @since 3.0 */ /*[deutsch] * <p>Enthält Konversionsmethoden für die Regeln des julianischen Kalenders. </p> * * @author Meno Hochschild * @since 3.0 */ class JulianMath { //~ Statische Felder/Initialisierungen -------------------------------- /** * Minimum of supported year range (-999999999). */ /*[deutsch] * Minimal unterstütze Jahreszahl (-999999999). */ public static final int MIN_YEAR = -999999999; /** * Maximum of supported year range (999999999). */ /*[deutsch] * Maximal unterstütze Jahreszahl (999999999). */ public static final int MAX_YEAR = 999999999; // Tage zwischen [0000-03-01] und [1970-01-01] (julianische Datumsangaben) minus MJD-Epoche private static final int OFFSET = 719470 - 40587; //~ Konstruktoren ----------------------------------------------------- private JulianMath() { // keine Instanzierung } //~ Methoden ---------------------------------------------------------- /** * <p>Queries if given year is a julian leap year. </p> * * @param year proleptic julian year * @return {@code true} if it is a leap year else {@code false} */ /*[deutsch] * <p>Ist das angegebene Jahr ein julianisches Schaltjahr? </p> * * @param year proleptic julian year * @return {@code true} if it is a leap year else {@code false} */ public static boolean isLeapYear(int year) { return (MathUtils.floorModulo(year, 4) == 0); } /** * <p>Determines the maximum length of month in days dependent on given * year (leap years!) and month. </p> * * @param year proleptic julian year * @param month julian month (1-12) * @return length of month in days * @throws IllegalArgumentException if month is out of range (1-12) */ /*[deutsch] * <p>Ermittelt die maximale Länge des Monats in Tagen abhängig * vom angegebenen Jahr (Schaltjahre!) und Monat. </p> * * @param year proleptic julian year * @param month julian month (1-12) * @return length of month in days * @throws IllegalArgumentException if month is out of range (1-12) */ public static int getLengthOfMonth( int year, int month ) { switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; case 4: case 6: case 9: case 11: return 30; case 2: return (isLeapYear(year) ? 29 : 28); default: throw new IllegalArgumentException("Invalid month: " + month); } } /** * <p>Queries if given values form a well defined proleptic julian date. </p> * * <p>This method only checks the range limits, not if the date is * historically correct. </p> * * @param year proleptic julian year [(-999999999) - 999999999] * @param month julian month (1-12) * @param dayOfMonth day of month (1-31) * @return {@code true} if valid else {@code false} * @see #checkDate(int, int, int) */ /*[deutsch] * <p>Handelt es sich um ein wohldefiniertes proleptisch-julianisches Datum? </p> * * <p>Hier werden nur die Bereichsgrenzen überprüft, nicht die * historische Sinnhaftigkeit. </p> * * @param year proleptic julian year [(-999999999) - 999999999] * @param month julian month (1-12) * @param dayOfMonth day of month (1-31) * @return {@code true} if valid else {@code false} * @see #checkDate(int, int, int) */ public static boolean isValid( int year, int month, int dayOfMonth ) { return ( (year >= MIN_YEAR) && (year <= MAX_YEAR) && (month >= 1) && (month <= 12) && (dayOfMonth >= 1) && (dayOfMonth <= getLengthOfMonth(year, month))); } /** * <p>Checks the range limits of date values according to the rules * of julian calendar. </p> * * @param year proleptic julian year [(-999999999) - 999999999] * @param month julian month (1-12) * @param dayOfMonth day of month (1-31) * @throws IllegalArgumentException if any argument is out of range * @see #isValid(int, int, int) */ /*[deutsch] * <p>Überprüft die Bereichsgrenzen der Datumswerte nach * den julianischen Kalenderregeln. </p> * * @param year proleptic julian year [(-999999999) - 999999999] * @param month julian month (1-12) * @param dayOfMonth day of month (1-31) * @throws IllegalArgumentException if any argument is out of range * @see #isValid(int, int, int) */ public static void checkDate( int year, int month, int dayOfMonth ) { if (year < MIN_YEAR || year > MAX_YEAR) { throw new IllegalArgumentException("YEAR out of range: " + year); } else if ((month < 1) || (month > 12)) { throw new IllegalArgumentException("MONTH out of range: " + month); } else if ((dayOfMonth < 1) || (dayOfMonth > 31)) { throw new IllegalArgumentException("DAY_OF_MONTH out of range: " + dayOfMonth); } else if (dayOfMonth > getLengthOfMonth(year, month)) { throw new IllegalArgumentException( "DAY_OF_MONTH exceeds month length in given year: " + toString(year, month, dayOfMonth)); } } /** * <p>Returns the year from given binary compressed date. </p> * * @param packedDate packed date in binary format * @return proleptic julian year * @see #toPackedDate(long) */ /*[deutsch] * <p>Liefert das Jahr des angegebenen binär gepackten Datums. </p> * * @param packedDate packed date in binary format * @return proleptic julian year * @see #toPackedDate(long) */ public static int readYear(long packedDate) { return (int) (packedDate >> 32); } /** * <p>Returns the month from given binary compressed date. </p> * * @param packedDate packed date in binary format * @return julian month (1-12) * @see #toPackedDate(long) */ /*[deutsch] * <p>Liefert den Monat des angegebenen binär gepackten Datums. </p> * * @param packedDate packed date in binary format * @return julian month (1-12) * @see #toPackedDate(long) */ public static int readMonth(long packedDate) { return (int) ((packedDate >> 16) & 0xFF); } /** * <p>Returns the day of month from given binary compressed date. </p> * * @param packedDate packed date in binary format * @return day of month (1-31) * @see #toPackedDate(long) */ /*[deutsch] * <p>Liefert den Tag des Monats im angegebenen binär gepackten * Datum. </p> * * @param packedDate packed date in binary format * @return day of month (1-31) * @see #toPackedDate(long) */ public static int readDayOfMonth(long packedDate) { return (int) (packedDate & 0xFF); } /** * <p>Calculates the packed date based on given modified julian date * in binary compressed format. </p> * * <p>Applications can extract the single date components from the result * of this method by mean of {@code readYear()}, {@code readMonth()} and * {@code readDayOfMonth()}. </p> * * @param mjd days since [1858-11-17] (modified julian date) * @return packed date in binary format * @throws IllegalArgumentException if the calculated year is not in * range [(-999999999)-999999999)] * @see #readYear(long) * @see #readMonth(long) * @see #readDayOfMonth(long) */ /*[deutsch] * <p>Berechnet das gepackte Datum auf Basis des angegebenen * modifizierten julianischen Datums. </p> * * <p>Mit Hilfe von {@code readYear()}, {@code readMonth()} und * {@code readDayOfMonth()} können aus dem Ergebnis die einzelnen * Datumselemente extrahiert werden. </p> * * @param mjd days since [1858-11-17] (modified julian date) * @return packed date in binary format * @throws IllegalArgumentException if the calculated year is not in * range [(-999999999)-999999999)] * @see #readYear(long) * @see #readMonth(long) * @see #readDayOfMonth(long) */ public static long toPackedDate(long mjd) { long y; int m; int d; long days = MathUtils.safeAdd(mjd, OFFSET); long q4 = MathUtils.floorDivide(days, 1461); int r4 = MathUtils.floorModulo(days, 1461); if (r4 == 1460) { y = (q4 + 1) * 4; m = 2; d = 29; } else { int q1 = (r4 / 365); int r1 = (r4 % 365); y = q4 * 4 + q1; m = (((r1 + 31) * 5) / 153) + 2; d = r1 - (((m + 1) * 153) / 5) + 123; if (m > 12) { y++; m -= 12; } } if (y < JulianMath.MIN_YEAR || y > JulianMath.MAX_YEAR) { throw new IllegalArgumentException( "Year out of range: " + y); } long result = (y << 32); result |= (m << 16); result |= d; return result; } /** * <p>Calculates the modified julian date. </p> * * @param year proleptic julian year [(-999999999) - 999999999] * @param month julian month (1-12) * @param dayOfMonth day of month in range (1-31) * @return days since [1858-11-17] (modified julian date) * @throws IllegalArgumentException if any argument is out of range */ /*[deutsch] * <p>Ermittelt das modifizierte julianische Datum. </p> * * @param year proleptic julian year [(-999999999) - 999999999] * @param month julian month (1-12) * @param dayOfMonth day of month in range (1-31) * @return days since [1858-11-17] (modified julian date) * @throws IllegalArgumentException if any argument is out of range */ public static long toMJD( int year, int month, int dayOfMonth ) { checkDate(year, month, dayOfMonth); long y = year; int m = month; if (m < 3) { y--; m += 12; } long days = ( (y * 365) + MathUtils.floorDivide(y, 4) + (((m + 1) * 153) / 5) - 123 + dayOfMonth); return days - OFFSET; } private static String toString(int year, int month, int dom) { StringBuilder calendar = new StringBuilder(); calendar.append(year); calendar.append('-'); if (month < 10) { calendar.append('0'); } calendar.append(month); calendar.append('-'); if (dom < 10) { calendar.append('0'); } calendar.append(dom); return calendar.toString(); } }