/**
  * Returns the offset of this time zone from UTC at the specified date. If Daylight Saving Time is
  * in effect at the specified date, the offset value is adjusted with the amount of daylight
  * saving.
  *
  * @param date the date represented in milliseconds since January 1, 1970 00:00:00 GMT
  * @return the amount of time in milliseconds to add to UTC to get local time.
  * @see Calendar#ZONE_OFFSET
  * @see Calendar#DST_OFFSET
  * @see #getOffset(long, boolean, int[])
  * @stable ICU 2.8
  */
 public int getOffset(long date) {
   int[] result = new int[2];
   getOffset(date, false, result);
   return result[0] + result[1];
 }
  /**
   * {@icu} Checks if the time zone has equivalent transitions in the time range. This method
   * returns true when all of transition times, from/to standard offsets and DST savings used by
   * this time zone match the other in the time range.
   *
   * @param tz The instance of <code>TimeZone</code>
   * @param start The start time of the evaluated time range (inclusive)
   * @param end The end time of the evaluated time range (inclusive)
   * @param ignoreDstAmount When true, any transitions with only daylight saving amount changes will
   *     be ignored, except either of them is zero. For example, a transition from rawoffset
   *     3:00/dstsavings 1:00 to rawoffset 2:00/dstsavings 2:00 is excluded from the comparison, but
   *     a transtion from rawoffset 2:00/dstsavings 1:00 to rawoffset 3:00/dstsavings 0:00 is
   *     included.
   * @return true if the other time zone has the equivalent transitions in the time range. When tz
   *     is not a <code>BasicTimeZone</code>, this method returns false.
   * @stable ICU 3.8
   */
  public boolean hasEquivalentTransitions(
      TimeZone tz, long start, long end, boolean ignoreDstAmount) {
    if (hasSameRules(tz)) {
      return true;
    }
    if (!(tz instanceof BasicTimeZone)) {
      return false;
    }

    // Check the offsets at the start time
    int[] offsets1 = new int[2];
    int[] offsets2 = new int[2];

    getOffset(start, false, offsets1);
    tz.getOffset(start, false, offsets2);

    if (ignoreDstAmount) {
      if ((offsets1[0] + offsets1[1] != offsets2[0] + offsets2[1])
          || (offsets1[1] != 0 && offsets2[1] == 0)
          || (offsets1[1] == 0 && offsets2[1] != 0)) {
        return false;
      }
    } else {
      if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) {
        return false;
      }
    }

    // Check transitions in the range
    long time = start;
    while (true) {
      TimeZoneTransition tr1 = getNextTransition(time, false);
      TimeZoneTransition tr2 = ((BasicTimeZone) tz).getNextTransition(time, false);

      if (ignoreDstAmount) {
        // Skip a transition which only differ the amount of DST savings
        while (true) {
          if (tr1 != null
              && tr1.getTime() <= end
              && (tr1.getFrom().getRawOffset() + tr1.getFrom().getDSTSavings()
                  == tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings())
              && (tr1.getFrom().getDSTSavings() != 0 && tr1.getTo().getDSTSavings() != 0)) {
            tr1 = getNextTransition(tr1.getTime(), false);
          } else {
            break;
          }
        }
        while (true) {
          if (tr2 != null
              && tr2.getTime() <= end
              && (tr2.getFrom().getRawOffset() + tr2.getFrom().getDSTSavings()
                  == tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings())
              && (tr2.getFrom().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() != 0)) {
            tr2 = ((BasicTimeZone) tz).getNextTransition(tr2.getTime(), false);
          } else {
            break;
          }
        }
      }

      boolean inRange1 = false;
      boolean inRange2 = false;
      if (tr1 != null) {
        if (tr1.getTime() <= end) {
          inRange1 = true;
        }
      }
      if (tr2 != null) {
        if (tr2.getTime() <= end) {
          inRange2 = true;
        }
      }
      if (!inRange1 && !inRange2) {
        // No more transition in the range
        break;
      }
      if (!inRange1 || !inRange2) {
        return false;
      }
      if (tr1.getTime() != tr2.getTime()) {
        return false;
      }
      if (ignoreDstAmount) {
        if (tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings()
                != tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings()
            || tr1.getTo().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() == 0
            || tr1.getTo().getDSTSavings() == 0 && tr2.getTo().getDSTSavings() != 0) {
          return false;
        }
      } else {
        if (tr1.getTo().getRawOffset() != tr2.getTo().getRawOffset()
            || tr1.getTo().getDSTSavings() != tr2.getTo().getDSTSavings()) {
          return false;
        }
      }
      time = tr1.getTime();
    }
    return true;
  }