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