1 /* 2 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 import java.util.ArrayList; 27 import java.util.List; 28 29 /** 30 * Timezone represents all information of a single point of time to 31 * generate its time zone database. 32 * 33 * @since 1.4 34 */ 35 class Timezone { 36 /** 37 * zone name of this time zone 38 */ 39 private String name; 40 41 /** 42 * transition time values in UTC (millisecond) 43 */ 44 private List<Long> transitions; 45 46 /** 47 * All offset values in millisecond 48 * @see sun.util.calendar.ZoneInfo 49 */ 50 private List<Integer> offsets; 51 52 /** 53 * Indices of GMT offset values (both raw and raw+saving) 54 * at transitions 55 */ 56 private List<Integer> gmtOffsets; 57 58 /** 59 * Indices of regular or "direct" saving time values 60 * at transitions 61 */ 62 private List<Integer> dstOffsets; 63 64 /** 65 * Zone records of this time zone 66 */ 67 private List<ZoneRec> usedZoneRecs; 68 69 /** 70 * Rule records referred to by this time zone 71 */ 72 private List<RuleRec> usedRuleRecs; 73 74 /** 75 * Type of DST rules in this time zone 76 */ 77 private int dstType; 78 static final int UNDEF_DST = 0; // DST type not set yet 79 static final int NO_DST = 1; // never observed DST 80 static final int LAST_DST = 2; // last rule ends in DST (all year round DST-only) 81 static final int X_DST = 3; // used to observe DST 82 static final int DST = 4; // observing DST regularly 83 84 /** 85 * Raw GMT offset of this time zone in the last rule 86 */ 87 private int rawOffset; 88 89 /** 90 * The CRC32 value of the transitions data 91 */ 92 private int crc32; 93 94 /** 95 * The last ZoneRec 96 */ 97 private ZoneRec lastZoneRec; 98 99 /** 100 * The last DST rules. lastRules[0] is the DST start 101 * rule. lastRules[1] is the DST end rules. 102 */ 103 private List<RuleRec> lastRules; 104 105 /** 106 * The amount of DST saving value (millisecond) in the last DST 107 * rule. 108 */ 109 private int lastSaving; 110 111 /** 112 * true if the raw offset will change in the future time. 113 */ 114 private boolean willRawOffsetChange = false; 115 116 117 /** 118 * Constracts a Timezone object with the given zone name. 119 * @param name the zone name 120 */ 121 Timezone(String name) { 122 this.name = name; 123 } 124 125 /** 126 * @return the number of transitions 127 */ 128 int getNTransitions() { 129 if (transitions == null) { 130 return 0; 131 } 132 return transitions.size(); 133 } 134 135 /** 136 * @return the zone name 137 */ 138 String getName() { 139 return name; 140 } 141 142 /** 143 * Returns the list of all rule records that have been referred to 144 * by this time zone. 145 * @return the rule records list 146 */ 147 List<RuleRec> getRules() { 148 return usedRuleRecs; 149 } 150 151 /** 152 * Returns the list of all zone records that have been referred to 153 * by this time zone. 154 * @return the zone records list 155 */ 156 List<ZoneRec> getZones() { 157 return usedZoneRecs; 158 } 159 160 /** 161 * @return the transition table (list) 162 */ 163 List<Long> getTransitions() { 164 return transitions; 165 } 166 167 /** 168 * @return the offsets list 169 */ 170 List<Integer> getOffsets() { 171 return offsets; 172 } 173 174 /** 175 * @return the DST saving offsets list 176 */ 177 List<Integer> getDstOffsets() { 178 return dstOffsets; 179 } 180 181 /** 182 * @return the GMT offsets list 183 */ 184 List<Integer> getGmtOffsets() { 185 return gmtOffsets; 186 } 187 188 /** 189 * @return the checksum (crc32) value of the trasition table 190 */ 191 int getCRC32() { 192 return crc32; 193 } 194 195 /** 196 * @return true if the GMT offset of this time zone would change 197 * after the time zone database has been generated, false, otherwise. 198 */ 199 boolean willGMTOffsetChange() { 200 return willRawOffsetChange; 201 } 202 203 /** 204 * @return the last known GMT offset value in milliseconds 205 */ 206 int getRawOffset() { 207 return rawOffset; 208 } 209 210 /** 211 * Sets time zone's GMT offset to <code>offset</code>. 212 * @param offset the GMT offset value in milliseconds 213 */ 214 void setRawOffset(int offset) { 215 rawOffset = offset; 216 } 217 218 /** 219 * Sets time zone's GMT offset value to <code>offset</code>. If 220 * <code>startTime</code> is future time, then the {@link 221 * #willRawOffsetChange} value is set to true. 222 * @param offset the GMT offset value in milliseconds 223 * @param startTime the UTC time at which the GMT offset is in effective 224 */ 225 void setRawOffset(int offset, long startTime) { 226 // if this rawOffset is for the future time, let the run-time 227 // look for the current GMT offset. 228 if (startTime > Time.getCurrentTime()) { 229 willRawOffsetChange = true; 230 } 231 setRawOffset(offset); 232 } 233 234 /** 235 * Adds the specified transition information to the end of the transition table. 236 * @param time the UTC time at which this transition happens 237 * @param offset the total amount of the offset from GMT in milliseconds 238 * @param dstOffset the amount of time in milliseconds saved at this transition 239 */ 240 void addTransition(long time, int offset, int dstOffset) { 241 if (transitions == null) { 242 transitions = new ArrayList<Long>(); 243 offsets = new ArrayList<Integer>(); 244 dstOffsets = new ArrayList<Integer>(); 245 } 246 transitions.add(time); 247 offsets.add(offset); 248 dstOffsets.add(dstOffset); 249 } 250 251 /** 252 * Sets the type of historical daylight saving time 253 * observation. For example, China used to observed daylight 254 * saving time, but it no longer does. Then, X_DST is set to the 255 * China time zone. 256 * @param type the type of daylight saving time 257 */ 258 void setDSTType(int type) { 259 dstType = type; 260 } 261 262 /** 263 * @return the type of historical daylight saving time 264 * observation. 265 */ 266 int getDSTType() { 267 return dstType; 268 } 269 270 /** 271 * Adds the specified zone record to the zone records list. 272 * @param rec the zone record 273 */ 274 void addUsedRec(ZoneRec rec) { 275 if (usedZoneRecs == null) { 276 usedZoneRecs = new ArrayList<ZoneRec>(); 277 } 278 usedZoneRecs.add(rec); 279 } 280 281 /** 282 * Adds the specified rule record to the rule records list. 283 * @param rec the rule record 284 */ 285 void addUsedRec(RuleRec rec) { 286 if (usedRuleRecs == null) { 287 usedRuleRecs = new ArrayList<RuleRec>(); 288 } 289 // if the last used rec is the same as the given rec, avoid 290 // putting the same rule. 291 int n = usedRuleRecs.size(); 292 for (int i = 0; i < n; i++) { 293 if (usedRuleRecs.get(i).equals(rec)) { 294 return; 295 } 296 } 297 usedRuleRecs.add(rec); 298 } 299 300 /** 301 * Sets the last zone record for this time zone. 302 * @param the last zone record 303 */ 304 void setLastZoneRec(ZoneRec zrec) { 305 lastZoneRec = zrec; 306 } 307 308 /** 309 * @return the last zone record for this time zone. 310 */ 311 ZoneRec getLastZoneRec() { 312 return lastZoneRec; 313 } 314 315 /** 316 * Sets the last rule records for this time zone. Those are used 317 * for generating SimpleTimeZone parameters. 318 * @param rules the last rule records 319 */ 320 void setLastRules(List<RuleRec> rules) { 321 int n = rules.size(); 322 if (n > 0) { 323 lastRules = rules; 324 RuleRec rec = rules.get(0); 325 int offset = rec.getSave(); 326 if (offset > 0) { 327 setLastDSTSaving(offset); 328 } else { 329 System.err.println("\t No DST starting rule in the last rules."); 330 } 331 } 332 } 333 334 /** 335 * @return the last rule records for this time zone. 336 */ 337 List<RuleRec> getLastRules() { 338 return lastRules; 339 } 340 341 /** 342 * Sets the last daylight saving amount. 343 * @param the daylight saving amount 344 */ 345 void setLastDSTSaving(int offset) { 346 lastSaving = offset; 347 } 348 349 /** 350 * @return the last daylight saving amount. 351 */ 352 int getLastDSTSaving() { 353 return lastSaving; 354 } 355 356 /** 357 * Calculates the CRC32 value from the transition table and sets 358 * the value to <code>crc32</code>. 359 */ 360 void checksum() { 361 if (transitions == null) { 362 crc32 = 0; 363 return; 364 } 365 Checksum sum = new Checksum(); 366 for (int i = 0; i < transitions.size(); i++) { 367 int offset = offsets.get(i); 368 // adjust back to make the transition in local time 369 sum.update(transitions.get(i) + offset); 370 sum.update(offset); 371 sum.update(dstOffsets.get(i)); 372 } 373 crc32 = (int)sum.getValue(); 374 } 375 376 /** 377 * Removes unnecessary transitions for Java time zone support. 378 */ 379 void optimize() { 380 // if there is only one offset, delete all transitions. This 381 // could happen if only time zone abbreviations changed. 382 if (gmtOffsets.size() == 1) { 383 transitions = null; 384 usedRuleRecs = null; 385 setDSTType(NO_DST); 386 return; 387 } 388 for (int i = 0; i < (transitions.size() - 2); i++) { // don't remove the last one 389 if (transitions.get(i) == transitions.get(i+1)) { 390 transitions.remove(i); 391 offsets.remove(i); 392 dstOffsets.remove(i); 393 i--; 394 } 395 } 396 397 for (int i = 0; i < (transitions.size() - 2); i++) { // don't remove the last one 398 if (offsets.get(i) == offsets.get(i+1) 399 && dstOffsets.get(i) == dstOffsets.get(i+1)) { 400 transitions.remove(i+1); 401 offsets.remove(i+1); 402 dstOffsets.remove(i+1); 403 i--; 404 } 405 } 406 } 407 408 /** 409 * Stores the specified offset value from GMT in the GMT offsets 410 * table and returns its index. The offset value includes the base 411 * GMT offset and any additional daylight saving if applicable. If 412 * the same value as the specified offset is already in the table, 413 * its index is returned. 414 * @param offset the offset value in milliseconds 415 * @return the index to the offset value in the GMT offsets table. 416 */ 417 int getOffsetIndex(int offset) { 418 return getOffsetIndex(offset, 0); 419 } 420 421 /** 422 * Stores the specified daylight saving value in the GMT offsets 423 * table and returns its index. If the same value as the specified 424 * offset is already in the table, its index is returned. If 0 is 425 * specified, it's not stored in the table and -1 is returned. 426 * @param offset the offset value in milliseconds 427 * @return the index to the specified offset value in the GMT 428 * offsets table, or -1 if 0 is specified. 429 */ 430 int getDstOffsetIndex(int offset) { 431 if (offset == 0) { 432 return -1; 433 } 434 return getOffsetIndex(offset, 1); 435 } 436 437 private int getOffsetIndex(int offset, int index) { 438 if (gmtOffsets == null) { 439 gmtOffsets = new ArrayList<Integer>(); 440 } 441 for (int i = index; i < gmtOffsets.size(); i++) { 442 if (offset == gmtOffsets.get(i)) { 443 return i; 444 } 445 } 446 if (gmtOffsets.size() < index) { 447 gmtOffsets.add(0); 448 } 449 gmtOffsets.add(offset); 450 return gmtOffsets.size() - 1; 451 } 452 }