1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2007-2014, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.util; 10 11 import java.util.BitSet; 12 import java.util.Date; 13 import java.util.LinkedList; 14 import java.util.List; 15 16 import com.ibm.icu.impl.Grego; 17 18 /** 19 * {@icu} BasicTimeZone extends <code>TimeZone</code> with additional methods to access 20 * time zone transitions and rules. All ICU <code>TimeZone</code> concrete subclasses 21 * extend this class. APIs added to <code>java.util.TimeZone</code> by 22 * <code>BasicTimeZone</code> are annotated with <strong>'<font 23 * style="color:red">[icu]</font>'</strong>. 24 * 25 * @see com.ibm.icu.util.TimeZoneRule 26 * @see com.ibm.icu.util.TimeZoneTransition 27 * 28 * @stable ICU 3.8 29 */ 30 public abstract class BasicTimeZone extends TimeZone { 31 32 private static final long serialVersionUID = -3204278532246180932L; 33 34 private static final long MILLIS_PER_YEAR = 365*24*60*60*1000L; 35 36 /** 37 * {@icu} Returns the first time zone transition after the base time. 38 * <p>Example code:{@.jcite com.ibm.icu.samples.util.timezone.BasicTimeZoneExample:---getNextTransitionExample} 39 * 40 * @param base The base time. 41 * @param inclusive Whether the base time is inclusive or not. 42 * 43 * @return A <code>Date</code> holding the first time zone transition time 44 * after the given base time, or null if no time zone transitions 45 * are available after the base time. 46 * 47 * @stable ICU 3.8 48 */ getNextTransition(long base, boolean inclusive)49 public abstract TimeZoneTransition getNextTransition(long base, boolean inclusive); 50 51 /** 52 * {@icu} Returns the last time zone transition before the base time. 53 * <p>Example code:{@.jcite com.ibm.icu.samples.util.timezone.BasicTimeZoneExample:---getPreviousTransitionExample} 54 * 55 * @param base The base time. 56 * @param inclusive Whether the base time is inclusive or not. 57 * 58 * @return A <code>Date</code> holding the last time zone transition time 59 * before the given base time, or null if no time zone transitions 60 * are available before the base time. 61 * 62 * @stable ICU 3.8 63 */ getPreviousTransition(long base, boolean inclusive)64 public abstract TimeZoneTransition getPreviousTransition(long base, boolean inclusive); 65 66 /** 67 * {@icu} Checks if the time zone has equivalent transitions in the time range. 68 * This method returns true when all of transition times, from/to standard 69 * offsets and DST savings used by this time zone match the other in the 70 * time range. 71 * <p>Example code:{@.jcite com.ibm.icu.samples.util.timezone.BasicTimeZoneExample:---hasEquivalentTransitionsExample} 72 * 73 * @param tz The instance of <code>TimeZone</code> 74 * @param start The start time of the evaluated time range (inclusive) 75 * @param end The end time of the evaluated time range (inclusive) 76 * 77 * @return true if the other time zone has the equivalent transitions in the 78 * time range. When tz is not a <code>BasicTimeZone</code>, this method 79 * returns false. 80 * 81 * @stable ICU 3.8 82 */ hasEquivalentTransitions(TimeZone tz, long start, long end)83 public boolean hasEquivalentTransitions(TimeZone tz, long start, long end) { 84 return hasEquivalentTransitions(tz, start, end, false); 85 } 86 87 /** 88 * {@icu} Checks if the time zone has equivalent transitions in the time range. 89 * This method returns true when all of transition times, from/to standard 90 * offsets and DST savings used by this time zone match the other in the 91 * time range. 92 * 93 * @param tz The instance of <code>TimeZone</code> 94 * @param start The start time of the evaluated time range (inclusive) 95 * @param end The end time of the evaluated time range (inclusive) 96 * @param ignoreDstAmount When true, any transitions with only daylight saving amount 97 * changes will be ignored, except either of them is zero. For example, a transition 98 * from rawoffset 3:00/dstsavings 1:00 to rawoffset 2:00/dstsavings 2:00 is excluded 99 * from the comparison, but a transtion from rawoffset 2:00/dstsavings 1:00 to 100 * rawoffset 3:00/dstsavings 0:00 is included. 101 * 102 * @return true if the other time zone has the equivalent transitions in the 103 * time range. When tz is not a <code>BasicTimeZone</code>, this method 104 * returns false. 105 * 106 * @stable ICU 3.8 107 */ hasEquivalentTransitions(TimeZone tz, long start, long end, boolean ignoreDstAmount)108 public boolean hasEquivalentTransitions(TimeZone tz, long start, long end, 109 boolean ignoreDstAmount) { 110 if (this == tz) { 111 return true; 112 } 113 114 if (!(tz instanceof BasicTimeZone)) { 115 return false; 116 } 117 118 // Check the offsets at the start time 119 int[] offsets1 = new int[2]; 120 int[] offsets2 = new int[2]; 121 122 getOffset(start, false, offsets1); 123 tz.getOffset(start, false, offsets2); 124 125 if (ignoreDstAmount) { 126 if ((offsets1[0] + offsets1[1] != offsets2[0] + offsets2[1]) 127 || (offsets1[1] != 0 && offsets2[1] == 0) 128 || (offsets1[1] == 0 && offsets2[1] != 0)) { 129 return false; 130 } 131 } else { 132 if (offsets1[0] != offsets2[0] || offsets1[1] != offsets2[1]) { 133 return false; 134 } 135 } 136 137 // Check transitions in the range 138 long time = start; 139 while (true) { 140 TimeZoneTransition tr1 = getNextTransition(time, false); 141 TimeZoneTransition tr2 = ((BasicTimeZone)tz).getNextTransition(time, false); 142 143 if (ignoreDstAmount) { 144 // Skip a transition which only differ the amount of DST savings 145 while (true) { 146 if (tr1 != null 147 && tr1.getTime() <= end 148 && (tr1.getFrom().getRawOffset() + tr1.getFrom().getDSTSavings() 149 == tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings()) 150 && (tr1.getFrom().getDSTSavings() != 0 && tr1.getTo().getDSTSavings() != 0)) { 151 tr1 = getNextTransition(tr1.getTime(), false); 152 } else { 153 break; 154 } 155 } 156 while (true) { 157 if (tr2 != null 158 && tr2.getTime() <= end 159 && (tr2.getFrom().getRawOffset() + tr2.getFrom().getDSTSavings() 160 == tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings()) 161 && (tr2.getFrom().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() != 0)) { 162 tr2 = ((BasicTimeZone)tz).getNextTransition(tr2.getTime(), false); 163 } else { 164 break; 165 } 166 } 167 } 168 169 boolean inRange1 = false; 170 boolean inRange2 = false; 171 if (tr1 != null) { 172 if (tr1.getTime() <= end) { 173 inRange1 = true; 174 } 175 } 176 if (tr2 != null) { 177 if (tr2.getTime() <= end) { 178 inRange2 = true; 179 } 180 } 181 if (!inRange1 && !inRange2) { 182 // No more transition in the range 183 break; 184 } 185 if (!inRange1 || !inRange2) { 186 return false; 187 } 188 if (tr1.getTime() != tr2.getTime()) { 189 return false; 190 } 191 if (ignoreDstAmount) { 192 if (tr1.getTo().getRawOffset() + tr1.getTo().getDSTSavings() 193 != tr2.getTo().getRawOffset() + tr2.getTo().getDSTSavings() 194 || tr1.getTo().getDSTSavings() != 0 && tr2.getTo().getDSTSavings() == 0 195 || tr1.getTo().getDSTSavings() == 0 && tr2.getTo().getDSTSavings() != 0) { 196 return false; 197 } 198 } else { 199 if (tr1.getTo().getRawOffset() != tr2.getTo().getRawOffset() || 200 tr1.getTo().getDSTSavings() != tr2.getTo().getDSTSavings()) { 201 return false; 202 } 203 } 204 time = tr1.getTime(); 205 } 206 return true; 207 } 208 209 /** 210 * {@icu} Returns the array of <code>TimeZoneRule</code> which represents the rule 211 * of this time zone object. The first element in the result array will 212 * be the <code>InitialTimeZoneRule</code> instance for the initial rule. 213 * The rest will be either <code>AnnualTimeZoneRule</code> or 214 * <code>TimeArrayTimeZoneRule</code> instances representing transitions. 215 * 216 * @return The array of <code>TimeZoneRule</code> which represents this 217 * time zone. 218 * 219 * @stable ICU 3.8 220 */ getTimeZoneRules()221 public abstract TimeZoneRule[] getTimeZoneRules(); 222 223 /** 224 * {@icu} Returns the array of <code>TimeZoneRule</code> which represents the rule 225 * of this time zone object since the specified start time. The first 226 * element in the result array will be the <code>InitialTimeZoneRule</code> 227 * instance for the initial rule. The rest will be either 228 * <code>AnnualTimeZoneRule</code> or <code>TimeArrayTimeZoneRule</code> 229 * instances representing transitions. 230 * <p>Example code:{@.jcite com.ibm.icu.samples.util.timezone.BasicTimeZoneExample:---getTimeZoneRulesExample} 231 * 232 * @param start The start time (inclusive). 233 * @return The array of <code>TimeZoneRule</code> which represents this 234 * time zone since the start time. 235 * 236 * @stable ICU 3.8 237 */ getTimeZoneRules(long start)238 public TimeZoneRule[] getTimeZoneRules(long start) { 239 TimeZoneRule[] all = getTimeZoneRules(); 240 TimeZoneTransition tzt = getPreviousTransition(start, true); 241 if (tzt == null) { 242 // No need to filter out rules only applicable to time before the start 243 return all; 244 } 245 246 BitSet isProcessed = new BitSet(all.length); 247 List<TimeZoneRule> filteredRules = new LinkedList<TimeZoneRule>(); 248 249 // Create initial rule 250 TimeZoneRule initial = new InitialTimeZoneRule(tzt.getTo().getName(), 251 tzt.getTo().getRawOffset(), tzt.getTo().getDSTSavings()); 252 filteredRules.add(initial); 253 isProcessed.set(0); 254 255 // Mark rules which does not need to be processed 256 for (int i = 1; i < all.length; i++) { 257 Date d = all[i].getNextStart(start, initial.getRawOffset(), 258 initial.getDSTSavings(), false); 259 if (d == null) { 260 isProcessed.set(i); 261 } 262 } 263 264 long time = start; 265 boolean bFinalStd = false, bFinalDst = false; 266 while(!bFinalStd || !bFinalDst) { 267 tzt = getNextTransition(time, false); 268 if (tzt == null) { 269 break; 270 } 271 time = tzt.getTime(); 272 273 TimeZoneRule toRule = tzt.getTo(); 274 int ruleIdx = 1; 275 for (; ruleIdx < all.length; ruleIdx++) { 276 if (all[ruleIdx].equals(toRule)) { 277 break; 278 } 279 } 280 if (ruleIdx >= all.length) { 281 throw new IllegalStateException("The rule was not found"); 282 } 283 if (isProcessed.get(ruleIdx)) { 284 continue; 285 } 286 if (toRule instanceof TimeArrayTimeZoneRule) { 287 TimeArrayTimeZoneRule tar = (TimeArrayTimeZoneRule)toRule; 288 289 // Get the previous raw offset and DST savings before the very first start time 290 long t = start; 291 while(true) { 292 tzt = getNextTransition(t, false); 293 if (tzt == null) { 294 break; 295 } 296 if (tzt.getTo().equals(tar)) { 297 break; 298 } 299 t = tzt.getTime(); 300 } 301 if (tzt != null) { 302 // Check if the entire start times to be added 303 Date firstStart = tar.getFirstStart(tzt.getFrom().getRawOffset(), 304 tzt.getFrom().getDSTSavings()); 305 if (firstStart.getTime() > start) { 306 // Just add the rule as is 307 filteredRules.add(tar); 308 } else { 309 // Collect transitions after the start time 310 long[] times = tar.getStartTimes(); 311 int timeType = tar.getTimeType(); 312 int idx; 313 for (idx = 0; idx < times.length; idx++) { 314 t = times[idx]; 315 if (timeType == DateTimeRule.STANDARD_TIME) { 316 t -= tzt.getFrom().getRawOffset(); 317 } 318 if (timeType == DateTimeRule.WALL_TIME) { 319 t -= tzt.getFrom().getDSTSavings(); 320 } 321 if (t > start) { 322 break; 323 } 324 } 325 int asize = times.length - idx; 326 if (asize > 0) { 327 long[] newtimes = new long[asize]; 328 System.arraycopy(times, idx, newtimes, 0, asize); 329 TimeArrayTimeZoneRule newtar = new TimeArrayTimeZoneRule( 330 tar.getName(), tar.getRawOffset(), tar.getDSTSavings(), 331 newtimes, tar.getTimeType()); 332 filteredRules.add(newtar); 333 } 334 } 335 } 336 } else if (toRule instanceof AnnualTimeZoneRule) { 337 AnnualTimeZoneRule ar = (AnnualTimeZoneRule)toRule; 338 Date firstStart = ar.getFirstStart(tzt.getFrom().getRawOffset(), 339 tzt.getFrom().getDSTSavings()); 340 if (firstStart.getTime() == tzt.getTime()) { 341 // Just add the rule as is 342 filteredRules.add(ar); 343 } else { 344 // Calculate the transition year 345 int[] dfields = new int[6]; 346 Grego.timeToFields(tzt.getTime(), dfields); 347 // Recreate the rule 348 AnnualTimeZoneRule newar = new AnnualTimeZoneRule(ar.getName(), 349 ar.getRawOffset(), ar.getDSTSavings(), 350 ar.getRule(), dfields[0], ar.getEndYear()); 351 filteredRules.add(newar); 352 } 353 // Check if this is a final rule 354 if (ar.getEndYear() == AnnualTimeZoneRule.MAX_YEAR) { 355 // After both final standard and dst rule are processed, 356 // exit this while loop. 357 if (ar.getDSTSavings() == 0) { 358 bFinalStd = true; 359 } else { 360 bFinalDst = true; 361 } 362 } 363 } 364 isProcessed.set(ruleIdx); 365 } 366 TimeZoneRule[] rules = filteredRules.toArray(new TimeZoneRule[filteredRules.size()]); 367 return rules; 368 } 369 370 /** 371 * {@icu} Returns the array of <code>TimeZoneRule</code> which represents the rule of 372 * this time zone object near the specified date. Some applications are not 373 * capable to handle historic time zone rule changes. Also some applications 374 * can only handle certain type of rule definitions. This method returns 375 * either a single <code>InitialTimeZoneRule</code> if this time zone does not 376 * have any daylight saving time within 1 year from the specified time, or a 377 * pair of <code>AnnualTimeZoneRule</code> whose rule type is 378 * <code>DateTimeRule.DOW</code> for date and <code>DateTimeRule.WALL_TIME</code> 379 * for time with a single <code>InitialTimeZoneRule</code> representing the 380 * initial time, when this time zone observes daylight saving time near the 381 * specified date. Thus, the result may be only valid for dates around the 382 * specified date. 383 * 384 * @param date The date to be used for <code>TimeZoneRule</code> extraction. 385 * @return The array of <code>TimeZoneRule</code>, either a single 386 * <code>InitialTimeZoneRule</code> object, or a pair of <code>AnnualTimeZoneRule</code> 387 * with a single <code>InitialTimeZoneRule</code>. The first element in the 388 * array is always a <code>InitialTimeZoneRule</code>. 389 * 390 * @stable ICU 3.8 391 */ getSimpleTimeZoneRulesNear(long date)392 public TimeZoneRule[] getSimpleTimeZoneRulesNear(long date) { 393 AnnualTimeZoneRule[] annualRules = null; 394 TimeZoneRule initialRule = null; 395 // Get the next transition 396 TimeZoneTransition tr = getNextTransition(date, false); 397 if (tr != null) { 398 String initialName = tr.getFrom().getName(); 399 int initialRaw = tr.getFrom().getRawOffset(); 400 int initialDst = tr.getFrom().getDSTSavings(); 401 402 // Check if the next transition is either DST->STD or STD->DST and 403 // within roughly 1 year from the specified date 404 long nextTransitionTime = tr.getTime(); 405 if (((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0) 406 || (tr.getFrom().getDSTSavings() != 0 && tr.getTo().getDSTSavings() == 0)) 407 && date + MILLIS_PER_YEAR > nextTransitionTime) { 408 annualRules = new AnnualTimeZoneRule[2]; 409 // Get local wall time for the transition time 410 int dtfields[] = Grego.timeToFields(nextTransitionTime 411 + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(), null); 412 int weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], dtfields[2]); 413 // Create DOW rule 414 DateTimeRule dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3], 415 dtfields[5], DateTimeRule.WALL_TIME); 416 417 AnnualTimeZoneRule secondRule = null; 418 419 // Note: SimpleTimeZone does not support raw offset change. 420 // So we always use raw offset of the given time for the rule, 421 // even raw offset is changed. This will result that the result 422 // zone to return wrong offset after the transition. 423 // When we encounter such case, we do not inspect next next 424 // transition for another rule. 425 annualRules[0] = new AnnualTimeZoneRule(tr.getTo().getName(), 426 initialRaw, tr.getTo().getDSTSavings(), 427 dtr, dtfields[0], AnnualTimeZoneRule.MAX_YEAR); 428 429 if (tr.getTo().getRawOffset() == initialRaw) { 430 431 // Get the next next transition 432 tr = getNextTransition(nextTransitionTime, false); 433 if (tr != null) { 434 // Check if the next next transition is either DST->STD or STD->DST 435 // and within roughly 1 year from the next transition 436 if (((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0) 437 || (tr.getFrom().getDSTSavings() != 0 438 && tr.getTo().getDSTSavings() == 0)) 439 && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) { 440 // Generate another DOW rule 441 dtfields = Grego.timeToFields(tr.getTime() 442 + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(), 443 dtfields); 444 weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], 445 dtfields[2]); 446 dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3], 447 dtfields[5], DateTimeRule.WALL_TIME); 448 secondRule = new AnnualTimeZoneRule(tr.getTo().getName(), 449 tr.getTo().getRawOffset(), tr.getTo().getDSTSavings(), 450 dtr, dtfields[0] - 1, AnnualTimeZoneRule.MAX_YEAR); 451 // Make sure this rule can be applied to the specified date 452 Date d = secondRule.getPreviousStart(date, tr.getFrom().getRawOffset(), 453 tr.getFrom().getDSTSavings(), true); 454 if (d != null && d.getTime() <= date 455 && initialRaw == tr.getTo().getRawOffset() 456 && initialDst == tr.getTo().getDSTSavings()) { 457 // We can use this rule as the second transition rule 458 annualRules[1] = secondRule; 459 } 460 } 461 } 462 } 463 464 if (annualRules[1] == null) { 465 // Try previous transition 466 tr = getPreviousTransition(date, true); 467 if (tr != null) { 468 // Check if the previous transition is either DST->STD or STD->DST. 469 // The actual transition time does not matter here. 470 if ((tr.getFrom().getDSTSavings() == 0 && tr.getTo().getDSTSavings() != 0) 471 || (tr.getFrom().getDSTSavings() != 0 472 && tr.getTo().getDSTSavings() == 0)) { 473 // Generate another DOW rule 474 dtfields = Grego.timeToFields(tr.getTime() 475 + tr.getFrom().getRawOffset() + tr.getFrom().getDSTSavings(), 476 dtfields); 477 weekInMonth = Grego.getDayOfWeekInMonth(dtfields[0], dtfields[1], 478 dtfields[2]); 479 dtr = new DateTimeRule(dtfields[1], weekInMonth, dtfields[3], 480 dtfields[5], DateTimeRule.WALL_TIME); 481 482 // second rule raw/dst offsets should match raw/dst offsets 483 // at the given time 484 secondRule = new AnnualTimeZoneRule( 485 tr.getTo().getName(), initialRaw, initialDst, dtr, 486 annualRules[0].getStartYear() - 1, AnnualTimeZoneRule.MAX_YEAR); 487 488 // Check if this rule start after the first rule after the 489 // specified date 490 Date d = secondRule.getNextStart(date, tr.getFrom().getRawOffset(), 491 tr.getFrom().getDSTSavings(), false); 492 if (d.getTime() > nextTransitionTime) { 493 // We can use this rule as the second transition rule 494 annualRules[1] = secondRule; 495 } 496 } 497 } 498 } 499 if (annualRules[1] == null) { 500 // Cannot generate a good pair of AnnualTimeZoneRule 501 annualRules = null; 502 } else { 503 // The initial rule should represent the rule before the previous transition 504 initialName = annualRules[0].getName(); 505 initialRaw = annualRules[0].getRawOffset(); 506 initialDst = annualRules[0].getDSTSavings(); 507 } 508 } 509 initialRule = new InitialTimeZoneRule(initialName, initialRaw, initialDst); 510 } else { 511 // Try the previous one 512 tr = getPreviousTransition(date, true); 513 if (tr != null) { 514 initialRule = new InitialTimeZoneRule(tr.getTo().getName(), 515 tr.getTo().getRawOffset(), tr.getTo().getDSTSavings()); 516 } else { 517 // No transitions in the past. Just use the current offsets 518 int[] offsets = new int[2]; 519 getOffset(date, false, offsets); 520 initialRule = new InitialTimeZoneRule(getID(), offsets[0], offsets[1]); 521 } 522 } 523 524 TimeZoneRule[] result = null; 525 if (annualRules == null) { 526 result = new TimeZoneRule[1]; 527 result[0] = initialRule; 528 } else { 529 result = new TimeZoneRule[3]; 530 result[0] = initialRule; 531 result[1] = annualRules[0]; 532 result[2] = annualRules[1]; 533 } 534 535 return result; 536 } 537 538 /** 539 * {@icu} The time type option for standard time used by 540 * {@link #getOffsetFromLocal(long, int, int, int[])} 541 * @internal 542 * @deprecated This API is ICU internal only. 543 */ 544 @Deprecated 545 public static final int LOCAL_STD = 0x01; 546 547 /** 548 * {@icu} The time type option for daylight saving time used by 549 * {@link #getOffsetFromLocal(long, int, int, int[])} 550 * @internal 551 * @deprecated This API is ICU internal only. 552 */ 553 @Deprecated 554 public static final int LOCAL_DST = 0x03; 555 556 /** 557 * {@icu} The option designate former time to be used by 558 * {@link #getOffsetFromLocal(long, int, int, int[])} 559 * @internal 560 * @deprecated This API is ICU internal only. 561 */ 562 @Deprecated 563 public static final int LOCAL_FORMER = 0x04; 564 565 /** 566 * {@icu} The option designate latter time to be used by 567 * {@link #getOffsetFromLocal(long, int, int, int[])} 568 * @internal 569 * @deprecated This API is ICU internal only. 570 */ 571 @Deprecated 572 public static final int LOCAL_LATTER = 0x0C; 573 574 /** 575 * {@icu} The bit mask for the time type option used by 576 * {@link #getOffsetFromLocal(long, int, int, int[])} 577 * @internal 578 * @deprecated This API is ICU internal only. 579 */ 580 @Deprecated 581 protected static final int STD_DST_MASK = 0x03; 582 583 /** 584 * {@icu} The bit mask for the former/latter option used by 585 * {@link #getOffsetFromLocal(long, int, int, int[])} 586 * @internal 587 * @deprecated This API is ICU internal only. 588 */ 589 @Deprecated 590 protected static final int FORMER_LATTER_MASK = 0x0C; 591 592 /** 593 * {@icu} Returns time zone offsets from local wall time. 594 * @internal 595 * @deprecated This API is ICU internal only. 596 */ 597 @Deprecated getOffsetFromLocal(long date, int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets)598 public void getOffsetFromLocal(long date, 599 int nonExistingTimeOpt, int duplicatedTimeOpt, int[] offsets) { 600 throw new IllegalStateException("Not implemented"); 601 } 602 603 /** 604 * Protected no arg constructor. 605 * @stable ICU 3.8 606 */ BasicTimeZone()607 protected BasicTimeZone() { 608 } 609 610 /** 611 * Constructing a BasicTimeZone with the given time zone ID. 612 * @param ID the time zone ID. 613 * @internal 614 * @deprecated This API is ICU internal only. 615 */ 616 @Deprecated BasicTimeZone(String ID)617 protected BasicTimeZone(String ID) { 618 super(ID); 619 } 620 } 621