/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (PlainTimestamp.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;
import net.time4j.base.GregorianDate;
import net.time4j.base.MathUtils;
import net.time4j.base.TimeSource;
import net.time4j.base.UnixTime;
import net.time4j.base.WallTime;
import net.time4j.engine.AttributeQuery;
import net.time4j.engine.BridgeChronology;
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.Converter;
import net.time4j.engine.DisplayStyle;
import net.time4j.engine.ElementRule;
import net.time4j.engine.EpochDays;
import net.time4j.engine.IntElementRule;
import net.time4j.engine.Normalizer;
import net.time4j.engine.Temporal;
import net.time4j.engine.ThreetenAdapter;
import net.time4j.engine.TimeAxis;
import net.time4j.engine.TimeMetric;
import net.time4j.engine.TimePoint;
import net.time4j.engine.TimeSpan;
import net.time4j.engine.UnitRule;
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.scale.TimeScale;
import net.time4j.tz.TZID;
import net.time4j.tz.Timezone;
import net.time4j.tz.TransitionStrategy;
import net.time4j.tz.ZonalOffset;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import static net.time4j.CalendarUnit.*;
import static net.time4j.ClockUnit.*;
import static net.time4j.PlainDate.*;
import static net.time4j.PlainTime.*;
/**
* <p>Represents a plain composition of calendar date and wall time as defined
* in ISO-8601, but without any timezone. </p>
*
* <p>Following elements which are declared as constants are registered by
* this class: </p>
*
* <ul>
* <li>{@link PlainDate#COMPONENT}</li>
* <li>{@link PlainDate#YEAR}</li>
* <li>{@link PlainDate#YEAR_OF_WEEKDATE}</li>
* <li>{@link PlainDate#QUARTER_OF_YEAR}</li>
* <li>{@link PlainDate#MONTH_OF_YEAR}</li>
* <li>{@link PlainDate#MONTH_AS_NUMBER}</li>
* <li>{@link PlainDate#DAY_OF_MONTH}</li>
* <li>{@link PlainDate#DAY_OF_QUARTER}</li>
* <li>{@link PlainDate#DAY_OF_WEEK}</li>
* <li>{@link PlainDate#DAY_OF_YEAR}</li>
* <li>{@link PlainDate#WEEKDAY_IN_MONTH}</li>
* <li>{@link PlainTime#COMPONENT}</li>
* <li>{@link PlainTime#AM_PM_OF_DAY}</li>
* <li>{@link PlainTime#CLOCK_HOUR_OF_AMPM}</li>
* <li>{@link PlainTime#CLOCK_HOUR_OF_DAY}</li>
* <li>{@link PlainTime#DIGITAL_HOUR_OF_AMPM}</li>
* <li>{@link PlainTime#DIGITAL_HOUR_OF_DAY}</li>
* <li>{@link PlainTime#ISO_HOUR}</li>
* <li>{@link PlainTime#MINUTE_OF_HOUR}</li>
* <li>{@link PlainTime#MINUTE_OF_DAY}</li>
* <li>{@link PlainTime#SECOND_OF_MINUTE}</li>
* <li>{@link PlainTime#SECOND_OF_DAY}</li>
* <li>{@link PlainTime#MILLI_OF_SECOND}</li>
* <li>{@link PlainTime#MICRO_OF_SECOND}</li>
* <li>{@link PlainTime#NANO_OF_SECOND}</li>
* <li>{@link PlainTime#MILLI_OF_DAY}</li>
* <li>{@link PlainTime#MICRO_OF_DAY}</li>
* <li>{@link PlainTime#NANO_OF_DAY}</li>
* <li>{@link PlainTime#DECIMAL_HOUR}</li>
* <li>{@link PlainTime#DECIMAL_MINUTE}</li>
* <li>{@link PlainTime#DECIMAL_SECOND}</li>
* <li>{@link PlainTime#PRECISION}</li>
* </ul>
*
* <p>Furthermore, all elements of class {@link Weekmodel} are supported. As
* timestamp units can be used: {@link CalendarUnit} and {@link ClockUnit}. </p>
*
* <p>Note: The special time value 24:00 is only supported in the factory
* methods which normalize the resulting timestamp to midnight of the following
* day. In element access and manipulations this value is not supported. </p>
*
* @author Meno Hochschild
* @doctags.concurrency {immutable}
*/
/*[deutsch]
* <p>Komposition aus Datum und Uhrzeit nach dem ISO-8601-Standard. </p>
*
* <p>Registriert sind folgende als Konstanten deklarierte Elemente: </p>
*
* <ul>
* <li>{@link PlainDate#COMPONENT}</li>
* <li>{@link PlainDate#YEAR}</li>
* <li>{@link PlainDate#YEAR_OF_WEEKDATE}</li>
* <li>{@link PlainDate#QUARTER_OF_YEAR}</li>
* <li>{@link PlainDate#MONTH_OF_YEAR}</li>
* <li>{@link PlainDate#MONTH_AS_NUMBER}</li>
* <li>{@link PlainDate#DAY_OF_MONTH}</li>
* <li>{@link PlainDate#DAY_OF_QUARTER}</li>
* <li>{@link PlainDate#DAY_OF_WEEK}</li>
* <li>{@link PlainDate#DAY_OF_YEAR}</li>
* <li>{@link PlainDate#WEEKDAY_IN_MONTH}</li>
* <li>{@link PlainTime#COMPONENT}</li>
* <li>{@link PlainTime#AM_PM_OF_DAY}</li>
* <li>{@link PlainTime#CLOCK_HOUR_OF_AMPM}</li>
* <li>{@link PlainTime#CLOCK_HOUR_OF_DAY}</li>
* <li>{@link PlainTime#DIGITAL_HOUR_OF_AMPM}</li>
* <li>{@link PlainTime#DIGITAL_HOUR_OF_DAY}</li>
* <li>{@link PlainTime#ISO_HOUR}</li>
* <li>{@link PlainTime#MINUTE_OF_HOUR}</li>
* <li>{@link PlainTime#MINUTE_OF_DAY}</li>
* <li>{@link PlainTime#SECOND_OF_MINUTE}</li>
* <li>{@link PlainTime#SECOND_OF_DAY}</li>
* <li>{@link PlainTime#MILLI_OF_SECOND}</li>
* <li>{@link PlainTime#MICRO_OF_SECOND}</li>
* <li>{@link PlainTime#NANO_OF_SECOND}</li>
* <li>{@link PlainTime#MILLI_OF_DAY}</li>
* <li>{@link PlainTime#MICRO_OF_DAY}</li>
* <li>{@link PlainTime#NANO_OF_DAY}</li>
* <li>{@link PlainTime#DECIMAL_HOUR}</li>
* <li>{@link PlainTime#DECIMAL_MINUTE}</li>
* <li>{@link PlainTime#DECIMAL_SECOND}</li>
* <li>{@link PlainTime#PRECISION}</li>
* </ul>
*
* <p>Darüberhinaus sind alle Elemente der Klasse {@link Weekmodel}
* nutzbar. Als Zeiteinheiten kommen vor allem {@link CalendarUnit} und
* {@link ClockUnit} in Betracht. </p>
*
* <p>Notiz: Unterstützung für den speziellen Zeitwert T24:00 gibt es
* nur in den Fabrikmethoden, die dann diesen Wert zum nächsten Tag hin
* normalisieren, nicht aber in den Elementen. </p>
*
* @author Meno Hochschild
* @doctags.concurrency {immutable}
*/
@CalendarType("iso8601")
public final class PlainTimestamp
extends TimePoint<IsoUnit, PlainTimestamp>
implements GregorianDate, WallTime,
Temporal<PlainTimestamp>, ThreetenAdapter, Normalizer<IsoUnit>, LocalizedPatternSupport {
//~ Statische Felder/Initialisierungen --------------------------------
private static final int MRD = 1000000000;
private static final PlainTimestamp MIN =
new PlainTimestamp(PlainDate.MIN, PlainTime.MIN);
private static final PlainTimestamp MAX =
new PlainTimestamp(PlainDate.MAX, WALL_TIME.getDefaultMaximum());
private static final Map<CalendarUnit, UnitRule<PlainTimestamp>> CALENDAR_UNIT_RULE_MAP;
private static final Map<ClockUnit, UnitRule<PlainTimestamp>> CLOCK_UNIT_RULE_MAP;
private static final Map<Object, ChronoElement<?>> CHILDREN;
private static final TimeAxis<IsoUnit, PlainTimestamp> ENGINE;
private static final TimeMetric<IsoUnit, Duration<IsoUnit>> STD_METRIC;
static {
Map<CalendarUnit, UnitRule<PlainTimestamp>> m1 = new EnumMap<>(CalendarUnit.class);
Map<ClockUnit, UnitRule<PlainTimestamp>> m2 = new EnumMap<>(ClockUnit.class);
for (CalendarUnit unit : CalendarUnit.values()) {
m1.put(unit, new CompositeUnitRule(unit));
}
for (ClockUnit unit : ClockUnit.values()) {
m2.put(unit, new CompositeUnitRule(unit));
}
CALENDAR_UNIT_RULE_MAP = m1;
CLOCK_UNIT_RULE_MAP = m2;
Map<Object, ChronoElement<?>> children = new HashMap<>();
children.put(CALENDAR_DATE, WALL_TIME);
children.put(YEAR, MONTH_AS_NUMBER);
children.put(YEAR_OF_WEEKDATE, Weekmodel.ISO.weekOfYear());
children.put(QUARTER_OF_YEAR, DAY_OF_QUARTER);
children.put(MONTH_OF_YEAR, DAY_OF_MONTH);
children.put(MONTH_AS_NUMBER, DAY_OF_MONTH);
children.put(DAY_OF_MONTH, WALL_TIME);
children.put(DAY_OF_WEEK, WALL_TIME);
children.put(DAY_OF_YEAR, WALL_TIME);
children.put(DAY_OF_QUARTER, WALL_TIME);
children.put(WEEKDAY_IN_MONTH, WALL_TIME);
children.put(AM_PM_OF_DAY, DIGITAL_HOUR_OF_AMPM);
children.put(CLOCK_HOUR_OF_AMPM, MINUTE_OF_HOUR);
children.put(CLOCK_HOUR_OF_DAY, MINUTE_OF_HOUR);
children.put(DIGITAL_HOUR_OF_AMPM, MINUTE_OF_HOUR);
children.put(DIGITAL_HOUR_OF_DAY, MINUTE_OF_HOUR);
children.put(ISO_HOUR, MINUTE_OF_HOUR);
children.put(MINUTE_OF_HOUR, SECOND_OF_MINUTE);
children.put(MINUTE_OF_DAY, SECOND_OF_MINUTE);
children.put(SECOND_OF_MINUTE, NANO_OF_SECOND);
children.put(SECOND_OF_DAY, NANO_OF_SECOND);
CHILDREN = Collections.unmodifiableMap(children);
TimeAxis.Builder<IsoUnit, PlainTimestamp> builder =
TimeAxis.Builder
.setUp(
IsoUnit.class,
PlainTimestamp.class,
new Merger(),
MIN,
MAX)
.appendElement(
CALENDAR_DATE,
FieldRule.of(CALENDAR_DATE),
DAYS)
.appendElement(
YEAR,
new IntFieldRule(YEAR),
YEARS)
.appendElement(
YEAR_OF_WEEKDATE,
new IntFieldRule(YEAR_OF_WEEKDATE),
Weekcycle.YEARS)
.appendElement(
QUARTER_OF_YEAR,
FieldRule.of(QUARTER_OF_YEAR),
QUARTERS)
.appendElement(
MONTH_OF_YEAR,
FieldRule.of(MONTH_OF_YEAR),
MONTHS)
.appendElement(
MONTH_AS_NUMBER,
new IntFieldRule(MONTH_AS_NUMBER),
MONTHS)
.appendElement(
DAY_OF_MONTH,
new IntFieldRule(DAY_OF_MONTH),
DAYS)
.appendElement(
DAY_OF_WEEK,
FieldRule.of(DAY_OF_WEEK),
DAYS)
.appendElement(
DAY_OF_YEAR,
new IntFieldRule(DAY_OF_YEAR),
DAYS)
.appendElement(
DAY_OF_QUARTER,
new IntFieldRule(DAY_OF_QUARTER),
DAYS)
.appendElement(
WEEKDAY_IN_MONTH,
new IntFieldRule(WEEKDAY_IN_MONTH),
WEEKS)
.appendElement(
WALL_TIME,
FieldRule.of(WALL_TIME))
.appendElement(
AM_PM_OF_DAY,
FieldRule.of(AM_PM_OF_DAY))
.appendElement(
CLOCK_HOUR_OF_AMPM,
new IntFieldRule(CLOCK_HOUR_OF_AMPM),
HOURS)
.appendElement(
CLOCK_HOUR_OF_DAY,
new IntFieldRule(CLOCK_HOUR_OF_DAY),
HOURS)
.appendElement(
DIGITAL_HOUR_OF_AMPM,
new IntFieldRule(DIGITAL_HOUR_OF_AMPM),
HOURS)
.appendElement(
DIGITAL_HOUR_OF_DAY,
new IntFieldRule(DIGITAL_HOUR_OF_DAY),
HOURS)
.appendElement(
ISO_HOUR,
new IntFieldRule(ISO_HOUR),
HOURS)
.appendElement(
MINUTE_OF_HOUR,
new IntFieldRule(MINUTE_OF_HOUR),
MINUTES)
.appendElement(
MINUTE_OF_DAY,
new IntFieldRule(MINUTE_OF_DAY),
MINUTES)
.appendElement(
SECOND_OF_MINUTE,
new IntFieldRule(SECOND_OF_MINUTE),
SECONDS)
.appendElement(
SECOND_OF_DAY,
new IntFieldRule(SECOND_OF_DAY),
SECONDS)
.appendElement(
MILLI_OF_SECOND,
new IntFieldRule(MILLI_OF_SECOND),
MILLIS)
.appendElement(
MICRO_OF_SECOND,
new IntFieldRule(MICRO_OF_SECOND),
MICROS)
.appendElement(
NANO_OF_SECOND,
new IntFieldRule(NANO_OF_SECOND),
NANOS)
.appendElement(
MILLI_OF_DAY,
new IntFieldRule(MILLI_OF_DAY),
MILLIS)
.appendElement(
MICRO_OF_DAY,
FieldRule.of(MICRO_OF_DAY),
MICROS)
.appendElement(
NANO_OF_DAY,
FieldRule.of(NANO_OF_DAY),
NANOS)
.appendElement(
DECIMAL_HOUR,
new DecimalRule(DECIMAL_HOUR))
.appendElement(
DECIMAL_MINUTE,
new DecimalRule(DECIMAL_MINUTE))
.appendElement(
DECIMAL_SECOND,
new DecimalRule(DECIMAL_SECOND))
.appendElement(
PRECISION,
FieldRule.of(PRECISION));
registerCalendarUnits(builder);
registerClockUnits(builder);
registerExtensions(builder);
ENGINE = builder.build();
IsoUnit[] units =
{YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, NANOS};
STD_METRIC = Duration.in(units);
}
private static final long serialVersionUID = 7458380065762437714L;
//~ Instanzvariablen --------------------------------------------------
private transient final PlainDate date;
private transient final PlainTime time;
//~ Konstruktoren -----------------------------------------------------
private PlainTimestamp(
PlainDate date,
PlainTime time
) {
super();
if (time.getHour() == 24) { // T24 normalisieren
this.date = date.plus(1, DAYS);
this.time = PlainTime.MIN;
} else if (date == null) {
throw new NullPointerException("Missing date.");
} else {
this.date = date;
this.time = time;
}
}
//~ Methoden ----------------------------------------------------------
/**
* <p>Creates a new local timestamp with calendar date and wall time. </p>
*
* <p>The special time value 24:00 will automatically normalized such
* that the resulting timestamp is on starting midnight of following
* day. </p>
*
* @param date calendar date component
* @param time wall time component (24:00 will always be normalized)
* @return timestamp as composition of date and time
* @see #of(int, int, int, int, int)
* @see #of(int, int, int, int, int, int)
*/
/*[deutsch]
* <p>Erzeugt eine neue Instanz mit Datum und Uhrzeit. </p>
*
* <p>Der Spezialwert T24:00 wird automatisch so normalisiert, daß
* der resultierende Zeitstempel auf Mitternacht des Folgetags zeigt. </p>
*
* @param date calendar date component
* @param time wall time component (24:00 will always be normalized)
* @return timestamp as composition of date and time
* @see #of(int, int, int, int, int)
* @see #of(int, int, int, int, int, int)
*/
public static PlainTimestamp of(
PlainDate date,
PlainTime time
) {
return new PlainTimestamp(date, time);
}
/**
* <p>Creates a new local timestamp in minute precision. </p>
*
* <p>The special time value 24:00 will automatically normalized such
* that the resulting timestamp is on starting midnight of following
* day. </p>
*
* @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)
* @param hour hour in the range {@code 0-23} or {@code 24}
* if minute and second are equal to {@code 0}
* @param minute minute in the range {@code 0-59}
* @return timestamp as composition of date and time
*/
/*[deutsch]
* <p>Erzeugt einen neuen minutengenauen Zeitstempel. </p>
*
* <p>Der Spezialwert T24:00 wird automatisch so normalisiert, daß
* der resultierende Zeitstempel auf Mitternacht des Folgetags zeigt. </p>
*
* @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)
* @param hour hour in the range {@code 0-23} or {@code 24}
* if minute and second are equal to {@code 0}
* @param minute minute in the range {@code 0-59}
* @return timestamp as composition of date and time
*/
public static PlainTimestamp of(
int year,
int month,
int dayOfMonth,
int hour,
int minute
) {
return PlainTimestamp.of(year, month, dayOfMonth, hour, minute, 0);
}
/**
* <p>Creates a new local timestamp in second precision. </p>
*
* <p>The special time value 24:00 will automatically normalized such
* that the resulting timestamp is on starting midnight of following
* day. </p>
*
* @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)
* @param hour hour in the range {@code 0-23} or {@code 24}
* if minute and second are equal to {@code 0}
* @param minute minute in the range {@code 0-59}
* @param second second in the range {@code 0-59}
* @return timestamp as composition of date and time
*/
/*[deutsch]
* <p>Erzeugt einen neuen sekundengenauen Zeitstempel. </p>
*
* <p>Der Spezialwert T24:00 wird automatisch so normalisiert, daß
* der resultierende Zeitstempel auf Mitternacht des Folgetags zeigt. </p>
*
* @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)
* @param hour hour in the range {@code 0-23} or {@code 24}
* if minute and second are equal to {@code 0}
* @param minute minute in the range {@code 0-59}
* @param second second in the range {@code 0-59}
* @return timestamp as composition of date and time
*/
public static PlainTimestamp of(
int year,
int month,
int dayOfMonth,
int hour,
int minute,
int second
) {
return PlainTimestamp.of(
PlainDate.of(year, month, dayOfMonth),
PlainTime.of(hour, minute, second)
);
}
/**
* <p>Obtains the current timestamp in system time. </p>
*
* <p>Convenient short-cut for: {@code SystemClock.inLocalView().now()}. </p>
*
* @return current timestamp (without zone) in system time zone using the system clock
* @see SystemClock#inLocalView()
* @see ZonalClock#now()
* @since 3.23/4.19
*/
/*[deutsch]
* <p>Ermittelt den aktuellen Zeitstempel in der Systemzeit. </p>
*
* <p>Bequeme Abkürzung für: {@code SystemClock.inLocalView().now()}. </p>
*
* @return current timestamp (without zone) in system time zone using the system clock
* @see SystemClock#inLocalView()
* @see ZonalClock#now()
* @since 3.23/4.19
*/
public static PlainTimestamp nowInSystemTime() {
return ZonalClock.ofSystem().now();
}
/**
* <p>Short cut for {@code TemporalType.LOCAL_DATE_TIME.translate(ldt)}. </p>
*
* @param ldt Threeten-equivalent of this instance
* @return PlainTimestamp
* @since 4.0
* @see TemporalType#LOCAL_DATE_TIME
*/
/*[deutsch]
* <p>Abkürzung für {@code TemporalType.LOCAL_DATE_TIME.translate(ldt)}. </p>
*
* @param ldt Threeten-equivalent of this instance
* @return PlainTimestamp
* @since 4.0
* @see TemporalType#LOCAL_DATE_TIME
*/
public static PlainTimestamp from(LocalDateTime ldt) {
return TemporalType.LOCAL_DATE_TIME.translate(ldt);
}
/**
* <p>Provides the calendar date part. </p>
*
* @return calendar date component
*/
/*[deutsch]
* <p>Liefert die Datumskomponente. </p>
*
* @return calendar date component
*/
public PlainDate getCalendarDate() {
return this.date;
}
/**
* <p>Provides the wall time part. </p>
*
* @return wall time component
*/
/*[deutsch]
* <p>Liefert die Uhrzeitkomponente. </p>
*
* @return wall time component
*/
public PlainTime getWallTime() {
return this.time;
}
@Override
public int getYear() {
return this.date.getYear();
}
@Override
public int getMonth() {
return this.date.getMonth();
}
@Override
public int getDayOfMonth() {
return this.date.getDayOfMonth();
}
@Override
public int getHour() {
return this.time.getHour();
}
@Override
public int getMinute() {
return this.time.getMinute();
}
@Override
public int getSecond() {
return this.time.getSecond();
}
@Override
public int getNanosecond() {
return this.time.getNanosecond();
}
/**
* <p>Adjusts this timestamp by given operator. </p>
*
* @param operator element-related operator
* @return changed copy of this timestamp
* @see ChronoEntity#with(net.time4j.engine.ChronoOperator)
*/
/*[deutsch]
* <p>Passt diesen Zeitstempel mit Hilfe des angegebenen Operators an. </p>
*
* @param operator element-related operator
* @return changed copy of this timestamp
* @see ChronoEntity#with(net.time4j.engine.ChronoOperator)
*/
public PlainTimestamp with(ElementOperator<?> operator) {
return this.with(operator.onTimestamp());
}
/**
* <p>Adjusts the calendar part of this timestamp. </p>
*
* @param date new calendar date component
* @return changed copy of this timestamp
* @see PlainDate#COMPONENT
*/
/*[deutsch]
* <p>Passt die Datumskomponente an. </p>
*
* @param date new calendar date component
* @return changed copy of this timestamp
* @see PlainDate#COMPONENT
*/
public PlainTimestamp with(PlainDate date) {
return this.with(CALENDAR_DATE, date);
}
/**
* <p>Adjusts the wall time part of this timestamp. </p>
*
* @param time new wall time component
* @return changed copy of this timestamp
* @see PlainTime#COMPONENT
*/
/*[deutsch]
* <p>Passt die Uhrzeitkomponente an. </p>
*
* @param time new wall time component
* @return changed copy of this timestamp
* @see PlainTime#COMPONENT
*/
public PlainTimestamp with(PlainTime time) {
return this.with(WALL_TIME, time);
}
@Override
public boolean isBefore(PlainTimestamp timestamp) {
return (this.compareTo(timestamp) < 0);
}
@Override
public boolean isAfter(PlainTimestamp timestamp) {
return (this.compareTo(timestamp) > 0);
}
@Override
public boolean isSimultaneous(PlainTimestamp timestamp) {
return (this.compareTo(timestamp) == 0);
}
/**
* <p>Defines the temporal order of date and time as natural order. </p>
*
* <p>The comparison is consistent with {@code equals()}. </p>
*
* @see #isBefore(PlainTimestamp)
* @see #isAfter(PlainTimestamp)
*/
/*[deutsch]
* <p>Definiert eine natürliche Ordnung, die auf der zeitlichen
* Position basiert. </p>
*
* <p>Der Vergleich ist konsistent mit {@code equals()}. </p>
*
* @see #isBefore(PlainTimestamp)
* @see #isAfter(PlainTimestamp)
*/
@Override
public int compareTo(PlainTimestamp timestamp) {
if (this.date.isAfter(timestamp.date)) {
return 1;
} else if (this.date.isBefore(timestamp.date)) {
return -1;
}
return this.time.compareTo(timestamp.time);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj instanceof PlainTimestamp) {
PlainTimestamp that = (PlainTimestamp) obj;
return (this.date.equals(that.date) && this.time.equals(that.time));
} else {
return false;
}
}
@Override
public int hashCode() {
return 13 * this.date.hashCode() + 37 * this.time.hashCode();
}
/**
* <p>Creates a canonical representation of the form
* "yyyy-MM-dd'T'HH:mm:ss,fffffffff". </p>
*
* <p>Dependent on the precision (that is last non-zero part of time)
* the time representation might be shorter. </p>
*
* @return canonical ISO-8601-formatted string
* @see PlainTime#toString()
*/
/*[deutsch]
* <p>Erzeugt eine kanonische Darstellung im Format
* "yyyy-MM-dd'T'HH:mm:ss,fffffffff". </p>
*
* <p>Je nach Genauigkeit kann der Uhrzeitanteil auch kürzer sein. </p>
*
* @return canonical ISO-8601-formatted string
* @see PlainTime#toString()
*/
@Override
public String toString() {
return this.date.toString() + this.time.toString();
}
/**
* <p>Synonym for {@code getCalendarDate()}. </p>
*
* @return calendar date component
* @see #getCalendarDate()
* @since 3.6/4.4
*/
/*[deutsch]
* <p>Synonym für {@code getCalendarDate()}. </p>
*
* @return calendar date component
* @since 3.6/4.4
*/
public PlainDate toDate() {
return this.date;
}
/**
* <p>Synonym for {@code getWallTime()}. </p>
*
* @return wall time component
* @see #getWallTime()
* @since 3.6/4.4
*/
/*[deutsch]
* <p>Synonym für {@code getWallTime()}. </p>
*
* @return wall time component
* @see #getWallTime()
* @since 3.6/4.4
*/
public PlainTime toTime() {
return this.time;
}
/**
* <p>Creates a new formatter which uses the given pattern in the
* default locale for formatting and parsing plain timestamps. </p>
*
* @param <P> generic pattern type
* @param formatPattern format definition as pattern
* @param patternType pattern dialect
* @return format object for formatting {@code PlainTimestamp}-objects
* using system locale
* @throws IllegalArgumentException if resolving of pattern fails
* @since 3.0
*/
/*[deutsch]
* <p>Erzeugt ein neues Format-Objekt mit Hilfe des angegebenen Musters
* in der Standard-Sprach- und Ländereinstellung. </p>
*
* @param <P> generic pattern type
* @param formatPattern format definition as pattern
* @param patternType pattern dialect
* @return format object for formatting {@code PlainTimestamp}-objects
* using system locale
* @throws IllegalArgumentException if resolving of pattern fails
* @since 3.0
*/
public static <P extends ChronoPattern<P>> TemporalFormatter<PlainTimestamp> localFormatter(
String formatPattern,
P patternType
) {
return FormatSupport.createFormatter(PlainTimestamp.class, formatPattern, patternType, Locale.getDefault());
}
/**
* <p>Creates a new formatter which uses the given pattern and locale
* for formatting and parsing plain timestamps. </p>
*
* @param <P> generic pattern type
* @param formatPattern format definition as pattern
* @param patternType pattern dialect
* @param locale locale setting
* @return format object for formatting {@code PlainTimestamp}-objects using given locale
* @throws IllegalArgumentException if resolving of pattern fails
* @since 3.0
* @see #localFormatter(String,ChronoPattern)
*/
/*[deutsch]
* <p>Erzeugt ein neues Format-Objekt mit Hilfe des angegebenen Musters
* in der angegebenen Sprach- und Ländereinstellung. </p>
*
* @param <P> generic pattern type
* @param formatPattern format definition as pattern
* @param patternType pattern dialect
* @param locale locale setting
* @return format object for formatting {@code PlainTimestamp}-objects using given locale
* @throws IllegalArgumentException if resolving of pattern fails
* @since 3.0
* @see #localFormatter(String,ChronoPattern)
*/
public static <P extends ChronoPattern<P>> TemporalFormatter<PlainTimestamp> formatter(
String formatPattern,
P patternType,
Locale locale
) {
return FormatSupport.createFormatter(PlainTimestamp.class, formatPattern, patternType, locale);
}
@Override
public LocalDateTime toTemporalAccessor() {
return TemporalType.LOCAL_DATE_TIME.from(this);
}
/**
* <p>Provides a static access to the associated time axis respective
* chronology which contains the chronological rules. </p>
*
* @return chronological system as time axis (never {@code null})
*/
/*[deutsch]
* <p>Liefert die zugehörige Zeitachse, die alle notwendigen
* chronologischen Regeln enthält. </p>
*
* @return chronological system as time axis (never {@code null})
*/
public static TimeAxis<IsoUnit, PlainTimestamp> axis() {
return ENGINE;
}
/**
* <p>Provides a static access to the associated time axis using the foreign type S. </p>
*
* @param <S> foreign temporal type
* @param converter type converter
* @return chronological system for foreign type
* @see TemporalType#LOCAL_DATE_TIME
* @since 3.24/4.20
*/
/*[deutsch]
* <p>Liefert die zugehörige Zeitachse angepasst für den Fremdtyp S. </p>
*
* @param <S> foreign temporal type
* @param converter type converter
* @return chronological system for foreign type
* @see TemporalType#LOCAL_DATE_TIME
* @since 3.24/4.20
*/
public static <S> Chronology<S> axis(Converter<S, PlainTimestamp> converter) {
return new BridgeChronology<>(converter, ENGINE);
}
/**
* <p>Combines this local timestamp with the timezone offset UTC+00:00
* to a global UTC-moment. </p>
*
* @return global UTC-moment based on this local timestamp interpreted
* at offset UTC+00:00
* @see #at(ZonalOffset)
*/
/*[deutsch]
* <p>Kombiniert diesen lokalen Zeitstempel mit UTC+00:00 zu
* einem globalen UTC-Moment. </p>
*
* @return global UTC-moment based on this local timestamp interpreted
* at offset UTC+00:00
* @see #at(ZonalOffset)
*/
public Moment atUTC() {
return this.at(ZonalOffset.UTC);
}
/**
* <p>Combines this local timestamp with the given timezone offset
* to a global UTC-moment. </p>
*
* @param offset timezone offset
* @return global UTC-moment based on this local timestamp interpreted
* at given offset
* @since 1.2
* @see #atUTC()
* @see #in(Timezone)
*/
/*[deutsch]
* <p>Kombiniert diesen lokalen Zeitstempel mit dem angegebenen
* Zeitzonen-Offset zu einem globalen UTC-Moment. </p>
*
* @param offset timezone offset
* @return global UTC-moment based on this local timestamp interpreted
* at given offset
* @since 1.2
* @see #atUTC()
* @see #in(Timezone)
*/
public Moment at(ZonalOffset offset) {
long localSeconds =
MathUtils.safeMultiply(
this.date.getDaysSinceUTC() + 2 * 365,
86400);
localSeconds += (this.time.getHour() * 3600);
localSeconds += (this.time.getMinute() * 60);
localSeconds += this.time.getSecond();
int localNanos = this.time.getNanosecond();
long posixTime = localSeconds - offset.getIntegralAmount();
int posixNanos = localNanos - offset.getFractionalAmount();
if (posixNanos < 0) {
posixNanos += MRD;
posixTime--;
} else if (posixNanos >= MRD) {
posixNanos -= MRD;
posixTime++;
}
return Moment.of(posixTime, posixNanos, TimeScale.POSIX);
}
/**
* <p>Combines this local timestamp with the system timezone to a global
* UTC-moment. </p>
*
* @return global UTC-moment based on this local timestamp interpreted
* in system timezone
* @since 1.2
* @see Timezone#ofSystem()
* @see #inTimezone(TZID)
*/
/*[deutsch]
* <p>Kombiniert diesen lokalen Zeitstempel mit der System-Zeitzone
* zu einem UTC-Moment. </p>
*
* @return global UTC-moment based on this local timestamp interpreted
* in system timezone
* @since 1.2
* @see Timezone#ofSystem()
* @see #inTimezone(TZID)
*/
public Moment inStdTimezone() {
return this.in(Timezone.ofSystem());
}
/**
* <p>Combines this local timestamp with given timezone to a global
* UTC-moment. </p>
*
* @param tzid timezone id
* @return global UTC-moment based on this local timestamp interpreted
* in given timezone
* @throws IllegalArgumentException if given timezone cannot be loaded
* @since 1.2
* @see Timezone#of(TZID)
* @see #inStdTimezone()
*/
/*[deutsch]
* <p>Kombiniert diesen lokalen Zeitstempel mit der angegebenen Zeitzone
* zu einem UTC-Moment. </p>
*
* @param tzid timezone id
* @return global UTC-moment based on this local timestamp interpreted
* in given timezone
* @throws IllegalArgumentException if given timezone cannot be loaded
* @since 1.2
* @see Timezone#of(TZID)
* @see #inStdTimezone()
*/
public Moment inTimezone(TZID tzid) {
return this.in(Timezone.of(tzid));
}
/**
* <p>Combines this local timestamp with given timezone to a global
* UTC-moment. </p>
*
* @param tz timezone
* @return global UTC-moment based on this local timestamp interpreted
* in given timezone
* @since 1.2
* @see Timezone#of(String)
*/
/*[deutsch]
* <p>Kombiniert diesen lokalen Zeitstempel mit der angegebenen Zeitzone
* zu einem UTC-Moment. </p>
*
* @param tz timezone
* @return global UTC-moment based on this local timestamp interpreted
* in given timezone
* @since 1.2
* @see Timezone#of(String)
*/
public Moment in(Timezone tz) {
if (tz.isFixed()) { // optimization
return this.at(tz.getOffset(this.date, this.time));
}
TransitionStrategy strategy = tz.getStrategy();
long posixTime = strategy.resolve(this.date, this.time, tz);
Moment moment =
Moment.of(posixTime, this.time.getNanosecond(), TimeScale.POSIX);
if (strategy == Timezone.STRICT_MODE) {
Moment.checkNegativeLS(posixTime, this);
}
return moment;
}
/**
* <p>Equivalent to {@link #inZonalView(Timezone) inZonalView(Timezone.ofSystem()}. </p>
*
* @return ZonalDateTime
* @since 3.16/4.13
*/
/*[deutsch]
* <p>Äquivalent zu {@link #inZonalView(Timezone) inZonalView(Timezone.ofSystem()}. </p>
*
* @return ZonalDateTime
* @since 3.16/4.13
*/
public ZonalDateTime inLocalView() {
return this.inZonalView(Timezone.ofSystem());
}
/**
* <p>Converts this instance to a combination of UTC-moment, given timezone and its zonal timestamp. </p>
*
* <p><strong>Attention:</strong> Due to winter/summer-time-changes the resulting zonal timestamp
* ({@link ZonalDateTime#toTimestamp()}) can deviate from this plain timestamp. </p>
*
* @param tz timezone
* @return ZonalDateTime
* @since 3.16/4.13
*/
/*[deutsch]
* <p>Converts this instance to a combination of UTC-moment, given timezone and its zonal timestamp. </p>
*
* <p><strong>Achtung:</strong> Wegen Winter-/Sommerzeitumstellungen kann der resultierende zonale
* Zeitstempel ({@link ZonalDateTime#toTimestamp()}) von diesem Zeitstempel abweichen. </p>
*
* @param tz timezone
* @return ZonalDateTime
* @since 3.16/4.13
*/
public ZonalDateTime inZonalView(Timezone tz) {
Moment m = this.in(tz);
return ZonalDateTime.of(m, tz);
}
/**
* <p>Does this local timestamp exist in given timezone? </p>
*
* @param tzid timezone id (optional)
* @return {@code true} if this timestamp is valid in given timezone
* @throws IllegalArgumentException if given timezone cannot be loaded
*/
/*[deutsch]
* <p>Existiert dieser Zeitstempel in der angegebenen Zeitzone? </p>
*
* @param tzid timezone id (optional)
* @return {@code true} if this timestamp is valid in given timezone
* @throws IllegalArgumentException if given timezone cannot be loaded
*/
public boolean isValid(TZID tzid) {
if (tzid == null) {
return false;
}
return !Timezone.of(tzid).isInvalid(this.date, this.time);
}
/**
* <p>Normalized given timespan using years, months, days and
* all clock units. </p>
*
* <p>This normalizer can also convert from days to months. Example: </p>
*
* <pre>
* Duration<CalendarUnit> dur = Duration.of(30, CalendarUnit.DAYS);
* Duration<IsoUnit> result =
* PlainTimestamp.of(2012, 2, 28, 0, 0).normalize(dur);
* System.out.println(result); // output: P1M1D (leap year!)
* </pre>
*
* @param timespan to be normalized
* @return normalized duration
* @since 2.0
*/
/*[deutsch]
* <p>Normalisiert die angegebene Zeitspanne, indem Jahre, Monate, Tage
* und alle Uhrzeiteinheiten verwendet werden. </p>
*
* <p>Dieser Normalisierer kann auch von Tagen zu Monaten konvertieren.
* Beispiel: </p>
*
* <pre>
* Duration<CalendarUnit> dur = Duration.of(30, CalendarUnit.DAYS);
* Duration<IsoUnit> result =
* PlainTimestamp.of(2012, 2, 28, 0, 0).normalize(dur);
* System.out.println(result); // Ausgabe: P1M1D (Schaltjahr!)
* </pre>
*
* @param timespan to be normalized
* @return normalized duration
* @since 2.0
*/
@Override
public Duration<IsoUnit> normalize(TimeSpan<? extends IsoUnit> timespan) {
return this.until(this.plus(timespan), STD_METRIC);
}
/**
* <p>Adds given amount in units to this timestamp and yields the result of addition. </p>
*
* <p>Covers the most important calendar units and is overloaded for performance reasons. </p>
*
* @param amount the amount of units to be added to this timestamp (maybe negative)
* @param unit the unit to be used in addition
* @return result of addition as changed copy while this instance remains unaffected
* @throws ArithmeticException in case of numerical overflow
* @see #plus(long, Object) plus(long, IsoUnit)
* @since 4.19
*/
/*[deutsch]
* <p>Addiert den angegebenen Betrag der entsprechenden Zeiteinheit
* zu diesem Zeitstempel und liefert das Additionsergebnis zurück. </p>
*
* <p>Deckt die wichtigsten kalendarischen Zeiteinheiten ab, die mit diesem Typ verwendet werden
* können und ist aus Performance-Gründen überladen. </p>
*
* @param amount the amount of units to be added to this timestamp (maybe negative)
* @param unit the unit to be used in addition
* @return result of addition as changed copy while this instance remains unaffected
* @throws ArithmeticException in case of numerical overflow
* @see #plus(long, Object) plus(long, IsoUnit)
* @since 4.19
*/
public PlainTimestamp plus(
long amount,
CalendarUnit unit
) {
if (unit == null) {
throw new NullPointerException("Missing unit.");
} else if (amount == 0) {
return this;
}
try {
return CALENDAR_UNIT_RULE_MAP.get(unit).addTo(this, amount);
} catch (IllegalArgumentException iae) {
ArithmeticException ex = new ArithmeticException("Result beyond boundaries of time axis.");
ex.initCause(iae);
throw ex;
}
}
/**
* <p>Adds given amount in units to this timestamp and yields the result of addition. </p>
*
* <p>Covers the most important clock units and is overloaded for performance reasons. </p>
*
* @param amount the amount of units to be added to this timestamp (maybe negative)
* @param unit the unit to be used in addition
* @return result of addition as changed copy while this instance remains unaffected
* @throws ArithmeticException in case of numerical overflow
* @see #plus(long, Object) plus(long, IsoUnit)
* @since 4.19
*/
/*[deutsch]
* <p>Addiert den angegebenen Betrag der entsprechenden Zeiteinheit
* zu diesem Zeitstempel und liefert das Additionsergebnis zurück. </p>
*
* <p>Deckt die wichtigsten Uhrzeiteinheiten ab, die mit diesem Typ verwendet werden
* können und ist aus Performance-Gründen überladen. </p>
*
* @param amount the amount of units to be added to this timestamp (maybe negative)
* @param unit the unit to be used in addition
* @return result of addition as changed copy while this instance remains unaffected
* @throws ArithmeticException in case of numerical overflow
* @see #plus(long, Object) plus(long, IsoUnit)
* @since 4.19
*/
public PlainTimestamp plus(
long amount,
ClockUnit unit
) {
if (unit == null) {
throw new NullPointerException("Missing unit.");
} else if (amount == 0) {
return this;
}
try {
return CLOCK_UNIT_RULE_MAP.get(unit).addTo(this, amount);
} catch (IllegalArgumentException iae) {
ArithmeticException ex = new ArithmeticException("Result beyond boundaries of time axis.");
ex.initCause(iae);
throw ex;
}
}
/**
* <p>Subtracts given amount in units from this timestamp and yields the result of subtraction. </p>
*
* <p>Covers the most important calendar units and is overloaded for performance reasons. </p>
*
* @param amount the amount of units to be subtracted from this timestamp (maybe negative)
* @param unit the unit to be used in subtraction
* @return result of subtraction as changed copy while this instance remains unaffected
* @throws ArithmeticException in case of numerical overflow
* @see #minus(long, Object) minus(long, IsoUnit)
* @since 4.19
*/
/*[deutsch]
* <p>Subtrahiert den angegebenen Betrag der entsprechenden Zeiteinheit
* von diesem Zeitstempel und liefert das Subtraktionsergebnis zurück. </p>
*
* <p>Deckt die wichtigsten kalendarischen Zeiteinheiten ab, die mit diesem Typ verwendet werden
* können und ist aus Performance-Gründen überladen. </p>
*
* @param amount the amount of units to be subtracted from this timestamp (maybe negative)
* @param unit the unit to be used in subtraction
* @return result of subtraction as changed copy while this instance remains unaffected
* @throws ArithmeticException in case of numerical overflow
* @see #minus(long, Object) minus(long, IsoUnit)
* @since 4.19
*/
public PlainTimestamp minus(
long amount,
CalendarUnit unit
) {
return this.plus(Math.negateExact(amount), unit);
}
/**
* <p>Subtracts given amount in units from this timestamp and yields the result of subtraction. </p>
*
* <p>Covers the most important clock units and is overloaded for performance reasons. </p>
*
* @param amount the amount of units to be subtracted from this timestamp (maybe negative)
* @param unit the unit to be used in subtraction
* @return result of subtraction as changed copy while this instance remains unaffected
* @throws ArithmeticException in case of numerical overflow
* @see #minus(long, Object) minus(long, IsoUnit)
* @since 4.19
*/
/*[deutsch]
* <p>Subtrahiert den angegebenen Betrag der entsprechenden Zeiteinheit
* von diesem Zeitstempel und liefert das Subtraktionsergebnis zurück. </p>
*
* <p>Deckt die wichtigsten Uhrzeiteinheiten ab, die mit diesem Typ verwendet werden
* können und ist aus Performance-Gründen überladen. </p>
*
* @param amount the amount of units to be subtracted from this timestamp (maybe negative)
* @param unit the unit to be used in subtraction
* @return result of subtraction as changed copy while this instance remains unaffected
* @throws ArithmeticException in case of numerical overflow
* @see #minus(long, Object) minus(long, IsoUnit)
* @since 4.19
*/
public PlainTimestamp minus(
long amount,
ClockUnit unit
) {
return this.plus(Math.negateExact(amount), unit);
}
@Override
protected TimeAxis<IsoUnit, PlainTimestamp> getChronology() {
return ENGINE;
}
@Override
protected PlainTimestamp getContext() {
return this;
}
/**
* <p>Erzeugt eine neue Uhrzeit passend zur angegebenen absoluten Zeit. </p>
*
* @param ut unix time in seconds
* @param offset shift of local timestamp relative to UTC
* @return new or cached local timestamp
*/
static PlainTimestamp from(
UnixTime ut,
ZonalOffset offset
) {
long localSeconds = ut.getPosixTime() + offset.getIntegralAmount();
int localNanos = ut.getNanosecond() + offset.getFractionalAmount();
if (localNanos < 0) {
localNanos += MRD;
localSeconds--;
} else if (localNanos >= MRD) {
localNanos -= MRD;
localSeconds++;
}
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,
localNanos
);
return PlainTimestamp.of(date, time);
}
private static void registerCalendarUnits(
TimeAxis.Builder<IsoUnit, PlainTimestamp> builder
) {
Set<CalendarUnit> monthly = EnumSet.range(MILLENNIA, MONTHS);
Set<CalendarUnit> daily = EnumSet.range(WEEKS, DAYS);
for (CalendarUnit unit : CalendarUnit.values()) {
builder.appendUnit(
unit,
CALENDAR_UNIT_RULE_MAP.get(unit),
unit.getLength(),
(unit.compareTo(WEEKS) < 0) ? monthly : daily
);
}
}
private static void registerClockUnits(
TimeAxis.Builder<IsoUnit, PlainTimestamp> builder
) {
for (ClockUnit unit : ClockUnit.values()) {
builder.appendUnit(
unit,
CLOCK_UNIT_RULE_MAP.get(unit),
unit.getLength(),
EnumSet.allOf(ClockUnit.class)
);
}
}
private static void registerExtensions(
TimeAxis.Builder<IsoUnit, PlainTimestamp> builder
) {
for (ChronoExtension extension : PlainDate.axis().getExtensions()) {
builder.appendExtension(extension);
}
for (ChronoExtension extension : PlainTime.axis().getExtensions()) {
builder.appendExtension(extension);
}
}
/**
* @serialData Uses <a href="../../serialized-form.html#net.time4j.SPX">
* a dedicated serialization form</a> as proxy. The layout
* is bit-compressed. The first byte contains within the
* four most significant bits the type id {@code 8}. Then
* the data bytes for date and time component follow.
*
* Schematic algorithm:
*
* <pre>
int range;
if (year >= 1850 && year <= 2100) {
range = 1;
} else if (Math.abs(year) < 10000) {
range = 2;
} else {
range = 3;
}
int header = 8; // type-id
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);
}
if (time.nano == 0) {
if (time.second == 0) {
if (time.minute == 0) {
out.writeByte(~time.hour);
} else {
out.writeByte(time.hour);
out.writeByte(~time.minute);
}
} else {
out.writeByte(time.hour);
out.writeByte(time.minute);
out.writeByte(~time.second);
}
} else {
out.writeByte(time.hour);
out.writeByte(time.minute);
out.writeByte(time.second);
out.writeInt(time.nano);
}
</pre>
*
* @return replacement object in serialization graph
*/
private Object writeReplace() {
return new SPX(this, SPX.TIMESTAMP_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<PlainTimestamp> {
//~ Methoden ------------------------------------------------------
@Override
public String getFormatPattern(
DisplayStyle style,
Locale locale
) {
DisplayMode mode = DisplayMode.ofStyle(style.getStyleValue());
return CalendarText.patternForTimestamp(mode, mode, locale);
}
@Override
public PlainTimestamp createFrom(
TimeSource<?> clock,
final 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 PlainTimestamp.from(ut, zone.getOffset(ut));
}
@Override
public PlainTimestamp createFrom(
TemporalAccessor threeten,
AttributeQuery attributes
) {
PlainDate date = PlainDate.axis().createFrom(threeten, attributes);
PlainTime time = PlainTime.axis().createFrom(threeten, attributes);
if ((date != null) && (time != null)) {
return PlainTimestamp.of(date, time);
}
return null;
}
@Override
@Deprecated
public PlainTimestamp createFrom(
ChronoEntity<?> entity,
AttributeQuery attributes,
boolean preparsing
) {
boolean lenient = attributes.get(Attributes.LENIENCY, Leniency.SMART).isLax();
return this.createFrom(entity, attributes, lenient, preparsing);
}
@Override
public PlainTimestamp createFrom(
ChronoEntity<?> entity,
AttributeQuery attributes,
boolean lenient,
boolean preparsing
) {
if (entity instanceof UnixTime) {
TZID tzid;
if (attributes.contains(Attributes.TIMEZONE_ID)) {
tzid = attributes.get(Attributes.TIMEZONE_ID);
} else if (lenient) {
tzid = ZonalOffset.UTC;
} else {
throw new IllegalArgumentException(
"Missing timezone attribute for type conversion.");
}
Moment ut = Moment.from(UnixTime.class.cast(entity));
return ut.toZonalTimestamp(tzid);
}
boolean leapsecond = (preparsing && (entity.getInt(SECOND_OF_MINUTE) == 60));
if (leapsecond) { // temporär, wird später kompensiert
entity.with(SECOND_OF_MINUTE, 59);
}
PlainDate date;
PlainTime time;
if (entity.contains(CALENDAR_DATE)) {
date = entity.get(CALENDAR_DATE);
} else {
date = PlainDate.axis().createFrom(entity, attributes, lenient, false);
}
if (date == null) {
return null;
} else if (entity.contains(WALL_TIME)) {
time = entity.get(WALL_TIME);
} else {
time = PlainTime.axis().createFrom(entity, attributes, lenient, false);
if ((time == null) && lenient) {
time = PlainTime.MIN;
}
}
if (time == null) {
return null;
} else {
if (entity.contains(LongElement.DAY_OVERFLOW)) {
date =
date.plus(
entity.get(LongElement.DAY_OVERFLOW).longValue(),
DAYS);
}
if (
leapsecond
&& entity.isValid(LeapsecondElement.INSTANCE, Boolean.TRUE)
) {
entity.with(
LeapsecondElement.INSTANCE,
Boolean.TRUE);
}
return PlainTimestamp.of(date, time);
}
}
}
private static class FieldRule<V>
implements ElementRule<PlainTimestamp, V> {
//~ Instanzvariablen ----------------------------------------------
final ChronoElement<V> element;
//~ Konstruktoren -------------------------------------------------
private FieldRule(ChronoElement<V> element) {
super();
this.element = element;
}
//~ Methoden ------------------------------------------------------
static <V> FieldRule<V> of(ChronoElement<V> element) {
return new FieldRule<>(element);
}
@Override
public V getValue(PlainTimestamp context) {
if (this.element.isDateElement()) {
return context.date.get(this.element);
} else if (this.element.isTimeElement()) {
return context.time.get(this.element);
}
throw new ChronoException(
"Missing rule for: " + this.element.name());
}
@Override
public V getMinimum(PlainTimestamp context) {
if (this.element.isDateElement()) {
return context.date.getMinimum(this.element);
} else if (this.element.isTimeElement()) {
return this.element.getDefaultMinimum();
}
throw new ChronoException(
"Missing rule for: " + this.element.name());
}
@Override
public V getMaximum(PlainTimestamp context) {
if (this.element.isDateElement()) {
return context.date.getMaximum(this.element);
} else if (this.element.isTimeElement()) {
return this.element.getDefaultMaximum();
}
throw new ChronoException(
"Missing rule for: " + this.element.name());
}
@Override
public boolean isValid(
PlainTimestamp context,
V value
) {
if (value == null) {
return false;
}
if (this.element.isDateElement()) {
return context.date.isValid(this.element, value);
} else if (this.element.isTimeElement()) {
if (Number.class.isAssignableFrom(this.element.getType())) {
long min = this.toNumber(this.element.getDefaultMinimum());
long max = this.toNumber(this.element.getDefaultMaximum());
long val = this.toNumber(value);
return ((min <= val) && (max >= val));
} else if (
this.element.equals(WALL_TIME)
&& PlainTime.MAX.equals(value)
) {
return false;
} else {
return context.time.isValid(this.element, value);
}
}
throw new ChronoException(
"Missing rule for: " + this.element.name());
}
@Override
public PlainTimestamp withValue(
PlainTimestamp context,
V value,
boolean lenient
) {
if (value == null) {
throw new IllegalArgumentException("Missing element value.");
}
if (value.equals(this.getValue(context))) {
return context;
} else if (lenient) { // nur auf numerischen Elementen definiert
IsoUnit unit = ENGINE.getBaseUnit(this.element);
long oldValue = this.toNumber(this.getValue(context));
long newValue = this.toNumber(value);
long amount = MathUtils.safeSubtract(newValue, oldValue);
return context.plus(amount, unit);
} else if (this.element.isDateElement()) {
PlainDate date = context.date.with(this.element, value);
return PlainTimestamp.of(date, context.time);
} else if (this.element.isTimeElement()) {
if (Number.class.isAssignableFrom(this.element.getType())) {
long min = this.toNumber(this.element.getDefaultMinimum());
long max = this.toNumber(this.element.getDefaultMaximum());
long val = this.toNumber(value);
if ((min > val) || (max < val)) {
throw new IllegalArgumentException("Out of range: " + value);
}
} else if (
this.element.equals(WALL_TIME)
&& value.equals(PlainTime.MAX)
) {
throw new IllegalArgumentException("Out of range: " + value);
}
PlainTime time = context.time.with(this.element, value);
return PlainTimestamp.of(context.date, time);
}
throw new ChronoException(
"Missing rule for: " + this.element.name());
}
// optional
@Override
public ChronoElement<?> getChildAtFloor(PlainTimestamp context) {
return CHILDREN.get(this.element);
}
// optional
@Override
public ChronoElement<?> getChildAtCeiling(PlainTimestamp context) {
return CHILDREN.get(this.element);
}
private long toNumber(V value) {
return Number.class.cast(value).longValue();
}
}
private static class IntFieldRule
extends FieldRule<Integer>
implements IntElementRule<PlainTimestamp> {
//~ Konstruktoren -------------------------------------------------
private IntFieldRule(ChronoElement<Integer> element) {
super(element);
}
//~ Methoden ------------------------------------------------------
@Override
public int getInt(PlainTimestamp context) {
if (this.element.isDateElement()) {
return context.date.getInt(this.element);
} else if (this.element.isTimeElement()) {
return context.time.getInt(this.element);
}
throw new ChronoException(
"Missing rule for: " + this.element.name());
}
@Override
public boolean isValid(
PlainTimestamp context,
int value
) {
if (this.element.isDateElement()) {
return context.date.isValid(this.element, value);
} else if (this.element.isTimeElement()) {
int min = this.element.getDefaultMinimum().intValue();
int max = this.element.getDefaultMaximum().intValue();
return ((min <= value) && (max >= value));
}
throw new ChronoException(
"Missing rule for: " + this.element.name());
}
@Override
public PlainTimestamp withValue(
PlainTimestamp context,
int value,
boolean lenient
) {
if (value == this.getInt(context)) {
return context;
} else if (lenient) { // nur auf numerischen Elementen definiert
IsoUnit unit = ENGINE.getBaseUnit(this.element);
long amount = MathUtils.safeSubtract((long) value, this.getInt(context));
return context.plus(amount, unit);
} else if (this.element.isDateElement()) {
PlainDate date = context.date.with(this.element, value);
return PlainTimestamp.of(date, context.time);
} else if (this.element.isTimeElement()) {
int min = this.element.getDefaultMinimum().intValue();
int max = this.element.getDefaultMaximum().intValue();
if ((min > value) || (max < value)) {
throw new IllegalArgumentException("Out of range: " + value);
}
PlainTime time = context.time.with(this.element, value);
return PlainTimestamp.of(context.date, time);
}
throw new ChronoException(
"Missing rule for: " + this.element.name());
}
}
private static class DecimalRule
extends FieldRule<BigDecimal> {
//~ Konstruktoren -------------------------------------------------
DecimalRule(ChronoElement<BigDecimal> element) {
super(element);
}
//~ Methoden ------------------------------------------------------
@Override
public boolean isValid(
PlainTimestamp context,
BigDecimal value
) {
if (value == null) {
return false;
}
BigDecimal min = super.element.getDefaultMinimum();
BigDecimal max = super.element.getDefaultMaximum();
return (
(min.compareTo(value) <= 0)
&& (value.compareTo(max) <= 0)
);
}
@Override
public PlainTimestamp withValue(
PlainTimestamp context,
BigDecimal value,
boolean lenient
) {
if (!this.isValid(context, value)) {
throw new IllegalArgumentException("Out of range: " + value);
}
PlainTime time = context.time.with(super.element, value);
return PlainTimestamp.of(context.date, time);
}
}
private static class CompositeUnitRule
implements UnitRule<PlainTimestamp> {
//~ Instanzvariablen ----------------------------------------------
private final CalendarUnit calendarUnit;
private final ClockUnit clockUnit;
//~ Konstruktoren -------------------------------------------------
CompositeUnitRule(CalendarUnit unit) {
super();
this.calendarUnit = unit;
this.clockUnit = null;
}
CompositeUnitRule(ClockUnit unit) {
super();
this.calendarUnit = null;
this.clockUnit = unit;
}
//~ Methoden ------------------------------------------------------
@Override
public PlainTimestamp addTo(
PlainTimestamp timepoint,
long amount
) {
PlainDate d;
PlainTime t;
if (this.calendarUnit != null) {
d = timepoint.date.plus(amount, this.calendarUnit);
t = timepoint.time;
} else {
DayCycles cycles = timepoint.time.roll(amount, this.clockUnit);
d = timepoint.date.plus(cycles.getDayOverflow(), DAYS);
t = cycles.getWallTime();
}
return PlainTimestamp.of(d, t);
}
@Override
public long between(
PlainTimestamp start,
PlainTimestamp end
) {
long delta;
if (this.calendarUnit != null) {
delta = this.calendarUnit.between(start.date, end.date);
if (delta != 0) {
boolean needsTimeCorrection;
if (this.calendarUnit == DAYS) {
needsTimeCorrection = true;
} else {
PlainDate d = start.date.plus(delta, this.calendarUnit);
needsTimeCorrection = (d.compareByTime(end.date) == 0);
}
if (needsTimeCorrection) {
PlainTime t1 = start.time;
PlainTime t2 = end.time;
if ((delta > 0) && t1.isAfter(t2)) {
delta--;
} else if ((delta < 0) && t1.isBefore(t2)) {
delta++;
}
}
}
} else if (start.date.isAfter(end.date)) {
delta = -between(end, start);
} else {
long days = start.date.until(end.date, DAYS);
if (days == 0) {
return this.clockUnit.between(start.time, end.time);
} else if (this.clockUnit.compareTo(SECONDS) <= 0) {
// HOURS, MINUTES, SECONDS
delta =
MathUtils.safeAdd(
MathUtils.safeMultiply(days, 86400),
MathUtils.safeSubtract(
end.time.get(SECOND_OF_DAY).longValue(),
start.time.get(SECOND_OF_DAY).longValue()
)
);
if (start.time.getNanosecond() > end.time.getNanosecond()) {
delta--;
}
} else {
// MILLIS, MICROS, NANOS
delta =
MathUtils.safeAdd(
MathUtils.safeMultiply(days, 86400L * MRD),
MathUtils.safeSubtract(
end.time.get(NANO_OF_DAY).longValue(),
start.time.get(NANO_OF_DAY).longValue()
)
);
}
switch (this.clockUnit) {
case HOURS:
delta = delta / 3600;
break;
case MINUTES:
delta = delta / 60;
break;
case SECONDS:
break;
case MILLIS:
delta = delta / 1000000;
break;
case MICROS:
delta = delta / 1000;
break;
case NANOS:
break;
default:
throw new UnsupportedOperationException(this.clockUnit.name());
}
}
return delta;
}
}
}