gt:         if (offset >= 0) {
context: /*
   *  Copyright 2001-2014 Stephen Colebourne
   *
   *  Licensed under the Apache License, Version 2.0 (the "License");
   *  you may not use this file except in compliance with the License.
   *  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  package org.joda.time;
  
  import java.io.IOException;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.io.ObjectStreamException;
  import java.io.Serializable;
  import java.lang.ref.Reference;
  import java.lang.ref.SoftReference;
  import java.util.HashMap;
  import java.util.Locale;
  import java.util.Map;
  import java.util.Set;
  import java.util.TimeZone;
  
  import org.joda.convert.FromString;
  import org.joda.convert.ToString;
  import org.joda.time.chrono.BaseChronology;
  import org.joda.time.field.FieldUtils;
  import org.joda.time.format.DateTimeFormatter;
  import org.joda.time.format.DateTimeFormatterBuilder;
  import org.joda.time.format.FormatUtils;
  import org.joda.time.tz.DefaultNameProvider;
  import org.joda.time.tz.FixedDateTimeZone;
  import org.joda.time.tz.NameProvider;
  import org.joda.time.tz.Provider;
  import org.joda.time.tz.UTCProvider;
  import org.joda.time.tz.ZoneInfoProvider;
  
  /**
   * DateTimeZone represents a time zone.
   * <p>
   * A time zone is a system of rules to convert time from one geographic 
   * location to another. For example, Paris, France is one hour ahead of
   * London, England. Thus when it is 10:00 in London, it is 11:00 in Paris.
   * <p>
   * All time zone rules are expressed, for historical reasons, relative to
   * Greenwich, London. Local time in Greenwich is referred to as Greenwich Mean
   * Time (GMT).  This is similar, but not precisely identical, to Universal 
   * Coordinated Time, or UTC. This library only uses the term UTC.
   * <p>
   * Using this system, America/Los_Angeles is expressed as UTC-08:00, or UTC-07:00
   * in the summer. The offset -08:00 indicates that America/Los_Angeles time is
   * obtained from UTC by adding -08:00, that is, by subtracting 8 hours.
   * <p>
   * The offset differs in the summer because of daylight saving time, or DST.
   * The following definitions of time are generally used:
   * <ul>
   * <li>UTC - The reference time.
   * <li>Standard Time - The local time without a daylight saving time offset.
   * For example, in Paris, standard time is UTC+01:00.
   * <li>Daylight Saving Time - The local time with a daylight saving time 
   * offset. This offset is typically one hour, but not always. It is typically
   * used in most countries away from the equator.  In Paris, daylight saving 
   * time is UTC+02:00.
   * <li>Wall Time - This is what a local clock on the wall reads. This will be
   * either Standard Time or Daylight Saving Time depending on the time of year
   * and whether the location uses Daylight Saving Time.
   * </ul>
   * <p>
   * Unlike the Java TimeZone class, DateTimeZone is immutable. It also only
   * supports long format time zone ids. Thus EST and ECT are not accepted.
   * However, the factory that accepts a TimeZone will attempt to convert from
   * the old short id to a suitable long id.
   * <p>
   * DateTimeZone is thread-safe and immutable, and all subclasses must be as
   * well.
   * 
   * @author Brian S O'Neill
   * @author Stephen Colebourne
   * @since 1.0
   */
  public abstract class DateTimeZone implements Serializable {
      
      /** Serialization version. */
      private static final long serialVersionUID = 5546345482340108586L;
  
      /** The time zone for Universal Coordinated Time */
      public static final DateTimeZone UTC = new FixedDateTimeZone("UTC", "UTC", 0, 0);
      /** Maximum offset. */
      private static final int MAX_MILLIS = (86400 * 1000) - 1;
  
      /** The instance that is providing time zones. */
      private static Provider cProvider;
      /** The instance that is providing time zone names. */
      private static NameProvider cNameProvider;
      /** The set of ID strings. */
      private static Set<String> cAvailableIDs;
      /** The default time zone. */
      private static volatile DateTimeZone cDefault;
      /** A formatter for printing and parsing zones. */
      private static DateTimeFormatter cOffsetFormatter;
  
      /** Cache that maps fixed offset strings to softly referenced DateTimeZones */
      private static Map<String, SoftReference<DateTimeZone>> iFixedOffsetCache;
  
      /** Cache of old zone IDs to new zone IDs */
      private static Map<String, String> cZoneIdConversion;
  
      static {
          setProvider0(null);
          setNameProvider0(null);
      }
  
      //-----------------------------------------------------------------------
      /**
       * Gets the default time zone.
       * <p>
       * The default time zone is derived from the system property {@code user.timezone}.
       * If that is {@code null} or is not a valid identifier, then the value of the
       * JDK {@code TimeZone} default is converted. If that fails, {@code UTC} is used.
       * <p>
       * NOTE: If the {@code java.util.TimeZone} default is updated <i>after</i> calling this
       * method, then the change will not be picked up here.
       * 
       * @return the default datetime zone object
       */
      public static DateTimeZone getDefault() {
          DateTimeZone zone = cDefault;
          if (zone == null) {
              synchronized(DateTimeZone.class) {
                  zone = cDefault;
                  if (zone == null) {
                      DateTimeZone temp = null;
                      try {
                          try {
                              String id = System.getProperty("user.timezone");
                              if (id != null) {  // null check avoids stack overflow
                                  temp = forID(id);
                              }
                          } catch (RuntimeException ex) {
                              // ignored
                          }
                          if (temp == null) {
                              temp = forTimeZone(TimeZone.getDefault());
                          }
                      } catch (IllegalArgumentException ex) {
                          // ignored
                      }
                      if (temp == null) {
                          temp = UTC;
                      }
                      cDefault = zone = temp;
                  }
              }
          }
          return zone;
      }
  
      /**
       * Sets the default time zone.
       * <p>
       * NOTE: Calling this method does <i>not</i> set the {@code java.util.TimeZone} default.
       * 
       * @param zone  the default datetime zone object, must not be null
       * @throws IllegalArgumentException if the zone is null
       * @throws SecurityException if the application has insufficient security rights
       */
      public static void setDefault(DateTimeZone zone) throws SecurityException {
          SecurityManager sm = System.getSecurityManager();
          if (sm != null) {
              sm.checkPermission(new JodaTimePermission("DateTimeZone.setDefault"));
          }
          if (zone == null) {
              throw new IllegalArgumentException("The datetime zone must not be null");
          }
          synchronized(DateTimeZone.class) {
              cDefault = zone;
          }
      }
  
      //-----------------------------------------------------------------------
      /**
       * Gets a time zone instance for the specified time zone id.
       * <p>
       * The time zone id may be one of those returned by getAvailableIDs.
       * Short ids, as accepted by {@link java.util.TimeZone}, are not accepted.
       * All IDs must be specified in the long format.
       * The exception is UTC, which is an acceptable id.
       * <p>
       * Alternatively a locale independent, fixed offset, datetime zone can
       * be specified. The form <code>[+-]hh:mm</code> can be used.
       * 
       * @param id  the ID of the datetime zone, null means default
       * @return the DateTimeZone object for the ID
       * @throws IllegalArgumentException if the ID is not recognised
       */
      @FromString
      public static DateTimeZone forID(String id) {
          if (id == null) {
              return getDefault();
          }
          if (id.equals("UTC")) {
              return DateTimeZone.UTC;
          }
          DateTimeZone zone = cProvider.getZone(id);
          if (zone != null) {
              return zone;
          }
          if (id.startsWith("+") || id.startsWith("-")) {
              int offset = parseOffset(id);
              if (offset == 0L) {
                  return DateTimeZone.UTC;
              } else {
                  id = printOffset(offset);
                  return fixedOffsetZone(id, offset);
              }
          }
          throw new IllegalArgumentException("The datetime zone id '" + id + "' is not recognised");
      }
  
      /**
       * Gets a time zone instance for the specified offset to UTC in hours.
       * This method assumes standard length hours.
       * <p>
       * This factory is a convenient way of constructing zones with a fixed offset.
       * 
       * @param hoursOffset  the offset in hours from UTC, from -23 to +23
       * @return the DateTimeZone object for the offset
       * @throws IllegalArgumentException if the offset is too large or too small
       */
      public static DateTimeZone forOffsetHours(int hoursOffset) throws IllegalArgumentException {
          return forOffsetHoursMinutes(hoursOffset, 0);
      }
  
      /**
       * Gets a time zone instance for the specified offset to UTC in hours and minutes.
       * This method assumes 60 minutes in an hour, and standard length minutes.
       * <p>
       * This factory is a convenient way of constructing zones with a fixed offset.
       * The hours value must be in the range -23 to +23.
       * The minutes value must be in the range -59 to +59.
       * The following combinations of sign for the hour and minute are possible:
       * <pre>
       *  Hour    Minute    Example    Result
       * 
       *  +ve     +ve       (2, 15)    +02:15
       *  +ve     zero      (2, 0)     +02:00
       *  +ve     -ve       (2, -15)   IllegalArgumentException
       * 
       *  zero    +ve       (0, 15)    +00:15
       *  zero    zero      (0, 0)     +00:00
       *  zero    -ve       (0, -15)   -00:15
       * 
       *  -ve     +ve       (-2, 15)   -02:15
       *  -ve     zero      (-2, 0)    -02:00
       *  -ve     -ve       (-2, -15)  -02:15
       * </pre>
       * Note that in versions before 2.3, the minutes had to be zero or positive.
       * 
       * @param hoursOffset  the offset in hours from UTC, from -23 to +23
       * @param minutesOffset  the offset in minutes from UTC, from -59 to +59
       * @return the DateTimeZone object for the offset
       * @throws IllegalArgumentException if any value is out of range, the minutes are negative
       *  when the hours are positive, or the resulting offset exceeds +/- 23:59:59.000
       */
      public static DateTimeZone forOffsetHoursMinutes(int hoursOffset, int minutesOffset) throws IllegalArgumentException {
          if (hoursOffset == 0 && minutesOffset == 0) {
              return DateTimeZone.UTC;
          }
          if (hoursOffset < -23 || hoursOffset > 23) {
              throw new IllegalArgumentException("Hours out of range: " + hoursOffset);
          }
          if (minutesOffset < -59 || minutesOffset > 59) {
              throw new IllegalArgumentException("Minutes out of range: " + minutesOffset);
          }
          if (hoursOffset > 0 && minutesOffset < 0) {
              throw new IllegalArgumentException("Positive hours must not have negative minutes: " + minutesOffset);
          }
          int offset = 0;
          try {
              int hoursInMinutes = hoursOffset * 60;
              if (hoursInMinutes < 0) {
                  minutesOffset = hoursInMinutes - Math.abs(minutesOffset);
              } else {
                  minutesOffset = hoursInMinutes + minutesOffset;
              }
              offset = FieldUtils.safeMultiply(minutesOffset, DateTimeConstants.MILLIS_PER_MINUTE);
          } catch (ArithmeticException ex) {
              throw new IllegalArgumentException("Offset is too large");
          }
          return forOffsetMillis(offset);
      }
  
      /**
       * Gets a time zone instance for the specified offset to UTC in milliseconds.
       *
       * @param millisOffset  the offset in millis from UTC, from -23:59:59.999 to +23:59:59.999
       * @return the DateTimeZone object for the offset
       */
      public static DateTimeZone forOffsetMillis(int millisOffset) {
          if (millisOffset < -MAX_MILLIS || millisOffset > MAX_MILLIS) {
              throw new IllegalArgumentException("Millis out of range: " + millisOffset);
          }
          String id = printOffset(millisOffset);
          return fixedOffsetZone(id, millisOffset);
      }
  
      /**
       * Gets a time zone instance for a JDK TimeZone.
       * <p>
       * DateTimeZone only accepts a subset of the IDs from TimeZone. The
       * excluded IDs are the short three letter form (except UTC). This 
       * method will attempt to convert between time zones created using the
       * short IDs and the full version.
       * <p>
       * This method is not designed to parse time zones with rules created by
       * applications using <code>SimpleTimeZone</code> directly.
       * 
       * @param zone  the zone to convert, null means default
       * @return the DateTimeZone object for the zone
       * @throws IllegalArgumentException if the zone is not recognised
       */
      public static DateTimeZone forTimeZone(TimeZone zone) {
          if (zone == null) {
              return getDefault();
          }
          final String id = zone.getID();
          if (id == null) {
              throw new IllegalArgumentException("The TimeZone id must not be null");
          }
          if (id.equals("UTC")) {
              return DateTimeZone.UTC;
          }
  
          // Convert from old alias before consulting provider since they may differ.
          DateTimeZone dtz = null;
          String convId = getConvertedId(id);
          if (convId != null) {
              dtz = cProvider.getZone(convId);
          }
          if (dtz == null) {
              dtz = cProvider.getZone(id);
          }
          if (dtz != null) {
              return dtz;
          }
  
          // Support GMT+/-hh:mm formats
          if (convId == null) {
              convId = id;
              if (convId.startsWith("GMT+") || convId.startsWith("GMT-")) {
                  convId = convId.substring(3);
                  int offset = parseOffset(convId);
                  if (offset == 0L) {
                      return DateTimeZone.UTC;
                  } else {
                      convId = printOffset(offset);
                      return fixedOffsetZone(convId, offset);
                  }
              }
          }
          throw new IllegalArgumentException("The datetime zone id '" + id + "' is not recognised");
      }
  
      //-----------------------------------------------------------------------
      /**
       * Gets the zone using a fixed offset amount.
       * 
       * @param id  the zone id
       * @param offset  the offset in millis
       * @return the zone
       */
      private static synchronized DateTimeZone fixedOffsetZone(String id, int offset) {
          if (offset == 0) {
              return DateTimeZone.UTC;
          }
          if (iFixedOffsetCache == null) {
              iFixedOffsetCache = new HashMap<String, SoftReference<DateTimeZone>>();
          }
          DateTimeZone zone;
          Reference<DateTimeZone> ref = iFixedOffsetCache.get(id);
          if (ref != null) {
              zone = ref.get();
              if (zone != null) {
                  return zone;
              }
          }
          zone = new FixedDateTimeZone(id, null, offset, offset);
          iFixedOffsetCache.put(id, new SoftReference<DateTimeZone>(zone));
          return zone;
      }
  
      /**
       * Gets all the available IDs supported.
       * 
       * @return an unmodifiable Set of String IDs
       */
      public static Set<String> getAvailableIDs() {
          return cAvailableIDs;
      }
  
      //-----------------------------------------------------------------------
      /**
       * Gets the zone provider factory.
       * <p>
       * The zone provider is a pluggable instance factory that supplies the
       * actual instances of DateTimeZone.
       * 
       * @return the provider
       */
      public static Provider getProvider() {
          return cProvider;
      }
  
      /**
       * Sets the zone provider factory.
       * <p>
       * The zone provider is a pluggable instance factory that supplies the
       * actual instances of DateTimeZone.
       * 
       * @param provider  provider to use, or null for default
       * @throws SecurityException if you do not have the permission DateTimeZone.setProvider
       * @throws IllegalArgumentException if the provider is invalid
       */
      public static void setProvider(Provider provider) throws SecurityException {
          SecurityManager sm = System.getSecurityManager();
          if (sm != null) {
              sm.checkPermission(new JodaTimePermission("DateTimeZone.setProvider"));
          }
          setProvider0(provider);
      }
  
      /**
       * Sets the zone provider factory without performing the security check.
       * 
       * @param provider  provider to use, or null for default
       * @throws IllegalArgumentException if the provider is invalid
       */
      private static void setProvider0(Provider provider) {
          if (provider == null) {
              provider = getDefaultProvider();
          }
          Set<String> ids = provider.getAvailableIDs();
          if (ids == null || ids.size() == 0) {
              throw new IllegalArgumentException
                  ("The provider doesn't have any available ids");
          }
          if (!ids.contains("UTC")) {
              throw new IllegalArgumentException("The provider doesn't support UTC");
          }
          if (!UTC.equals(provider.getZone("UTC"))) {
              throw new IllegalArgumentException("Invalid UTC zone provided");
          }
          cProvider = provider;
          cAvailableIDs = ids;
      }
  
      /**
       * Gets the default zone provider.
       * <p>
       * Tries the system property <code>org.joda.time.DateTimeZone.Provider</code>.
       * Then tries a <code>ZoneInfoProvider</code> using the data in <code>org/joda/time/tz/data</code>.
       * Then uses <code>UTCProvider</code>.
       * 
       * @return the default name provider
       */
      private static Provider getDefaultProvider() {
          Provider provider = null;
  
          try {
              String providerClass = System.getProperty("org.joda.time.DateTimeZone.Provider");
              if (providerClass != null) {
                  try {
                      provider = (Provider) Class.forName(providerClass).newInstance();
                  } catch (Exception ex) {
                      throw new RuntimeException(ex);
                  }
              }
          } catch (SecurityException ex) {
              // ignored
          }
  
          if (provider == null) {
              try {
                  provider = new ZoneInfoProvider("org/joda/time/tz/data");
              } catch (Exception ex) {
                  ex.printStackTrace();
              }
          }
  
          if (provider == null) {
              provider = new UTCProvider();
          }
  
          return provider;
      }
  
      //-----------------------------------------------------------------------
      /**
       * Gets the name provider factory.
       * <p>
       * The name provider is a pluggable instance factory that supplies the
       * names of each DateTimeZone.
       * 
       * @return the provider
       */
      public static NameProvider getNameProvider() {
          return cNameProvider;
      }
  
      /**
       * Sets the name provider factory.
       * <p>
       * The name provider is a pluggable instance factory that supplies the
       * names of each DateTimeZone.
       * 
       * @param nameProvider  provider to use, or null for default
       * @throws SecurityException if you do not have the permission DateTimeZone.setNameProvider
       * @throws IllegalArgumentException if the provider is invalid
       */
      public static void setNameProvider(NameProvider nameProvider) throws SecurityException {
          SecurityManager sm = System.getSecurityManager();
          if (sm != null) {
              sm.checkPermission(new JodaTimePermission("DateTimeZone.setNameProvider"));
          }
          setNameProvider0(nameProvider);
      }
  
      /**
       * Sets the name provider factory without performing the security check.
       * 
       * @param nameProvider  provider to use, or null for default
       * @throws IllegalArgumentException if the provider is invalid
       */
      private static void setNameProvider0(NameProvider nameProvider) {
          if (nameProvider == null) {
              nameProvider = getDefaultNameProvider();
          }
          cNameProvider = nameProvider;
      }
  
      /**
       * Gets the default name provider.
       * <p>
       * Tries the system property <code>org.joda.time.DateTimeZone.NameProvider</code>.
       * Then uses <code>DefaultNameProvider</code>.
       * 
       * @return the default name provider
       */
      private static NameProvider getDefaultNameProvider() {
          NameProvider nameProvider = null;
          try {
              String providerClass = System.getProperty("org.joda.time.DateTimeZone.NameProvider");
              if (providerClass != null) {
                  try {
                      nameProvider = (NameProvider) Class.forName(providerClass).newInstance();
                  } catch (Exception ex) {
                      throw new RuntimeException(ex);
                  }
              }
          } catch (SecurityException ex) {
              // ignore
          }
  
          if (nameProvider == null) {
              nameProvider = new DefaultNameProvider();
          }
  
          return nameProvider;
      }
  
      //-----------------------------------------------------------------------
      /**
       * Converts an old style id to a new style id.
       * 
       * @param id  the old style id
       * @return the new style id, null if not found
       */
      private static synchronized String getConvertedId(String id) {
          Map<String, String> map = cZoneIdConversion;
          if (map == null) {
              // Backwards compatibility with TimeZone.
              map = new HashMap<String, String>();
              map.put("GMT", "UTC");
              map.put("WET", "WET");
              map.put("CET", "CET");
              map.put("MET", "CET");
              map.put("ECT", "CET");
              map.put("EET", "EET");
              map.put("MIT", "Pacific/Apia");
              map.put("HST", "Pacific/Honolulu");  // JDK 1.1 compatible
              map.put("AST", "America/Anchorage");
              map.put("PST", "America/Los_Angeles");
              map.put("MST", "America/Denver");  // JDK 1.1 compatible
              map.put("PNT", "America/Phoenix");
              map.put("CST", "America/Chicago");
              map.put("EST", "America/New_York");  // JDK 1.1 compatible
              map.put("IET", "America/Indiana/Indianapolis");
              map.put("PRT", "America/Puerto_Rico");
              map.put("CNT", "America/St_Johns");
              map.put("AGT", "America/Argentina/Buenos_Aires");
              map.put("BET", "America/Sao_Paulo");
              map.put("ART", "Africa/Cairo");
              map.put("CAT", "Africa/Harare");
              map.put("EAT", "Africa/Addis_Ababa");
              map.put("NET", "Asia/Yerevan");
              map.put("PLT", "Asia/Karachi");
              map.put("IST", "Asia/Kolkata");
              map.put("BST", "Asia/Dhaka");
              map.put("VST", "Asia/Ho_Chi_Minh");
              map.put("CTT", "Asia/Shanghai");
              map.put("JST", "Asia/Tokyo");
              map.put("ACT", "Australia/Darwin");
              map.put("AET", "Australia/Sydney");
              map.put("SST", "Pacific/Guadalcanal");
              map.put("NST", "Pacific/Auckland");
              cZoneIdConversion = map;
          }
          return map.get(id);
      }
  
      private static int parseOffset(String str) {
          // Can't use a real chronology if called during class
          // initialization. Offset parser doesn't need it anyhow.
          Chronology chrono = new BaseChronology() {
              private static final long serialVersionUID = -3128740902654445468L;
              public DateTimeZone getZone() {
                  return null;
              }
              public Chronology withUTC() {
                  return this;
              }
              public Chronology withZone(DateTimeZone zone) {
                  return this;
              }
              public String toString() {
                  return getClass().getName();
              }
          };
          return -(int) offsetFormatter().withChronology(chrono).parseMillis(str);
      }
  
      /**
       * Formats a timezone offset string.
       * <p>
       * This method is kept separate from the formatting classes to speed and
       * simplify startup and classloading.
       * 
       * @param offset  the offset in milliseconds
       * @return the time zone string
       */
      private static String printOffset(int offset) {
          StringBuffer buf = new StringBuffer();
  