/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (EthiopianTime.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.calendar; import net.time4j.Meridiem; import net.time4j.PlainTime; import net.time4j.base.MathUtils; import net.time4j.base.TimeSource; import net.time4j.calendar.service.EthiopianExtension; import net.time4j.engine.AttributeQuery; import net.time4j.engine.BasicElement; import net.time4j.engine.ChronoDisplay; import net.time4j.engine.ChronoElement; import net.time4j.engine.ChronoEntity; import net.time4j.engine.ChronoExtension; import net.time4j.engine.ChronoMerger; import net.time4j.engine.ChronoUnit; import net.time4j.engine.Chronology; import net.time4j.engine.DisplayStyle; import net.time4j.engine.ElementRule; import net.time4j.engine.FormattableElement; import net.time4j.engine.StartOfDay; import net.time4j.engine.Temporal; import net.time4j.engine.TimeAxis; import net.time4j.engine.TimePoint; import net.time4j.engine.UnitRule; import net.time4j.format.Attributes; import net.time4j.format.CalendarType; import net.time4j.format.Leniency; import net.time4j.format.LocalizedPatternSupport; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.text.DateFormat; import java.util.EnumSet; import java.util.Locale; import java.util.Set; /** * <p>Represents the 12-hour-time in second precision used in Ethiopia * starting in the morning at 6 AM as zero point. </p> * * <table border="1"> * <caption>Mapping table</caption> * <tr> * <th>ISO-8601</th> * <th>Ethiopic</th> * </tr> * <tr> * <td>00:00</td> * <td>6 (night)</td> * </tr> * <tr> * <td>01:00</td> * <td>7 (night)</td> * </tr> * <tr> * <td>02:00</td> * <td>8 (night)</td> * </tr> * <tr> * <td>03:00</td> * <td>9 (night)</td> * </tr> * <tr> * <td>04:00</td> * <td>10 (night)</td> * </tr> * <tr> * <td>05:00</td> * <td>11 (night)</td> * </tr> * <tr> * <td>06:00</td> * <td>12 (day)</td> * </tr> * <tr> * <td>07:00</td> * <td>1 (day)</td> * </tr> * <tr> * <td>08:00</td> * <td>2 (day)</td> * </tr> * <tr> * <td>09:00</td> * <td>3 (day)</td> * </tr> * <tr> * <td>10:00</td> * <td>4 (day)</td> * </tr> * <tr> * <td>11:00</td> * <td>5 (day)</td> * </tr> * <tr> * <td>12:00</td> * <td>6 (day)</td> * </tr> * <tr> * <td>13:00</td> * <td>7 (day)</td> * </tr> * <tr> * <td>14:00</td> * <td>8 (day)</td> * </tr> * <tr> * <td>15:00</td> * <td>9 (day)</td> * </tr> * <tr> * <td>16:00</td> * <td>10 (day)</td> * </tr> * <tr> * <td>17:00</td> * <td>11 (day)</td> * </tr> * <tr> * <td>18:00</td> * <td>12 (night)</td> * </tr> * <tr> * <td>19:00</td> * <td>1 (night)</td> * </tr> * <tr> * <td>20:00</td> * <td>2 (night)</td> * </tr> * <tr> * <td>21:00</td> * <td>3 (night)</td> * </tr> * <tr> * <td>22:00</td> * <td>4 (night)</td> * </tr> * <tr> * <td>23:00</td> * <td>5 (night)</td> * </tr> * </table> * * <p>This class applies following calendar specific day period scheme by default: </p> * * <table border="1"> * <caption>Standard scheme of day periods</caption> * <tr> * <th>Interval (ISO-8601)</th> * <th>Day period (transscription)</th> * </tr> * <tr> * <td>23:00-00:59</td> * <td>Ekul Leilit</td> * </tr> * <tr> * <td>01:00-03:59</td> * <td>Wedek'et</td> * </tr> * <tr> * <td>04:00-05:59</td> * <td>Nigat</td> * </tr> * <tr> * <td>06:00-08:59</td> * <td>T'uwat</td> * </tr> * <tr> * <td>09:00-11:59</td> * <td>Refad</td> * </tr> * <tr> * <td>12:00-12:59</td> * <td>Ekul K'en</td> * </tr> * <tr> * <td>13:00-15:59</td> * <td>Kese'at Behwala</td> * </tr> * <tr> * <td>16:00-17:59</td> * <td>Wedemata</td> * </tr> * <tr> * <td>18:00-19:59</td> * <td>Sidenegiz</td> * </tr> * <tr> * <td>20:00-22:59</td> * <td>Mishet</td> * </tr> * </table> * * <p>Following elements which are declared as constants are registered by this class: </p> * * <ul> * <li>{@link #ISO_TIME}</li> * <li>{@link #AM_PM_OF_DAY}</li> * <li>{@link #ETHIOPIAN_HOUR}</li> * <li>{@link #DIGITAL_HOUR_OF_DAY}</li> * <li>{@link #MINUTE_OF_HOUR}</li> * <li>{@link #SECOND_OF_MINUTE}</li> * </ul> * * @author Meno Hochschild * @since 3.11/4.8 * @doctags.concurrency {immutable} */ /*[deutsch] * <p>Repräsentiert die 12-Stunden-Uhr in Sekundengenauigkeit, die in Äthiopien verwendet wird * und morgens um 6 Uhr startet. </p> * * <table border="1"> * <caption>Konversionstabelle</caption> * <tr> * <th>ISO-8601</th> * <th>Äthiopisch</th> * </tr> * <tr> * <td>00:00</td> * <td>6 (night)</td> * </tr> * <tr> * <td>01:00</td> * <td>7 (night)</td> * </tr> * <tr> * <td>02:00</td> * <td>8 (night)</td> * </tr> * <tr> * <td>03:00</td> * <td>9 (night)</td> * </tr> * <tr> * <td>04:00</td> * <td>10 (night)</td> * </tr> * <tr> * <td>05:00</td> * <td>11 (night)</td> * </tr> * <tr> * <td>06:00</td> * <td>12 (day)</td> * </tr> * <tr> * <td>07:00</td> * <td>1 (day)</td> * </tr> * <tr> * <td>08:00</td> * <td>2 (day)</td> * </tr> * <tr> * <td>09:00</td> * <td>3 (day)</td> * </tr> * <tr> * <td>10:00</td> * <td>4 (day)</td> * </tr> * <tr> * <td>11:00</td> * <td>5 (day)</td> * </tr> * <tr> * <td>12:00</td> * <td>6 (day)</td> * </tr> * <tr> * <td>13:00</td> * <td>7 (day)</td> * </tr> * <tr> * <td>14:00</td> * <td>8 (day)</td> * </tr> * <tr> * <td>15:00</td> * <td>9 (day)</td> * </tr> * <tr> * <td>16:00</td> * <td>10 (day)</td> * </tr> * <tr> * <td>17:00</td> * <td>11 (day)</td> * </tr> * <tr> * <td>18:00</td> * <td>12 (night)</td> * </tr> * <tr> * <td>19:00</td> * <td>1 (night)</td> * </tr> * <tr> * <td>20:00</td> * <td>2 (night)</td> * </tr> * <tr> * <td>21:00</td> * <td>3 (night)</td> * </tr> * <tr> * <td>22:00</td> * <td>4 (night)</td> * </tr> * <tr> * <td>23:00</td> * <td>5 (night)</td> * </tr> * </table> * * <p>Diese Klasse wendet folgendes Tagesabschnittsschema per Vorgabe an: </p> * * <table border="1"> * <caption>Standard-Schema von Tagesabschnitten</caption> * <tr> * <th>Intervall (ISO-8601)</th> * <th>Tagesabschnitt (Transskript)</th> * </tr> * <tr> * <td>23:00-00:59</td> * <td>Ekul Leilit</td> * </tr> * <tr> * <td>01:00-03:59</td> * <td>Wedek'et</td> * </tr> * <tr> * <td>04:00-05:59</td> * <td>Nigat</td> * </tr> * <tr> * <td>06:00-08:59</td> * <td>T'uwat</td> * </tr> * <tr> * <td>09:00-11:59</td> * <td>Refad</td> * </tr> * <tr> * <td>12:00-12:59</td> * <td>Ekul K'en</td> * </tr> * <tr> * <td>13:00-15:59</td> * <td>Kese'at Behwala</td> * </tr> * <tr> * <td>16:00-17:59</td> * <td>Wedemata</td> * </tr> * <tr> * <td>18:00-19:59</td> * <td>Sidenegiz</td> * </tr> * <tr> * <td>20:00-22:59</td> * <td>Mishet</td> * </tr> * </table> * * <p>Registriert sind folgende als Konstanten deklarierte Elemente: </p> * * <ul> * <li>{@link #ISO_TIME}</li> * <li>{@link #AM_PM_OF_DAY}</li> * <li>{@link #ETHIOPIAN_HOUR}</li> * <li>{@link #DIGITAL_HOUR_OF_DAY}</li> * <li>{@link #MINUTE_OF_HOUR}</li> * <li>{@link #SECOND_OF_MINUTE}</li> * </ul> * * @author Meno Hochschild * @since 3.11/4.8 * @doctags.concurrency {immutable} */ @CalendarType("ethiopic") public final class EthiopianTime extends TimePoint<EthiopianTime.Unit, EthiopianTime> implements Temporal<EthiopianTime>, LocalizedPatternSupport { //~ Statische Felder/Initialisierungen -------------------------------- private static final int ETHIOPIAN_HOUR_INDEX = 0; private static final int DIGITAL_HOUR_INDEX = 1; private static final int MINUTE_INDEX = 2; private static final int SECOND_INDEX = 3; /** * Behaves like {@link PlainTime#COMPONENT} and serves for conversion. * * @since 3.12/4.9 */ /*[deutsch] * Verhält sich wie {@link PlainTime#COMPONENT} und dient der Konversion. * * @since 3.12/4.9 */ public static final ChronoElement<PlainTime> ISO_TIME = PlainTime.COMPONENT; /** * Behaves like {@link PlainTime#AM_PM_OF_DAY}. */ /*[deutsch] * Verhält sich wie {@link PlainTime#AM_PM_OF_DAY}. */ @FormattableElement(format = "a") public static final ChronoElement<Meridiem> AM_PM_OF_DAY = PlainTime.AM_PM_OF_DAY; /** * The Ethiopian hour with the range 1-12 which is 6 hours behind the western clock. */ /*[deutsch] * Die äthiopische Stunde mit dem Bereich 1-12, die der westlichen Uhr um 6 Stunden nacheilt. */ @FormattableElement(format = "h") public static final ChronoElement<Integer> ETHIOPIAN_HOUR = EthiopianHour.ELEMENT; /** * Behaves like {@link PlainTime#DIGITAL_HOUR_OF_DAY} with the hour range 0-23. */ /*[deutsch] * Verhält sich wie {@link PlainTime#DIGITAL_HOUR_OF_DAY} mit dem Stundenbereich 0-23. */ @FormattableElement(format = "H") public static final ChronoElement<Integer> DIGITAL_HOUR_OF_DAY = PlainTime.DIGITAL_HOUR_OF_DAY; /** * The minute of hour with the range 0-59. */ /*[deutsch] * Die Minute innerhalb einer Stunde mit dem Bereich 0-59. */ @FormattableElement(format = "m") public static final ChronoElement<Integer> MINUTE_OF_HOUR = PlainTime.MINUTE_OF_HOUR; /** * The second of minute with the range 0-59. */ /*[deutsch] * Die Sekunde innerhalb einer Minute mit dem Bereich 0-59. */ @FormattableElement(format = "s") public static final ChronoElement<Integer> SECOND_OF_MINUTE = PlainTime.SECOND_OF_MINUTE; private static final EthiopianTime MIN; private static final EthiopianTime MAX; private static final TimeAxis<Unit, EthiopianTime> ENGINE; static { MIN = new EthiopianTime(6, 0, 0); MAX = new EthiopianTime(5, 59, 59); TimeAxis.Builder<Unit, EthiopianTime> builder = TimeAxis.Builder.setUp( Unit.class, EthiopianTime.class, new Merger(), EthiopianTime.MIN, EthiopianTime.MAX) .appendElement( AM_PM_OF_DAY, new MeridiemRule()) .appendElement( ISO_TIME, new TimeRule()) .appendElement( ETHIOPIAN_HOUR, new IntegerElementRule(ETHIOPIAN_HOUR_INDEX), Unit.HOURS) .appendElement( DIGITAL_HOUR_OF_DAY, new IntegerElementRule(DIGITAL_HOUR_INDEX), Unit.HOURS) .appendElement( MINUTE_OF_HOUR, new IntegerElementRule(MINUTE_INDEX), Unit.MINUTES) .appendElement( SECOND_OF_MINUTE, new IntegerElementRule(SECOND_INDEX), Unit.SECONDS); registerUnits(builder); registerExtensions(builder); ENGINE = builder.build(); } private static final long serialVersionUID = 3576122091324773241L; //~ Instanzvariablen -------------------------------------------------- private transient final int hour24; private transient final int minute; private transient final int second; //~ Konstruktoren ----------------------------------------------------- private EthiopianTime( int hour24, int minute, int second ) { super(); if (hour24 < 0 || hour24 > 23) { throw new IllegalArgumentException( "HOUR_OF_DAY out of range: " + hour24); } if (minute < 0 || minute > 59) { throw new IllegalArgumentException( "MINUTE_OF_HOUR out of range: " + minute); } if (second < 0 || second > 59) { throw new IllegalArgumentException( "SECOND_OF_MINUTE out of range: " + second); } this.hour24 = hour24; this.minute = minute; this.second = second; } //~ Methoden ---------------------------------------------------------- /** * <p>Equivalent to {@link #ofDay(int, int, int) ofDay(hour, minute, 0)}. </p> * * @param hour ethiopian hour in range 1-12 during day * @param minute minute of hour * @return ethiopian time * @since 3.11/4.8 */ /*[deutsch] * <p>Äquivalent zu {@link #ofDay(int, int, int) ofDay(hour, minute, 0)}. </p> * * @param hour ethiopian hour in range 1-12 during day * @param minute minute of hour * @return ethiopian time * @since 3.11/4.8 */ public static EthiopianTime ofDay( int hour, int minute ) { return EthiopianTime.of(false, hour, minute, 0); } /** * <p>Creates a new instance for times in the ISO-range (06:00:00-17:59:59). </p> * * @param hour ethiopian hour in range 1-12 during day * @param minute minute of hour * @param second second of hour * @return ethiopian time * @see #ofNight(int, int, int) * @since 3.11/4.8 */ /*[deutsch] * <p>Erzeugt eine neue Instanz im ISO-Bereich (06:00:00-17:59:59). </p> * * @param hour ethiopian hour in range 1-12 during day * @param minute minute of hour * @param second second of hour * @return ethiopian time * @see #ofNight(int, int, int) * @since 3.11/4.8 */ public static EthiopianTime ofDay( int hour, int minute, int second ) { return EthiopianTime.of(false, hour, minute, second); } /** * <p>Equivalent to {@link #ofNight(int, int, int) ofNight(hour, minute, 0)}. </p> * * @param hour ethiopian hour in range 1-12 during night * @param minute minute of hour * @return ethiopian time * @since 3.11/4.8 */ /*[deutsch] * <p>Äquivalent zu {@link #ofNight(int, int, int) ofNight(hour, minute, 0)}. </p> * * @param hour ethiopian hour in range 1-12 during night * @param minute minute of hour * @return ethiopian time * @since 3.11/4.8 */ public static EthiopianTime ofNight( int hour, int minute ) { return EthiopianTime.of(true, hour, minute, 0); } /** * <p>Creates a new instance for times in the ISO-range (18:00:00-05:59:59 around midnight). </p> * * @param hour ethiopian hour in range 1-12 during night * @param minute minute of hour * @param second second of hour * @return ethiopian time * @see #ofDay(int, int, int) * @since 3.11/4.8 */ /*[deutsch] * <p>Erzeugt eine neue Instanz im ISO-Bereich (18:00:00-05:59:59 um Mitternacht herum). </p> * * @param hour ethiopian hour in range 1-12 during night * @param minute minute of hour * @param second second of hour * @return ethiopian time * @see #ofDay(int, int, int) * @since 3.11/4.8 */ public static EthiopianTime ofNight( int hour, int minute, int second ) { return EthiopianTime.of(true, hour, minute, second); } /** * <p>Is this time during day (ISO 06:00:00-17:59:59)? </p> * * @return boolean * @since 3.11/4.8 */ /*[deutsch] * <p>Liegt diese Uhrzeit am Tage (ISO 06:00:00-17:59:59)? </p> * * @return boolean * @since 3.11/4.8 */ public boolean isDay() { return ((this.hour24 >= 6) && (this.hour24 < 18)); } /** * <p>Is this time during night (ISO 18:00:00-05:59:59)? </p> * * @return boolean * @since 3.11/4.8 */ /*[deutsch] * <p>Liegt diese Uhrzeit in der Nacht (ISO 18:00:00-05:59:59)? </p> * * @return boolean * @since 3.11/4.8 */ public boolean isNight() { return !this.isDay(); } /** * <p>Yields the Ethiopian clock hour in range 1-12. </p> * * @return hour in range 1-12 * @see #isDay() * @see #isNight() * @since 3.11/4.8 */ /*[deutsch] * <p>Liefert die äthiopische Stunde im Bereich 1-12. </p> * * @return hour in range 1-12 * @see #isDay() * @see #isNight() * @since 3.11/4.8 */ public int getHour() { int h = this.hour24 - 6; if (h < 0) { h += 12; } else if (h >= 12) { h -= 12; } return ((h == 0) ? 12 : h); } /** * <p>Yields the minute of hour. </p> * * @return int in range 0-59 * @since 3.11/4.8 */ /*[deutsch] * <p>Liefert die Minute innerhalb der aktuellen Stunde. </p> * * @return int in range 0-59 * @since 3.11/4.8 */ public int getMinute() { return this.minute; } /** * <p>Yields the second of minute. </p> * * @return int in range 0-59 * @since 3.11/4.8 */ /*[deutsch] * <p>Liefert die Sekunde innerhalb der aktuellen Minute. </p> * * @return int in range 0-59 * @since 3.11/4.8 */ public int getSecond() { return this.second; } @Override public boolean isAfter(EthiopianTime other) { return (this.getTimeOfDay() > other.getTimeOfDay()); } @Override public boolean isBefore(EthiopianTime other) { return (this.getTimeOfDay() < other.getTimeOfDay()); } @Override public boolean isSimultaneous(EthiopianTime other) { return (this.getTimeOfDay() == other.getTimeOfDay()); } @Override public int compareTo(EthiopianTime other) { return (this.getTimeOfDay() - other.getTimeOfDay()); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof EthiopianTime) { EthiopianTime that = (EthiopianTime) obj; return (this.getTimeOfDay() == that.getTimeOfDay()); } else { return false; } } @Override public int hashCode() { return this.getTimeOfDay(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("ethiopic-"); sb.append(this.isDay() ? "day-" : "night-"); sb.append(this.getHour()); sb.append(':'); if (this.minute < 10) { sb.append('0'); } sb.append(this.minute); sb.append(':'); if (this.second < 10) { sb.append('0'); } sb.append(this.second); return sb.toString(); } /** * <p>Converts this instance to its ISO-analogy. </p> * * @return PlainTime * @since 3.11/4.8 */ /*[deutsch] * <p>Konvertiert diese Instanz zu ihrem ISO-Analogon. </p> * * @return PlainTime * @since 3.11/4.8 */ public PlainTime toISO() { return PlainTime.of(this.hour24, this.minute, this.second); } /** * <p>Converts given ISO-time to Ethiopian time. </p> * * <p>The special time 24:00 will be treated like 00:00. Fractions of second are lost during conversion. </p> * * @param time ISO-time * @return Ethiopian time * @since 3.11/4.8 */ /*[deutsch] * <p>Konvertiert die angegebene ISO-Uhrzeit zu einer äthiopischen Uhrzeit. </p> * * <p>Die spezielle Uhrzeit 24:00 wird wie 00:00 gehandhabt. Sekundenbruchteile gehen während der * Konversion verloren. </p> * * @param time ISO-time * @return Ethiopian time * @since 3.11/4.8 */ public static EthiopianTime from(PlainTime time) { int h24 = time.getHour(); return new EthiopianTime((h24 == 24) ? 0 : h24, time.getMinute(), time.getSecond()); } /** * <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<EthiopianTime.Unit, EthiopianTime> axis() { return ENGINE; } @Override protected TimeAxis<Unit, EthiopianTime> getChronology() { return ENGINE; } @Override protected EthiopianTime getContext() { return this; } private static EthiopianTime of( boolean night, int hour, int minute, int second ) { if (hour < 1 || hour > 12) { throw new IllegalArgumentException("Hour out of range 1-12: " + hour); } int h = ((hour == 12) ? 0 : hour); int hour24 = h + 6; if (night) { hour24 += 12; if (hour24 >= 24) { hour24 -= 24; } } return new EthiopianTime(hour24, minute, second); } private int getTimeOfDay() { return ( this.second + this.minute * 60 + ((this.hour24 < 6) ? this.hour24 + 24 : this.hour24) * 3600 ); } private static void registerUnits(TimeAxis.Builder<Unit, EthiopianTime> builder) { Set<Unit> convertibles = EnumSet.allOf(Unit.class); for (Unit unit : Unit.values()) { builder.appendUnit( unit, new ClockUnitRule(unit), unit.getLength(), convertibles); } } private static void registerExtensions(TimeAxis.Builder<Unit, EthiopianTime> builder) { builder.appendExtension(new EthiopianExtension()); for (ChronoExtension extension : PlainTime.axis().getExtensions()) { Set<ChronoElement<?>> elements = extension.getElements(Locale.ROOT, Attributes.empty()); if (elements.size() == 2) { for (ChronoElement<?> element : elements) { if (element.name().endsWith("_DAY_PERIOD")) { builder.appendExtension(extension); return; } } } } } /** * @serialData Uses <a href="../../../serialized-form.html#net.time4j.calendar.SPX"> * a dedicated serialization form</a> as proxy. The first byte contains * the type-ID {@code 5}. Then the time of day in seconds using western * format is written as integer. * * @return replacement object in serialization graph */ private Object writeReplace() { return new SPX(this, SPX.ETHIOPIAN_TIME); } /** * @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 ---------------------------------------------------- /** * <p>Defines the time units for the Ethiopian clock time. </p> * * @since 3.11/4.8 */ /*[deutsch] * <p>Definiert die Zeiteinheiten für die äthiopische Uhrzeit. </p> * * @since 3.11/4.8 */ public static enum Unit implements ChronoUnit { //~ Statische Felder/Initialisierungen ---------------------------- HOURS(3600.0), MINUTES(60.0), SECONDS(1.0); //~ Instanzvariablen ---------------------------------------------- private transient final double length; //~ Konstruktoren ------------------------------------------------- private Unit(double length) { this.length = length; } //~ Methoden ------------------------------------------------------ @Override public double getLength() { return this.length; } /** * <p>Calculates the difference between given Ethiopian times in this unit. </p> * * @param start start time (inclusive) * @param end end time (exclusive) * @return difference counted in this unit * @since 3.11/4.8 */ /*[deutsch] * <p>Berechnet die Differenz zwischen den angegebenen Zeitparametern in dieser Zeiteinheit. </p> * * @param start start time (inclusive) * @param end end time (exclusive) * @return difference counted in this unit * @since 3.11/4.8 */ public int between( EthiopianTime start, EthiopianTime end ) { return (int) start.until(end, this); // safe } } private static class ClockUnitRule implements UnitRule<EthiopianTime> { //~ Instanzvariablen ---------------------------------------------- private final Unit unit; //~ Konstruktoren ------------------------------------------------- private ClockUnitRule(Unit unit) { super(); this.unit = unit; } //~ Methoden ------------------------------------------------------ @Override public EthiopianTime addTo( EthiopianTime context, long amount ) { if (amount == 0) { return context; } long hours; long minutes; long seconds; int minute = context.minute; int second = context.second; switch (this.unit) { case HOURS: hours = MathUtils.safeAdd(context.hour24, amount); break; case MINUTES: minutes = MathUtils.safeAdd(context.minute, amount); hours = MathUtils.safeAdd( context.hour24, MathUtils.floorDivide(minutes, 60)); minute = MathUtils.floorModulo(minutes, 60); break; case SECONDS: seconds = MathUtils.safeAdd(context.second, amount); minutes = MathUtils.safeAdd( context.minute, MathUtils.floorDivide(seconds, 60)); hours = MathUtils.safeAdd( context.hour24, MathUtils.floorDivide(minutes, 60)); minute = MathUtils.floorModulo(minutes, 60); second = MathUtils.floorModulo(seconds, 60); break; default: throw new UnsupportedOperationException(this.unit.name()); } int h24 = MathUtils.floorModulo(hours, 24); return new EthiopianTime(h24, minute, second); } @Override public long between( EthiopianTime start, EthiopianTime end ) { long delta = (end.getTimeOfDay() - start.getTimeOfDay()); long factor; switch (this.unit) { case HOURS: factor = 3600; break; case MINUTES: factor = 60; break; case SECONDS: factor = 1; break; default: throw new UnsupportedOperationException(this.unit.name()); } return delta / factor; } } private static class EthiopianHour extends BasicElement<Integer> { //~ Statische Felder/Initialisierungen ---------------------------- private static final long serialVersionUID = -2095959121446847268L; static final EthiopianHour ELEMENT = new EthiopianHour(); //~ Konstruktoren ------------------------------------------------- private EthiopianHour() { super("ETHIOPIAN_HOUR"); } //~ Methoden ------------------------------------------------------ @Override public Class<Integer> getType() { return Integer.class; } @Override public char getSymbol() { return 'h'; } @Override public Integer getDefaultMinimum() { return Integer.valueOf(1); } @Override public Integer getDefaultMaximum() { return Integer.valueOf(12); } @Override public boolean isDateElement() { return false; } @Override public boolean isTimeElement() { return true; } @Override protected ChronoElement<?> getParent() { return PlainTime.CLOCK_HOUR_OF_AMPM; } @Override protected <T extends ChronoEntity<T>> ElementRule<T, Integer> derive(Chronology<T> chronology) { if (PlainTime.axis().equals(chronology)) { return new GeneralHourRule<>(); } return null; } @Override protected boolean isSingleton() { return true; } private Object readResolve() { return ELEMENT; } } private static class TimeRule implements ElementRule<EthiopianTime, PlainTime> { //~ Methoden ------------------------------------------------------ @Override public PlainTime getValue(EthiopianTime context) { return context.toISO(); } @Override public PlainTime getMinimum(EthiopianTime context) { return PlainTime.midnightAtStartOfDay(); } @Override public PlainTime getMaximum(EthiopianTime context) { return PlainTime.of(23, 59, 59); } @Override public boolean isValid( EthiopianTime context, PlainTime value ) { return (value != null); } @Override public EthiopianTime withValue( EthiopianTime context, PlainTime value, boolean lenient ) { if (value == null) { throw new IllegalArgumentException("Missing time value."); } return EthiopianTime.from(value); } @Override public ChronoElement<?> getChildAtFloor(EthiopianTime context) { return null; } @Override public ChronoElement<?> getChildAtCeiling(EthiopianTime context) { return null; } } private static class MeridiemRule implements ElementRule<EthiopianTime, Meridiem> { //~ Methoden ------------------------------------------------------ @Override public Meridiem getValue(EthiopianTime context) { return ((context.hour24 < 12) ? Meridiem.AM : Meridiem.PM); } @Override public EthiopianTime withValue( EthiopianTime context, Meridiem value, boolean lenient ) { int h = context.hour24; if (value == null) { throw new IllegalArgumentException("Missing am/pm-value."); } else if (value == Meridiem.AM) { if (h >= 12) { h -= 12; } } else if (value == Meridiem.PM) { if (h < 12) { h += 12; } } return new EthiopianTime(h, context.minute, context.second); } @Override public boolean isValid( EthiopianTime context, Meridiem value ) { return (value != null); } @Override public Meridiem getMinimum(EthiopianTime context) { return Meridiem.AM; } @Override public Meridiem getMaximum(EthiopianTime context) { return Meridiem.PM; } @Override public ChronoElement<?> getChildAtFloor(EthiopianTime context) { return null; } @Override public ChronoElement<?> getChildAtCeiling(EthiopianTime context) { return null; } } private static class GeneralHourRule<T extends ChronoEntity<T>> implements ElementRule<T, Integer> { //~ Methoden ------------------------------------------------------ @Override public Integer getValue(T context) { PlainTime time = context.get(PlainTime.COMPONENT); return EthiopianTime.from(time).getHour(); } @Override public Integer getMinimum(T context) { return Integer.valueOf(1); } @Override public Integer getMaximum(T context) { return Integer.valueOf(12); } @Override public boolean isValid( T context, Integer value ) { PlainTime time = context.get(PlainTime.COMPONENT); return EthiopianTime.from(time).isValid(ETHIOPIAN_HOUR, value); } @Override public T withValue( T context, Integer value, boolean lenient ) { PlainTime time = context.get(PlainTime.COMPONENT); EthiopianTime ethio = EthiopianTime.from(time).with(ETHIOPIAN_HOUR, value); return context.with(PlainTime.COMPONENT, ethio.toISO()); } @Override public ChronoElement<?> getChildAtFloor(T context) { return null; } @Override public ChronoElement<?> getChildAtCeiling(T context) { return null; } } private static class IntegerElementRule implements ElementRule<EthiopianTime, Integer> { //~ Instanzvariablen ---------------------------------------------- private final int index; //~ Konstruktoren ------------------------------------------------- IntegerElementRule(int index) { super(); this.index = index; } //~ Methoden ------------------------------------------------------ @Override public Integer getValue(EthiopianTime context) { switch (this.index) { case ETHIOPIAN_HOUR_INDEX: return context.getHour(); case DIGITAL_HOUR_INDEX: return context.hour24; case MINUTE_INDEX: return context.minute; case SECOND_INDEX: return context.second; default: throw new UnsupportedOperationException("Unknown element index: " + this.index); } } @Override public Integer getMinimum(EthiopianTime context) { switch (this.index) { case ETHIOPIAN_HOUR_INDEX: return Integer.valueOf(1); case DIGITAL_HOUR_INDEX: case MINUTE_INDEX: case SECOND_INDEX: return Integer.valueOf(0); default: throw new UnsupportedOperationException("Unknown element index: " + this.index); } } @Override public Integer getMaximum(EthiopianTime context) { switch (this.index) { case ETHIOPIAN_HOUR_INDEX: return Integer.valueOf(12); case DIGITAL_HOUR_INDEX: return Integer.valueOf(23); case MINUTE_INDEX: case SECOND_INDEX: return Integer.valueOf(59); default: throw new UnsupportedOperationException("Unknown element index: " + this.index); } } @Override public boolean isValid( EthiopianTime context, Integer value ) { if (value == null) { return false; } return ( (this.getMinimum(context).compareTo(value) <= 0) && (this.getMaximum(context).compareTo(value) >= 0) ); } @Override public EthiopianTime withValue( EthiopianTime context, Integer value, boolean lenient ) { if (value == null) { throw new IllegalArgumentException("Missing element value."); } int v = value.intValue(); switch (this.index) { case ETHIOPIAN_HOUR_INDEX: if (context.isDay()) { return EthiopianTime.ofDay(v, context.minute, context.second); } else { return EthiopianTime.ofNight(v, context.minute, context.second); } case DIGITAL_HOUR_INDEX: return new EthiopianTime(v, context.minute, context.second); case MINUTE_INDEX: return new EthiopianTime(context.hour24, v, context.second); case SECOND_INDEX: return new EthiopianTime(context.hour24, context.minute, v); default: throw new UnsupportedOperationException("Unknown element index: " + this.index); } } @Override public ChronoElement<?> getChildAtFloor(EthiopianTime context) { return null; } @Override public ChronoElement<?> getChildAtCeiling(EthiopianTime context) { return null; } } private static class Merger implements ChronoMerger<EthiopianTime> { //~ Methoden ------------------------------------------------------ @Override public String getFormatPattern( DisplayStyle style, Locale locale ) { return ((style.getStyleValue() == DateFormat.SHORT) ? "h:mm a" : "h:mm:ss a"); } @Override public EthiopianTime createFrom( TimeSource<?> clock, AttributeQuery attributes ) { return EthiopianTime.from(PlainTime.axis().createFrom(clock, attributes)); } @Override @Deprecated public EthiopianTime 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 EthiopianTime createFrom( ChronoEntity<?> entity, AttributeQuery attributes, boolean lenient, boolean preparsing ) { PlainTime time = PlainTime.axis().createFrom(entity, attributes, lenient, false); if (time != null) { return EthiopianTime.from(time); } return null; } @Override public ChronoDisplay preformat( EthiopianTime context, AttributeQuery attributes ) { return context; } @Override public StartOfDay getDefaultStartOfDay() { return StartOfDay.MORNING; } } }