All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.time4j.base.GregorianMath Maven / Gradle / Ivy

There is a newer version: 4.38
Show newest version
/*
 * -----------------------------------------------------------------------
 * Copyright © 2013-2014 Meno Hochschild, 
 * -----------------------------------------------------------------------
 * This file (GregorianMath.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 .
 * -----------------------------------------------------------------------
 */

package net.time4j.base;


/**
 * 

Contains some calendrical tools for the rules of gregorian calendar.

* * @author Meno Hochschild */ /*[deutsch] *

Enthält kalendarische Hilfsmittel für die Regeln des * gregorianischen Kalenders.

* * @author Meno Hochschild */ public final class GregorianMath { //~ 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] minus MJD-Epoche private static final int OFFSET = 719468 - 40587; //~ Konstruktoren ----------------------------------------------------- private GregorianMath() { // keine Instanzierung } //~ Methoden ---------------------------------------------------------- /** *

Queries if given year is a gregorian leap year.

* * @param year number of proleptic year * @return {@code true} if it is a leap year else {@code false} */ /*[deutsch] *

Ist das angegebene Jahr ein gregorianisches Schaltjahr?

* * @param year proleptisches Jahr * @return {@code true} if it is a leap year else {@code false} */ public static boolean isLeapYear(int year) { if ((year % 4) != 0) { return false; } return ((year % 100) != 0) || ((year % 400) == 0); } /** *

Determines the maximum length of month in days dependent on given * year (leap years!) and month.

* * @param year proleptic iso year * @param month gregorian month (1-12) * @return length of month in days * @throws IllegalArgumentException if month is out of range (1-12) */ /*[deutsch] *

Ermittelt die maximale Länge des Monats in Tagen abhängig * vom angegebenen Jahr (Schaltjahre!) und Monat.

* * @param year proleptic iso year * @param month gregorian 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); } } /** *

Queries if given values form a well defined gregorian date.

* *

This method only checks the range limits, not if the date is * historically correct.

* * @param year proleptic iso year [(-999999999) - 999999999] * @param month gregorian month (1-12) * @param dayOfMonth day of month (1-31) * @return {@code true} if valid else {@code false} * @see #checkDate(int, int, int) */ /*[deutsch] *

Handelt es sich um ein wohldefiniertes gregorianisches Datum?

* *

Hier werden nur die Bereichsgrenzen überprüft, nicht die * historische Sinnhaftigkeit.

* * @param year proleptic iso year [(-999999999) - 999999999] * @param month gregorian 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)) ); } /** *

Checks the range limits of date values according to the rules * of gregorian calendar.

* * @param year proleptic iso year [(-999999999) - 999999999] * @param month gregorian 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] *

Überprüft die Bereichsgrenzen der Datumswerte nach * den gregorianischen Kalenderregeln.

* * @param year proleptic iso year [(-999999999) - 999999999] * @param month gregorian 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)); } } /** *

Returns the day of week for given gregorian date.

* *

This method is based on ISO-8601 and assumes that Monday is the * first day of week.

* * @param year proleptic iso year * @param month gregorian month (1-12) * @param dayOfMonth day of month (1-31) * @return day of week (monday = 1, ..., sunday = 7) * @throws IllegalArgumentException if the month or the day are * out of range */ /*[deutsch] *

Liefert den Tag des Woche für das angegebene Datum.

* *

Diese Methode setzt gemäß dem ISO-8601-Standard den * Montag als ersten Tag der Woche voraus.

* * @param year proleptic iso year * @param month gregorian month (1-12) * @param dayOfMonth day of month (1-31) * @return day of week (monday = 1, ..., sunday = 7) * @throws IllegalArgumentException if the month or the day are * out of range */ public static int getDayOfWeek( int year, int month, int dayOfMonth ) { if ((dayOfMonth < 1) || (dayOfMonth > 31)) { throw new IllegalArgumentException( "Day out of range: " + dayOfMonth); } else if (dayOfMonth > getLengthOfMonth(year, month)) { throw new IllegalArgumentException( "Day exceeds month length: " + toString(year, month, dayOfMonth)); } int m = gaussianWeekTerm(month); int y = (year % 100); int c = MathUtils.floorDivide(year, 100); if (y < 0) { y += 100; } if (month <= 2) { // Januar oder Februar y--; if (y < 0) { y = 99; c--; } } // Gauß'sche Wochentagsformel int k = MathUtils.floorDivide(c, 4); int w = ((dayOfMonth + m + y + (y / 4) + k - 2 * c) % 7); if (w <= 0) { w += 7; } return w; } /** *

Returns the year from given binary compressed date.

* * @param packedDate packed date in binary format * @return proleptic iso year * @see #toPackedDate(long) */ /*[deutsch] *

Liefert das Jahr des angegebenen binär gepackten Datums.

* * @param packedDate packed date in binary format * @return proleptic iso year * @see #toPackedDate(long) */ public static int readYear(long packedDate) { return (int) ((packedDate >> 32) & 0xFFFFFFFF); } /** *

Returns the month from given binary compressed date.

* * @param packedDate packed date in binary format * @return gregorian month (1-12) * @see #toPackedDate(long) */ /*[deutsch] *

Liefert den Monat des angegebenen binär gepackten Datums.

* * @param packedDate packed date in binary format * @return gregorian month (1-12) * @see #toPackedDate(long) */ public static int readMonth(long packedDate) { return (int) ((packedDate >> 16) & 0xFF); } /** *

Returns the day of month from given binary compressed date.

* * @param packedDate packed date in binary format * @return day of month (1-31) * @see #toPackedDate(long) */ /*[deutsch] *

Liefert den Tag des Monats im angegebenen binär gepackten * Datum.

* * @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); } /** *

Calculates the gregorian Date based on given modified julian date * in binary compressed format.

* *

Applications can extract the single date components from the result * of this method by mean of {@code readYear()}, {@code readMonth()} and * {@code readDayOfMonth()}.

* * @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] *

Berechnet das gregorianische Datum auf Basis des angegebenen * modifizierten julianischen Datums.

* *

Mit Hilfe von {@code readYear()}, {@code readMonth()} und * {@code readDayOfMonth()} können aus dem Ergebnis die einzelnen * Datumselemente extrahiert werden.

* * @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); // TODO: Optimierung der Performance im Jahresbereich 1901-2099 // mit vereinfachtem Schaltjahresalgorithmus // if (days >= year1901 && days < year2100) { ... } long q400 = MathUtils.floorDivide(days, 146097); int r400 = MathUtils.floorModulo(days, 146097); if (r400 == 146096) { y = (q400 + 1) * 400; m = 2; d = 29; } 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); m = 2; d = 29; } else { int q1 = (r4 / 365); int r1 = (r4 % 365); y = (q400 * 400 + q100 * 100 + q4 * 4 + q1); m = (((r1 + 31) * 5) / 153) + 2; d = r1 - (((m + 1) * 153) / 5) + 123; if (m > 12) { y++; m -= 12; } } } if (y < GregorianMath.MIN_YEAR || y > GregorianMath.MAX_YEAR) { throw new IllegalArgumentException( "Year out of range: " + y); } long result = (y << 32); result |= (m << 16); result |= d; return result; } /** *

Calculates the modified julian date.

* * @param date gregorian date * @return days since [1858-11-17] (modified julian date) * @throws IllegalArgumentException if the argument is out of range * @see #toMJD(int, int, int) */ /*[deutsch] *

Ermittelt das modifizierte julianische Datum.

* * @param date gregorian date * @return days since [1858-11-17] (modified julian date) * @throws IllegalArgumentException if the argument is out of range * @see #toMJD(int, int, int) */ public static long toMJD(GregorianDate date) { return GregorianMath.toMJD( date.getYear(), date.getMonth(), date.getDayOfMonth()); } /** *

Calculates the modified julian date.

* * @param year proleptic iso year [(-999999999) - 999999999] * @param month gregorian 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] *

Ermittelt das modifizierte julianische Datum.

* * @param year proleptic iso year [(-999999999) - 999999999] * @param month gregorian 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 */ // TODO: Optimierung der Performance mit Jahres-Array im Bereich 1901-2099 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) - MathUtils.floorDivide(y, 100) + MathUtils.floorDivide(y, 400) + (((m + 1) * 153) / 5) - 123 + dayOfMonth ); return days - OFFSET; } /** *

Liefert eine ISO-konforme Standard-Darstellung eines Datums.

* * @param year proleptic iso year * @param month gregorian month * @param dom day of month * @return String in iso format YYYY-MM-DD */ 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(); } // entspricht dem Ausdruck [2.6 * m - 0.2] in der Gauß-Formel private static int gaussianWeekTerm(int month) { switch (month) { case 1: return 28; case 2: return 31; case 3: return 2; case 4: return 5; case 5: return 7; case 6: return 10; case 7: return 12; case 8: return 15; case 9: return 18; case 10: return 20; case 11: return 23; case 12: return 25; default: throw new IllegalArgumentException( "Month out of range: " + month); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy