/*
* -----------------------------------------------------------------------
* Copyright © 2013-2014 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (EpochDays.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.engine;
import net.time4j.base.MathUtils;
/**
* <p>Defines elements based on different epoch-related day numbers. </p>
*
* <p>Some day number procedures really use decimal fractions in order to
* represent wall time fractions in calendar dates (for example julian days).
* This enum only supports pure date types of type {@code Calendrical} and
* always uses the largest integer which is still smaller than or equal to
* the decimal fraction in question (mathematically a
* {@code floor()}-function. </p>
*
* <p>Instances of this element class will be automatically registered in
* any chronology which is compatible to {@code Calendrical}. </p>
*
* @author Meno Hochschild
* @see Calendrical
*/
/*[deutsch]
* <p>Definiert Elemente auf der Basis von verschiedenen epochenbezogenen
* Tagesnummern. </p>
*
* <p>Einige Tagesnummerierungsverfahren benutzen real Dezimalbrüche
* zur Darstellung von Uhrzeitanteilen in Datumsangaben. Dieses Enum
* unterstützt nur reine Datumstypen vom Typ {@code Calendrical}
* und benutzt immer die größte Integer-Zahl, die noch kleiner
* oder gleich dem jeweiligen Dezimalbruch ist (mathematisch eine
* {@code floor()}-Funktion. </p>
*
* <p>Instanzen dieser Elementklasse werden in einer zur Klasse
* {@code Calendrical} kompatiblen Chronologie automatisch registriert. </p>
*
* @author Meno Hochschild
* @see Calendrical
*/
public enum EpochDays
implements ChronoElement<Long> {
//~ Statische Felder/Initialisierungen --------------------------------
/**
* <p>Reference point is the introduction of UTC on date [1972-01-01]
* at midnight as day zero. </p>
*/
/*[deutsch]
* <p>Bezugspunkt ist die Einführung von UTC zum Datum [1972-01-01]
* zu Mitternacht als Tag 0. </p>
*/
UTC(2441317),
/**
* <p>Reference point is the first of January 1970 at midnight as
* day zero. </p>
*
* <p>Example: </p>
*
* <pre>
* import static net.time4j.Month.FEBRUARY;
*
* PlainDate date = PlainDate.of(1970, FEBRUARY, 4);
* System.out.println(date.get(EpochDays.UNIX)); // output: 34
* </pre>
*
* <p>Strictly spoken, the UNIX time is counted in seconds. This element
* counts in days based on the relation "1 day = 86400 seconds".
* </p>
*/
/**
* <p>Bezugspunkt ist der erste Januar 1970 Mitternacht als Tag 0. </p>
*
* <p>Beispiel: </p>
*
* <pre>
* import static net.time4j.Month.FEBRUARY;
*
* PlainDate date = PlainDate.of(1970, FEBRUARY, 4);
* System.out.println(date.get(EpochDays.UNIX)); // Ausgabe: 34
* </pre>
*
* <p>Streng genommen wird die UNIX-Zeit in Sekunden gezählt. Dieses
* Element benutzt für den Zweck der Datumsumrechnung die Zählung
* in ganzen Tagen auf der Basis "1 Tag = 86400 Sekunden". </p>
*/
UNIX(2440587),
/**
* <p>Count of days relative to the ISO-date [1858-11-17] as day zero. </p>
*
* <p>Used by astronomers for example. </p>
*/
/*[deutsch]
* <p>Anzahl der Tage relativ zum ISO-Datum [1858-11-17] als Tag 0. </p>
*
* <p>Wird u.a. von Astronomen verwendet. </p>
*/
@FormattableElement(format = "g")
MODIFIED_JULIAN_DATE(2400000),
/**
* <p>Count of days relative to the ISO-date [1900-01-01] as day 1. </p>
*
* <p>Used by MS-Excel in Windows operating systems. </p>
*/
/*[deutsch]
* <p>Anzahl der Tage relativ zum ISO-Datum [1900-01-01] als Tag 1. </p>
*
* <p>Wird von MS-Excel in Windows-Betriebssystemen verwendet. </p>
*/
EXCEL(2415019),
/**
* <p>Count of days relative to the ISO-date [1601-01-01] as day 1. </p>
*
* <p>Used by COBOL for example. </p>
*/
/*[deutsch]
* <p>Anzahl der Tage relativ zum ISO-Datum [1601-01-01] als Tag 1. </p>
*
* <p>Wird u.a. von COBOL verwendet. </p>
*/
ANSI(2305812),
/**
* <p>Count of days relative to the ISO-date [0001-01-01] as day 1. </p>
*
* <p>Used by the autors Nachum Dershowitz and Edward M. Reingold in
* their standard work "CalendarDate Calculations". </p>
*/
/*[deutsch]
* <p>Anzahl der Tage relativ zum ISO-Datum [0001-01-01] als Tag 1. </p>
*
* <p>Wird im Standard-Werk "CalendarDate Calculations" von den
* Autoren Nachum Dershowitz und Edward M. Reingold benutzt. </p>
*/
RATA_DIE(1721424),
/**
* <p>Count of julian days relative to the the julian calendar date
* [1st of January 4713 BC] at noon [12:00] as day zero. </p>
*
* <p>The julian epoch date corresponds to the proleptic ISO-date
* [-4713-11-24]. The ISO-day centered around noon will be taken into
* account whose associated julian day starts at noon. </p>
*/
/*[deutsch]
* <p>Anzahl der julianischen Tage relativ zum julianischen Datum
* [1 Januar 4713 BC] zur Mittagszeit [12:00] als Tag 0. </p>
*
* <p>Das julianische Epochendatum entspricht im proleptischen
* gregorianischen Kalender (ISO) dem Datum [-4713-11-24]. Als Tag
* wird der zentrisch um 12 Uhr Mittag angeordnete Tag gezählt,
* dessen zugeordneter julianischer Tag erst ab Mittag beginnt. </p>
*/
// eigentlich -0.5, Umrechnung zur Mittagszeit
JULIAN_DAY_NUMBER(-1),
/**
* <p>Count of days relative to gregorian cut-over date
* [1582-10-15] as day 1. </p>
*/
/*[deutsch]
* <p>Anzahl der Tage relativ zum gregorianischen Umstellungsdatum
* [1582-10-15] als Tag 1. </p>
*/
LILIAN_DAY_NUMBER(2299159);
//~ Instanzvariablen --------------------------------------------------
private final int offset;
//~ Konstruktoren -----------------------------------------------------
private EpochDays(int julianDays) {
this.offset = julianDays - 2440587 - 2 * 365;
}
//~ Methoden ----------------------------------------------------------
/**
* <p>Converts given day number to a day number based on this epoch
* at reference time of midnight. </p>
*
* @param amount count of days relative to given epoch at noon
* @param epoch epoch reference
* @return count of days relative to this epoch
*/
/*[deutsch]
* <p>Rechnet die angegebene Tageszahl in eine Tageszahl auf der
* Nummerierungsbasis dieser Instanz zur Mittagszeit um. </p>
*
* @param amount count of days relative to given epoch at noon
* @param epoch epoch reference
* @return count of days relative to this epoch
*/
public long transform(
long amount,
EpochDays epoch
) {
return MathUtils.safeAdd(
amount,
(epoch.offset - this.offset)
);
}
@Override
public Class<Long> getType() {
return Long.class;
}
/**
* <p>Defines the format symbol. </p>
*
* @return "g" if MODIFIED_JULIAN_DATE else ASCII-0
*/
/*[deutsch]
* <p>Definiert das Formatsymbol. </p>
*
* @return "g" if MODIFIED_JULIAN_DATE else ASCII-0
*/
@Override
public char getSymbol() {
return ((this == MODIFIED_JULIAN_DATE) ? 'g' : '\u0000');
}
@Override
public int compare(
ChronoDisplay o1,
ChronoDisplay o2
) {
return o1.get(this).compareTo(o2.get(this));
}
@Override
public Long getDefaultMinimum() {
// PlainTime.MIN.get(EpochDays.UTC) - offset
return Long.valueOf(-365243219892L - this.offset);
}
@Override
public Long getDefaultMaximum() {
// PlainTime.MAX.get(EpochDays.UTC) - offset
return Long.valueOf(365241779741L - this.offset);
}
/**
* <p>This element is a date element. </p>
*
* @return {@code true}
*/
/*[deutsch]
* <p>Dieses Element ist ein Datumselement. </p>
*
* @return {@code true}
*/
@Override
public boolean isDateElement() {
return true;
}
/**
* <p>This element is no wall time element. </p>
*
* @return {@code false}
*/
/*[deutsch]
* <p>Dieses Element ist kein Uhrzeitelement. </p>
*
* @return {@code false}
*/
@Override
public boolean isTimeElement() {
return false;
}
@Override
public boolean isLenient() {
return false;
}
/**
* <p>Leitet eine Regel ab. </p>
*
* @param <D> generic chronology type
* @param calsys calendar system
* @return new element rule for this epoch reference
*/
<D extends ChronoEntity<D>> ElementRule<D, Long> derive(CalendarSystem<D> calsys) {
return new Rule<>(this, calsys);
}
//~ Innere Klassen ----------------------------------------------------
private static class Rule<D extends ChronoEntity<D>>
implements ElementRule<D, Long> {
//~ Statische Felder/Initialisierungen ----------------------------
private static final int UTC_OFFSET = 2 * 365;
//~ Instanzvariablen ----------------------------------------------
private final EpochDays element;
private final CalendarSystem<D> calsys;
//~ Konstruktoren -------------------------------------------------
Rule(
EpochDays element,
CalendarSystem<D> calsys
) {
super();
this.element = element;
this.calsys = calsys;
}
//~ Methoden ------------------------------------------------------
@Override
public Long getValue(D context) {
long days =
this.element.transform(
this.calsys.transform(context) + UTC_OFFSET,
EpochDays.UNIX);
return Long.valueOf(days);
}
@Override
public boolean isValid(
D context,
Long value
) {
if (value == null) {
return false;
}
long days =
MathUtils.safeSubtract(
EpochDays.UNIX.transform(value.longValue(), this.element),
UTC_OFFSET
);
return (
(days <= this.calsys.getMaximumSinceUTC())
&& (days >= this.calsys.getMinimumSinceUTC())
);
}
@Override
public D withValue(
D context,
Long value,
boolean lenient
) {
if (value == null) {
throw new IllegalArgumentException("Missing epoch day value.");
}
long utcDays =
MathUtils.safeSubtract(
EpochDays.UNIX.transform(value.longValue(), this.element),
UTC_OFFSET
);
return this.calsys.transform(utcDays);
}
@Override
public Long getMinimum(D context) {
long days =
this.element.transform(
this.calsys.getMinimumSinceUTC() + UTC_OFFSET,
EpochDays.UNIX
);
return Long.valueOf(days);
}
@Override
public Long getMaximum(D context) {
long days =
this.element.transform(
this.calsys.getMaximumSinceUTC() + UTC_OFFSET,
EpochDays.UNIX
);
return Long.valueOf(days);
}
@Override
public ChronoElement<?> getChildAtFloor(D context) {
return null;
}
@Override
public ChronoElement<?> getChildAtCeiling(D context) {
return null;
}
}
}