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

org.threeten.bp.ZoneId.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  * Neither the name of JSR-310 nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.threeten.bp

import java.io.Serializable
import java.util.{ Locale, Objects, TimeZone }
import org.threeten.bp.format.DateTimeFormatterBuilder
import org.threeten.bp.format.TextStyle
import org.threeten.bp.temporal.TemporalAccessor
import org.threeten.bp.temporal.TemporalField
import org.threeten.bp.temporal.TemporalQueries
import org.threeten.bp.temporal.TemporalQuery
import org.threeten.bp.temporal.UnsupportedTemporalTypeException
import org.threeten.bp.zone.ZoneRules
import org.threeten.bp.zone.ZoneRulesException
import org.threeten.bp.zone.ZoneRulesProvider
import scala.collection.JavaConverters._

object ZoneId {

  /**
   * A map of zone overrides to enable the short time-zone names to be used.
   *
   * Use of short zone IDs has been deprecated in {@code java.util.TimeZone}. This map allows the
   * IDs to continue to be used via the {@link #of(String, Map)} factory method.
   *
   * This map contains a mapping of the IDs that is in line with TZDB 2005r and later, where 'EST',
   * 'MST' and 'HST' map to IDs which do not include daylight savings.
   *
   * This maps as follows: 
  • EST - -05:00
  • HST - -10:00
  • MST - -07:00
  • *
  • ACT - Australia/Darwin
  • AET - Australia/Sydney
  • AGT - * America/Argentina/Buenos_Aires
  • ART - Africa/Cairo
  • AST - * America/Anchorage
  • BET - America/Sao_Paulo
  • BST - Asia/Dhaka
  • CAT - * Africa/Harare
  • CNT - America/St_Johns
  • CST - America/Chicago
  • CTT - * Asia/Shanghai
  • EAT - Africa/Addis_Ababa
  • ECT - Europe/Paris
  • IET - * America/Indiana/Indianapolis
  • IST - Asia/Kolkata
  • JST - Asia/Tokyo
  • MIT * - Pacific/Apia
  • NET - Asia/Yerevan
  • NST - Pacific/Auckland
  • PLT - * Asia/Karachi
  • PNT - America/Phoenix
  • PRT - America/Puerto_Rico
  • PST * - America/Los_Angeles
  • SST - Pacific/Guadalcanal
  • VST - * Asia/Ho_Chi_Minh

The map is unmodifiable. */ lazy val SHORT_IDS: java.util.Map[String, String] = ShortIds.asJava private lazy val ShortIds: Map[String, String] = Map[String, String]( "ACT" -> "Australia/Darwin", "AET" -> "Australia/Sydney", "AGT" -> "America/Argentina/Buenos_Aires", "ART" -> "Africa/Cairo", "AST" -> "America/Anchorage", "BET" -> "America/Sao_Paulo", "BST" -> "Asia/Dhaka", "CAT" -> "Africa/Harare", "CNT" -> "America/St_Johns", "CST" -> "America/Chicago", "CTT" -> "Asia/Shanghai", "EAT" -> "Africa/Addis_Ababa", "ECT" -> "Europe/Paris", "IET" -> "America/Indiana/Indianapolis", "IST" -> "Asia/Kolkata", "JST" -> "Asia/Tokyo", "MIT" -> "Pacific/Apia", "NET" -> "Asia/Yerevan", "NST" -> "Pacific/Auckland", "PLT" -> "Asia/Karachi", "PNT" -> "America/Phoenix", "PRT" -> "America/Puerto_Rico", "PST" -> "America/Los_Angeles", "SST" -> "Pacific/Guadalcanal", "VST" -> "Asia/Ho_Chi_Minh", "EST" -> "-05:00", "MST" -> "-07:00", "HST" -> "-10:00" ) /** * Gets the system default time-zone. * * This queries {@link TimeZone#getDefault()} to find the default time-zone and converts it to a * {@code ZoneId}. If the system default time-zone is changed, then the result of this method will * also change. * * @return * the zone ID, not null * @throws DateTimeException * if the converted zone ID has an invalid format * @throws ZoneRulesException * if the converted zone region ID cannot be found */ def systemDefault: ZoneId = of2(TimeZone.getDefault.getID, ShortIds) /** * Gets the set of available zone IDs. * * This set includes the string form of all available region-based IDs. Offset-based zone IDs are * not included in the returned set. The ID can be passed to {@link #of(String)} to create a * {@code ZoneId}. * * The set of zone IDs can increase over time, although in a typical application the set of IDs is * fixed. Each call to this method is thread-safe. * * @return * a modifiable copy of the set of zone IDs, not null */ def getAvailableZoneIds: java.util.Set[String] = new java.util.HashSet(ZoneRulesProvider.getAvailableZoneIds) /** * Obtains an instance of {@code ZoneId} using its ID using a map of aliases to supplement the * standard zone IDs. * * Many users of time-zones use short abbreviations, such as PST for 'Pacific Standard Time' and * PDT for 'Pacific Daylight Time'. These abbreviations are not unique, and so cannot be used as * IDs. This method allows a map of string to time-zone to be setup and reused within an * application. * * @param zoneId * the time-zone ID, not null * @param aliasMap * a map of alias zone IDs (typically abbreviations) to real zone IDs, not null * @return * the zone ID, not null * @throws DateTimeException * if the zone ID has an invalid format * @throws ZoneRulesException * if the zone ID is a region ID that cannot be found */ def of(zoneId: String, aliasMap: java.util.Map[String, String]): ZoneId = of2(zoneId, aliasMap.asScala.toMap) private def of2(zoneId: String, aliasMap: Map[String, String]): ZoneId = of(aliasMap.get(zoneId).getOrElse(zoneId)) /** * Obtains an instance of {@code ZoneId} from an ID ensuring that the ID is valid and available * for use. * * This method parses the ID producing a {@code ZoneId} or {@code ZoneOffset}. A {@code * ZoneOffset} is returned if the ID is 'Z', or starts with '+' or '-'. The result will always be * a valid ID for which {@link ZoneRules} can be obtained. * * Parsing matches the zone ID step by step as follows.

  • If the zone ID equals 'Z', the * result is {@code ZoneOffset.UTC}.
  • If the zone ID consists of a single letter, the zone ID * is invalid and {@code DateTimeException} is thrown.
  • If the zone ID starts with '+' or '-', * the ID is parsed as a {@code ZoneOffset} using {@link ZoneOffset#of(String)}.
  • If the zone * ID equals 'GMT', 'UTC' or 'UT' then the result is a {@code ZoneId} with the same ID and rules * equivalent to {@code ZoneOffset.UTC}.
  • If the zone ID starts with 'UTC+', 'UTC-', 'GMT+', * 'GMT-', 'UT+' or 'UT-' then the ID is a prefixed offset-based ID. The ID is split in two, with * a two or three letter prefix and a suffix starting with the sign. The suffix is parsed as a * {@link ZoneOffset#of(String) ZoneOffset}. The result will be a {@code ZoneId} with the * specified UTC/GMT/UT prefix and the normalized offset ID as per {@link ZoneOffset#getId()}. The * rules of the returned {@code ZoneId} will be equivalent to the parsed {@code ZoneOffset}. *
  • All other IDs are parsed as region-based zone IDs. Region IDs must match the regular * expression [A-Za-z][A-Za-z0-9~/._+-]+ otherwise a {@code DateTimeException} is * thrown. If the zone ID is not in the configured set of IDs, {@code ZoneRulesException} is * thrown. The detailed format of the region ID depends on the group supplying the data. The * default set of data is supplied by the IANA Time Zone Database (TZDB). This has region IDs of * the form '{area}/{city}', such as 'Europe/Paris' or 'America/New_York'. This is compatible with * most IDs from {@link java.util.TimeZone}.
* * @param zoneId * the time-zone ID, not null * @return * the zone ID, not null * @throws DateTimeException * if the zone ID has an invalid format * @throws ZoneRulesException * if the zone ID is a region ID that cannot be found */ def of(zoneId: String): ZoneId = { Objects.requireNonNull(zoneId, "zoneId") if (zoneId == "Z") ZoneOffset.UTC else if (zoneId.length == 1) throw new DateTimeException(s"Invalid ID for ZoneOffset, invalid format: $zoneId") else if (zoneId.startsWith("+") || zoneId.startsWith("-")) ZoneOffset.of(zoneId) else if ((zoneId == "UTC") || (zoneId == "GMT") || (zoneId == "UT")) new ZoneRegion(zoneId, ZoneOffset.UTC.getRules) else if ( zoneId.startsWith("UTC+") || zoneId.startsWith("GMT+") || zoneId.startsWith("UTC-") || zoneId .startsWith("GMT-") ) { val offset: ZoneOffset = ZoneOffset.of(zoneId.substring(3)) if (offset.getTotalSeconds == 0) new ZoneRegion(zoneId.substring(0, 3), offset.getRules) else new ZoneRegion(zoneId.substring(0, 3) + offset.getId, offset.getRules) } else if (zoneId.startsWith("UT+") || zoneId.startsWith("UT-")) { val offset: ZoneOffset = ZoneOffset.of(zoneId.substring(2)) if (offset.getTotalSeconds == 0) new ZoneRegion("UT", offset.getRules) else new ZoneRegion(s"UT${offset.getId}", offset.getRules) } else ZoneRegion.ofId(zoneId, true) } /** * Obtains an instance of {@code ZoneId} wrapping an offset. * * If the prefix is "GMT", "UTC", or "UT" a {@code ZoneId} with the prefix and the non-zero offset * is returned. If the prefix is empty {@code ""} the {@code ZoneOffset} is returned. * * @param prefix * the time-zone ID, not null * @param offset * the offset, not null * @return * the zone ID, not null * @throws IllegalArgumentException * if the prefix is not one of "GMT", "UTC", or "UT", or "" */ def ofOffset(prefix: String, offset: ZoneOffset): ZoneId = { Objects.requireNonNull(prefix, "prefix") Objects.requireNonNull(offset, "offset") if (prefix.length == 0) return offset if ((prefix == "GMT") || (prefix == "UTC") || (prefix == "UT")) { if (offset.getTotalSeconds == 0) return new ZoneRegion(prefix, offset.getRules) return new ZoneRegion(prefix + offset.getId, offset.getRules) } throw new IllegalArgumentException(s"Invalid prefix, must be GMT, UTC or UT: $prefix") } /** * Obtains an instance of {@code ZoneId} from a temporal object. * * A {@code TemporalAccessor} represents some form of date and time information. This factory * converts the arbitrary temporal object to an instance of {@code ZoneId}. * * The conversion will try to obtain the zone in a way that favours region-based zones over * offset-based zones using {@link TemporalQueries#zone()}. * * This method matches the signature of the functional interface {@link TemporalQuery} allowing it * to be used in queries via method reference, {@code ZoneId::from}. * * @param temporal * the temporal object to convert, not null * @return * the zone ID, not null * @throws DateTimeException * if unable to convert to a { @code ZoneId} */ def from(temporal: TemporalAccessor): ZoneId = { val obj: ZoneId = temporal.query(TemporalQueries.zone) if (obj == null) throw new DateTimeException( s"Unable to obtain ZoneId from TemporalAccessor: $temporal, type ${temporal.getClass.getName}" ) else obj } } /** * A time-zone ID, such as {@code Europe/Paris}. * * A {@code ZoneId} is used to identify the rules used to convert between an {@link Instant} and a * {@link LocalDateTime}. There are two distinct types of ID:
  • Fixed offsets - a fully * resolved offset from UTC/Greenwich, that uses the same offset for all local date-times *
  • Geographical regions - an area where a specific set of rules for finding the offset from * UTC/Greenwich apply
Most fixed offsets are represented by {@link ZoneOffset}. Calling * {@link #normalized()} on any {@code ZoneId} will ensure that a fixed offset ID will be * represented as a {@code ZoneOffset}. * * The actual rules, describing when and how the offset changes, are defined by {@link ZoneRules}. * This class is simply an ID used to obtain the underlying rules. This approach is taken because * rules are defined by governments and change frequently, whereas the ID is stable. * * The distinction has other effects. Serializing the {@code ZoneId} will only send the ID, whereas * serializing the rules sends the entire data set. Similarly, a comparison of two IDs only examines * the ID, whereas a comparison of two rules examines the entire data set. * *

Time-zone IDs

The ID is unique within the system. There are three types of ID. * * The simplest type of ID is that from {@code ZoneOffset}. This consists of 'Z' and IDs starting * with '+' or '-'. * * The next type of ID are offset-style IDs with some form of prefix, such as 'GMT+2' or * 'UTC+01:00'. The recognised prefixes are 'UTC', 'GMT' and 'UT'. The offset is the suffix and will * be normalized during creation. These IDs can be normalized to a {@code ZoneOffset} using {@code * normalized()}. * * The third type of ID are region-based IDs. A region-based ID must be of two or more characters, * and not start with 'UTC', 'GMT', 'UT' '+' or '-'. Region-based IDs are defined by configuration, * see {@link ZoneRulesProvider}. The configuration focuses on providing the lookup from the ID to * the underlying {@code ZoneRules}. * * Time-zone rules are defined by governments and change frequently. There are a number of * organizations, known here as groups, that monitor time-zone changes and collate them. The default * group is the IANA Time Zone Database (TZDB). Other organizations include IATA (the airline * industry body) and Microsoft. * * Each group defines its own format for the region ID it provides. The TZDB group defines IDs such * as 'Europe/London' or 'America/New_York'. TZDB IDs take precedence over other groups. * * It is strongly recommended that the group name is included in all IDs supplied by groups other * than TZDB to avoid conflicts. For example, IATA airline time-zone region IDs are typically the * same as the three letter airport code. However, the airport of Utrecht has the code 'UTC', which * is obviously a conflict. The recommended format for region IDs from groups other than TZDB is * 'group~region'. Thus if IATA data were defined, Utrecht airport would be 'IATA~UTC'. * *

Serialization

This class can be serialized and stores the string zone ID in the external * form. The {@code ZoneOffset} subclass uses a dedicated format that only stores the offset from * UTC/Greenwich. * * A {@code ZoneId} can be deserialized in a Java Runtime where the ID is unknown. For example, if a * server-side Java Runtime has been updated with a new zone ID, but the client-side Java Runtime * has not been updated. In this case, the {@code ZoneId} object will exist, and can be queried * using {@code getId}, {@code equals}, {@code hashCode}, {@code toString}, {@code getDisplayName} * and {@code normalized}. However, any call to {@code getRules} will fail with {@code * ZoneRulesException}. This approach is designed to allow a {@link ZonedDateTime} to be loaded and * queried, but not modified, on a Java Runtime with incomplete time-zone information. * *

Specification for implementors

This abstract class has two implementations, both of * which are immutable and thread-safe. One implementation models region-based IDs, the other is * {@code ZoneOffset} modelling offset-based IDs. This difference is visible in serialization. * * @constructor * Constructor only accessible within the package. */ @SerialVersionUID(8352817235686L) abstract class ZoneId private[bp] () extends Serializable { if ((getClass ne classOf[ZoneOffset]) && (getClass ne classOf[ZoneRegion])) throw new AssertionError("Invalid subclass") /** * Gets the unique time-zone ID. * * This ID uniquely defines this object. The format of an offset based ID is defined by {@link * ZoneOffset#getId()}. * * @return * the time-zone unique ID, not null */ def getId: String /** * Gets the time-zone rules for this ID allowing calculations to be performed. * * The rules provide the functionality associated with a time-zone, such as finding the offset for * a given instant or local date-time. * * A time-zone can be invalid if it is deserialized in a Java Runtime which does not have the same * rules loaded as the Java Runtime that stored it. In this case, calling this method will throw a * {@code ZoneRulesException}. * * The rules are supplied by {@link ZoneRulesProvider}. An advanced provider may support dynamic * updates to the rules without restarting the Java Runtime. If so, then the result of this method * may change over time. Each individual call will be still remain thread-safe. * * {@link ZoneOffset} will always return a set of rules where the offset never changes. * * @return * the rules, not null * @throws ZoneRulesException * if no rules are available for this ID */ def getRules: ZoneRules /** * Gets the textual representation of the zone, such as 'British Time' or '+02:00'. * * This returns the textual name used to identify the time-zone ID, suitable for presentation to * the user. The parameters control the style of the returned text and the locale. * * If no textual mapping is found then the {@link #getId() full ID} is returned. * * @param style * the length of the text required, not null * @param locale * the locale to use, not null * @return * the text value of the zone, not null */ def getDisplayName(style: TextStyle, locale: Locale): String = new DateTimeFormatterBuilder() .appendZoneText(style) .toFormatter(locale) .format(new TemporalAccessor() { def isSupported(field: TemporalField): Boolean = false def getLong(field: TemporalField): Long = throw new UnsupportedTemporalTypeException(s"Unsupported field: $field") override def query[R](query: TemporalQuery[R]): R = if (query eq TemporalQueries.zoneId) ZoneId.this.asInstanceOf[R] else super.query(query) }) /** * Normalizes the time-zone ID, returning a {@code ZoneOffset} where possible. * * The returns a normalized {@code ZoneId} that can be used in place of this ID. The result will * have {@code ZoneRules} equivalent to those returned by this object, however the ID returned by * {@code getId()} may be different. * * The normalization checks if the rules of this {@code ZoneId} have a fixed offset. If they do, * then the {@code ZoneOffset} equal to that offset is returned. Otherwise {@code this} is * returned. * * @return * the time-zone unique ID, not null */ def normalized: ZoneId = { try { val rules: ZoneRules = getRules if (rules.isFixedOffset) return rules.getOffset(Instant.EPOCH) } catch { case _: ZoneRulesException => } this } /** * Checks if this time-zone ID is equal to another time-zone ID. * * The comparison is based on the ID. * * @param obj * the object to check, null returns false * @return * true if this is equal to the other time-zone ID */ override def equals(obj: Any): Boolean = obj match { case other: ZoneId => (this eq other) || (getId == other.getId) case _ => false } /** * A hash code for this time-zone ID. * * @return * a suitable hash code */ override def hashCode: Int = getId.hashCode /** * Outputs this zone as a {@code String}, using the ID. * * @return * a string representation of this time-zone ID, not null */ override def toString: String = getId }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy