All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.time4j.tz.ZonalOffset Maven / Gradle / Ivy

There is a newer version: 4.38
Show newest version
/*
 * -----------------------------------------------------------------------
 * Copyright © 2013-2015 Meno Hochschild, 
 * -----------------------------------------------------------------------
 * This file (ZonalOffset.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.tz;


import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;


/**
 * 

Represents the shift of a local timestamp relative to UTC timezone * usually in full seconds.

* *

Following rule is the guideline (all data in seconds):

* *

{@code [Total Offset] = [Local Wall Time] - [POSIX Time]}

* * @author Meno Hochschild * @doctags.concurrency */ /*[deutsch] *

Repräsentiert die Verschiebung der lokalen Zeit relativ zur * UTC-Zeitzone normalerweise in vollen Sekunden.

* *

Es gilt folgende Beziehung zwischen einer lokalen Zeit und der * POSIX-Zeit (alle Angaben in Sekunden):

* *

{@code [Total Offset] = [Local Wall Time] - [POSIX Time]}

* * @author Meno Hochschild * @doctags.concurrency */ public final class ZonalOffset implements Comparable, TZID, Serializable { //~ Statische Felder/Initialisierungen -------------------------------- private static final ConcurrentMap OFFSET_CACHE = new ConcurrentHashMap(); private static final BigDecimal DECIMAL_60 = new BigDecimal(60); private static final BigDecimal DECIMAL_3600 = new BigDecimal(3600); private static final BigDecimal DECIMAL_NEG_180 = new BigDecimal(-180); private static final BigDecimal DECIMAL_POS_180 = new BigDecimal(180); private static final BigDecimal DECIMAL_240 = new BigDecimal(240); private static final BigDecimal MRD = new BigDecimal(1000000000); /** *

Constant for the UTC timezone representing a shift of * {@code 0} seconds with the canonical representation "Z".

*/ /*[deutsch] *

Konstante für eine zeitliche Verschiebung von {@code 0} Sekunden * mit der kanonischen Darstellung "Z".

*/ public static final ZonalOffset UTC; static { UTC = new ZonalOffset(0, 0); OFFSET_CACHE.put(Integer.valueOf(0), UTC); } private static final long serialVersionUID = -1410512619471503090L; //~ Instanzvariablen -------------------------------------------------- private transient final int total; private transient final int fraction; private transient final String name; //~ Konstruktoren ----------------------------------------------------- private ZonalOffset( int total, int fraction ) { super(); if (fraction == 0) { if ( (total < -18 * 3600) || (total > 18 * 3600) ) { throw new IllegalArgumentException( "Total seconds out of range: " + total); } } else if (Math.abs(fraction) > 999999999) { throw new IllegalArgumentException( "Fraction out of range: " + fraction); } else if ( (total < -11 * 3600) || (total > 11 * 3600) ) { throw new IllegalArgumentException( "Total seconds out of range while fraction is non-zero: " + total); } else if ( ((total < 0) && (fraction > 0)) || ((total > 0) && (fraction < 0)) ) { throw new IllegalArgumentException( "Different signs: offset=" + total + ", fraction=" + fraction); } boolean negative = ((total < 0) || (fraction < 0)); StringBuilder sb = new StringBuilder(); sb.append(negative ? '-' : '+'); int absValue = Math.abs(total); int hours = absValue / 3600; int minutes = (absValue / 60) % 60; int seconds = absValue % 60; if (hours < 10) { sb.append('0'); } sb.append(hours); sb.append(':'); if (minutes < 10) { sb.append('0'); } sb.append(minutes); if ( (seconds != 0) || (fraction != 0) ) { sb.append(':'); if (seconds < 10) { sb.append('0'); } sb.append(seconds); if (fraction != 0) { sb.append('.'); String f = String.valueOf(Math.abs(fraction)); for (int i = 0, len = 9 - f.length(); i < len; i++) { sb.append('0'); } sb.append(f); } } this.name = sb.toString(); this.total = total; this.fraction = fraction; } //~ Methoden ---------------------------------------------------------- // /** // *

Konstruiert eine neue Verschiebung auf Basis einer geographischen // * Längenangabe.

// * // *

Hinweis: Fraktionale Verschiebungen werden im Zeitzonenkontext // * nicht verwendet, sondern nur dann, wenn ein {@code PlainTimestamp} // * zu einem {@code Moment} oder zurück konvertiert wird. Diese // * Methode ist weniger genau als die {@code BigDecimal}-Variante. Ein // * Beispiel:

// * // *
//     *  System.out.println(ZonalOffset.atLongitude(new BigDecimal("-14.001")));
//     *  // Ausgabe: -00:56:00.240000000
//     *  System.out.println(ZonalOffset.atLongitude(-14.001));
//     *  // Ausgabe: -00:56:00.239999999
//     * 
// * // * @param longitude geographical longitude in degrees defined in // * range {@code -180.0 <= longitude <= 180.0} // * @return zonal offset in double precision // * @throws IllegalArgumentException if range check fails or if // * no defined longitude is given // * @see #atLongitude(BigDecimal) // */ // public static ZonalOffset atLongitude(double longitude) { // // if (Double.isNaN(longitude)) { // throw new IllegalArgumentException("Undefined longitude."); // } else if (Double.isInfinite(longitude)) { // throw new IllegalArgumentException("Infinite longitude."); // } else if ( // (Double.compare(180.0, longitude) < 0) // || (Double.compare(-180.0, longitude) > 0) // ) { // throw new IllegalArgumentException("Out of range: " + longitude); // } // // double offset = longitude * 240.0; // (longitude * 3600 / 15.0) // int total = (int) offset; // int fraction = (int) ((offset - total) * 1000000000); // // if (fraction == 0) { // return ZonalOffset.ofTotalSeconds(total); // } else { // return new ZonalOffset(total, fraction); // } // // } /** *

Creates a new shift based on a geographical longitude.

* *

Note that fractional offsets are not used in context of timezones, * but can only be applied to conversions between {@code PlainTimestamp} * and {@code Moment}.

* * @param longitude geographical longitude in degrees defined in * range {@code -180.0 <= longitude <= 180.0} * @return zonal offset in decimal precision * @throws IllegalArgumentException if range check fails */ /*[deutsch] *

Konstruiert eine neue Verschiebung auf Basis einer geographischen * Längenangabe.

* *

Hinweis: Fraktionale Verschiebungen werden im Zeitzonenkontext * nicht verwendet, sondern nur dann, wenn ein {@code PlainTimestamp} * zu einem {@code Moment} oder zurück konvertiert wird.

* * @param longitude geographical longitude in degrees defined in * range {@code -180.0 <= longitude <= 180.0} * @return zonal offset in decimal precision * @throws IllegalArgumentException if range check fails */ public static ZonalOffset atLongitude(BigDecimal longitude) { if ( (longitude.compareTo(DECIMAL_POS_180) > 0) || (longitude.compareTo(DECIMAL_NEG_180) < 0) ) { throw new IllegalArgumentException("Out of range: " + longitude); } BigDecimal offset = longitude.multiply(DECIMAL_240); BigDecimal integral = offset.setScale(0, RoundingMode.DOWN); BigDecimal delta = offset.subtract(integral); BigDecimal decimal = delta.setScale(9, RoundingMode.HALF_UP).multiply(MRD); int total = integral.intValueExact(); int fraction = decimal.intValueExact(); if (fraction == 0) { return ZonalOffset.ofTotalSeconds(total); } else { return new ZonalOffset(total, fraction); } } /** *

Creates a new shift based on a geographical longitude.

* *

Note that fractional offsets are not used in context of timezones, * but can only be applied to conversions between {@code PlainTimestamp} * and {@code Moment}.

* * @param sign sign of shift relative to zero meridian * @param degrees geographical length in degreed, defined in * range {@code 0 <= degrees <= 180} * @param arcMinutes arc minute part ({@code 0 <= arcMinutes <= 59}) * @param arcSeconds arc second part ({@code 0 <= arcSeconds <= 59}) * @return zonal offset in decimal precision * @throws IllegalArgumentException if range check fails (also if total * absolute offset goes beyond 180 degrees) * @see #atLongitude(BigDecimal) */ /*[deutsch] *

Konstruiert eine neue Verschiebung auf Basis einer geographischen * Längenangabe.

* *

Hinweis: Fraktionale Verschiebungen werden im Zeitzonenkontext * nicht verwendet, sondern nur dann, wenn ein {@code PlainTimestamp} * zu einem {@code Moment} oder zurück konvertiert wird.

* * @param sign sign of shift relative to zero meridian * @param degrees geographical length in degreed, defined in * range {@code 0 <= degrees <= 180} * @param arcMinutes arc minute part ({@code 0 <= arcMinutes <= 59}) * @param arcSeconds arc second part ({@code 0 <= arcSeconds <= 59}) * @return zonal offset in decimal precision * @throws IllegalArgumentException if range check fails (also if total * absolute offset goes beyond 180 degrees) * @see #atLongitude(BigDecimal) */ public static ZonalOffset atLongitude( OffsetSign sign, int degrees, int arcMinutes, int arcSeconds ) { if (sign == null) { throw new NullPointerException("Missing sign."); } else if ((degrees < 0) || (degrees > 180)) { throw new IllegalArgumentException( "Degrees of longitude out of range (0 <= degrees <= 180)."); } else if ((arcMinutes < 0) || (arcMinutes > 59)) { throw new IllegalArgumentException( "Arc minute out of range (0 <= arcMinutes <= 59)."); } else if ((arcSeconds < 0) || (arcSeconds > 59)) { throw new IllegalArgumentException( "Arc second out of range (0 <= arcSeconds <= 59)."); } BigDecimal longitude = BigDecimal.valueOf(degrees); if (arcMinutes != 0) { BigDecimal arcMin = BigDecimal.valueOf(arcMinutes) .setScale(15, RoundingMode.UNNECESSARY) .divide(DECIMAL_60, RoundingMode.HALF_UP); longitude = longitude.add(arcMin); } if (arcSeconds != 0) { BigDecimal arcSec = BigDecimal.valueOf(arcSeconds) .setScale(15, RoundingMode.UNNECESSARY) .divide(DECIMAL_3600, RoundingMode.HALF_UP); longitude = longitude.add(arcSec); } if (sign == OffsetSign.BEHIND_UTC) { longitude = longitude.negate(); } return ZonalOffset.atLongitude(longitude); } /** *

Static factory method for a shift which has the given full * hour part.

* *

Is equivalent to {@code ofHoursMinutes(sign, hours, 0}.

* * @param sign sign of shift relative to zero meridian * @param hours hour part ({@code 0 <= hours <= 18}) * @return zonal offset in hour precision * @throws IllegalArgumentException if range check fails * @see #ofHoursMinutes(OffsetSign, int, int) */ /*[deutsch] *

Statische Fabrikmethode für eine Zeitverschiebung, die den * angegebenen vollen Stundenanteil hat.

* *

Entspricht {@code ofHoursMinutes(sign, hours, 0}.

* * @param sign sign of shift relative to zero meridian * @param hours hour part ({@code 0 <= hours <= 18}) * @return zonal offset in hour precision * @throws IllegalArgumentException if range check fails * @see #ofHoursMinutes(OffsetSign, int, int) */ public static ZonalOffset ofHours( OffsetSign sign, int hours ) { return ofHoursMinutes(sign, hours, 0); } /** *

Static factory method for a shift which has given * hour and minute parts.

* *

The given numerical values are identical to the numerical * parts of the canonical representation ±hh:mm". * The second part is always {@code 0}. Only values in the range * {@code -18:00 <= [total-offset] <= +18:00} are allowed. When * calculating the total offset the sign relates to both hour * and minute part. Example: The expression * {@code ZonalOffset.ofHoursMinutes(BEHIND_UTC, 4, 30)} has the * representation {@code -04:30} and a total shift in seconds of * {@code -(4 * 3600 + 30 * 60) = 16200}.

* * @param sign sign ofHoursMinutes shift relative to zero meridian * @param hours hour part ({@code 0 <= hours <= 18}) * @param minutes minute part ({@code 0 <= minutes <= 59}) * @return zonal offset in minute precision * @throws IllegalArgumentException if range check fails */ /*[deutsch] *

Statische Fabrikmethode für eine Zeitverschiebung, die die * angegebenen Stunden- und Minutenanteile hat.

* *

Die angegebenen Zahlenwerte entsprechen exakt den numerischen * Bestandteilen der kanonischen Darstellung ±hh:mm". Der * Sekundenanteil ist hier immer {@code 0}. Erlaubt sind nur Werte im * Bereich {@code -18:00 <= [total-offset] <= +18:00}. Bei der Berechnung * der gesamten Verschiebung wird das Vorzeichen nicht nur auf den Stunden-, * sondern auch auf den Minutenteil mit übertragen. Beispiel: Der * Ausdruck {@code ZonalOffset.ofHoursMinutes(BEHIND_UTC, 4, 30)} hat die * String-Darstellung {@code -04:30} und eine Gesamtverschiebung in * Sekunden von {@code -(4 * 3600 + 30 * 60) = 16200}.

* * @param sign sign ofHoursMinutes shift relative to zero meridian * @param hours hour part ({@code 0 <= hours <= 18}) * @param minutes minute part ({@code 0 <= minutes <= 59}) * @return zonal offset in minute precision * @throws IllegalArgumentException if range check fails */ public static ZonalOffset ofHoursMinutes( OffsetSign sign, int hours, int minutes ) { if (sign == null) { throw new NullPointerException("Missing sign."); } else if ((hours < 0) || (hours > 18)) { throw new IllegalArgumentException( "Hour part out of range (0 <= hours <= 18) in: " + format(hours, minutes)); } else if ((minutes < 0) || (minutes > 59)) { throw new IllegalArgumentException( "Minute part out of range (0 <= minutes <= 59) in: " + format(hours, minutes)); } else if ( (hours == 18) && (minutes != 0) ) { throw new IllegalArgumentException( "Time zone offset out of range " + "(-18:00:00 <= offset <= 18:00:00) in: " + format(hours, minutes)); } int total = hours * 3600 + minutes * 60; if (sign == OffsetSign.BEHIND_UTC) { total = -total; } return ZonalOffset.ofTotalSeconds(total); } /** *

Creates a shift of the local time relative to UTC timezone * in integer seconds.

* * @param total total shift in seconds defined in range * {@code -18 * 3600 <= total <= 18 * 3600} * @return zonal offset in second precision * @throws IllegalArgumentException if range check fails * @see #getIntegralAmount() */ /*[deutsch] *

Konstruiert eine Verschiebung der lokalen Zeit relativ zur * UTC-Zeitzone in integralen Sekunden.

* * @param total total shift in seconds defined in range * {@code -18 * 3600 <= total <= 18 * 3600} * @return zonal offset in second precision * @throws IllegalArgumentException if range check fails * @see #getIntegralAmount() */ public static ZonalOffset ofTotalSeconds(int total) { return ZonalOffset.ofTotalSeconds(total, 0); } /** *

Creates a shift of the local time relative to UTC timezone * in integer seconds or fractional seconds.

* *

Note that fractional offsets are not used in context of timezones, * but can only be applied to conversions between {@code PlainTimestamp} * and {@code Moment}.

* * @param total total shift in seconds defined in range * {@code -18 * 3600 <= total <= 18 * 3600} * @param fraction fraction of second * @return zonal offset in (sub-)second precision * @throws IllegalArgumentException if any arguments are out of range * or have different signs * @see #getIntegralAmount() * @see #getFractionalAmount() */ /*[deutsch] *

Konstruiert eine Verschiebung der lokalen Zeit relativ zur * UTC-Zeitzone in integralen oder fraktionalen Sekunden.

* *

Hinweis: Fraktionale Verschiebungen werden im Zeitzonenkontext * nicht verwendet, sondern nur dann, wenn ein {@code PlainTimestamp} * zu einem {@code Moment} oder zurück konvertiert wird.

* * @param total total shift in seconds defined in range * {@code -18 * 3600 <= total <= 18 * 3600} * @param fraction fraction of second * @return zonal offset in (sub-)second precision * @throws IllegalArgumentException if any arguments are out of range * or have different signs * @see #getIntegralAmount() * @see #getFractionalAmount() */ public static ZonalOffset ofTotalSeconds( int total, int fraction ) { if (fraction != 0) { return new ZonalOffset(total, fraction); } else if (total == 0) { return UTC; } else if ((total % (15 * 60)) == 0) { // Viertelstundenintervall Integer value = Integer.valueOf(total); ZonalOffset result = OFFSET_CACHE.get(value); if (result == null) { result = new ZonalOffset(total, 0); OFFSET_CACHE.putIfAbsent(value, result); result = OFFSET_CACHE.get(value); } return result; } else { return new ZonalOffset(total, 0); } } /** *

Return the sign of this zonal shift.

* * @return {@code BEHIND_UTC} if sign is negative else {@code AHEAD_OF_UTC} */ /*[deutsch] *

Liefert das Vorzeichen der zonalen Verschiebung.

* * @return {@code BEHIND_UTC} if sign is negative else {@code AHEAD_OF_UTC} */ public OffsetSign getSign() { return ( ((this.total < 0) || (this.fraction < 0)) ? OffsetSign.BEHIND_UTC : OffsetSign.AHEAD_OF_UTC ); } /** *

Returns the hour part of this shift as absolute amount.

* * @return absolute hour part in range {@code 0 <= x <= 18} * @see #getSign() */ /*[deutsch] *

Liefert den Stundenanteil der Verschiebung als Absolutbetrag.

* * @return absolute hour part in range {@code 0 <= x <= 18} * @see #getSign() */ public int getAbsoluteHours() { return (Math.abs(this.total) / 3600); } /** *

Returns the minute part of this shift as absolute amount.

* * @return absolute minute part in range {@code 0 <= x <= 59} * @see #getSign() */ /*[deutsch] *

Liefert den Minutenanteil der Verschiebung als Absolutbetrag.

* * @return absolute minute part in range {@code 0 <= x <= 59} * @see #getSign() */ public int getAbsoluteMinutes() { return ((Math.abs(this.total) / 60) % 60); } /** *

Returns the second part of this shift as absolute amount.

* * @return absolute second part in range {@code 0 <= x <= 59} * @see #getSign() */ /*[deutsch] *

Liefert den Sekundenanteil der Verschiebung als Absolutbetrag.

* * @return absolute second part in range {@code 0 <= x <= 59} * @see #getSign() */ public int getAbsoluteSeconds() { return (Math.abs(this.total) % 60); } /** *

Total shift in integer seconds without fractional part.

* * @return integral part in seconds {@code -18 * 3600 <= x <= 18 * 3600} * @see #getFractionalAmount() */ /*[deutsch] *

Verschiebung in integralen Sekunden ohne Sekundenbruchteil.

* * @return integral part in seconds {@code -18 * 3600 <= x <= 18 * 3600} * @see #getFractionalAmount() */ public int getIntegralAmount() { return this.total; } /** *

Returns the fractional second part of this shift in nanoseconds.

* *

Only longitudinal offsets may have fractional parts.

* * @return fractional part in range {@code -999999999 <= x <= 999999999} * @see #getIntegralAmount() */ /*[deutsch] *

Liefert den Sekundenbruchteil der Verschiebung in * Nanosekundenform.

* *

Nur longitudinale Verschiebungen können einen fraktionalen * Anteil haben.

* * @return fractional part in range {@code -999999999 <= x <= 999999999} * @see #getIntegralAmount() */ public int getFractionalAmount() { return this.fraction; } /** *

Compares the whole state with sign, hours, minutes, seconds and * fractional seconds in ascending order.

* *

Shifts with sign west for Greenwich (behind UTC) are considered as * smaller than shifts with sign east for Greenwich (ahead of UTC).

* *

The natural order is consistent with {@code equals()}.

*/ /*[deutsch] *

Vergleicht die gesamte zeitliche Verschiebung.

* *

Es wird aufsteigend sortiert. Verschiebungen westlich von Greenwich * gelten als kleiner im Vergleich zu Verschiebungen östlich von * Greenwich.

* *

Die natürliche Ordnung ist konsistent mit {@code equals()}.

*/ @Override public int compareTo(ZonalOffset obj) { if (this.total < obj.total) { return -1; } else if (this.total > obj.total) { return 1; } else { int delta = (this.fraction - obj.fraction); return ((delta < 0) ? -1 : ((delta == 0) ? 0 : 1)); } } /** *

Compares the whole state.

*/ /*[deutsch] *

Vergleicht die internen Verschiebungswerte.

*/ @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof ZonalOffset) { ZonalOffset that = (ZonalOffset) obj; return ( (this.total == that.total) && (this.fraction == that.fraction) ); } else { return false; } } /** *

Calculates the hash value.

*/ /*[deutsch] *

Berechnet den Hash-Wert.

*/ @Override public int hashCode() { return (~this.total + (this.fraction % 64000)); } /** *

Returns a complete short representation of this shift including * the sign.

* *

Notes: If there are only full minutes or hours the representation * is exactly as described in ISO-8601. Another long canonical * representation can be obtained by the method {@code canonical()}.

* * @return String in ISO-8601 format "±hh:mm" or * "±hh:mm:ss" if there is a second part * or "±hh:mm:ss.fffffffff" if any fractional * part exists */ /*[deutsch] *

Liefert eine vollständige Kurzdarstellung der Verschiebung * mit Vorzeichen.

* *

Hinweise: Sofern nur volle Minuten oder Stunden vorliegen, entspricht * die Darstellung exakt dem empfohlenen ISO-8601-Standard. Eine andere * lange kanonische Darstellung ist mittels {@code canonical()} * erhältlich.

* * @return String in ISO-8601 format "±hh:mm" or * "±hh:mm:ss" if there is a second part * or "±hh:mm:ss.fffffffff" if any fractional * part exists */ @Override public String toString() { return this.name; } /** *

Returns a long canonical representation of this shift.

* *

Notes: If this instance denotes the UTC timezone then this method * will yield the string "Z". Another short canonical * representation can be obtained by the method {@code toString()}.

* * @return String in format "UTC±hh:mm" or * "UTC±hh:mm:ss" if there is a second part * or "UTC±hh:mm:ss.fffffffff" if any fractional * part exists or "Z" in timezone UTC * @see #toString() */ /*[deutsch] *

Liefert eine lange kanonische Darstellung der Verschiebung.

* *

Hinweise: Handelt es sich um die UTC-Zeitzone selbst, liefert die * Methode die Darstellung "Z". Eine andere kurze kanonische * Darstellung ist mittels {@code toString()} erhältlich.

* * @return String in format "UTC±hh:mm" or * "UTC±hh:mm:ss" if there is a second part * or "UTC±hh:mm:ss.fffffffff" if any fractional * part exists or "Z" in timezone UTC * @see #toString() */ @Override public String canonical() { if ( (this.total == 0) && (this.fraction == 0) ) { return "Z"; } return "UTC" + this.name; } /** *

Interpretes a canonical representation as zonal offset.

* *

All string produced by the methods {@code canonical()} or * {@code toString()} are supported. Due to the technical nature * of canonical representations this method is not designed to * parse any kind of user-defined input, especially the use of * GMT-prefix is NOT canonical and outdated from a scientific * point of view.

* *

Examples for supported formats:

* *
    *
  • UTC+5
  • *
  • UTC+05
  • *
  • UTC+5:30
  • *
  • UTC+05:30
  • *
  • UTC+5:30:21
  • *
  • UTC+05:30:21
  • *
  • UTC+5:30:21.123456789
  • *
  • UTC+05:30:21.123456789
  • *
  • +5
  • *
  • +05
  • *
  • +5:30
  • *
  • +05:30
  • *
  • +5:30:21
  • *
  • +05:30:21
  • *
  • +5:30:21.123456789
  • *
  • +05:30:21.123456789
  • *
* *

Note: All formats containing only the hour or the hour with only one digit * are first supported in version 3.1 or later.

* * @param canonical zonal offset in canonical form to be parsed * @return parsed {@code ZonalOffset} * @throws IllegalArgumentException if given input is not canonical * @since 2.2 * @see #canonical() * @see #toString() */ /*[deutsch] *

Interpretiert eine kanonische Darstellung als Verschiebung.

* *

Unterstützt werden alle von den Methoden {@code canonical()} * oder {@code toString()} produzierten Ausgaben. Aufgrund der technischen * Natur kanonischer Darstellungen ist diese Methode nicht dazu gedacht, * irgendeine benutzerdefinierte Darstellung zu interpretieren. Zu * beachten: Die Verwendung des GMT-Präfix ist NICHT kanonisch * und von einem wissenschaftlichen Standpunkt aus gesehen veraltet.

* *

Beispiele für unterstützte Formate:

* *
    *
  • UTC+5
  • *
  • UTC+05
  • *
  • UTC+5:30
  • *
  • UTC+05:30
  • *
  • UTC+5:30:21
  • *
  • UTC+05:30:21
  • *
  • UTC+5:30:21.123456789
  • *
  • UTC+05:30:21.123456789
  • *
  • +5
  • *
  • +05
  • *
  • +5:30
  • *
  • +05:30
  • *
  • +5:30:21
  • *
  • +05:30:21
  • *
  • +5:30:21.123456789
  • *
  • +05:30:21.123456789
  • *
* *

Anmerkung: Alle Formate mit einem einstelligen Stundenanteil oder nur mit Stundenanteil * werden erst seit Version 3.1 unterstützt.

* * @param canonical zonal offset in canonical form to be parsed * @return parsed {@code ZonalOffset} * @throws IllegalArgumentException if given input is not canonical * @since 2.2 * @see #canonical() * @see #toString() */ public static ZonalOffset parse(String canonical) { return parse(canonical, true); } /** *

Interpretiert eine kanonische Darstellung als Verschiebung.

* *

Unterstützt werden alle von den Methoden {@code canonical} oder * {@code toString()} produzierten Ausgaben.

* * @param offset zonal offset in canonical form to be parsed * @param wantsException shall an exception be thrown in case of error? * @return parsed {@code ZonalOffset} or {@code null} in case of error * @see #canonical() * @see #toString() */ static ZonalOffset parse( String offset, boolean wantsException ) { if (offset.equals("Z")) { return ZonalOffset.UTC; } int n = offset.length(); String test = offset; if (n >= 3) { if (test.startsWith("UTC")) { test = offset.substring(3); n -= 3; } else if (test.startsWith("GMT")) { if (wantsException) { throw new IllegalArgumentException( "Use UTC-prefix for canonical offset instead: " + offset); } else { return null; } } } if (n >= 2) { OffsetSign sign = null; if (test.charAt(0) == '-') { sign = OffsetSign.BEHIND_UTC; } else if (test.charAt(0) == '+') { sign = OffsetSign.AHEAD_OF_UTC; } int hours = parse(test, 1, 2); if (hours >= 0) { if (n <= 3){ return ZonalOffset.ofHours(sign, hours); } int start = 4; if ((test.charAt(2) == ':')) { start = 3; } int minutes = parse(test, start, 2); if ( (test.charAt(start - 1) == ':') && (minutes >= 0) ) { if (n == start + 2) { return ZonalOffset.ofHoursMinutes(sign, hours, minutes); } else if ( (n >= start + 5) && (test.charAt(start + 2) == ':') ) { int seconds = parse(test, start + 3, 2); if (seconds >= 0) { int total = hours * 3600 + minutes * 60 + seconds; if (sign == OffsetSign.BEHIND_UTC) { total = -total; } if (n == start + 5) { return ZonalOffset.ofTotalSeconds(total); } else if ( (n == start + 15) && (test.charAt(start + 5) == '.') ) { int fraction = parse(test, start + 6, 9); if (fraction >= 0) { if (sign == OffsetSign.BEHIND_UTC) { fraction = -fraction; } return ZonalOffset.ofTotalSeconds( total, fraction); } } } } } } } if (wantsException) { throw new IllegalArgumentException( "No canonical zonal offset: " + offset); } else { return null; } } /** *

Liefert die Verschiebung als Zeitzonenmodell.

* * @return timezone data with fixed shift in full seconds */ SingleOffsetTimezone getModel() { return SingleOffsetTimezone.of(this); } private static int parse( String offset, int index, int len ) { int amount = -1; for (int i = 0, n = Math.min(offset.length() - index, len); i < n; i++) { char c = offset.charAt(index + i); if ((c >= '0') && (c <= '9')) { if (amount == -1) { amount = (c - '0'); } else { amount = amount * 10 + (c - '0'); } } else { break; } } return amount; } private static String format( int hours, int minutes ) { StringBuilder sb = new StringBuilder(); sb.append("[hours="); sb.append(hours); sb.append(",minutes="); sb.append(minutes); sb.append(']'); return sb.toString(); } /** * @serialData Uses a specialized serialisation form as proxy. The format * is bit-compressed. The first byte contains in the four * most significant bits the type id {@code 15}. If there is * any fractional part then the four least significant bits * are {@code 1} else {@code 0}. After that the data bits * for the integral total shift and optionally the fractional * part follow. * * Schematic algorithm: * *
     *  boolean hasFraction = (this.getFractionalAmount() != 0);
     *  int header = (15 << 4);
     *
     *  if (hasFraction) {
     *      header |= 1;
     *  }
     *
     *  out.writeByte(header);
     *  out.writeInt(this.getIntegralAmount());
     *
     *  if (hasFraction) {
     *      out.writeInt(this.getFractionalAmount());
     *  }
     * 
* * @return replacement object in serialization graph */ private Object writeReplace() { return new SPX(this, SPX.ZONAL_OFFSET_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."); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy