1 7 package com.ibm.icu.impl; 8 9 import java.util.Date ; 10 11 import com.ibm.icu.util.Calendar; 12 import com.ibm.icu.util.GregorianCalendar; 13 import com.ibm.icu.util.SimpleTimeZone; 14 import com.ibm.icu.util.TimeZone; 15 16 106 public class OlsonTimeZone extends TimeZone { 107 108 static final long serialVersionUID = -6281977362477515376L; 110 111 private static final boolean ASSERT = false; 112 113 116 public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) { 117 if (month < Calendar.JANUARY || month > Calendar.DECEMBER) { 118 throw new IllegalArgumentException ("Month is not in the legal range: " +month); 119 } else { 120 return getOffset(era, year, month, day, dayOfWeek, milliseconds,MONTH_LENGTH[month + (isLeapYear(year)?12:0)]); 121 } 122 } 123 124 127 public int getOffset(int era, int year, int month,int dom, int dow, int millis, int monthLength){ 128 129 if ((era != GregorianCalendar.AD && era != GregorianCalendar.BC) 130 || month < Calendar.JANUARY 131 || month > Calendar.DECEMBER 132 || dom < 1 133 || dom > monthLength 134 || dow < Calendar.SUNDAY 135 || dow > Calendar.SATURDAY 136 || millis < 0 137 || millis >= MILLIS_PER_DAY 138 || monthLength < 28 139 || monthLength > 31) { 140 throw new IllegalArgumentException (); 141 } 142 143 if (era == GregorianCalendar.BC) { 144 year = -year; 145 } 146 147 if (year > finalYear) { if (ASSERT) Assert.assrt("(finalZone != null)", finalZone != null); 149 return finalZone.getOffset(era, year, month, dom, dow, 150 millis, monthLength); 151 } 152 153 double time = fieldsToDay(year, month, dom) * SECONDS_PER_DAY + 155 Math.floor(millis / (double) MILLIS_PER_SECOND); 156 157 int[] offsets = new int[2]; 158 getHistoricalOffset(time, true, offsets); 159 return offsets[0] + offsets[1]; 160 } 161 164 public void setRawOffset(int offsetMillis) { 165 finalZone.setRawOffset(offsetMillis); 166 } 167 public Object clone() { 168 OlsonTimeZone other = (OlsonTimeZone) super.clone(); 169 if(finalZone!=null){ 170 finalZone.setID(getID()); 171 other.finalZone = (SimpleTimeZone)finalZone.clone(); 172 } 173 other.transitionTimes = (int[])transitionTimes.clone(); 174 other.typeData = (byte[])typeData.clone(); 175 other.typeOffsets = (int[])typeOffsets.clone(); 176 return other; 177 } 178 181 public void getOffset(long date, boolean local, int[] offsets) { 182 int rawoff, dstoff; 183 if (date >= finalMillis && finalZone != null) { 187 double[] doub = floorDivide(date, (double)MILLIS_PER_DAY); 188 double millis=doub[1]; 189 double days=doub[0]; 190 int[] temp = dayToFields(days); 191 int year=temp[0], month=temp[1], dom=temp[2], dow=temp[3]; 192 rawoff = finalZone.getRawOffset(); 193 194 if (!local) { 195 date += rawoff; 197 doub = floorDivide(date, (double)MILLIS_PER_DAY); 198 double days2 = doub[0]; 199 millis = doub[1]; 200 if (days2 != days) { 201 temp = dayToFields(days2); 202 year=temp[0]; 203 month=temp[1]; 204 dom=temp[2]; 205 dow=temp[3]; 206 } 207 } 208 209 dstoff = finalZone.getOffset(GregorianCalendar.AD, year, month, dom, 210 dow, (int)millis) 211 - rawoff; 212 offsets[0]=rawoff; 213 offsets[1]=dstoff; 214 return; 215 } 216 217 double secs = Math.floor(date / MILLIS_PER_SECOND); 218 getHistoricalOffset(secs, local, offsets); 219 return; 220 } 221 double[] floorDivide(double dividend, double divisor) { 222 double remainder; 223 double[] ret = new double[2]; 224 if (ASSERT) Assert.assrt("divisor > 0", divisor > 0); 226 double quotient = Math.floor(dividend/divisor); 227 remainder = dividend - (quotient * divisor); 228 if (remainder < 0 || remainder >= divisor) { 232 double q = quotient; 236 quotient += (remainder < 0) ? -1 : +1; 237 if (q == quotient) { 238 remainder = 0; 248 } else { 249 remainder = dividend - (quotient * divisor); 250 } 251 } 252 if (ASSERT) Assert.assrt("0 <= remainder && remainder < divisor", 0 <= remainder && remainder < divisor); 253 ret[0]=quotient; 254 ret[1]=remainder; 255 return ret; 256 } 257 260 public int getRawOffset() { 261 int[] ret = new int[2]; 262 getOffset( System.currentTimeMillis(), false, ret); 263 return ret[0]; 264 } 265 266 269 public boolean useDaylightTime() { 270 276 double[] dt = floorDivide(System.currentTimeMillis(), (double)MILLIS_PER_DAY); int days = (int)dt[0]; 278 int[] it = dayToFields(days); 279 280 int year=it[0]; 281 if (year > finalYear) { if (ASSERT) Assert.assrt("finalZone != null && finalZone.useDaylightTime()", finalZone != null && finalZone.useDaylightTime()); 283 return true; 284 } 285 286 int start = (int) fieldsToDay(year, 0, 1) * SECONDS_PER_DAY; 288 int limit = (int) fieldsToDay(year+1, 0, 1) * SECONDS_PER_DAY; 289 290 for (int i=0; i<transitionCount; ++i) { 293 if (transitionTimes[i] >= limit) { 294 break; 295 } 296 if (transitionTimes[i] >= start && 297 dstOffset(typeData[i]) != 0) { 298 return true; 299 } 300 } 301 return false; 302 } 303 308 public int getDSTSavings() { 309 if(finalZone!=null){ 310 return finalZone.getDSTSavings(); 311 } 312 return super.getDSTSavings(); 313 314 } 315 318 public boolean inDaylightTime(Date date) { 319 int[] temp = new int[2]; 320 getOffset(date.getTime(), false, temp); 321 return temp[1] != 0; 322 } 323 324 328 private void constructEmpty(){ 329 transitionCount = 0; 330 typeCount = 1; 331 transitionTimes = typeOffsets = new int[]{0,0}; 332 typeData = new byte[2]; 333 334 } 335 341 public OlsonTimeZone(ICUResourceBundle top, ICUResourceBundle res){ 342 construct(top, res); 343 } 344 private void construct(ICUResourceBundle top, ICUResourceBundle res){ 345 346 if ((top == null || res == null)) { 347 throw new IllegalArgumentException (); 348 } 349 if(DEBUG) System.out.println("OlsonTimeZone(" + res.getKey() +")"); 350 351 352 356 int size = res.getSize(); 363 if (size < 3 || size > 6) { 364 throw new IllegalArgumentException ("Invalid Format"); 366 } 367 368 ICUResourceBundle r = res.get(0); 370 transitionTimes = r.getIntVector(); 371 372 if ((transitionTimes.length<0 || transitionTimes.length>0x7FFF) ) { 373 throw new IllegalArgumentException ("Invalid Format"); 374 } 375 transitionCount = (int) transitionTimes.length; 376 377 r = res.get( 1); 379 typeOffsets = r.getIntVector(); 380 if ((typeOffsets.length<2 || typeOffsets.length>0x7FFE || ((typeOffsets.length&1)!=0))) { 381 throw new IllegalArgumentException ("Invalid Format"); 382 } 383 typeCount = (int) typeOffsets.length >> 1; 384 385 r = res.get(2); 387 typeData = r.getBinary().array(); 388 if (typeData.length != transitionCount) { 389 throw new IllegalArgumentException ("Invalid Format"); 390 } 391 392 if (size >= 5) { 394 String ruleid = res.getString(3); 395 r = res.get(4); 396 int[] data = r.getIntVector(); 397 398 if (data != null && data.length == 2) { 399 int rawOffset = data[0] * MILLIS_PER_SECOND; 400 if (ASSERT) Assert.assrt("data[1] > Integer.MIN_VALUE", data[1] > Integer.MIN_VALUE); 406 finalYear = data[1] - 1; 407 finalMillis = fieldsToDay(data[1], 0, 1) * TimeZone.MILLIS_PER_DAY; 410 r = loadRule(top, ruleid); 413 414 data = r.getIntVector(); 416 if ( data.length == 11) { 417 finalZone = new SimpleTimeZone(rawOffset, "", 420 data[0], data[1], data[2], 421 data[3] * MILLIS_PER_SECOND, 422 data[4], 423 data[5], data[6], data[7], 424 data[8] * MILLIS_PER_SECOND, 425 data[9], 426 data[10] * MILLIS_PER_SECOND); 427 } else { 428 throw new IllegalArgumentException ("Invalid Format"); 429 } 430 } else { 431 throw new IllegalArgumentException ("Invalid Format"); 432 } 433 } 434 } 435 public OlsonTimeZone(){ 436 442 constructEmpty(); 443 } 444 445 446 public OlsonTimeZone(String id){ 447 ICUResourceBundle top = (ICUResourceBundle)ICUResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "zoneinfo", ICUResourceBundle.ICU_DATA_CLASS_LOADER); 448 ICUResourceBundle res = ZoneMeta.openOlsonResource(id); 449 construct(top, res); 450 if(finalZone!=null){ 451 finalZone.setID(id); 452 } 453 super.setID(id); 454 } 455 public void setID(String id){ 456 if(finalZone!= null){ 457 finalZone.setID(id); 458 } 459 super.setID(id); 460 } 461 private static final int UNSIGNED_BYTE_MASK =0xFF; 462 private int getInt(byte val){ 463 return (int)(UNSIGNED_BYTE_MASK & val); 464 } 465 private void getHistoricalOffset(double time, boolean local, int[] offsets) { 466 if (transitionCount != 0) { 467 int i = 0; 470 for (i = transitionCount - 1; i > 0; --i) { 471 int transition = transitionTimes[i]; 472 if (local) { 473 int zoneOffsetPrev = zoneOffset(getInt(typeData[i-1])); 474 int zoneOffsetCurr = zoneOffset(getInt(typeData[i])); 475 if(zoneOffsetPrev < zoneOffsetCurr) { 476 transition += zoneOffsetPrev; 477 } else { 478 transition += zoneOffsetCurr; 479 } 480 } 481 if (time >= transition) { 482 break; 483 } 484 } 485 486 if (ASSERT) Assert.assrt("i>=0 && i<transitionCount", i>=0 && i<transitionCount); 487 488 if (ASSERT) { 491 Assert.assrt("local || time < transitionTimes[0] || time >= transitionTimes[i]", 492 local || time < transitionTimes[0] || time >= transitionTimes[i]); 493 Assert.assrt("local || i == transitionCount-1 || time < transitionTimes[i+1]", 494 local || i == transitionCount-1 || time < transitionTimes[i+1]); 495 } 496 if (i == 0) { 497 int firstTransition = transitionTimes[0]; 499 int initialRawOffset = rawOffset(getInt(typeData[0])); 500 if (local) { 501 firstTransition += initialRawOffset; 502 } 503 if (time >= firstTransition) { 504 offsets[0] = initialRawOffset * MILLIS_PER_SECOND; 506 offsets[1] = dstOffset(getInt(typeData[0])) * MILLIS_PER_SECOND; 507 } else { 508 offsets[0] = initialRawOffset * MILLIS_PER_SECOND; 510 offsets[1] = 0; 511 } 512 } else { 513 int index = getInt(typeData[i]); 514 offsets[0] = rawOffset(index) * MILLIS_PER_SECOND; 515 offsets[1] = dstOffset(index) * MILLIS_PER_SECOND; 516 } 517 } else { 518 offsets[0] = rawOffset(0) * MILLIS_PER_SECOND; 520 offsets[1] = dstOffset(0) * MILLIS_PER_SECOND; 521 } 522 } 523 private int zoneOffset(int index){ 524 index=index << 1; 525 return typeOffsets[index] + typeOffsets[index+1]; 526 } 527 private int rawOffset(int index){ 528 return typeOffsets[(int)(index << 1)]; 529 } 530 private int dstOffset(int index){ 531 return typeOffsets[(int)((index << 1) + 1)]; 532 } 533 534 public String toString() { 536 StringBuffer buf = new StringBuffer (); 537 buf.append(super.toString()); 538 buf.append('['); 539 buf.append("transitionCount=" + transitionCount); 540 buf.append(",typeCount=" + typeCount); 541 buf.append(",transitionTimes="); 542 if (transitionTimes != null) { 543 buf.append('['); 544 for (int i = 0; i < transitionTimes.length; ++i) { 545 if (i > 0) { 546 buf.append(','); 547 } 548 buf.append(Integer.toString(transitionTimes[i])); 549 } 550 buf.append(']'); 551 } else { 552 buf.append("null"); 553 } 554 buf.append(",typeOffsets="); 555 if (typeOffsets != null) { 556 buf.append('['); 557 for (int i = 0; i < typeOffsets.length; ++i) { 558 if (i > 0) { 559 buf.append(','); 560 } 561 buf.append(Integer.toString(typeOffsets[i])); 562 } 563 buf.append(']'); 564 } else { 565 buf.append("null"); 566 } 567 buf.append(",finalYear=" + finalYear); 568 buf.append(",finalMillis=" + finalMillis); 569 buf.append(",finalZone=" + finalZone); 570 buf.append(']'); 571 572 return buf.toString(); 573 } 574 577 private int transitionCount; 578 579 582 private int typeCount; 583 584 588 private int[] transitionTimes; 590 594 private int[] typeOffsets; 596 601 private byte[] typeData; 603 609 private int finalYear = Integer.MAX_VALUE; 610 611 616 private double finalMillis = Double.MAX_VALUE; 617 618 622 private SimpleTimeZone finalZone = null; 624 private static final boolean DEBUG = ICUDebug.enabled("olson"); 625 private static final int[] DAYS_BEFORE = new int[] {0,31,59,90,120,151,181,212,243,273,304,334, 626 0,31,60,91,121,152,182,213,244,274,305,335}; 627 628 private static final int[] MONTH_LENGTH = new int[]{31,28,31,30,31,30,31,31,30,31,30,31, 629 31,29,31,30,31,30,31,31,30,31,30,31}; 630 private static final int JULIAN_1_CE = 1721426; private static final int JULIAN_1970_CE = 2440588; private static final int MILLIS_PER_SECOND = 1000; 633 private static final int SECONDS_PER_DAY = 24*60*60; 634 635 private static final double fieldsToDay(int year, int month, int dom) { 636 int y = year - 1; 637 double julian = 365 * y + myFloorDivide(y, 4) + (JULIAN_1_CE - 3) + myFloorDivide(y, 400) - myFloorDivide(y, 100) + 2 + DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; 641 return julian - JULIAN_1970_CE; } 643 private static final boolean isLeapYear(int year) { 644 return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0)); 646 } 647 648 private static ICUResourceBundle loadRule(ICUResourceBundle top, String ruleid) { 649 ICUResourceBundle r = top.get("Rules"); 650 r = r.get(ruleid); 651 return r; 652 } 653 664 private static final long myFloorDivide(long numerator, long denominator) { 665 return (numerator >= 0) ? 668 numerator / denominator : 669 ((numerator + 1) / denominator) - 1; 670 } 671 int[] dayToFields(double day) { 672 int year, month, dom, dow; 673 double doy; 674 int[] ret = new int[5]; 675 676 day += JULIAN_1970_CE - JULIAN_1_CE; 678 679 double[]temp = floorDivide(day, 146097); double n400 = temp[0]; 685 doy = temp[1]; 686 temp = floorDivide(doy, 36524); double n100 = temp[0]; 688 doy = temp[1]; 689 temp = floorDivide(doy, 1461); double n4 = temp[0]; 691 doy = temp[1]; 692 temp = floorDivide(doy, 365); 693 double n1 = temp[0]; 694 doy = temp[1]; 695 year = (int)( 400*n400 + 100*n100 + 4*n4 + n1); 696 if (n100 == 4 || n1 == 4) { 697 doy = 365; } else { 699 ++year; 700 } 701 702 boolean isLeap = isLeapYear(year); 703 704 dow = (int) ((day + 1) % 7); 706 dow += (dow < 0) ? (Calendar.SUNDAY + 7) : Calendar.SUNDAY; 707 708 int correction = 0; 710 int march1 = isLeap ? 60 : 59; if (doy >= march1) { 712 correction = isLeap ? 1 : 2; 713 } 714 month = (int)((12 * (doy + correction) + 6) / 367); dom = (int)(doy - DAYS_BEFORE[month + (isLeap ? 12 : 0)] + 1); doy++; ret[0]=year; 718 ret[1]=month; 719 ret[2]=dom; 720 ret[3]=dow; 721 ret[4]=(int)doy; 722 return ret; 723 } 724 public boolean equals(Object obj){ 725 if (!super.equals(obj)) return false; 727 OlsonTimeZone z = (OlsonTimeZone) obj; 728 729 return (Utility.arrayEquals(typeData, z.typeData) || 730 (finalYear == z.finalYear && 733 ((finalZone == null && z.finalZone == null) || 735 (finalZone != null && z.finalZone != null && 736 finalZone.equals(z.finalZone)) && 737 transitionCount == z.transitionCount && 738 typeCount == z.typeCount && 739 Utility.arrayEquals(transitionTimes, z.transitionTimes) && 740 Utility.arrayEquals(typeOffsets, z.typeOffsets) && 741 Utility.arrayEquals(typeData, z.typeData) 742 ))); 743 744 } 745 public int hashCode(){ 746 int ret = (int) (finalYear ^ (finalYear>>>4) + 747 transitionCount ^ (transitionCount>>>6) + 748 typeCount ^ (typeCount>>>8) + 749 Double.doubleToLongBits(finalMillis)+ 750 (finalZone == null ? 0 : finalZone.hashCode()) + 751 super.hashCode()); 752 for(int i=0; i<transitionTimes.length; i++){ 753 ret+=transitionTimes[i]^(transitionTimes[i]>>>8); 754 } 755 for(int i=0; i<typeOffsets.length; i++){ 756 ret+=typeOffsets[i]^(typeOffsets[i]>>>8); 757 } 758 for(int i=0; i<typeData.length; i++){ 759 ret+=typeData[i] & UNSIGNED_BYTE_MASK; 760 } 761 return ret; 762 } 763 771 } 772 773 | Popular Tags |