1 /* 2 * Copyright (c) 2012, 2016, 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 /* 27 * This file is available under and governed by the GNU General Public 28 * License version 2 only, as published by the Free Software Foundation. 29 * However, the following notice accompanied the original version of this 30 * file: 31 * 32 * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos 33 * 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions are met: 38 * 39 * * Redistributions of source code must retain the above copyright notice, 40 * this list of conditions and the following disclaimer. 41 * 42 * * Redistributions in binary form must reproduce the above copyright notice, 43 * this list of conditions and the following disclaimer in the documentation 44 * and/or other materials provided with the distribution. 45 * 46 * * Neither the name of JSR-310 nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 54 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 */ 62 package build.tools.tzdb; 63 64 import java.io.DataOutput; 65 import java.io.IOException; 66 import java.io.ObjectOutput; 67 import java.time.LocalDateTime; 68 import java.time.LocalTime; 69 import java.time.ZoneOffset; 70 import java.time.zone.ZoneOffsetTransition; 71 import java.time.zone.ZoneOffsetTransitionRule; 72 import java.time.zone.ZoneOffsetTransitionRule.TimeDefinition; 73 import java.util.Arrays; 74 import java.util.ArrayList; 75 import java.util.List; 76 77 /** 78 * Duplicated code of javax.time.zone.ZoneRules, ZoneOffsetTransitionRule 79 * and Ser to generate the serialization form output of ZoneRules for 80 * tzdb.jar. 81 * 82 * Implementation here is the copy/paste of ZoneRules, ZoneOffsetTransitionRule 83 * and Ser in javax.time.zone package. Make sure the code here is synchrionozed 84 * with the serialization implementation there. 85 * 86 * @since 1.8 87 */ 88 89 final class ZoneRules { 90 91 /** 92 * The transitions between standard offsets (epoch seconds), sorted. 93 */ 94 private final long[] standardTransitions; 95 /** 96 * The standard offsets. 97 */ 98 private final ZoneOffset[] standardOffsets; 99 /** 100 * The transitions between instants (epoch seconds), sorted. 101 */ 102 private final long[] savingsInstantTransitions; 103 104 /** 105 * The wall offsets. 106 */ 107 private final ZoneOffset[] wallOffsets; 108 /** 109 * The last rule. 110 */ 111 private final ZoneOffsetTransitionRule[] lastRules; 112 113 /** 114 * Creates an instance. 115 * 116 * @param baseStandardOffset the standard offset to use before legal rules were set, not null 117 * @param baseWallOffset the wall offset to use before legal rules were set, not null 118 * @param standardOffsetTransitionList the list of changes to the standard offset, not null 119 * @param transitionList the list of transitions, not null 120 * @param lastRules the recurring last rules, size 16 or less, not null 121 */ 122 ZoneRules(ZoneOffset baseStandardOffset, 123 ZoneOffset baseWallOffset, 124 List<ZoneOffsetTransition> standardOffsetTransitionList, 125 List<ZoneOffsetTransition> transitionList, 126 List<ZoneOffsetTransitionRule> lastRules) { 127 128 this.standardTransitions = new long[standardOffsetTransitionList.size()]; 129 130 this.standardOffsets = new ZoneOffset[standardOffsetTransitionList.size() + 1]; 131 this.standardOffsets[0] = baseStandardOffset; 132 for (int i = 0; i < standardOffsetTransitionList.size(); i++) { 133 this.standardTransitions[i] = standardOffsetTransitionList.get(i).toEpochSecond(); 134 this.standardOffsets[i + 1] = standardOffsetTransitionList.get(i).getOffsetAfter(); 135 } 136 137 // convert savings transitions to locals 138 List<ZoneOffset> localTransitionOffsetList = new ArrayList<>(); 139 localTransitionOffsetList.add(baseWallOffset); 140 for (ZoneOffsetTransition trans : transitionList) { 141 localTransitionOffsetList.add(trans.getOffsetAfter()); 142 } 143 144 this.wallOffsets = localTransitionOffsetList.toArray(new ZoneOffset[localTransitionOffsetList.size()]); 145 146 // convert savings transitions to instants 147 this.savingsInstantTransitions = new long[transitionList.size()]; 148 for (int i = 0; i < transitionList.size(); i++) { 149 this.savingsInstantTransitions[i] = transitionList.get(i).toEpochSecond(); 150 } 151 152 // last rules 153 if (lastRules.size() > 16) { 154 throw new IllegalArgumentException("Too many transition rules"); 155 } 156 this.lastRules = lastRules.toArray(new ZoneOffsetTransitionRule[lastRules.size()]); 157 } 158 159 /** Type for ZoneRules. */ 160 static final byte ZRULES = 1; 161 162 /** 163 * Writes the state to the stream. 164 * 165 * @param out the output stream, not null 166 * @throws IOException if an error occurs 167 */ 168 void writeExternal(DataOutput out) throws IOException { 169 out.writeByte(ZRULES); 170 out.writeInt(standardTransitions.length); 171 for (long trans : standardTransitions) { 172 writeEpochSec(trans, out); 173 } 174 for (ZoneOffset offset : standardOffsets) { 175 writeOffset(offset, out); 176 } 177 out.writeInt(savingsInstantTransitions.length); 178 for (long trans : savingsInstantTransitions) { 179 writeEpochSec(trans, out); 180 } 181 for (ZoneOffset offset : wallOffsets) { 182 writeOffset(offset, out); 183 } 184 out.writeByte(lastRules.length); 185 for (ZoneOffsetTransitionRule rule : lastRules) { 186 writeRule(rule, out); 187 } 188 } 189 190 /** 191 * Writes the state the ZoneOffset to the stream. 192 * 193 * @param offset the offset, not null 194 * @param out the output stream, not null 195 * @throws IOException if an error occurs 196 */ 197 static void writeOffset(ZoneOffset offset, DataOutput out) throws IOException { 198 final int offsetSecs = offset.getTotalSeconds(); 199 int offsetByte = offsetSecs % 900 == 0 ? offsetSecs / 900 : 127; // compress to -72 to +72 200 out.writeByte(offsetByte); 201 if (offsetByte == 127) { 202 out.writeInt(offsetSecs); 203 } 204 } 205 206 /** 207 * Writes the epoch seconds to the stream. 208 * 209 * @param epochSec the epoch seconds, not null 210 * @param out the output stream, not null 211 * @throws IOException if an error occurs 212 */ 213 static void writeEpochSec(long epochSec, DataOutput out) throws IOException { 214 if (epochSec >= -4575744000L && epochSec < 10413792000L && epochSec % 900 == 0) { // quarter hours between 1825 and 2300 215 int store = (int) ((epochSec + 4575744000L) / 900); 216 out.writeByte((store >>> 16) & 255); 217 out.writeByte((store >>> 8) & 255); 218 out.writeByte(store & 255); 219 } else { 220 out.writeByte(255); 221 out.writeLong(epochSec); 222 } 223 } 224 225 /** 226 * Writes the state of the transition rule to the stream. 227 * 228 * @param rule the transition rule, not null 229 * @param out the output stream, not null 230 * @throws IOException if an error occurs 231 */ 232 static void writeRule(ZoneOffsetTransitionRule rule, DataOutput out) throws IOException { 233 int month = rule.getMonth().getValue(); 234 byte dom = (byte)rule.getDayOfMonthIndicator(); 235 int dow = (rule.getDayOfWeek() == null ? -1 : rule.getDayOfWeek().getValue()); 236 LocalTime time = rule.getLocalTime(); 237 boolean timeEndOfDay = rule.isMidnightEndOfDay(); 238 TimeDefinition timeDefinition = rule.getTimeDefinition(); 239 ZoneOffset standardOffset = rule.getStandardOffset(); 240 ZoneOffset offsetBefore = rule.getOffsetBefore(); 241 ZoneOffset offsetAfter = rule.getOffsetAfter(); 242 243 int timeSecs = (timeEndOfDay ? 86400 : time.toSecondOfDay()); 244 int stdOffset = standardOffset.getTotalSeconds(); 245 int beforeDiff = offsetBefore.getTotalSeconds() - stdOffset; 246 int afterDiff = offsetAfter.getTotalSeconds() - stdOffset; 247 int timeByte = (timeSecs % 3600 == 0 ? (timeEndOfDay ? 24 : time.getHour()) : 31); 248 int stdOffsetByte = (stdOffset % 900 == 0 ? stdOffset / 900 + 128 : 255); 249 int beforeByte = (beforeDiff == 0 || beforeDiff == 1800 || beforeDiff == 3600 ? beforeDiff / 1800 : 3); 250 int afterByte = (afterDiff == 0 || afterDiff == 1800 || afterDiff == 3600 ? afterDiff / 1800 : 3); 251 int dowByte = (dow == -1 ? 0 : dow); 252 int b = (month << 28) + // 4 bytes 253 ((dom + 32) << 22) + // 6 bytes 254 (dowByte << 19) + // 3 bytes 255 (timeByte << 14) + // 5 bytes 256 (timeDefinition.ordinal() << 12) + // 2 bytes 257 (stdOffsetByte << 4) + // 8 bytes 258 (beforeByte << 2) + // 2 bytes 259 afterByte; // 2 bytes 260 out.writeInt(b); 261 if (timeByte == 31) { 262 out.writeInt(timeSecs); 263 } 264 if (stdOffsetByte == 255) { 265 out.writeInt(stdOffset); 266 } 267 if (beforeByte == 3) { 268 out.writeInt(offsetBefore.getTotalSeconds()); 269 } 270 if (afterByte == 3) { 271 out.writeInt(offsetAfter.getTotalSeconds()); 272 } 273 } 274 275 /** 276 * Checks if this set of rules equals another. 277 * <p> 278 * Two rule sets are equal if they will always result in the same output 279 * for any given input instant or local date-time. 280 * Rules from two different groups may return false even if they are in fact the same. 281 * <p> 282 * This definition should result in implementations comparing their entire state. 283 * 284 * @param otherRules the other rules, null returns false 285 * @return true if this rules is the same as that specified 286 */ 287 @Override 288 public boolean equals(Object otherRules) { 289 if (this == otherRules) { 290 return true; 291 } 292 if (otherRules instanceof ZoneRules) { 293 ZoneRules other = (ZoneRules) otherRules; 294 return Arrays.equals(standardTransitions, other.standardTransitions) && 295 Arrays.equals(standardOffsets, other.standardOffsets) && 296 Arrays.equals(savingsInstantTransitions, other.savingsInstantTransitions) && 297 Arrays.equals(wallOffsets, other.wallOffsets) && 298 Arrays.equals(lastRules, other.lastRules); 299 } 300 return false; 301 } 302 303 /** 304 * Returns a suitable hash code given the definition of {@code #equals}. 305 * 306 * @return the hash code 307 */ 308 @Override 309 public int hashCode() { 310 return Arrays.hashCode(standardTransitions) ^ 311 Arrays.hashCode(standardOffsets) ^ 312 Arrays.hashCode(savingsInstantTransitions) ^ 313 Arrays.hashCode(wallOffsets) ^ 314 Arrays.hashCode(lastRules); 315 } 316 317 }