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

net.time4j.PlainDate Maven / Gradle / Ivy

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

import net.time4j.base.GregorianDate;
import net.time4j.base.GregorianMath;
import net.time4j.base.MathUtils;
import net.time4j.base.ResourceLoader;
import net.time4j.base.TimeSource;
import net.time4j.base.UnixTime;
import net.time4j.engine.AttributeQuery;
import net.time4j.engine.CalendarDate;
import net.time4j.engine.CalendarEra;
import net.time4j.engine.CalendarSystem;
import net.time4j.engine.Calendrical;
import net.time4j.engine.ChronoDisplay;
import net.time4j.engine.ChronoElement;
import net.time4j.engine.ChronoEntity;
import net.time4j.engine.ChronoException;
import net.time4j.engine.ChronoExtension;
import net.time4j.engine.ChronoMerger;
import net.time4j.engine.Chronology;
import net.time4j.engine.DisplayStyle;
import net.time4j.engine.ElementRule;
import net.time4j.engine.EpochDays;
import net.time4j.engine.FormattableElement;
import net.time4j.engine.Normalizer;
import net.time4j.engine.StartOfDay;
import net.time4j.engine.TimeAxis;
import net.time4j.engine.TimeSpan;
import net.time4j.engine.ValidationElement;
import net.time4j.format.Attributes;
import net.time4j.format.CalendarText;
import net.time4j.format.CalendarType;
import net.time4j.format.ChronoPattern;
import net.time4j.format.DisplayMode;
import net.time4j.format.Leniency;
import net.time4j.format.LocalizedPatternSupport;
import net.time4j.format.TemporalFormatter;
import net.time4j.tz.TZID;
import net.time4j.tz.Timezone;
import net.time4j.tz.TransitionHistory;
import net.time4j.tz.ZonalOffset;
import net.time4j.tz.ZonalTransition;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;


