/** * {@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; }