Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
net.time4j.PlainTimestamp Maven / Gradle / Ivy
/*
* -----------------------------------------------------------------------
* Copyright © 2013-2015 Meno Hochschild,
* -----------------------------------------------------------------------
* 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 .
* -----------------------------------------------------------------------
*/
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.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.ElementRule;
import net.time4j.engine.EpochDays;
import net.time4j.engine.Normalizer;
import net.time4j.engine.Temporal;
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.CalendarType;
import net.time4j.format.ChronoPattern;
import net.time4j.format.Leniency;
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.util.Collections;
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.HOURS;
import static net.time4j.ClockUnit.*;
import static net.time4j.PlainDate.*;
import static net.time4j.PlainTime.*;
/**
* Represents a plain composition of calendar date and wall time as defined
* in ISO-8601, but without any timezone.
*
* Following elements which are declared as constants are registered by
* this class:
*
*
* {@link PlainDate#COMPONENT}
* {@link PlainDate#YEAR}
* {@link PlainDate#YEAR_OF_WEEKDATE}
* {@link PlainDate#QUARTER_OF_YEAR}
* {@link PlainDate#MONTH_OF_YEAR}
* {@link PlainDate#MONTH_AS_NUMBER}
* {@link PlainDate#DAY_OF_MONTH}
* {@link PlainDate#DAY_OF_QUARTER}
* {@link PlainDate#DAY_OF_WEEK}
* {@link PlainDate#DAY_OF_YEAR}
* {@link PlainDate#WEEKDAY_IN_MONTH}
* {@link PlainTime#COMPONENT}
* {@link PlainTime#AM_PM_OF_DAY}
* {@link PlainTime#CLOCK_HOUR_OF_AMPM}
* {@link PlainTime#CLOCK_HOUR_OF_DAY}
* {@link PlainTime#DIGITAL_HOUR_OF_AMPM}
* {@link PlainTime#DIGITAL_HOUR_OF_DAY}
* {@link PlainTime#ISO_HOUR}
* {@link PlainTime#MINUTE_OF_HOUR}
* {@link PlainTime#MINUTE_OF_DAY}
* {@link PlainTime#SECOND_OF_MINUTE}
* {@link PlainTime#SECOND_OF_DAY}
* {@link PlainTime#MILLI_OF_SECOND}
* {@link PlainTime#MICRO_OF_SECOND}
* {@link PlainTime#NANO_OF_SECOND}
* {@link PlainTime#MILLI_OF_DAY}
* {@link PlainTime#MICRO_OF_DAY}
* {@link PlainTime#NANO_OF_DAY}
* {@link PlainTime#DECIMAL_HOUR}
* {@link PlainTime#DECIMAL_MINUTE}
* {@link PlainTime#DECIMAL_SECOND}
*
*
* Furthermore, all elements of class {@link Weekmodel} are supported. As
* timestamp units can be used: {@link CalendarUnit} and {@link ClockUnit}.
*
* 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.
*
* @author Meno Hochschild
* @doctags.concurrency
*/
/*[deutsch]
* Komposition aus Datum und Uhrzeit nach dem ISO-8601-Standard.
*
* Registriert sind folgende als Konstanten deklarierte Elemente:
*
*
* {@link PlainDate#COMPONENT}
* {@link PlainDate#YEAR}
* {@link PlainDate#YEAR_OF_WEEKDATE}
* {@link PlainDate#QUARTER_OF_YEAR}
* {@link PlainDate#MONTH_OF_YEAR}
* {@link PlainDate#MONTH_AS_NUMBER}
* {@link PlainDate#DAY_OF_MONTH}
* {@link PlainDate#DAY_OF_QUARTER}
* {@link PlainDate#DAY_OF_WEEK}
* {@link PlainDate#DAY_OF_YEAR}
* {@link PlainDate#WEEKDAY_IN_MONTH}
* {@link PlainTime#COMPONENT}
* {@link PlainTime#AM_PM_OF_DAY}
* {@link PlainTime#CLOCK_HOUR_OF_AMPM}
* {@link PlainTime#CLOCK_HOUR_OF_DAY}
* {@link PlainTime#DIGITAL_HOUR_OF_AMPM}
* {@link PlainTime#DIGITAL_HOUR_OF_DAY}
* {@link PlainTime#ISO_HOUR}
* {@link PlainTime#MINUTE_OF_HOUR}
* {@link PlainTime#MINUTE_OF_DAY}
* {@link PlainTime#SECOND_OF_MINUTE}
* {@link PlainTime#SECOND_OF_DAY}
* {@link PlainTime#MILLI_OF_SECOND}
* {@link PlainTime#MICRO_OF_SECOND}
* {@link PlainTime#NANO_OF_SECOND}
* {@link PlainTime#MILLI_OF_DAY}
* {@link PlainTime#MICRO_OF_DAY}
* {@link PlainTime#NANO_OF_DAY}
* {@link PlainTime#DECIMAL_HOUR}
* {@link PlainTime#DECIMAL_MINUTE}
* {@link PlainTime#DECIMAL_SECOND}
*
*
* Darüberhinaus sind alle Elemente der Klasse {@link Weekmodel}
* nutzbar. Als Zeiteinheiten kommen vor allem {@link CalendarUnit} und
* {@link ClockUnit} in Betracht.
*
* 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.
*
* @author Meno Hochschild
* @doctags.concurrency
*/
@CalendarType("iso8601")
public final class PlainTimestamp
extends TimePoint
implements GregorianDate,
WallTime,
Temporal,
Normalizer {
//~ 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> CHILDREN;
private static final TimeAxis ENGINE;
private static final TimeMetric> STD_METRIC;
static {
Map> 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 builder =
TimeAxis.Builder
.setUp(
IsoUnit.class,
PlainTimestamp.class,
new Merger(),
MIN,
MAX)
.appendElement(
CALENDAR_DATE,
FieldRule.of(CALENDAR_DATE),
DAYS)
.appendElement(
YEAR,
FieldRule.of(YEAR),
YEARS)
.appendElement(
YEAR_OF_WEEKDATE,
FieldRule.of(YEAR_OF_WEEKDATE),
YOWElement.YOWUnit.WEEK_BASED_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,
FieldRule.of(MONTH_AS_NUMBER),
MONTHS)
.appendElement(
DAY_OF_MONTH,
FieldRule.of(DAY_OF_MONTH),
DAYS)
.appendElement(
DAY_OF_WEEK,
FieldRule.of(DAY_OF_WEEK),
DAYS)
.appendElement(
DAY_OF_YEAR,
FieldRule.of(DAY_OF_YEAR),
DAYS)
.appendElement(
DAY_OF_QUARTER,
FieldRule.of(DAY_OF_QUARTER),
DAYS)
.appendElement(
WEEKDAY_IN_MONTH,
FieldRule.of(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,
FieldRule.of(CLOCK_HOUR_OF_AMPM),
HOURS)
.appendElement(
CLOCK_HOUR_OF_DAY,
FieldRule.of(CLOCK_HOUR_OF_DAY),
HOURS)
.appendElement(
DIGITAL_HOUR_OF_AMPM,
FieldRule.of(DIGITAL_HOUR_OF_AMPM),
HOURS)
.appendElement(
DIGITAL_HOUR_OF_DAY,
FieldRule.of(DIGITAL_HOUR_OF_DAY),
HOURS)
.appendElement(
ISO_HOUR,
FieldRule.of(ISO_HOUR),
HOURS)
.appendElement(
MINUTE_OF_HOUR,
FieldRule.of(MINUTE_OF_HOUR),
MINUTES)
.appendElement(
MINUTE_OF_DAY,
FieldRule.of(MINUTE_OF_DAY),
MINUTES)
.appendElement(
SECOND_OF_MINUTE,
FieldRule.of(SECOND_OF_MINUTE),
SECONDS)
.appendElement(
SECOND_OF_DAY,
FieldRule.of(SECOND_OF_DAY),
SECONDS)
.appendElement(
MILLI_OF_SECOND,
FieldRule.of(MILLI_OF_SECOND),
MILLIS)
.appendElement(
MICRO_OF_SECOND,
FieldRule.of(MICRO_OF_SECOND),
MICROS)
.appendElement(
NANO_OF_SECOND,
FieldRule.of(NANO_OF_SECOND),
NANOS)
.appendElement(
MILLI_OF_DAY,
FieldRule.of(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));
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 ----------------------------------------------------------
/**
* Creates a new local timestamp with calendar date and wall time.
*
* The special time value 24:00 will automatically normalized such
* that the resulting timestamp is on starting midnight of following
* day.
*
* @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]
* Erzeugt eine neue Instanz mit Datum und Uhrzeit.
*
* Der Spezialwert T24:00 wird automatisch so normalisiert, daß
* der resultierende Zeitstempel auf Mitternacht des Folgetags zeigt.
*
* @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);
}
/**
* Creates a new local timestamp in minute precision.
*
* The special time value 24:00 will automatically normalized such
* that the resulting timestamp is on starting midnight of following
* day.
*
* @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]
* Erzeugt einen neuen minutengenauen Zeitstempel.
*
* Der Spezialwert T24:00 wird automatisch so normalisiert, daß
* der resultierende Zeitstempel auf Mitternacht des Folgetags zeigt.
*
* @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);
}
/**
* Creates a new local timestamp in second precision.
*
* The special time value 24:00 will automatically normalized such
* that the resulting timestamp is on starting midnight of following
* day.
*
* @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]
* Erzeugt einen neuen sekundengenauen Zeitstempel.
*
* Der Spezialwert T24:00 wird automatisch so normalisiert, daß
* der resultierende Zeitstempel auf Mitternacht des Folgetags zeigt.
*
* @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)
);
}
/**
* Provides the calendar date part.
*
* @return calendar date component
*/
/*[deutsch]
* Liefert die Datumskomponente.
*
* @return calendar date component
*/
public PlainDate getCalendarDate() {
return this.date;
}
/**
* Provides the wall time part.
*
* @return wall time component
*/
/*[deutsch]
* Liefert die Uhrzeitkomponente.
*
* @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();
}
/**
* Adjusts this timestamp by given operator.
*
* @param operator element-related operator
* @return changed copy of this timestamp
* @see ChronoEntity#with(net.time4j.engine.ChronoOperator)
*/
/*[deutsch]
* Passt diesen Zeitstempel mit Hilfe des angegebenen Operators an.
*
* @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());
}
/**
* Adjusts the calendar part of this timestamp.
*
* @param date new calendar date component
* @return changed copy of this timestamp
* @see PlainDate#COMPONENT
*/
/*[deutsch]
* Passt die Datumskomponente an.
*
* @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);
}
/**
* Adjusts the wall time part of this timestamp.
*
* @param time new wall time component
* @return changed copy of this timestamp
* @see PlainTime#COMPONENT
*/
/*[deutsch]
* Passt die Uhrzeitkomponente an.
*
* @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);
}
/**
* Defines the temporal order of date and time as natural order.
*
* The comparison is consistent with {@code equals()}.
*
* @see #isBefore(PlainTimestamp)
* @see #isAfter(PlainTimestamp)
*/
/*[deutsch]
* Definiert eine natürliche Ordnung, die auf der zeitlichen
* Position basiert.
*
* Der Vergleich ist konsistent mit {@code equals()}.
*
* @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();
}
/**
* Creates a canonical representation of the form
* "yyyy-MM-dd'T'HH:mm:ss,fffffffff".
*
* Dependent on the precision (that is last non-zero part of time)
* the time representation might be shorter.
*
* @return canonical ISO-8601-formatted string
* @see PlainTime#toString()
*/
/*[deutsch]
* Erzeugt eine kanonische Darstellung im Format
* "yyyy-MM-dd'T'HH:mm:ss,fffffffff".
*
* Je nach Genauigkeit kann der Uhrzeitanteil auch kürzer sein.
*
* @return canonical ISO-8601-formatted string
* @see PlainTime#toString()
*/
@Override
public String toString() {
return this.date.toString() + this.time.toString();
}
/**
* Creates a new formatter which uses the given pattern in the
* default locale for formatting and parsing plain timestamps.
*
* @param 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]
*
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 PlainTimestamp}-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(PlainTimestamp.class, formatPattern, patternType, Locale.getDefault());
}
/**
* Creates a new formatter which uses the given pattern and locale
* for formatting and parsing plain timestamps.
*
* @param 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]
*
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 PlainTimestamp}-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(PlainTimestamp.class, formatPattern, patternType, locale);
}
/**
* Provides a static access to the associated time axis respective
* chronology 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;
}
/**
* Combines this local timestamp with the timezone offset UTC+00:00
* to a global UTC-moment.
*
* @return global UTC-moment based on this local timestamp interpreted
* at offset UTC+00:00
* @see #at(ZonalOffset)
*/
/*[deutsch]
* Kombiniert diesen lokalen Zeitstempel mit UTC+00:00 zu
* einem globalen UTC-Moment.
*
* @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);
}
/**
* Combines this local timestamp with the given timezone offset
* to a global UTC-moment.
*
* @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]
* Kombiniert diesen lokalen Zeitstempel mit dem angegebenen
* Zeitzonen-Offset zu einem globalen UTC-Moment.
*
* @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);
}
/**
* Combines this local timestamp with the system timezone to a global
* UTC-moment.
*
* @return global UTC-moment based on this local timestamp interpreted
* in system timezone
* @since 1.2
* @see Timezone#ofSystem()
* @see #inTimezone(TZID)
*/
/*[deutsch]
* Kombiniert diesen lokalen Zeitstempel mit der System-Zeitzone
* zu einem UTC-Moment.
*
* @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());
}
/**
* Combines this local timestamp with given timezone to a global
* UTC-moment.
*
* @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]
* Kombiniert diesen lokalen Zeitstempel mit der angegebenen Zeitzone
* zu einem UTC-Moment.
*
* @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));
}
/**
* Combines this local timestamp with given timezone to a global
* UTC-moment.
*
* @param tz timezone
* @return global UTC-moment based on this local timestamp interpreted
* in given timezone
* @since 1.2
* @see Timezone#of(String)
*/
/*[deutsch]
* Kombiniert diesen lokalen Zeitstempel mit der angegebenen Zeitzone
* zu einem UTC-Moment.
*
* @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;
}
/**
* Does this local timestamp exist in given timezone?
*
* @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]
* Existiert dieser Zeitstempel in der angegebenen Zeitzone?
*
* @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);
}
/**
* Normalized given timespan using years, months, days and
* all clock units.
*
* This normalizer can also convert from days to months. Example:
*
*
* 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!)
*
*
* @param timespan to be normalized
* @return normalized duration
* @since 2.0
*/
/*[deutsch]
* Normalisiert die angegebene Zeitspanne, indem Jahre, Monate, Tage
* und alle Uhrzeiteinheiten verwendet werden.
*
* Dieser Normalisierer kann auch von Tagen zu Monaten konvertieren.
* Beispiel:
*
*
* 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!)
*
*
* @param timespan to be normalized
* @return normalized duration
* @since 2.0
*/
@Override
public Duration normalize(TimeSpan extends IsoUnit> timespan) {
return this.until(this.plus(timespan), STD_METRIC);
}
/**
* @doctags.exclude
*/
@Override
protected TimeAxis getChronology() {
return ENGINE;
}
/**
* @doctags.exclude
*/
@Override
protected PlainTimestamp getContext() {
return this;
}
/**
* Erzeugt eine neue Uhrzeit passend zur angegebenen absoluten Zeit.
*
* @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 builder
) {
Set monthly = EnumSet.range(MILLENNIA, MONTHS);
Set daily = EnumSet.range(WEEKS, DAYS);
for (CalendarUnit unit : CalendarUnit.values()) {
builder.appendUnit(
unit,
new CompositeUnitRule(unit),
unit.getLength(),
(unit.compareTo(WEEKS) < 0) ? monthly : daily
);
}
}
private static void registerClockUnits(
TimeAxis.Builder builder
) {
for (ClockUnit unit : ClockUnit.values()) {
builder.appendUnit(
unit,
new CompositeUnitRule(unit),
unit.getLength(),
EnumSet.allOf(ClockUnit.class)
);
}
}
private static void registerExtensions(
TimeAxis.Builder builder
) {
for (ChronoExtension extension : PlainDate.axis().getExtensions()) {
builder.appendExtension(extension);
}
for (ChronoExtension extension : PlainTime.axis().getExtensions()) {
builder.appendExtension(extension);
}
}
/**
* @serialData Uses
* a dedicated serialization form 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:
*
*
* 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);
* }
*
*
* @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 {
//~ Methoden ------------------------------------------------------
@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 {
zone = Timezone.ofSystem();
}
final UnixTime ut = clock.currentTime();
return PlainTimestamp.from(ut, zone.getOffset(ut));
}
@Override
public PlainTimestamp createFrom(
ChronoEntity> entity,
AttributeQuery attributes,
boolean preparsing
) {
Leniency leniency =
attributes.get(Attributes.LENIENCY, Leniency.SMART);
if (entity instanceof UnixTime) {
TZID tzid;
if (attributes.contains(Attributes.TIMEZONE_ID)) {
tzid = attributes.get(Attributes.TIMEZONE_ID);
} else if (leniency.isLax()) {
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.contains(SECOND_OF_MINUTE)
&& (entity.get(SECOND_OF_MINUTE).intValue() == 60);
if (leapsecond) { // temporär, wird später kompensiert
entity.with(SECOND_OF_MINUTE, Integer.valueOf(59));
}
PlainDate date;
PlainTime time;
if (entity.contains(CALENDAR_DATE)) {
date = entity.get(CALENDAR_DATE);
} else {
date = PlainDate.axis().createFrom(entity, attributes, false);
}
if (date == null) {
return null;
} else if (entity.contains(WALL_TIME)) {
time = entity.get(WALL_TIME);
} else {
time = PlainTime.axis().createFrom(entity, attributes, false);
if (
(time == null)
&& leniency.isLax()
) {
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);
}
}
@Override
public ChronoDisplay preformat(
PlainTimestamp context,
AttributeQuery attributes
) {
return context;
}
@Override
public Chronology> preparser() {
return null;
}
}
private static class FieldRule
implements ElementRule {
//~ Instanzvariablen ----------------------------------------------
private final ChronoElement element;
//~ Konstruktoren -------------------------------------------------
private FieldRule(ChronoElement element) {
super();
this.element = element;
}
//~ Methoden ------------------------------------------------------
static FieldRule of(ChronoElement 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 (this.element.isDateElement()) {
return context.date.isValid(this.element, value);
} else if (this.element.isTimeElement()) {
if (Number.class.isAssignableFrom(this.element.getType())) {
if (value == null) {
return false;
}
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.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 DecimalRule
extends FieldRule {
//~ Konstruktoren -------------------------------------------------
DecimalRule(ChronoElement 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 {
//~ 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;
}
}
}