예제 #1
0
  /**
   * Returns the time zone raw and GMT offset for the given moment in time. Upon return,
   * local-millis = GMT-millis + rawOffset + dstOffset. All computations are performed in the
   * proleptic Gregorian calendar. The default implementation in the TimeZone class delegates to the
   * 8-argument getOffset().
   *
   * @param date moment in time for which to return offsets, in units of milliseconds from January
   *     1, 1970 0:00 GMT, either GMT time or local wall time, depending on `local'.
   * @param local if true, `date' is local wall time; otherwise it is in GMT time.
   * @param offsets output parameter to receive the raw offset, that is, the offset not including
   *     DST adjustments, in offsets[0], and the DST offset, that is, the offset to be added to
   *     `rawOffset' to obtain the total offset between local and GMT time, in offsets[1]. If DST is
   *     not in effect, the DST offset is zero; otherwise it is a positive value, typically one
   *     hour.
   * @stable ICU 2.8
   */
  public void getOffset(long date, boolean local, int[] offsets) {
    offsets[0] = getRawOffset();
    if (!local) {
      date += offsets[0]; // now in local standard millis
    }

    // When local == true, date might not be in local standard
    // millis.  getOffset taking 6 parameters used here assume
    // the given time in day is local standard time.
    // At STD->DST transition, there is a range of time which
    // does not exist.  When 'date' is in this time range
    // (and local == true), this method interprets the specified
    // local time as DST.  At DST->STD transition, there is a
    // range of time which occurs twice.  In this case, this
    // method interprets the specified local time as STD.
    // To support the behavior above, we need to call getOffset
    // (with 6 args) twice when local == true and DST is
    // detected in the initial call.
    int fields[] = new int[6];
    for (int pass = 0; ; pass++) {
      Grego.timeToFields(date, fields);
      offsets[1] =
          getOffset(GregorianCalendar.AD, fields[0], fields[1], fields[2], fields[3], fields[5])
              - offsets[0];

      if (pass != 0 || !local || offsets[1] == 0) {
        break;
      }
      // adjust to local standard millis
      date -= offsets[1];
    }
  }
  /**
   * {@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;
  }
  /**
   * {@icu} Returns the array of <code>TimeZoneRule</code> which represents the rule of this time
   * zone object since the specified start time. The first element in the result array will be the
   * <code>InitialTimeZoneRule</code> instance for the initial rule. The rest will be either <code>
   * AnnualTimeZoneRule</code> or <code>TimeArrayTimeZoneRule</code> instances representing
   * transitions.
   *
   * @param start The start time (inclusive).
   * @return The array of <code>TimeZoneRule</code> which represents this time zone since the start
   *     time.
   * @stable ICU 3.8
   */
  public TimeZoneRule[] getTimeZoneRules(long start) {
    TimeZoneRule[] all = getTimeZoneRules();
    TimeZoneTransition tzt = getPreviousTransition(start, true);
    if (tzt == null) {
      // No need to filter out rules only applicable to time before the start
      return all;
    }

    BitSet isProcessed = new BitSet(all.length);
    List<TimeZoneRule> filteredRules = new LinkedList<TimeZoneRule>();

    // Create initial rule
    TimeZoneRule initial =
        new InitialTimeZoneRule(
            tzt.getTo().getName(), tzt.getTo().getRawOffset(), tzt.getTo().getDSTSavings());
    filteredRules.add(initial);
    isProcessed.set(0);

    // Mark rules which does not need to be processed
    for (int i = 1; i < all.length; i++) {
      Date d = all[i].getNextStart(start, initial.getRawOffset(), initial.getDSTSavings(), false);
      if (d == null) {
        isProcessed.set(i);
      }
    }

    long time = start;
    boolean bFinalStd = false, bFinalDst = false;
    while (!bFinalStd || !bFinalDst) {
      tzt = getNextTransition(time, false);
      if (tzt == null) {
        break;
      }
      time = tzt.getTime();

      TimeZoneRule toRule = tzt.getTo();
      int ruleIdx = 1;
      for (; ruleIdx < all.length; ruleIdx++) {
        if (all[ruleIdx].equals(toRule)) {
          break;
        }
      }
      if (ruleIdx >= all.length) {
        throw new IllegalStateException("The rule was not found");
      }
      if (isProcessed.get(ruleIdx)) {
        continue;
      }
      if (toRule instanceof TimeArrayTimeZoneRule) {
        TimeArrayTimeZoneRule tar = (TimeArrayTimeZoneRule) toRule;

        // Get the previous raw offset and DST savings before the very first start time
        long t = start;
        while (true) {
          tzt = getNextTransition(t, false);
          if (tzt == null) {
            break;
          }
          if (tzt.getTo().equals(tar)) {
            break;
          }
          t = tzt.getTime();
        }
        if (tzt != null) {
          // Check if the entire start times to be added
          Date firstStart =
              tar.getFirstStart(tzt.getFrom().getRawOffset(), tzt.getFrom().getDSTSavings());
          if (firstStart.getTime() > start) {
            // Just add the rule as is
            filteredRules.add(tar);
          } else {
            // Collect transitions after the start time
            long[] times = tar.getStartTimes();
            int timeType = tar.getTimeType();
            int idx;
            for (idx = 0; idx < times.length; idx++) {
              t = times[idx];
              if (timeType == DateTimeRule.STANDARD_TIME) {
                t -= tzt.getFrom().getRawOffset();
              }
              if (timeType == DateTimeRule.WALL_TIME) {
                t -= tzt.getFrom().getDSTSavings();
              }
              if (t > start) {
                break;
              }
            }
            int asize = times.length - idx;
            if (asize > 0) {
              long[] newtimes = new long[asize];
              System.arraycopy(times, idx, newtimes, 0, asize);
              TimeArrayTimeZoneRule newtar =
                  new TimeArrayTimeZoneRule(
                      tar.getName(),
                      tar.getRawOffset(),
                      tar.getDSTSavings(),
                      newtimes,
                      tar.getTimeType());
              filteredRules.add(newtar);
            }
          }
        }
      } else if (toRule instanceof AnnualTimeZoneRule) {
        AnnualTimeZoneRule ar = (AnnualTimeZoneRule) toRule;
        Date firstStart =
            ar.getFirstStart(tzt.getFrom().getRawOffset(), tzt.getFrom().getDSTSavings());
        if (firstStart.getTime() == tzt.getTime()) {
          // Just add the rule as is
          filteredRules.add(ar);
        } else {
          // Calculate the transition year
          int[] dfields = new int[6];
          Grego.timeToFields(tzt.getTime(), dfields);
          // Recreate the rule
          AnnualTimeZoneRule newar =
              new AnnualTimeZoneRule(
                  ar.getName(),
                  ar.getRawOffset(),
                  ar.getDSTSavings(),
                  ar.getRule(),
                  dfields[0],
                  ar.getEndYear());
          filteredRules.add(newar);
        }
        // Check if this is a final rule
        if (ar.getEndYear() == AnnualTimeZoneRule.MAX_YEAR) {
          // After both final standard and dst rule are processed,
          // exit this while loop.
          if (ar.getDSTSavings() == 0) {
            bFinalStd = true;
          } else {
            bFinalDst = true;
          }
        }
      }
      isProcessed.set(ruleIdx);
    }
    TimeZoneRule[] rules = filteredRules.toArray(new TimeZoneRule[filteredRules.size()]);
    return rules;
  }