/**
   * {@icu} Returns the array of <code>TimeZoneRule</code> which represents the rule of this time
   * zone object near the specified date. Some applications are not capable to handle historic time
   * zone rule changes. Also some applications can only handle certain type of rule definitions.
   * This method returns either a single <code>InitialTimeZoneRule</code> if this time zone does not
   * have any daylight saving time within 1 year from the specified time, or a pair of <code>
   * AnnualTimeZoneRule</code> whose rule type is <code>DateTimeRule.DOW</code> for date and <code>
   * DateTimeRule.WALL_TIME</code> for time with a single <code>InitialTimeZoneRule</code>
   * representing the initial time, when this time zone observes daylight saving time near the
   * specified date. Thus, the result may be only valid for dates around the specified date.
   *
   * @param date The date to be used for <code>TimeZoneRule</code> extraction.
   * @return The array of <code>TimeZoneRule</code>, either a single <code>InitialTimeZoneRule
   *     </code> object, or a pair of <code>AnnualTimeZoneRule</code> with a single <code>
   *     InitialTimeZoneRule</code>. The first element in the array is always a <code>
   *     InitialTimeZoneRule</code>.
   * @stable ICU 3.8
   */
  public TimeZoneRule[] getSimpleTimeZoneRulesNear(long date) {
    AnnualTimeZoneRule[] annualRules = null;
    TimeZoneRule initialRule = null;
    // Get the next transition
    TimeZoneTransition tr = getNextTransition(date, false);
    if (tr != null) {
      String initialName = tr.getFrom().getName();
      int initialRaw = tr.getFrom().getRawOffset();
      int initialDst = tr.getFrom().getDSTSavings();

      // Check if the next transition is either DST->STD or STD->DST and
      // within roughly 1 year from the specified date
      long nextTransitionTime = tr.getTime();
      if (((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0)
              || (tr.getFrom().getDSTSavings() != 0 && tr.getTo().getDSTSavings() == 0))
          && date + MILLIS_PER_YEAR > nextTransitionTime) {
        annualRules = new AnnualTimeZoneRule[2];
        // Get local wall time for the transition time
        int dtfields[] =
            Grego.timeToFields(
                nextTransitionTime + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(),
                null);
        int weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], dtfields[2]);
        // Create DOW rule
        DateTimeRule dtr =
            new DateTimeRule(
                dtfields[1], weekInMonth, dtfields[3], dtfields[5], DateTimeRule.WALL_TIME);

        AnnualTimeZoneRule secondRule = null;

        // Note:  SimpleTimeZone does not support raw offset change.
        // So we always use raw offset of the given time for the rule,
        // even raw offset is changed.  This will result that the result
        // zone to return wrong offset after the transition.
        // When we encounter such case, we do not inspect next next
        // transition for another rule.
        annualRules[0] =
            new AnnualTimeZoneRule(
                tr.getTo().getName(),
                initialRaw,
                tr.getTo().getDSTSavings(),
                dtr,
                dtfields[0],
                AnnualTimeZoneRule.MAX_YEAR);

        if (tr.getTo().getRawOffset() == initialRaw) {

          // Get the next next transition
          tr = getNextTransition(nextTransitionTime, false);
          if (tr != null) {
            // Check if the next next transition is either DST->STD or STD->DST
            // and within roughly 1 year from the next transition
            if (((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0)
                    || (tr.getFrom().getDSTSavings() != 0 && tr.getTo().getDSTSavings() == 0))
                && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
              // Generate another DOW rule
              dtfields =
                  Grego.timeToFields(
                      tr.getTime() + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(),
                      dtfields);
              weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], dtfields[2]);
              dtr =
                  new DateTimeRule(
                      dtfields[1], weekInMonth, dtfields[3], dtfields[5], DateTimeRule.WALL_TIME);
              secondRule =
                  new AnnualTimeZoneRule(
                      tr.getTo().getName(),
                      tr.getTo().getRawOffset(),
                      tr.getTo().getDSTSavings(),
                      dtr,
                      dtfields[0] - 1,
                      AnnualTimeZoneRule.MAX_YEAR);
              // Make sure this rule can be applied to the specified date
              Date d =
                  secondRule.getPreviousStart(
                      date, tr.getFrom().getRawOffset(), tr.getFrom().getDSTSavings(), true);
              if (d != null
                  && d.getTime() <= date
                  && initialRaw == tr.getTo().getRawOffset()
                  && initialDst == tr.getTo().getDSTSavings()) {
                // We can use this rule as the second transition rule
                annualRules[1] = secondRule;
              }
            }
          }
        }

        if (annualRules[1] == null) {
          // Try previous transition
          tr = getPreviousTransition(date, true);
          if (tr != null) {
            // Check if the previous transition is either DST->STD or STD->DST.
            // The actual transition time does not matter here.
            if ((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0)
                || (tr.getFrom().getDSTSavings() != 0 && tr.getTo().getDSTSavings() == 0)) {
              // Generate another DOW rule
              dtfields =
                  Grego.timeToFields(
                      tr.getTime() + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(),
                      dtfields);
              weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], dtfields[2]);
              dtr =
                  new DateTimeRule(
                      dtfields[1], weekInMonth, dtfields[3], dtfields[5], DateTimeRule.WALL_TIME);

              // second rule raw/dst offsets should match raw/dst offsets
              // at the given time
              secondRule =
                  new AnnualTimeZoneRule(
                      tr.getTo().getName(),
                      initialRaw,
                      initialDst,
                      dtr,
                      annualRules[0].getStartYear() - 1,
                      AnnualTimeZoneRule.MAX_YEAR);

              // Check if this rule start after the first rule after the
              // specified date
              Date d =
                  secondRule.getNextStart(
                      date, tr.getFrom().getRawOffset(), tr.getFrom().getDSTSavings(), false);
              if (d.getTime() > nextTransitionTime) {
                // We can use this rule as the second transition rule
                annualRules[1] = secondRule;
              }
            }
          }
        }
        if (annualRules[1] == null) {
          // Cannot generate a good pair of AnnualTimeZoneRule
          annualRules = null;
        } else {
          // The initial rule should represent the rule before the previous transition
          initialName = annualRules[0].getName();
          initialRaw = annualRules[0].getRawOffset();
          initialDst = annualRules[0].getDSTSavings();
        }
      }
      initialRule = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
    } else {
      // Try the previous one
      tr = getPreviousTransition(date, true);
      if (tr != null) {
        initialRule =
            new InitialTimeZoneRule(
                tr.getTo().getName(), tr.getTo().getRawOffset(), tr.getTo().getDSTSavings());
      } else {
        // No transitions in the past.  Just use the current offsets
        int[] offsets = new int[2];
        getOffset(date, false, offsets);
        initialRule = new InitialTimeZoneRule(getID(), offsets[0], offsets[1]);
      }
    }

    TimeZoneRule[] result = null;
    if (annualRules == null) {
      result = new TimeZoneRule[1];
      result[0] = initialRule;
    } else {
      result = new TimeZoneRule[3];
      result[0] = initialRule;
      result[1] = annualRules[0];
      result[2] = annualRules[1];
    }

    return result;
  }