/**
 * 

Represents a plain calendar date in conformance to ISO-8601-standard.

* *

The value range also contains negative years down to {@code -999999999}. * These years cannot be interpreted in a historical way, as in general no past * year, too. Instead such related dates can and must rather be interpreted * as a different way of counting days - like epoch days. The rules of * gregorian calendar are applied in a proleptic way that is backwards into * the past even before the earliest introduction of gregorian calendar in * Rome (a non-historical mathematical abstraction).

* *

Following elements which are declared as constants are registered by * this class:

* *
    *
  • {@link #COMPONENT}
  • *
  • {@link #DAY_OF_MONTH}
  • *
  • {@link #DAY_OF_QUARTER}
  • *
  • {@link #DAY_OF_WEEK}
  • *
  • {@link #DAY_OF_YEAR}
  • *
  • {@link #MONTH_AS_NUMBER}
  • *
  • {@link #MONTH_OF_YEAR}
  • *
  • {@link #QUARTER_OF_YEAR}
  • *
  • {@link #WEEKDAY_IN_MONTH}
  • *
  • {@link #YEAR}
  • *
  • {@link #YEAR_OF_WEEKDATE}
  • *
* *

Furthermore, all elements of class {@link Weekmodel} and class * {@link EpochDays} are supported.

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

Repräsentiert ein reines Kalenderdatum im ISO-8601-Standard.

* *

Der Wertebereich fasst auch negative Jahre bis zu {@code -999999999}. * Diese Jahre sind nicht historisch zu interpretieren, sondern vielmehr * als eine andere Zählweise ähnlich zur Zählung von * Epochentagen. Die Schaltjahrregeln des gregorianischen Kalenders werden * proleptisch, also rückwirkend auch in die ferne Vergangenheit * angewandt (eine ahistorische mathematische Abstraktion).

* *

Registriert sind folgende als Konstanten deklarierte Elemente:

* *
    *
  • {@link #COMPONENT}
  • *
  • {@link #DAY_OF_MONTH}
  • *
  • {@link #DAY_OF_QUARTER}
  • *
  • {@link #DAY_OF_WEEK}
  • *
  • {@link #DAY_OF_YEAR}
  • *
  • {@link #MONTH_AS_NUMBER}
  • *
  • {@link #MONTH_OF_YEAR}
  • *
  • {@link #QUARTER_OF_YEAR}
  • *
  • {@link #WEEKDAY_IN_MONTH}
  • *
  • {@link #YEAR}
  • *
  • {@link #YEAR_OF_WEEKDATE}
  • *
* *

Darüberhinaus sind alle Elemente der Klasse {@link Weekmodel} * und der Klasse {@link EpochDays} nutzbar.

* * @author Meno Hochschild */ @CalendarType("iso8601") public final class PlainDate extends Calendrical implements GregorianDate, Normalizer, LocalizedPatternSupport { //~ Statische Felder/Initialisierungen -------------------------------- /** Frühestmögliches Datum [-999999999-01-01]. */ static final PlainDate MIN = new PlainDate(GregorianMath.MIN_YEAR, 1, 1); /** Spätestmögliches Datum [+999999999-12-31]. */ static final PlainDate MAX = new PlainDate(GregorianMath.MAX_YEAR, 12, 31); /** Entspricht dem Jahr {@code -999999999}. */ static final Integer MIN_YEAR = Integer.valueOf(GregorianMath.MIN_YEAR); /** Entspricht dem Jahr {@code +999999999}. */ static final Integer MAX_YEAR = Integer.valueOf(GregorianMath.MAX_YEAR); private static final Integer STD_YEAR_LEN = Integer.valueOf(365); private static final Integer LEAP_YEAR_LEN = Integer.valueOf(366); private static final int[] DAY_OF_YEAR_PER_MONTH = new int[12]; private static final int[] DAY_OF_LEAP_YEAR_PER_MONTH = new int[12]; static { DAY_OF_YEAR_PER_MONTH[0] = 31; DAY_OF_YEAR_PER_MONTH[1] = 59; DAY_OF_YEAR_PER_MONTH[2] = 90; DAY_OF_YEAR_PER_MONTH[3] = 120; DAY_OF_YEAR_PER_MONTH[4] = 151; DAY_OF_YEAR_PER_MONTH[5] = 181; DAY_OF_YEAR_PER_MONTH[6] = 212; DAY_OF_YEAR_PER_MONTH[7] = 243; DAY_OF_YEAR_PER_MONTH[8] = 273; DAY_OF_YEAR_PER_MONTH[9] = 304; DAY_OF_YEAR_PER_MONTH[10] = 334; DAY_OF_YEAR_PER_MONTH[11] = 365; DAY_OF_LEAP_YEAR_PER_MONTH[0] = 31; DAY_OF_LEAP_YEAR_PER_MONTH[1] = 60; DAY_OF_LEAP_YEAR_PER_MONTH[2] = 91; DAY_OF_LEAP_YEAR_PER_MONTH[3] = 121; DAY_OF_LEAP_YEAR_PER_MONTH[4] = 152; DAY_OF_LEAP_YEAR_PER_MONTH[5] = 182; DAY_OF_LEAP_YEAR_PER_MONTH[6] = 213; DAY_OF_LEAP_YEAR_PER_MONTH[7] = 244; DAY_OF_LEAP_YEAR_PER_MONTH[8] = 274; DAY_OF_LEAP_YEAR_PER_MONTH[9] = 305; DAY_OF_LEAP_YEAR_PER_MONTH[10] = 335; DAY_OF_LEAP_YEAR_PER_MONTH[11] = 366; } /** Datumskomponente. */ static final ChronoElement CALENDAR_DATE = DateElement.INSTANCE; /** *

Element with the calendar date in the value range * {@code [-999999999-01-01]} until {@code [+999999999-12-31]}.

* *

Example of usage:

* *
     *  PlainTimestamp tsp = PlainTimestamp.of(2014, 8, 21, 14, 30);
     *  tsp = tsp.with(PlainDate.COMPONENT, PlainDate.of(2015, 1, 1));
     *  System.out.println(tsp); // output: 2015-01-01T14:30
     * 
* * @since 1.2 */ /*[deutsch] *

Element mit dem Datum im Wertebereich {@code [-999999999-01-01]} * bis {@code [+999999999-12-31]}.

* *

Beispiel:

* *
     *  PlainTimestamp tsp = PlainTimestamp.of(2014, 8, 21, 14, 30);
     *  tsp = tsp.with(PlainDate.COMPONENT, PlainDate.of(2015, 1, 1));
     *  System.out.println(tsp); // output: 2015-01-01T14:30
     * 
* * @since 1.2 */ public static final CalendarDateElement COMPONENT = DateElement.INSTANCE; /** *

Element with the proleptic iso-year without any era reference and * the value range {@code -999999999} until {@code 999999999}.

* *

Examples:

* *
     *  import static net.time4j.PlainDate.YEAR;
     *
     *  PlainDate date = PlainDate.of(2012, 2, 29);
     *  System.out.println(date.get(YEAR)); // Ausgabe: 2012
     *
     *  date = date.with(YEAR, 2014);
     *  System.out.println(date); // Ausgabe: 2014-02-28
     *
     *  date = date.with(YEAR.incremented()); // nächstes Jahr
     *  System.out.println(date); // Ausgabe: 2015-02-28
     *
     *  date = date.with(YEAR.atCeiling()); // letzter Tag des Jahres
     *  System.out.println(date); // Ausgabe: 2015-12-31
     *
     *  date = date.with(YEAR.atFloor()); // erster Tag des Jahres
     *  System.out.println(date); // Ausgabe: 2015-01-01
     * 
* *

The term "proleptic" means that the rules of the gregorian * calendar and the associated way of year counting is applied backward * even before the introduction of gregorian calendar. The year {@code 0} * is permitted - and negative years, too. For historical year numbers, * this mathematical extrapolation is not recommended and usually * wrong.

*/ /*[deutsch] *

Element mit dem proleptischen ISO-Jahr ohne Ära-Bezug mit dem * Wertebereich {@code -999999999} bis {@code 999999999}.

* *

Beispiele:

* *
     *  import static net.time4j.PlainDate.YEAR;
     *
     *  PlainDate date = PlainDate.of(2012, 2, 29);
     *  System.out.println(date.get(YEAR)); // Ausgabe: 2012
     *
     *  date = date.with(YEAR, 2014);
     *  System.out.println(date); // Ausgabe: 2014-02-28
     *
     *  date = date.with(YEAR.incremented()); // nächstes Jahr
     *  System.out.println(date); // Ausgabe: 2015-02-28
     *
     *  date = date.with(YEAR.atCeiling()); // letzter Tag des Jahres
     *  System.out.println(date); // Ausgabe: 2015-12-31
     *
     *  date = date.with(YEAR.atFloor()); // erster Tag des Jahres
     *  System.out.println(date); // Ausgabe: 2015-01-01
     * 
* *

Der Begriff "proleptisch" bedeutet, daß die Regeln * des gregorianischen Kalenders und damit verbunden die Jahreszählung * auch rückwirkend vor der Einführung des Kalenders angewandt * werden. Insbesondere ist auch das Jahr {@code 0} zugelassen - nebst * negativen Jahreszahlen. Für historische Jahreszahlen ist diese * mathematische Extrapolation nicht geeignet.

*/ @FormattableElement(format = "u") public static final AdjustableElement YEAR = IntegerDateElement.create( "YEAR", IntegerDateElement.YEAR, GregorianMath.MIN_YEAR, GregorianMath.MAX_YEAR, 'u'); /** *

Defines an element for the week-based year in an * ISO-8601-weekdate.

* *

The week-based year is usually the same as the calendar year. * However, at the begin or end of a calendar year the situation is * different because the first week of the weekdate can start after * New Year and the last week of the weekdate can end before the last * day of the calendar year. Examples:

* *
  • Sunday, [1995-01-01] => [1994-W52-7]
  • *
  • Tuesday, [1996-31-12] => [1997-W01-2]
* *

Note: This element has a special basic unit which can be used such * that the day of the week will be conserved instead of the day of month * after adding one week-based year:

* *
     *  PlainDate date = PlainDate.of(2014, JANUARY, 2); // Thursday
     *  IsoDateUnit unit = CalendarUnit.weekBasedYears();
     *  System.out.println(date.plus(1, unit)); // output: 2015-01-01
     * 
* * @see CalendarUnit#weekBasedYears() * @see Weekmodel#ISO */ /*[deutsch] *

Definiert ein Element für das wochenbasierte Jahr in einem * ISO-Wochendatum.

* *

Das wochenbasierte Jahr stimmt in der Regel mit dem * Kalenderjahr überein. Ausnahmen sind der Beginn und * das Ende des Kalenderjahres, weil die erste Woche des Jahres * erst nach Neujahr anfangen und die letzte Woche des Jahres * bereits vor Sylvester enden kann. Beispiele:

* *
  • Sonntag, [1995-01-01] => [1994-W52-7]
  • *
  • Dienstag, [1996-31-12] => [1997-W01-2]
* *

Notiz: Dieses Element hat eine spezielle Basiseinheit, die so * verwendet werden kann, daß nicht der Tag des Monats nach * einer Jahresaddition erhalten bleibt, sondern der Tag der Woche:

* *
     *  PlainDate date = PlainDate.of(2014, JANUARY, 2); // Donnerstag
     *  IsoDateUnit unit = CalendarUnit.weekBasedYears();
     *  System.out.println(date.plus(1, unit)); // Ausgabe: 2015-01-01
     * 
* * @see CalendarUnit#weekBasedYears() * @see Weekmodel#ISO */ @FormattableElement(format = "Y") public static final AdjustableElement YEAR_OF_WEEKDATE = YOWElement.INSTANCE; /** *

Element with the quarter of year in the value range * {@code Q1-Q4}.

*/ /*[deutsch] *

Element mit dem Quartal des Jahres (Wertebereich {@code Q1-Q4}).

*/ @FormattableElement(format = "Q", standalone="q") public static final NavigableElement QUARTER_OF_YEAR = new EnumElement( "QUARTER_OF_YEAR", Quarter.class, Quarter.Q1, Quarter.Q4, EnumElement.QUARTER_OF_YEAR, 'Q'); /** *

Element with the calendar month as enum in the value range * {@code JANUARY-DECEMBER}).

* *

Examples:

* *
     *  import static net.time4j.PlainDate.MONTH_OF_YEAR;
     *  import static net.time4j.Month.*;
     *
     *  PlainDate date = PlainDate.of(2012, 2, 29);
     *  System.out.println(date.get(MONTH_OF_YEAR)); // output: February
     *
     *  date = date.with(MONTH_OF_YEAR, APRIL);
     *  System.out.println(date); // output: 2012-04-29
     *
     *  date = date.with(MONTH_OF_YEAR.incremented()); // next month
     *  System.out.println(date); // output: 2012-05-29
     *
     *  date = date.with(MONTH_OF_YEAR.maximized()); // last month of year
     *  System.out.println(date); // output: 2012-12-29
     *
     *  date = date.with(MONTH_OF_YEAR.atCeiling()); // last day of month
     *  System.out.println(date); // output: 2012-12-31
     *
     *  date = date.with(MONTH_OF_YEAR.atFloor()); // first day of month
     *  System.out.println(date); // output: 2012-12-01
     *
     *  date = date.with(MONTH_OF_YEAR.setToNext(JULY)); // move to July
     *  System.out.println(date); // output: 2013-07-01
     * 
*/ /*[deutsch] *

Element mit dem Monat als Enum * (Wertebereich {@code JANUARY-DECEMBER}).

* *

Beispiele:

* *
     *  import static net.time4j.PlainDate.MONTH_OF_YEAR;
     *  import static net.time4j.Month.*;
     *
     *  PlainDate date = PlainDate.of(2012, 2, 29);
     *  System.out.println(date.get(MONTH_OF_YEAR)); // Ausgabe: February
     *
     *  date = date.with(MONTH_OF_YEAR, APRIL);
     *  System.out.println(date); // Ausgabe: 2012-04-29
     *
     *  date = date.with(MONTH_OF_YEAR.incremented()); // nächster Monat
     *  System.out.println(date); // Ausgabe: 2012-05-29
     *
     *  date = date.with(MONTH_OF_YEAR.maximized()); // letzter Monat im Jahr
     *  System.out.println(date); // Ausgabe: 2012-12-29
     *
     *  date = date.with(MONTH_OF_YEAR.atCeiling()); // letzter Monatstag
     *  System.out.println(date); // Ausgabe: 2012-12-31
     *
     *  date = date.with(MONTH_OF_YEAR.atFloor()); // erster Tag im Monat
     *  System.out.println(date); // Ausgabe: 2012-12-01
     *
     *  date = date.with(MONTH_OF_YEAR.setToNext(JULY)); // zum Juli vorangehen
     *  System.out.println(date); // Ausgabe: 2013-07-01
     * 
*/ @FormattableElement(format = "M", standalone="L") public static final NavigableElement MONTH_OF_YEAR = new EnumElement( "MONTH_OF_YEAR", Month.class, Month.JANUARY, Month.DECEMBER, EnumElement.MONTH, 'M'); /** *

Element with the calendar month in numerical form and the value range * {@code 1-12}.

* *

Normally the enum-variant is recommended due to clarity and * type-safety. The enum-form can also be formatted as text. However, * if users want to set any month number in a lenient way with possible * carry-over then they can do it like in following example:

* *
     *  import static net.time4j.PlainDate.MONTH_AS_NUMBER;
     *
     *  PlainDate date = PlainDate.of(2012, 2, 29);
     *  date = date.with(MONTH_AS_NUMBER.setLenient(13);
     *  System.out.println(date); // Ausgabe: 2013-01-29
     * 
* * @see #MONTH_OF_YEAR */ /*[deutsch] *

Element mit dem Monat in Nummernform (Wertebereich {@code 1-12}).

* *

Im allgemeinen empfiehlt sich wegen der Typsicherheit und sprachlichen * Klarheit die enum-Form, die zudem auch als Text formatierbar ist. Wenn * Anwender jedoch irgendeine Monatsnummer nachsichtig mit möglichem * Überlauf setzen wollen, können sie es wie folgt tun:

* *
     *  import static net.time4j.PlainDate.MONTH_AS_NUMBER;
     *
     *  PlainDate date = PlainDate.of(2012, 2, 29);
     *  date = date.with(MONTH_AS_NUMBER.setLenient(13);
     *  System.out.println(date); // Ausgabe: 2013-01-29
     * 
* * @see #MONTH_OF_YEAR */ public static final ProportionalElement MONTH_AS_NUMBER = IntegerDateElement.create( "MONTH_AS_NUMBER", IntegerDateElement.MONTH, 1, 12, '\u0000'); /** *

Element with the day of month in the value range * {@code 1-28/29/30/31}.

*/ /*[deutsch] *

Element mit dem Tag des Monats * (Wertebereich {@code 1-28/29/30/31}).

*/ @FormattableElement(format = "d") public static final ProportionalElement DAY_OF_MONTH = IntegerDateElement.create( "DAY_OF_MONTH", IntegerDateElement.DAY_OF_MONTH, 1, 31, 'd'); /** *

Element with the day of week in the value range * {@code MONDAY-SUNDAY}.

* *

A localized form is available by {@link Weekmodel#localDayOfWeek()}. * In US sunday is considered as first day of week, different from * definition used here (monday as start of calendar week according to * ISO-8601). Therefore, if users need localized weekday-numbers, users * can use the expression {@code Weekmodel.of(Locale.US).localDayOfWeek()} * in a country like US.

*/ /*[deutsch] *

Element mit dem Tag der Woche * (Wertebereich {@code MONDAY-SUNDAY}).

* *

Eine lokalisierte Form ist mittels {@link Weekmodel#localDayOfWeek()} * verfügbar. In den USA z.B. ist der Sonntag der erste Tag der * Woche, anders als hier definiert. Daher sollte in den USA vielmehr * der Ausdruck {@code Weekmodel.of(Locale.US).localDayOfWeek()} * verwendet werden.

*/ @FormattableElement(format = "E") public static final NavigableElement DAY_OF_WEEK = new EnumElement( "DAY_OF_WEEK", Weekday.class, Weekday.MONDAY, Weekday.SUNDAY, EnumElement.DAY_OF_WEEK, 'E'); /** *

Element with the day of year in the value range * {@code 1-365/366}).

*/ /*[deutsch] *

Element mit dem Tag des Jahres (Wertebereich {@code 1-365/366}).

*/ @FormattableElement(format = "D") public static final ProportionalElement DAY_OF_YEAR = IntegerDateElement.create( "DAY_OF_YEAR", IntegerDateElement.DAY_OF_YEAR, 1, 365, 'D'); /** *

Element with the day within a quarter of year in the value range * {@code 1-90/91/92}.

*/ /*[deutsch] *

Element mit dem Tag des Quartals * (Wertebereich {@code 1-90/91/92}).

*/ public static final ProportionalElement DAY_OF_QUARTER = IntegerDateElement.create( "DAY_OF_QUARTER", IntegerDateElement.DAY_OF_QUARTER, 1, 92, '\u0000'); /** *

Element with the ordinal day-of-week within given calendar month * in the value range {@code 1-5}.

* *

Example:

* *
     *  import static net.time4j.PlainDate.WEEKDAY_IN_MONTH;
     *  import static net.time4j.Weekday.*;
     *
     *  PlainDate date = PlainDate.of(2013, 3, 1); // first of march 2013
     *  System.out.println(date.with(WEEKDAY_IN_MONTH.setToThird(WEDNESDAY)));
     *  // output: 2013-03-20 (third Wednesday in march)
     * 
*/ /*[deutsch] *

Element mit dem x-ten Wochentag im Monat * (Wertebereich {@code 1-5}).

* *

Beispiel:

* *
     *  import static net.time4j.PlainDate.WEEKDAY_IN_MONTH;
     *  import static net.time4j.Weekday.*;
     *
     *  PlainDate date = PlainDate.of(2013, 3, 1); // 1. März 2013
     *  System.out.println(date.with(WEEKDAY_IN_MONTH.setToThird(WEDNESDAY)));
     *  // Ausgabe: 2013-03-20 (Mittwoch)
     * 
*/ @FormattableElement(format = "F") public static final OrdinalWeekdayElement WEEKDAY_IN_MONTH = WeekdayInMonthElement.INSTANCE; // Dient der Serialisierungsunterstützung. private static final long serialVersionUID = -6698431452072325688L; private static final Map ELEMENTS; private static final CalendarSystem TRANSFORMER; private static final TimeAxis ENGINE; static { Map constants = new HashMap(); fill(constants, CALENDAR_DATE); fill(constants, YEAR); fill(constants, YEAR_OF_WEEKDATE); fill(constants, QUARTER_OF_YEAR); fill(constants, MONTH_OF_YEAR); fill(constants, MONTH_AS_NUMBER); fill(constants, DAY_OF_MONTH); fill(constants, DAY_OF_WEEK); fill(constants, DAY_OF_YEAR); fill(constants, DAY_OF_QUARTER); fill(constants, WEEKDAY_IN_MONTH); ELEMENTS = Collections.unmodifiableMap(constants); TRANSFORMER = new Transformer(); TimeAxis.Builder builder = TimeAxis.Builder.setUp( IsoDateUnit.class, PlainDate.class, new Merger(), TRANSFORMER) .appendElement( CALENDAR_DATE, new DateElementRule(), CalendarUnit.DAYS) .appendElement( YEAR, new IntegerElementRule(YEAR), CalendarUnit.YEARS) .appendElement( YEAR_OF_WEEKDATE, YOWElement.elementRule(PlainDate.class), YOWElement.YOWUnit.WEEK_BASED_YEARS) .appendElement( QUARTER_OF_YEAR, EnumElementRule.of(QUARTER_OF_YEAR), CalendarUnit.QUARTERS) .appendElement( MONTH_OF_YEAR, EnumElementRule.of(MONTH_OF_YEAR), CalendarUnit.MONTHS) .appendElement( MONTH_AS_NUMBER, new IntegerElementRule(MONTH_AS_NUMBER), CalendarUnit.MONTHS) .appendElement( DAY_OF_MONTH, new IntegerElementRule(DAY_OF_MONTH), CalendarUnit.DAYS) .appendElement( DAY_OF_WEEK, EnumElementRule.of(DAY_OF_WEEK), CalendarUnit.DAYS) .appendElement( DAY_OF_YEAR, new IntegerElementRule(DAY_OF_YEAR), CalendarUnit.DAYS) .appendElement( DAY_OF_QUARTER, new IntegerElementRule(DAY_OF_QUARTER), CalendarUnit.DAYS) .appendElement( WEEKDAY_IN_MONTH, new WIMRule(), CalendarUnit.WEEKS); registerUnits(builder); registerExtensions(builder); builder.appendExtension(new WeekExtension()); ENGINE = builder.build(); } //~ Instanzvariablen -------------------------------------------------- private transient final int year; private transient final byte month; private transient final byte dayOfMonth; //~ Konstruktoren ----------------------------------------------------- private PlainDate( int year, int month, int dayOfMonth ) { super(); this.year = year; this.month = (byte) month; this.dayOfMonth = (byte) dayOfMonth; } //~ Methoden ---------------------------------------------------------- /** *

Creates a new calendar date conforming to ISO-8601.

* * @param year proleptic iso year [(-999,999,999)-999,999,999] * @param month gregorian month in range (1-12) * @param dayOfMonth day of month in range (1-31) * @return new or cached calendar date instance * @throws IllegalArgumentException if any argument is out of range * @see #of(int, Month, int) * @see #of(int, int) * @see #of(int, int, Weekday) */ /*[deutsch] *

Erzeugt ein neues ISO-konformes Kalenderdatum.

* * @param year proleptic iso year [(-999,999,999)-999,999,999] * @param month gregorian month in range (1-12) * @param dayOfMonth day of month in range (1-31) * @return new or cached calendar date instance * @throws IllegalArgumentException if any argument is out of range * @see #of(int, Month, int) * @see #of(int, int) * @see #of(int, int, Weekday) */ public static PlainDate of( int year, int month, int dayOfMonth ) { return of(year, month, dayOfMonth, true); } /** *

Creates a new calendar date conforming to ISO-8601.

* * @param year proleptic iso year [(-999,999,999)-999,999,999] * @param month gregorian month in range (January-December) * @param dayOfMonth day of month in range (1-31) * @return new or cached calendar date instance * @throws IllegalArgumentException if any argument is out of range * @see #of(int, int, int) */ /*[deutsch] *

Erzeugt ein neues ISO-konformes Kalenderdatum.

* * @param year proleptic iso year [(-999,999,999)-999,999,999] * @param month gregorian month in range (January-December) * @param dayOfMonth day of month in range (1-31) * @return new or cached calendar date instance * @throws IllegalArgumentException if any argument is out of range * @see #of(int, int, int) */ public static PlainDate of( int year, Month month, int dayOfMonth ) { return PlainDate.of(year, month.getValue(), dayOfMonth, true); } /** *

Creates a new ordinal date conforming to ISO-8601.

* * @param year proleptic iso year [(-999,999,999)-999,999,999] * @param dayOfYear day of year in the range (1-366) * @return new or cached ordinal date instance * @throws IllegalArgumentException if any argument is out of range */ /*[deutsch] *

Erzeugt ein neues ISO-konformes Ordinaldatum.

* * @param year proleptic iso year [(-999,999,999)-999,999,999] * @param dayOfYear day of year in the range (1-366) * @return new or cached ordinal date instance * @throws IllegalArgumentException if any argument is out of range */ public static PlainDate of( int year, int dayOfYear ) { if (dayOfYear < 1) { throw new IllegalArgumentException( "Day of year out of range: " + dayOfYear); } else if (dayOfYear <= 31) { return PlainDate.of(year, 1, dayOfYear); } int[] table = ( GregorianMath.isLeapYear(year) ? DAY_OF_LEAP_YEAR_PER_MONTH : DAY_OF_YEAR_PER_MONTH); for (int i = (dayOfYear > table[6] ? 7 : 1); i < 12; i++) { if (dayOfYear <= table[i]) { int dom = dayOfYear - table[i - 1]; return PlainDate.of(year, i + 1, dom, false); } } throw new IllegalArgumentException( "Day of year out of range: " + dayOfYear); } /** *

Creates a new week-date conforming to ISO-8601.

* * @param yearOfWeekdate week-based-year according to ISO-definition * @param weekOfYear week of year in the range (1-52/53) * @param dayOfWeek day of week in the range (MONDAY-SUNDAY) * @return new or cached week date instance * @throws IllegalArgumentException if any argument is out of range */ /*[deutsch] *

Erzeugt ein neues ISO-konformes Wochendatum.

* * @param yearOfWeekdate week-based-year according to ISO-definition * @param weekOfYear week of year in the range (1-52/53) * @param dayOfWeek day of week in the range (MONDAY-SUNDAY) * @return new or cached week date instance * @throws IllegalArgumentException if any argument is out of range */ public static PlainDate of( int yearOfWeekdate, int weekOfYear, Weekday dayOfWeek ) { return of(yearOfWeekdate, weekOfYear, dayOfWeek, true); } /** *

Creates a new date based on count of days since given epoch.

* * @param amount count of days * @param epoch reference date scale * @return found calendar date based on given epoch days * @throws IllegalArgumentException if first argument is out of range */ /*[deutsch] *

Erzeugt ein Datum zur gegebenen Anzahl von Tagen seit einer * Epoche.

* * @param amount count of days * @param epoch reference date scale * @return found calendar date based on given epoch days * @throws IllegalArgumentException if first argument is out of range */ public static PlainDate of( long amount, EpochDays epoch ) { return TRANSFORMER.transform(EpochDays.UTC.transform(amount, epoch)); } /** *

Common conversion method for proleptic gregorian dates.

* * @param date ISO-date * @return PlainDate */ /*[deutsch] *

Allgemeine Konversionsmethode für ein proleptisches * gregorianisches Datum.

* * @param date ISO-date * @return PlainDate */ public static PlainDate from(GregorianDate date) { if (date instanceof PlainDate) { return (PlainDate) date; } else { return PlainDate.of( date.getYear(), date.getMonth(), date.getDayOfMonth()); } } /** *

Creates a new local timestamp with this date at midnight at the * begin of associated day.

* * @return local timestamp as composition of this date and midnight * @see #at(PlainTime) */ /*[deutsch] *

Erzeugt einen lokalen Zeitstempel mit diesem Datum zu Mitternacht * am Beginn des Tages.

* * @return local timestamp as composition of this date and midnight * @see #at(PlainTime) */ public PlainTimestamp atStartOfDay() { return this.at(PlainTime.MIN); } /** *

Creates a new local timestamp with this date at earliest valid time * at the begin of associated day in given timezone.

* * @param tzid timezone id * @return local timestamp as composition of this date and earliest * valid time * @throws IllegalArgumentException if given timezone cannot be loaded * @throws UnsupportedOperationException if the underlying timezone * repository does not expose any public transition history * @since 2.2 * @see #atStartOfDay() */ /*[deutsch] *

Erzeugt einen lokalen Zeitstempel mit diesem Datum zur frühesten * gültigen Uhrzeit in der angegebenen Zeitzone.

* * @param tzid timezone id * @return local timestamp as composition of this date and earliest * valid time * @throws IllegalArgumentException if given timezone cannot be loaded * @throws UnsupportedOperationException if the underlying timezone * repository does not expose any public transition history * @since 2.2 * @see #atStartOfDay() */ public PlainTimestamp atStartOfDay(TZID tzid) { return this.atStartOfDay(Timezone.of(tzid).getHistory()); } /** *

Creates a new local timestamp with this date at earliest valid time * at the begin of associated day in given timezone.

* * @param tzid timezone id * @return local timestamp as composition of this date and earliest * valid time * @throws IllegalArgumentException if given timezone cannot be loaded * @throws UnsupportedOperationException if the underlying timezone * repository does not expose any public transition history * @since 2.2 * @see #atStartOfDay() */ /*[deutsch] *

Erzeugt einen lokalen Zeitstempel mit diesem Datum zur frühesten * gültigen Uhrzeit in der angegebenen Zeitzone.

* * @param tzid timezone id * @return local timestamp as composition of this date and earliest * valid time * @throws IllegalArgumentException if given timezone cannot be loaded * @throws UnsupportedOperationException if the underlying timezone * repository does not expose any public transition history * @since 2.2 * @see #atStartOfDay() */ public PlainTimestamp atStartOfDay(String tzid) { return this.atStartOfDay(Timezone.of(tzid).getHistory()); } /** *

Creates a new local timestamp with this date and given wall time.

* *

If the time {@link PlainTime#midnightAtEndOfDay() T24:00} is used * then the resulting timestamp will automatically be normalized such * that the timestamp will contain the following day instead.

* * @param time wall time * @return local timestamp as composition of this date and given time */ /*[deutsch] *

Erzeugt einen lokalen Zeitstempel mit diesem Datum und der * angegebenen Uhrzeit.

* *

Wenn {@link PlainTime#midnightAtEndOfDay() T24:00} angegeben wird, * dann wird der Zeitstempel automatisch so normalisiert, daß er auf * den nächsten Tag verweist.

* * @param time wall time * @return local timestamp as composition of this date and given time */ public PlainTimestamp at(PlainTime time) { return PlainTimestamp.of(this, time); } /** *

Is equivalent to {@code at(PlainTime.of(hour, minute))}.

* * @param hour hour of day in range (0-24) * @param minute minute of hour in range (0-59) * @return local timestamp as composition of this date and given time * @throws IllegalArgumentException if any argument is out of range */ /*[deutsch] *

Entspricht {@code at(PlainTime.of(hour, minute))}.

* * @param hour hour of day in range (0-24) * @param minute minute of hour in range (0-59) * @return local timestamp as composition of this date and given time * @throws IllegalArgumentException if any argument is out of range */ public PlainTimestamp atTime( int hour, int minute ) { return this.at(PlainTime.of(hour, minute)); } /** *

Is equivalent to {@code at(PlainTime.of(hour, minute, second))}.

* * @param hour hour of day in range (0-24) * @param minute minute of hour in range (0-59) * @param second second of hour in range (0-59) * @return local timestamp as composition of this date and given time * @throws IllegalArgumentException if any argument is out of range */ /*[deutsch] *

Entspricht {@code at(PlainTime.of(hour, minute, second))}.

* * @param hour hour of day in range (0-24) * @param minute minute of hour in range (0-59) * @param second second of hour in range (0-59) * @return local timestamp as composition of this date and given time * @throws IllegalArgumentException if any argument is out of range */ public PlainTimestamp atTime( int hour, int minute, int second ) { return this.at(PlainTime.of(hour, minute, second)); } @Override public int getYear() { return this.year; } @Override public int getMonth() { return this.month; } @Override public int getDayOfMonth() { return this.dayOfMonth; } /** *

Determines the day of week.

* * @return Weekday * @since 3.13/4.10 */ /*[deutsch] *

Ermittelt den Wochentag.

* * @return Weekday * @since 3.13/4.10 */ public Weekday getDayOfWeek() { return Weekday.valueOf( GregorianMath.getDayOfWeek( this.year, this.month, this.dayOfMonth ) ); } /** *

Yields the day of year.

* * @return int * @since 3.13/4.10 */ /*[deutsch] *

Liefert den Tag des Jahres.

* * @return int * @since 3.13/4.10 */ public int getDayOfYear() { switch (this.month) { case 1: return this.dayOfMonth; case 2: return 31 + this.dayOfMonth; default: return ( DAY_OF_YEAR_PER_MONTH[this.month - 2] + this.dayOfMonth + (GregorianMath.isLeapYear(this.year) ? 1 : 0)); } } /** *

Calculates the length of associated month in days.

* * @return int in value range {@code 28-31} */ /*[deutsch] *

Ermittelt die Länge des assoziierten Monats in Tagen.

* * @return int im Bereich {@code 28-31} */ public int lengthOfMonth() { return GregorianMath.getLengthOfMonth(this.year, this.month); } /** *

Calculates the length of associated year in days.

* * @return {@code 365} or {@code 366} if associated year is a leap year */ /*[deutsch] *

Ermittelt die Länge des assoziierten Jahres in Tagen.

* * @return {@code 365} or {@code 366} wenn das Jahr ein Schaltjahr ist */ public int lengthOfYear() { return this.isLeapYear() ? 366 : 365; } /** *

Is the year of this date a leap year?

* * @return boolean */ /*[deutsch] *

Liegt dieses Datum in einem Schaltjahr?

* * @return boolean */ public boolean isLeapYear() { return GregorianMath.isLeapYear(this.year); } /** *

Does this date fall on a week-end in given country?

* * @param country country setting with two-letter ISO-3166-code * @return {@code true} if in given country this date is on weekend * else {@code false} * @see Weekmodel#weekend() */ /*[deutsch] *

Liegt das Datum im angegebenen Land an einem Wochenende?

* * @param country country setting with two-letter ISO-3166-code * @return {@code true} if in given country this date is on weekend * else {@code false} * @see Weekmodel#weekend() */ public boolean isWeekend(Locale country) { return this.matches(Weekmodel.of(country).weekend()); } /** *

Creates a new formatter which uses the given pattern in the * default locale for formatting and parsing plain dates.

* * @param

generic pattern type * @param formatPattern format definition as pattern * @param patternType pattern dialect * @return format object for formatting {@code PlainDate}-objects * using system locale * @throws IllegalArgumentException if resolving of pattern fails * @since 3.0 */ /*[deutsch] *

Erzeugt ein neues Format-Objekt mit Hilfe des angegebenen Musters * in der Standard-Sprach- und Ländereinstellung.

* * @param

generic pattern type * @param formatPattern format definition as pattern * @param patternType pattern dialect * @return format object for formatting {@code PlainDate}-objects * using system locale * @throws IllegalArgumentException if resolving of pattern fails * @since 3.0 */ public static

> TemporalFormatter localFormatter( String formatPattern, P patternType ) { return FormatSupport.createFormatter(PlainDate.class, formatPattern, patternType, Locale.getDefault()); } /** *

Creates a new formatter which uses the given display mode in the * default locale for formatting and parsing plain dates.

* * @param mode formatting style * @return format object for formatting {@code PlainDate}-objects * using system locale * @throws IllegalStateException if format pattern cannot be retrieved * @since 3.0 */ /*[deutsch] *

Erzeugt ein neues Format-Objekt mit Hilfe des angegebenen Stils * in der Standard-Sprach- und Ländereinstellung.

* * @param mode formatting style * @return format object for formatting {@code PlainDate}-objects * using system locale * @throws IllegalStateException if format pattern cannot be retrieved * @since 3.0 */ public static TemporalFormatter localFormatter(DisplayMode mode) { return formatter(mode, Locale.getDefault()); } /** *

Creates a new formatter which uses the given pattern and locale * for formatting and parsing plain dates.

* * @param

generic pattern type * @param formatPattern format definition as pattern * @param patternType pattern dialect * @param locale locale setting * @return format object for formatting {@code PlainDate}-objects * using given locale * @throws IllegalArgumentException if resolving of pattern fails * @since 3.0 * @see #localFormatter(String,ChronoPattern) */ /*[deutsch] *

Erzeugt ein neues Format-Objekt mit Hilfe des angegebenen Musters * in der angegebenen Sprach- und Ländereinstellung.

* * @param

generic pattern type * @param formatPattern format definition as pattern * @param patternType pattern dialect * @param locale locale setting * @return format object for formatting {@code PlainDate}-objects * using given locale * @throws IllegalArgumentException if resolving of pattern fails * @since 3.0 * @see #localFormatter(String,ChronoPattern) */ public static

> TemporalFormatter formatter( String formatPattern, P patternType, Locale locale ) { return FormatSupport.createFormatter(PlainDate.class, formatPattern, patternType, locale); } /** *

Creates a new formatter which uses the given display mode and locale * for formatting and parsing plain dates.

* * @param mode formatting style * @param locale locale setting * @return format object for formatting {@code PlainDate}-objects * using given locale * @throws IllegalStateException if format pattern cannot be retrieved * @since 3.0 * @see #localFormatter(DisplayMode) */ /*[deutsch] *

Erzeugt ein neues Format-Objekt mit Hilfe des angegebenen Stils * und in der angegebenen Sprach- und Ländereinstellung.

* * @param mode formatting style * @param locale locale setting * @return format object for formatting {@code PlainDate}-objects * using given locale * @throws IllegalStateException if format pattern cannot be retrieved * @since 3.0 * @see #localFormatter(DisplayMode) */ public static TemporalFormatter formatter( DisplayMode mode, Locale locale ) { String formatPattern = CalendarText.patternForDate(mode, locale); return FormatSupport.createFormatter(PlainDate.class, formatPattern, locale); } /** *

Creates a canonical representation of the form * "YYYY-MM-DD" as documented in ISO-8601.

* * @return canonical ISO-8601-formatted string */ /*[deutsch] *

Erzeugt eine kanonische Darstellung im Format * "yyyy-MM-dd".

* * @return canonical ISO-8601-formatted string */ @Override public String toString() { StringBuilder sb = new StringBuilder(32); formatYear(sb, this.year); format2Digits(sb, this.month); format2Digits(sb, this.dayOfMonth); return sb.toString(); } /** *

Normalized given timespan using years, months and days.

* *

This normalizer can also convert from days to months. Example:

* *
     *  Duration<CalendarUnit> dur = Duration.of(30, CalendarUnit.DAYS);
     *  Duration<CalendarUnit> result =
     *      PlainDate.of(2012, 2, 28).normalize(dur);
     *  System.out.println(result); // output: P1M1D (leap year!)
     * 
* * @param timespan to be normalized * @return normalized duration in years, months and days */ /*[deutsch] *

Normalisiert die angegebene Zeitspanne, indem Jahre, Monate und Tage * verwendet werden.

* *

Dieser Normalisierer kann auch von Tagen zu Monaten konvertieren. * Beispiel:

* *
     *  Duration<CalendarUnit> dur = Duration.of(30, CalendarUnit.DAYS);
     *  Duration<CalendarUnit> result =
     *      PlainDate.of(2012, 2, 28).normalize(dur);
     *  System.out.println(result); // Ausgabe: P1M1D (Schaltjahr!)
     * 
* * @param timespan to be normalized * @return normalized duration in years, months and days */ @Override public Duration normalize( TimeSpan timespan) { return this.until(this.plus(timespan), Duration.inYearsMonthsDays()); } /** *

Provides a static access to the associated chronology on base of * epoch days which contains the chronological rules.

* * @return chronological system as time axis (never {@code null}) */ /*[deutsch] *

Liefert die zugehörige Zeitachse, die alle notwendigen * chronologischen Regeln enthält.

* * @return chronological system as time axis (never {@code null}) */ public static TimeAxis axis() { return ENGINE; } @Override protected TimeAxis getChronology() { return ENGINE; } @Override protected PlainDate getContext() { return this; } @Override protected int compareByTime(CalendarDate date) { if (date instanceof PlainDate) { // Optimierung PlainDate d1 = this; PlainDate d2 = (PlainDate) date; int delta = d1.year - d2.year; if (delta == 0) { delta = d1.month - d2.month; if (delta == 0) { delta = d1.dayOfMonth - d2.dayOfMonth; } } return delta; } return super.compareByTime(date); // basiert auf Epochentagen } /** *

Liefert die Tage seit der UTC-Epoche.

* * @return count of days since UTC (1972-01-01) */ long getDaysSinceUTC() { return TRANSFORMER.transform(this); } /** *

Wandelt die Tage seit der UTC-Epoche in ein Datum um.

* * @param utcDays count of days since UTC (1972-01-01) * @return found calendar date */ PlainDate withDaysSinceUTC(long utcDays) { return TRANSFORMER.transform(utcDays); } /** *

Erzeugt ein neues Datum passend zur angegebenen absoluten Zeit.

* * @param ut unix time * @param offset shift of local time relative to UTC * @return new calendar date */ static PlainDate from( UnixTime ut, ZonalOffset offset ) { long localSeconds = ut.getPosixTime() + offset.getIntegralAmount(); int localNanos = ut.getNanosecond() + offset.getFractionalAmount(); if (localNanos < 0) { localSeconds--; } else if (localNanos >= 1000000000) { localSeconds++; } long mjd = EpochDays.MODIFIED_JULIAN_DATE.transform( MathUtils.floorDivide(localSeconds, 86400), EpochDays.UNIX); long packedDate = GregorianMath.toPackedDate(mjd); return PlainDate.of( GregorianMath.readYear(packedDate), GregorianMath.readMonth(packedDate), GregorianMath.readDayOfMonth(packedDate) ); } /** *

Dient der Serialisierungsunterstützung.

* * @param elementName name of element * @return found element or {@code null} */ // optional static Object lookupElement(String elementName) { return ELEMENTS.get(elementName); } /** *

Additionsmethode.

* * @param unit calendar unit * @param context calendar date * @param amount amount to be added * @param policy overflow policy * @return result of addition */ static PlainDate doAdd( CalendarUnit unit, PlainDate context, long amount, int policy ) { switch (unit) { case MILLENNIA: return doAdd( CalendarUnit.MONTHS, context, MathUtils.safeMultiply(amount, 12 * 1000), policy); case CENTURIES: return doAdd( CalendarUnit.MONTHS, context, MathUtils.safeMultiply(amount, 12 * 100), policy); case DECADES: return doAdd( CalendarUnit.MONTHS, context, MathUtils.safeMultiply(amount, 12 * 10), policy); case YEARS: return doAdd( CalendarUnit.MONTHS, context, MathUtils.safeMultiply(amount, 12), policy); case QUARTERS: return doAdd( CalendarUnit.MONTHS, context, MathUtils.safeMultiply(amount, 3), policy); case MONTHS: long months = MathUtils.safeAdd(context.getEpochMonths(), amount); return PlainDate.fromEpochMonths( context, months, context.dayOfMonth, policy); case WEEKS: return doAdd( CalendarUnit.DAYS, context, MathUtils.safeMultiply(amount, 7), policy); case DAYS: PlainDate date = addDays(context, amount); if (policy == OverflowUnit.POLICY_END_OF_MONTH) { return PlainDate.of( date.year, date.month, GregorianMath.getLengthOfMonth(date.year, date.month) ); } else { return date; } default: throw new UnsupportedOperationException(unit.name()); } } /** *

Liefert die Epochenmonate relativ zu 1970.

* * @return epoch months relative to 1970 */ long getEpochMonths() { return ((this.year - 1970) * 12L + this.month - 1); } /** *

Liefert die ISO-Kalenderwoche des Jahres.

* *

Als erste Kalenderwoche gilt die Woche, die mindestens vier Tage hat * und mit dem Montag anfängt. Die Woche davor ist dann die letzte * Woche des vorherigen Jahres und kann noch in das aktuelle Jahr * hineinreichen.

* * @return week of year in the range (1-53) * @see Weekmodel#ISO */ int getWeekOfYear() { return this.get(Weekmodel.ISO.weekOfYear()).intValue(); } private PlainTimestamp atStartOfDay(TransitionHistory history) { if (history == null) { throw new UnsupportedOperationException( "Timezone repository does not expose its transition history: " + Timezone.getProviderInfo()); } ZonalTransition conflict = history.getConflictTransition(this, PlainTime.MIN); if ( (conflict != null) && conflict.isGap() ) { long localSeconds = conflict.getPosixTime() + conflict.getTotalOffset(); PlainDate date = PlainDate.of( MathUtils.floorDivide(localSeconds, 86400), EpochDays.UNIX); int secondsOfDay = MathUtils.floorModulo(localSeconds, 86400); int second = secondsOfDay % 60; int minutesOfDay = secondsOfDay / 60; int minute = minutesOfDay % 60; int hour = minutesOfDay / 60; PlainTime time = PlainTime.of(hour, minute, second); return PlainTimestamp.of(date, time); } return this.at(PlainTime.MIN); } private int getDayOfQuarter() { switch (this.month) { case 1: case 4: case 7: case 10: return this.dayOfMonth; case 2: case 8: case 11: return 31 + this.dayOfMonth; case 3: return ( (GregorianMath.isLeapYear(this.year) ? 60 : 59) + this.dayOfMonth); case 5: return 30 + this.dayOfMonth; case 6: case 12: return 61 + this.dayOfMonth; case 9: return 62 + this.dayOfMonth; default: throw new AssertionError("Unknown month: " + this.month); } } private PlainDate withYear(int year) { if (this.year == year) { return this; } int mlen = GregorianMath.getLengthOfMonth(year, this.month); return PlainDate.of( year, this.month, Math.min(mlen, this.dayOfMonth) ); } private PlainDate withMonth(int month) { if (this.month == month) { return this; } int mlen = GregorianMath.getLengthOfMonth(this.year, month); return PlainDate.of( this.year, month, Math.min(mlen, this.dayOfMonth) ); } private PlainDate withDayOfMonth(int dayOfMonth) { if (this.dayOfMonth == dayOfMonth) { return this; } return PlainDate.of(this.year, this.month, dayOfMonth); } private PlainDate withDayOfWeek(Weekday dayOfWeek) { Weekday old = this.getDayOfWeek(); if (old == dayOfWeek) { return this; } return TRANSFORMER.transform( MathUtils.safeAdd( this.getDaysSinceUTC(), dayOfWeek.getValue() - old.getValue() ) ); } private PlainDate withDayOfYear(int dayOfYear) { if (this.getDayOfYear() == dayOfYear) { return this; } return PlainDate.of(this.year, dayOfYear); } private static PlainDate fromEpochMonths( PlainDate context, long emonths, int dayOfMonth, int policy ) { if ( (policy == OverflowUnit.POLICY_KEEPING_LAST_DATE) && (context.dayOfMonth == context.lengthOfMonth()) ) { policy = OverflowUnit.POLICY_END_OF_MONTH; } int year = MathUtils.safeCast( MathUtils.safeAdd( MathUtils.floorDivide(emonths, 12), 1970 ) ); int month = MathUtils.floorModulo(emonths, 12) + 1; int max = GregorianMath.getLengthOfMonth(year, month); int dom = dayOfMonth; if (dayOfMonth > max) { switch (policy) { case OverflowUnit.POLICY_PREVIOUS_VALID_DATE: case OverflowUnit.POLICY_END_OF_MONTH: case OverflowUnit.POLICY_KEEPING_LAST_DATE: dom = max; break; case OverflowUnit.POLICY_NEXT_VALID_DATE: return PlainDate.fromEpochMonths( context, MathUtils.safeAdd(emonths, 1), 1, policy); case OverflowUnit.POLICY_CARRY_OVER: return PlainDate.fromEpochMonths( context, MathUtils.safeAdd(emonths, 1), dayOfMonth - max, policy); case OverflowUnit.POLICY_UNLESS_INVALID: StringBuilder sb = new StringBuilder(32); sb.append("Day of month out of range: "); formatYear(sb, year); format2Digits(sb, month); format2Digits(sb, dayOfMonth); throw new ChronoException(sb.toString()); default: throw new UnsupportedOperationException( "Overflow policy not implemented: " + policy); } } else if( (dayOfMonth < max) && (policy == OverflowUnit.POLICY_END_OF_MONTH) ) { dom = max; } return PlainDate.of(year, month, dom); } private static PlainDate addDays( PlainDate date, long amount ) { long dom = MathUtils.safeAdd(date.dayOfMonth, amount); if ((dom >= 1) && (dom <= 28)) { return PlainDate.of(date.year, date.month, (int) dom); } long doy = MathUtils.safeAdd(date.getDayOfYear(), amount); if ((doy >= 1) && (doy <= 365)) { return PlainDate.of(date.year, (int) doy); } long utcDays = MathUtils.safeAdd(date.getDaysSinceUTC(), amount); return TRANSFORMER.transform(utcDays); } private static void fill( Map map, ChronoElement element ) { map.put(element.name(), element); } private static void formatYear( StringBuilder sb, int year ) { int value = year; if (value < 0) { sb.append('-'); value = MathUtils.safeNegate(year); } if (value >= 10000) { if (year > 0) { sb.append('+'); } } else if (value < 1000) { sb.append('0'); if (value < 100) { sb.append('0'); if (value < 10) { sb.append('0'); } } } sb.append(value); } private static void format2Digits( StringBuilder sb, int value ) { sb.append('-'); if (value < 10) { sb.append('0'); } sb.append(value); } private static void registerExtensions(TimeAxis.Builder builder) { for (ChronoExtension extension : ResourceLoader.getInstance().services(ChronoExtension.class)) { if (extension.accept(PlainDate.class)) { builder.appendExtension(extension); } } } private static void registerUnits(TimeAxis.Builder builder) { Set monthly = EnumSet.range(CalendarUnit.MILLENNIA, CalendarUnit.MONTHS); Set daily = EnumSet.range(CalendarUnit.WEEKS, CalendarUnit.DAYS); for (CalendarUnit unit : CalendarUnit.values()) { builder.appendUnit( unit, new CalendarUnit.Rule(unit), unit.getLength(), (unit.compareTo(CalendarUnit.WEEKS) < 0) ? monthly : daily ); } } private static PlainDate of( int yearOfWeekdate, int weekOfYear, Weekday dayOfWeek, boolean validating ) { if ( (weekOfYear < 1) || (weekOfYear > 53) ) { if (validating) { throw new IllegalArgumentException(woyFailed(weekOfYear)); } else { return null; } } if ( validating && ((yearOfWeekdate < MIN_YEAR) || (yearOfWeekdate > MAX_YEAR)) ) { throw new IllegalArgumentException(yowFailed(yearOfWeekdate)); } Weekday wdNewYear = Weekday.valueOf(GregorianMath.getDayOfWeek(yearOfWeekdate, 1, 1)); int dow = wdNewYear.getValue(); int doy = ( ((dow <= 4) ? 2 - dow : 9 - dow) + (weekOfYear - 1) * 7 + dayOfWeek.getValue() - 1 ); int y = yearOfWeekdate; if (doy <= 0) { y--; doy += (GregorianMath.isLeapYear(y) ? 366 : 365); } else { int yearlen = (GregorianMath.isLeapYear(y) ? 366 : 365); if (doy > yearlen) { doy -= yearlen; y++; } } PlainDate result = PlainDate.of(y, doy); if ( (weekOfYear == 53) && (result.getWeekOfYear() != 53) ) { if (validating) { throw new IllegalArgumentException(woyFailed(weekOfYear)); } else { return null; } } return result; } private static String yowFailed(int yearOfWeekdate) { return "YEAR_OF_WEEKDATE (ISO) out of range: " + yearOfWeekdate; } private static String woyFailed(int weekOfYear) { return "WEEK_OF_YEAR (ISO) out of range: " + weekOfYear; } private static PlainDate of( int year, int month, int dayOfMonth, boolean validating ) { if (validating) { GregorianMath.checkDate(year, month, dayOfMonth); } // TODO: konfigurierbaren Cache einbauen? return new PlainDate(year, month, dayOfMonth); } /** * @serialData Uses * a dedicated serialization form as proxy. The format * is bit-compressed. The first byte contains in the four * most significant bits the type-ID {@code 1}. The following * bits 4-7 contain the month. The second byte contains * at the bits 1-2 a year mark: 1 = year in the range * 1850-2100, 2 = four-digit-year, 3 = year number with more * than four digits. The five least significant bits of second * byte contain the day of month. Then the year will be written * dependent on the year mark. Is the mark 1 then the year * will be written as byte, if the mark is 2 then the year * will be written as short else as int with four bytes. * * Schematic algorithm: * *
     *  int range;
     *
     *  if (year >= 1850 && year <= 2100) {
     *      range = 1;
     *  } else if (Math.abs(year) < 10000) {
     *      range = 2;
     *  } else {
     *      range = 3;
     *  }
     *
     *  int header = 1;
     *  header <<= 4;
     *  header |= month;
     *  out.writeByte(header);
     *
     *  int header2 = range;
     *  header2 <<= 5;
     *  header2 |= dayOfMonth;
     *  out.writeByte(header2);
     *
     *  if (range == 1) {
     *      out.writeByte(year - 1850 - 128);
     *  } else if (range == 2) {
     *      out.writeShort(year);
     *  } else {
     *      out.writeInt(year);
     *  }
     * 
* * @return replacement object in serialization graph */ private Object writeReplace() { return new SPX(this, SPX.DATE_TYPE); } /** * @serialData Blocks because a serialization proxy is required. * @param in object input stream * @throws InvalidObjectException (always) */ private void readObject(ObjectInputStream in) throws IOException { throw new InvalidObjectException("Serialization proxy required."); } //~ Innere Klassen ---------------------------------------------------- private static class Merger implements ChronoMerger { //~ Methoden ------------------------------------------------------ @Override public String getFormatPattern( DisplayStyle style, Locale locale ) { DisplayMode mode = DisplayMode.ofStyle(style.getStyleValue()); return CalendarText.patternForDate(mode, locale); } @Override public PlainDate createFrom( TimeSource clock, AttributeQuery attributes ) { Timezone zone; if (attributes.contains(Attributes.TIMEZONE_ID)) { zone = Timezone.of(attributes.get(Attributes.TIMEZONE_ID)); } else if (attributes.get(Attributes.LENIENCY, Leniency.SMART).isLax()) { zone = Timezone.ofSystem(); } else { return null; } final UnixTime ut = clock.currentTime(); return PlainDate.from(ut, zone.getOffset(ut)); } @Override public PlainDate createFrom( ChronoEntity entity, AttributeQuery attributes, boolean preparsing ) { if (entity instanceof UnixTime) { return PlainTimestamp.axis().createFrom(entity, attributes, preparsing).getCalendarDate(); } else if (entity.contains(CALENDAR_DATE)) { return entity.get(CALENDAR_DATE); } if (entity.contains(EpochDays.MODIFIED_JULIAN_DATE)) { Long mjd = entity.get(EpochDays.MODIFIED_JULIAN_DATE); long utcDays = EpochDays.UTC.transform( mjd.longValue(), EpochDays.MODIFIED_JULIAN_DATE); return TRANSFORMER.transform(utcDays); } Leniency leniency = attributes.get(Attributes.LENIENCY, Leniency.SMART); Integer year = null; if (entity.contains(YEAR)) { year = entity.get(YEAR); } if (year != null) { int y = year.intValue(); Integer month = null; if (entity.contains(MONTH_OF_YEAR)) { month = Integer.valueOf(entity.get(MONTH_OF_YEAR).getValue()); } else if (entity.contains(MONTH_AS_NUMBER)) { month = entity.get(MONTH_AS_NUMBER); } if ( (month != null) && entity.contains(DAY_OF_MONTH) ) { Integer dom = entity.get(DAY_OF_MONTH); int m = month.intValue(); int d = dom.intValue(); if (leniency.isLax()) { PlainDate date = PlainDate.of(y, 1, 1); date = date.with(MONTH_AS_NUMBER.setLenient(month)); return date.with(DAY_OF_MONTH.setLenient(dom)); } else if ( // Standardszenario validateYear(entity, y) && validateMonth(entity, m) && validateDayOfMonth(entity, y, m, d) ) { return PlainDate.of(y, m, d, false); } else { return null; } } if (entity.contains(DAY_OF_YEAR)) { Integer doy = entity.get(DAY_OF_YEAR); int d = doy.intValue(); if (leniency.isLax()) { PlainDate date = PlainDate.of(y, 1); return date.with(DAY_OF_YEAR.setLenient(doy)); } else if ( // Ordinaldatum validateYear(entity, y) && validateDayOfYear(entity, y, d) ) { return PlainDate.of(y, d); } else { return null; } } if ( entity.contains(QUARTER_OF_YEAR) && entity.contains(DAY_OF_QUARTER) ) { Quarter q = entity.get(QUARTER_OF_YEAR); boolean leapYear = GregorianMath.isLeapYear(y); int doq = entity.get(DAY_OF_QUARTER).intValue(); int doy = doq + (leapYear ? 91 : 90); if (q == Quarter.Q1) { doy = doq; } else if (q == Quarter.Q3) { doy += 91; } else if (q == Quarter.Q4) { doy += (91 + 92); } if (leniency.isLax()) { PlainDate date = PlainDate.of(y, 1); return date.with(DAY_OF_YEAR.setLenient(doy)); } else if ( // Quartalsdatum validateYear(entity, y) && validateDayOfQuarter(entity, leapYear, q, doq) ) { return PlainDate.of(y, doy); } else { return null; } } } if ( entity.contains(YEAR_OF_WEEKDATE) && entity.contains(Weekmodel.ISO.weekOfYear()) ) { int yearOfWeekdate = entity.get(YEAR_OF_WEEKDATE).intValue(); int weekOfYear = entity.get(Weekmodel.ISO.weekOfYear()).intValue(); Weekday dayOfWeek; if (entity.contains(DAY_OF_WEEK)) { dayOfWeek = entity.get(DAY_OF_WEEK); } else if (entity.contains(Weekmodel.ISO.localDayOfWeek())) { dayOfWeek = entity.get(Weekmodel.ISO.localDayOfWeek()); } else { return null; } // Wochendatum validieren und erzeugen if ( (yearOfWeekdate < GregorianMath.MIN_YEAR) || (yearOfWeekdate > GregorianMath.MAX_YEAR) ) { flagValidationError( entity, yowFailed(yearOfWeekdate)); return null; } PlainDate date = PlainDate.of( yearOfWeekdate, weekOfYear, dayOfWeek, false); if (date == null) { flagValidationError( entity, woyFailed(weekOfYear)); } return date; } return null; } @Override public ChronoDisplay preformat( PlainDate context, AttributeQuery attributes ) { return context; } @Override public Chronology preparser() { return null; } @Override public StartOfDay getDefaultStartOfDay() { return StartOfDay.MIDNIGHT; } private static boolean validateYear( ChronoEntity entity, int year ) { if ( (year < GregorianMath.MIN_YEAR) || (year > GregorianMath.MAX_YEAR) ) { flagValidationError( entity, "YEAR out of range: " + year); return false; } return true; } private static boolean validateMonth( ChronoEntity entity, int month ) { if ( (month < 1) || (month > 12) ) { flagValidationError( entity, "MONTH_OF_YEAR out of range: " + month); return false; } return true; } private static boolean validateDayOfMonth( ChronoEntity entity, int year, int month, int dom ) { if ( (dom < 1) || (dom > GregorianMath.getLengthOfMonth(year, month)) ) { flagValidationError( entity, "DAY_OF_MONTH out of range: " + dom); return false; } return true; } private static boolean validateDayOfYear( ChronoEntity entity, int year, int doy ) { if ( (doy < 1) || (doy > (GregorianMath.isLeapYear(year) ? 366 : 365)) ) { flagValidationError( entity, "DAY_OF_YEAR out of range: " + doy); return false; } return true; } private static boolean validateDayOfQuarter( ChronoEntity entity, boolean leapYear, Quarter q, int doq ) { int max; switch (q) { case Q1: max = (leapYear ? 91 : 90); break; case Q2: max = 91; break; default: max = 92; } if ( (doq < 1) || (doq > max) ) { flagValidationError( entity, "DAY_OF_QUARTER out of range: " + doq); return false; } return true; } private static void flagValidationError( ChronoEntity entity, String message ) { if (entity.isValid(ValidationElement.ERROR_MESSAGE, message)) { entity.with(ValidationElement.ERROR_MESSAGE, message); } } } private static class Transformer implements CalendarSystem { //~ Statische Felder/Initialisierungen ---------------------------- private static final long MIN_LONG = -365243219892L; // transform(PlainDate.MIN) private static final long MAX_LONG = 365241779741L; // transform(PlainDate.MAX) //~ Methoden ------------------------------------------------------ @Override public PlainDate transform(long utcDays) { if (utcDays == MIN_LONG) { return PlainDate.MIN; } else if (utcDays == MAX_LONG) { return PlainDate.MAX; } long mjd = EpochDays.MODIFIED_JULIAN_DATE.transform( utcDays, EpochDays.UTC); long packedDate = GregorianMath.toPackedDate(mjd); return PlainDate.of( GregorianMath.readYear(packedDate), GregorianMath.readMonth(packedDate), GregorianMath.readDayOfMonth(packedDate) ); } @Override public long transform(PlainDate date) { return EpochDays.UTC.transform( GregorianMath.toMJD(date), EpochDays.MODIFIED_JULIAN_DATE ); } @Override public long getMinimumSinceUTC() { return MIN_LONG; } @Override public long getMaximumSinceUTC() { return MAX_LONG; } @Override public List getEras() { return Collections.emptyList(); } } private static class DateElementRule implements ElementRule { //~ Methoden ------------------------------------------------------ @Override public PlainDate getValue(PlainDate context) { return context; } @Override public PlainDate withValue( PlainDate context, PlainDate value, boolean lenient ) { if (value == null) { throw new NullPointerException("Missing date value."); } return value; } @Override public boolean isValid( PlainDate context, PlainDate value ) { return (value != null); } @Override public PlainDate getMinimum(PlainDate context) { return PlainDate.MIN; } @Override public PlainDate getMaximum(PlainDate context) { return PlainDate.MAX; } @Override public ChronoElement getChildAtFloor(PlainDate context) { return null; } @Override public ChronoElement getChildAtCeiling(PlainDate context) { return null; } } private static class IntegerElementRule implements ElementRule { //~ Instanzvariablen ---------------------------------------------- private final ChronoElement ref; private final String name; private final int index; //~ Konstruktoren ------------------------------------------------- IntegerElementRule(ChronoElement element) { this(element.name(), ((IntegerDateElement) element).getIndex(), element); } IntegerElementRule( String name, int index, ChronoElement ref ) { super(); this.ref = ref; this.name = name; this.index = index; } //~ Methoden ------------------------------------------------------ @Override public Integer getValue(PlainDate context) { switch (this.index) { case IntegerDateElement.YEAR: return Integer.valueOf(context.year); case IntegerDateElement.MONTH: return Integer.valueOf(context.month); case IntegerDateElement.DAY_OF_MONTH: return Integer.valueOf(context.dayOfMonth); case IntegerDateElement.DAY_OF_YEAR: return Integer.valueOf(context.getDayOfYear()); case IntegerDateElement.DAY_OF_QUARTER: return Integer.valueOf(context.getDayOfQuarter()); default: throw new UnsupportedOperationException(this.name); } } @Override public PlainDate withValue( PlainDate context, Integer value, boolean lenient ) { int v = value.intValue(); if (lenient) { // nur auf numerischen Elementen definiert IsoDateUnit unit = ENGINE.getBaseUnit(this.ref); int old = Number.class.cast(this.getValue(context)).intValue(); int amount = MathUtils.safeSubtract(v, old); return context.plus(amount, unit); } switch (this.index) { case IntegerDateElement.YEAR: return context.withYear(v); case IntegerDateElement.MONTH: return context.withMonth(v); case IntegerDateElement.DAY_OF_MONTH: return context.withDayOfMonth(v); case IntegerDateElement.DAY_OF_YEAR: return context.withDayOfYear(v); case IntegerDateElement.DAY_OF_QUARTER: if ((v >= 1) && (v <= getMaximumOfQuarterDay(context))) { return context.plus( (v - context.getDayOfQuarter()), CalendarUnit.DAYS); } else { throw new IllegalArgumentException("Out of range: " + value); } default: throw new UnsupportedOperationException(this.name); } } @Override public boolean isValid( PlainDate context, Integer value ) { if (value == null) { return false; } int v = value.intValue(); switch (this.index) { case IntegerDateElement.YEAR: return ( (v >= GregorianMath.MIN_YEAR) && (v <= GregorianMath.MAX_YEAR) ); case IntegerDateElement.MONTH: return ((v >= 1) && (v <= 12)); case IntegerDateElement.DAY_OF_MONTH: int mlen = GregorianMath.getLengthOfMonth( context.year, context.month); return ((v >= 1) && (v <= mlen)); case IntegerDateElement.DAY_OF_YEAR: boolean leapyear = GregorianMath.isLeapYear(context.year); return ((v >= 1) && (v <= (leapyear ? 366 : 365))); case IntegerDateElement.DAY_OF_QUARTER: int max = getMaximumOfQuarterDay(context); return ((v >= 1) && (v <= max)); default: throw new UnsupportedOperationException(this.name); } } @Override public Integer getMinimum(PlainDate context) { switch (this.index) { case IntegerDateElement.YEAR: return MIN_YEAR; case IntegerDateElement.MONTH: case IntegerDateElement.DAY_OF_MONTH: case IntegerDateElement.DAY_OF_YEAR: case IntegerDateElement.DAY_OF_QUARTER: return Integer.valueOf(1); default: throw new UnsupportedOperationException(this.name); } } @Override public Integer getMaximum(PlainDate context) { switch (this.index) { case IntegerDateElement.YEAR: return MAX_YEAR; case IntegerDateElement.MONTH: return Integer.valueOf(12); case IntegerDateElement.DAY_OF_MONTH: return Integer.valueOf( GregorianMath.getLengthOfMonth( context.year, context.month)); case IntegerDateElement.DAY_OF_YEAR: return ( GregorianMath.isLeapYear(context.year) ? LEAP_YEAR_LEN : STD_YEAR_LEN); case IntegerDateElement.DAY_OF_QUARTER: return Integer.valueOf(getMaximumOfQuarterDay(context)); default: throw new UnsupportedOperationException(this.name); } } @Override public ChronoElement getChildAtFloor(PlainDate context) { return this.getChild(); } @Override public ChronoElement getChildAtCeiling(PlainDate context) { return this.getChild(); } private ChronoElement getChild() { switch (this.index) { case IntegerDateElement.YEAR: return MONTH_AS_NUMBER; case IntegerDateElement.MONTH: return DAY_OF_MONTH; case IntegerDateElement.DAY_OF_MONTH: case IntegerDateElement.DAY_OF_YEAR: case IntegerDateElement.DAY_OF_QUARTER: return null; default: throw new UnsupportedOperationException(this.name); } } private static int getMaximumOfQuarterDay(PlainDate context) { int q = ((context.month - 1) / 3) + 1; if (q == 1) { return (GregorianMath.isLeapYear(context.year) ? 91 : 90); } else if (q == 2) { return 91; } else { return 92; } } } private static class EnumElementRule> implements ElementRule { //~ Instanzvariablen ---------------------------------------------- private final String name; private final Class type; private final V min; private final V max; private final int index; //~ Konstruktoren ------------------------------------------------- EnumElementRule( String name, Class type, V min, V max, int index ) { super(); this.name = name; this.type = type; this.min = min; this.max = max; this.index = index; } //~ Methoden ------------------------------------------------------ static > EnumElementRule of(ChronoElement element) { return new EnumElementRule( element.name(), element.getType(), element.getDefaultMinimum(), element.getDefaultMaximum(), ((EnumElement) element).getIndex() ); } @Override public V getValue(PlainDate context) { Object ret; switch (this.index) { case EnumElement.MONTH: ret = Month.valueOf(context.month); break; case EnumElement.DAY_OF_WEEK: ret = context.getDayOfWeek(); break; case EnumElement.QUARTER_OF_YEAR: ret = Quarter.valueOf(((context.month - 1) / 3) + 1); break; default: throw new UnsupportedOperationException(this.name); } return this.type.cast(ret); } @Override public V getMinimum(PlainDate context) { return this.min; } @Override public V getMaximum(PlainDate context) { return this.max; } @Override public boolean isValid( PlainDate context, V value ) { return (value != null); } @Override public PlainDate withValue( PlainDate context, V value, boolean lenient ) { switch (this.index) { case EnumElement.MONTH: return context.withMonth(Month.class.cast(value).getValue()); case EnumElement.DAY_OF_WEEK: return context.withDayOfWeek(Weekday.class.cast(value)); case EnumElement.QUARTER_OF_YEAR: int q1 = ((context.month - 1) / 3) + 1; int q2 = Quarter.class.cast(value).getValue(); return context.plus((q2 - q1), CalendarUnit.QUARTERS); default: throw new UnsupportedOperationException(this.name); } } @Override public ChronoElement getChildAtFloor(PlainDate context) { return this.getChild(); } @Override public ChronoElement getChildAtCeiling(PlainDate context) { return this.getChild(); } private ChronoElement getChild() { switch (this.index) { case EnumElement.MONTH: return DAY_OF_MONTH; case EnumElement.DAY_OF_WEEK: return null; case EnumElement.QUARTER_OF_YEAR: return DAY_OF_QUARTER; default: throw new UnsupportedOperationException(this.name); } } } private static class WIMRule implements ElementRule { //~ Methoden ------------------------------------------------------ @Override public Integer getValue(PlainDate context) { return Integer.valueOf(((context.dayOfMonth - 1) / 7) + 1); } @Override public Integer getMinimum(PlainDate context) { return Integer.valueOf(1); } @Override public Integer getMaximum(PlainDate context) { int y = context.year; int m = context.month; int d = context.dayOfMonth; int maxday = GregorianMath.getLengthOfMonth(y, m); int n = 0; while (d + (n + 1) * 7 <= maxday) { n++; } return Integer.valueOf(((d + n * 7 - 1) / 7) + 1); } @Override public boolean isValid( PlainDate context, Integer value ) { if (value == null) { return false; } int wim = value.intValue(); int max = this.getMaximum(context).intValue(); return ((wim >= 1) && (wim <= max)); } @Override public PlainDate withValue( PlainDate context, Integer value, boolean lenient ) { int wim = value.intValue(); int max = this.getMaximum(context).intValue(); int old = ((context.dayOfMonth - 1) / 7) + 1; if ( lenient || ((wim >= 1) && (wim <= max)) ) { return context.plus(wim - old, CalendarUnit.WEEKS); } else { throw new IllegalArgumentException("Out of range: " + value); } } @Override public ChronoElement getChildAtFloor(PlainDate context) { return null; } @Override public ChronoElement getChildAtCeiling(PlainDate context) { return null; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy