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