/* * ----------------------------------------------------------------------- * Copyright © 2013-2015 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (ZonalClock.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.TimeSource; import net.time4j.base.UnixTime; import net.time4j.engine.CalendarFamily; import net.time4j.engine.CalendarVariant; import net.time4j.engine.ChronoEntity; import net.time4j.engine.Chronology; import net.time4j.engine.StartOfDay; import net.time4j.engine.VariantSource; import net.time4j.format.Attributes; import net.time4j.tz.TZID; import net.time4j.tz.Timezone; /** * <p>Represents a clock which yields the current local time according * to a timezone. </p> * * <p>This class is <i>immutable</i> as long as the underlying implementations * of time source and time zone are. </p> * * @author Meno Hochschild * @see SystemClock#inLocalView() * @see SystemClock#inZonalView(TZID) */ /*[deutsch] * <p>Repräsentiert eine Uhr, die die aktuelle lokale Zeit anzeigt. </p> * * <p>Diese Klasse ist solange <i>immutable</i> (unveränderlich), wie * die zugrundeliegenden Implementierungen der Zeitquelle und der Zeitzone * es sind. </p> * * @author Meno Hochschild * @see SystemClock#inLocalView() * @see SystemClock#inZonalView(TZID) */ public final class ZonalClock { // -------------------- // Implementation note: // -------------------- // This class does not implement the interface TimeSource intentionally // because it is really designed for querying "zonal" times but not for // being injected into any test class or business object. Otherwise // zonal dependencies could be obfuscated from a user-perspective. // Instead users are strongly encouraged to use expressions like // SystemClock#inZonalView(TZID). //~ Statische Felder/Initialisierungen -------------------------------- private static final ZonalClock SYSTEM = new ZonalClock(); //~ Instanzvariablen -------------------------------------------------- private final TimeSource<?> timeSource; private final Timezone timezone; //~ Konstruktoren ----------------------------------------------------- /** * <p>Constructs a new clock which can yield the current local time in * given timezone. </p> * * <p>Most users have no need to directly call this constructor. It is * mainly designed for being called by dedicated expressions like * {@code SystemClock.inZonalView(tzid)} etc. </p> * * @param timeSource source for current world time (UTC) * @param tzid timezone id * @throws IllegalArgumentException if given timezone cannot be loaded */ /*[deutsch] * <p>Konstruiert eine neue Uhr, die die aktuelle Zeit in einer Zeitzone * ermitteln kann. </p> * * <p>Die meisten Anwender brauchen diesen Konstruktor nicht. Er ist * im wesentlichen für den Aufruf durch spezielle Ausdrücke * wie {@code SystemClock.inZonalView(tzid)} etc. gedacht. </p> * * @param timeSource source for current world time (UTC) * @param tzid timezone id * @throws IllegalArgumentException if given timezone cannot be loaded */ public ZonalClock( TimeSource<?> timeSource, TZID tzid ) { this(timeSource, Timezone.of(tzid)); } /** * <p>Constructs a new clock which can yield the current local time in * given timezone. </p> * * <p>Most users have no need to directly call this constructor. It is * mainly designed for being called by dedicated expressions like * {@code SystemClock.inZonalView(tzid)} etc. </p> * * @param timeSource source for current world time (UTC) * @param tzid timezone id * @throws IllegalArgumentException if given timezone cannot be loaded */ /*[deutsch] * <p>Konstruiert eine neue Uhr, die die aktuelle Zeit in einer Zeitzone * ermitteln kann. </p> * * <p>Die meisten Anwender brauchen diesen Konstruktor nicht. Er ist * im wesentlichen für den Aufruf durch spezielle Ausdrücke * wie {@code SystemClock.inZonalView(tzid)} etc. gedacht. </p> * * @param timeSource source for current world time (UTC) * @param tzid timezone id * @throws IllegalArgumentException if given timezone cannot be loaded */ public ZonalClock( TimeSource<?> timeSource, String tzid ) { this(timeSource, Timezone.of(tzid)); } /** * <p>Constructs a new clock which can yield the current local time in * given timezone. </p> * * <p>Most users have no need to directly call this constructor. It is * mainly designed for being called by dedicated expressions like * {@code SystemClock.inLocalView()} etc. </p> * * @param timeSource source for current world time (UTC) * @param tz timezone * @throws IllegalArgumentException if given timezone cannot be loaded * @since 3.22/4.18 */ /*[deutsch] * <p>Konstruiert eine neue Uhr, die die aktuelle Zeit in einer Zeitzone * ermitteln kann. </p> * * <p>Die meisten Anwender brauchen diesen Konstruktor nicht. Er ist * im wesentlichen für den Aufruf durch spezielle Ausdrücke * wie {@code SystemClock.inLocalView()} etc. gedacht. </p> * * @param timeSource source for current world time (UTC) * @param tz timezone * @throws IllegalArgumentException if given timezone cannot be loaded * @since 3.22/4.18 */ public ZonalClock( TimeSource<?> timeSource, Timezone tz ) { if (timeSource == null) { throw new NullPointerException("Missing time source."); } else if (tz == null) { throw new NullPointerException("Missing timezone."); } this.timeSource = timeSource; this.timezone = tz; } private ZonalClock() { super(); this.timeSource = SystemClock.INSTANCE; this.timezone = null; } //~ Methoden ---------------------------------------------------------- /** * <p>Gets the current date in the associated timezone. </p> * * <p>The result dynamically depends on the associated timezone meaning if and only if the underlying * timezone is the system timezone. </p> * * @return calendar date representing today */ /** * <p>Ermittelt das aktuelle Datum in der assoziierten Zeitzone. </p> * * <p>Das Ergebnis hängt genau dann dynamisch von der assoziierten Zeitzone ab, wenn die * System-Zeitzone vorliegt. </p> * * @return calendar date representing today */ public PlainDate today() { final UnixTime ut = this.timeSource.currentTime(); Timezone tz = (this.timezone == null) ? Timezone.ofSystem() : this.timezone; return PlainDate.from(ut, tz.getOffset(ut)); } /** * <p>Gets the current timestamp in the associated timezone. </p> * * <p>The result dynamically depends on the associated timezone meaning if and only if the underlying * timezone is the system timezone. </p> * * @return current local timestamp */ /*[deutsch] * <p>Ermittelt die aktuelle Zeit in der assoziierten Zeitzone. </p> * * <p>Das Ergebnis hängt genau dann dynamisch von der assoziierten Zeitzone ab, wenn die * System-Zeitzone vorliegt. </p> * * @return current local timestamp */ public PlainTimestamp now() { final UnixTime ut = this.timeSource.currentTime(); Timezone tz = (this.timezone == null) ? Timezone.ofSystem() : this.timezone; return PlainTimestamp.from(ut, tz.getOffset(ut)); } /** * <p>Gets the current timestamp in the associated timezone and given chronology. </p> * * <p>The result always dynamically depends on the associated timezone meaning if the underlying * timezone data change then the result will also change by next call. </p> * * <p>Code example: </p> * * <pre> * System.out.println(SystemClock.inLocalView().now(PlainTime.axis())); // local wall time * </pre> * * @param <T> generic type of chronology * @param chronology chronology to be used * @return current local timestamp or date in given chronology * @throws IllegalArgumentException if given chronology requires a calendar variant * @since 3.3/4.2 */ /*[deutsch] * <p>Ermittelt die aktuelle Zeit in der assoziierten Zeitzone und angegebenen Chronologie. </p> * * <p>Das Ergebnis hängt immer dynamisch von der assoziierten Zeitzone ab. Wenn deren Daten sich * ändern, dann wird diese Methode beim nächsten Aufruf ein angepasstes Ergebnis liefern. </p> * * <p>Code-Beispiel: </p> * * <pre> * System.out.println(SystemClock.inLocalView().now(PlainTime.axis())); // lokale Uhrzeit * </pre> * * @param <T> generic type of chronology * @param chronology chronology to be used * @return current local timestamp or date in given chronology * @throws IllegalArgumentException if given chronology requires a calendar variant * @since 3.3/4.2 */ public <T extends ChronoEntity<T>> T now(Chronology<T> chronology) { Timezone tz = (this.timezone == null) ? Timezone.ofSystem() : this.timezone; Attributes attrs = new Attributes.Builder().setTimezone(tz.getID()).build(); T result = chronology.createFrom(this.timeSource, attrs); if (result == null) { Class<?> type = chronology.getChronoType(); if (CalendarVariant.class.isAssignableFrom(type)) { throw new IllegalArgumentException("Calendar variant required: " + type.getName()); } else { throw new IllegalArgumentException("Insufficient data: " + type.getName()); } } else { return result; } } /** * <p>Gets the current timestamp in the associated timezone and given chronology taking into account * given calendar variant and start of day. </p> * * <p>The result always dynamically depends on the associated timezone meaning if the underlying * timezone data change then the result will also change by next call. </p> * * <p>Code example: </p> * * <pre> * HijriCalendar hijriDate = * CLOCK.now( * HijriCalendar.family(), * HijriCalendar.VARIANT_UMALQURA, * StartOfDay.EVENING) * .toDate(); * System.out.println(hijriDate); // AH-1436-10-02[islamic-umalqura] * </pre> * * <p>Note that this example is even true if the current timestamp is 2015-07-17T18:00 which would * normally map to AH-1436-10-01 (if the clock time is not considered). Reason is that the islamic * day starts on the evening of the previous day. </p> * * @param <C> generic type of chronology * @param family calendar family to be used * @param variant calendar variant * @param startOfDay start of calendar day * @return current general timestamp in given chronology * @throws IllegalArgumentException if given variant is not supported * @since 3.8/4.5 */ /*[deutsch] * <p>Ermittelt die aktuelle Zeit in der assoziierten Zeitzone und angegebenen Chronologie unter * Berücksichtigung von Kalendervariante und Start des Kalendertages. </p> * * <p>Das Ergebnis hängt immer dynamisch von der assoziierten Zeitzone ab. Wenn deren Daten sich * ändern, dann wird diese Methode beim nächsten Aufruf ein angepasstes Ergebnis liefern. </p> * * <p>Code-Beispiel: </p> * * <pre> * HijriCalendar hijriDate = * CLOCK.now( * HijriCalendar.family(), * HijriCalendar.VARIANT_UMALQURA, * StartOfDay.EVENING) * .toDate(); * System.out.println(hijriDate); // AH-1436-10-02[islamic-umalqura] * </pre> * * <p>Zu beachten: Dieses Beispiel stimmt sogar dann, wenn der aktuelle Zeitstempel 2015-07-17T18:00 ist, * welcher normalerweise auf das islamische Datum AH-1436-10-01 abgebildet wird (wenn die Uhrzeit nicht * betrachtet wird), denn der islamische Tag beginnt am Abend des Vortags. </p> * * @param <C> generic type of chronology * @param family calendar family to be used * @param variant calendar variant * @param startOfDay start of calendar day * @return current general timestamp in given chronology * @throws IllegalArgumentException if given variant is not supported * @since 3.8/4.5 */ public <C extends CalendarVariant<C>> GeneralTimestamp<C> now( CalendarFamily<C> family, String variant, StartOfDay startOfDay ) { Timezone tz = (this.timezone == null) ? Timezone.ofSystem() : this.timezone; Moment moment = Moment.from(this.timeSource.currentTime()); return moment.toGeneralTimestamp(family, variant, tz.getID(), startOfDay); } /** * <p>Equivalent to {@code now(chronology, variantSource.getVariant(), startOfDay)}. </p> * * @param <C> generic type of chronology * @param family calendar family to be used * @param variantSource source of calendar variant * @param startOfDay start of calendar day * @return current local timestamp or date in given chronology * @throws IllegalArgumentException if given variant is not supported * @see #now(CalendarFamily, String, StartOfDay) * @since 3.8/4.5 */ /*[deutsch] * <p>Äquivalent to {@code now(chronology, variantSource.getVariant(), startOfDay)}. </p> * * @param <C> generic type of chronology * @param family calendar family to be used * @param variantSource source of calendar variant * @param startOfDay start of calendar day * @return current local timestamp or date in given chronology * @throws IllegalArgumentException if given variant is not supported * @see #now(CalendarFamily, String, StartOfDay) * @since 3.8/4.5 */ public <C extends CalendarVariant<C>> GeneralTimestamp<C> now( CalendarFamily<C> family, VariantSource variantSource, StartOfDay startOfDay ) { return this.now(family, variantSource.getVariant(), startOfDay); } /** * <p>Gets the associated clock. </p> * * @return time source */ /*[deutsch] * <p>Liefert die assoziierte Uhr. </p> * * @return Zeitquelle */ public TimeSource<?> getSource() { return this.timeSource; } /** * <p>Gets the associated timezone. </p> * * @return timezone id */ /*[deutsch] * <p>Liefert die assoziierte Zeitzone. </p> * * @return timezone id */ public TZID getTimezone() { Timezone tz = (this.timezone == null) ? Timezone.ofSystem() : this.timezone; return tz.getID(); } /** * <p>Zonale Uhr basierend auf den Systemeinstellungen beim Laden * dieser Klasse. </p> * * @return local clock in default system timezone */ static ZonalClock ofSystem() { return SYSTEM; } }