1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.libcore.timezone.tzlookup.zonetree; 17 18 import com.ibm.icu.text.TimeZoneNames; 19 import com.ibm.icu.util.BasicTimeZone; 20 import com.ibm.icu.util.TimeZone; 21 22 import java.time.Instant; 23 import java.util.ArrayList; 24 import java.util.List; 25 26 /** 27 * Contains information about a tzdb-defined time zone for a time period. 28 */ 29 final class ZoneInfo { 30 31 private static final int MIN_PRIORITY = 1; 32 33 /** 34 * Priority can be used to establish dominance of one zone info over another if they are 35 * otherwise identical for a zone offset period. Highest numerical priority "wins". 36 */ 37 private final int priority; 38 39 /** The zone offset periods for the zone. They are stored in ascending order of start time. */ 40 private final List<ZoneOffsetPeriod> zoneOffsetPeriods; 41 42 /** The time zone ID. */ 43 private final String zoneId; 44 ZoneInfo(String zoneId, int priority, List<ZoneOffsetPeriod> zoneOffsetPeriods)45 private ZoneInfo(String zoneId, int priority, List<ZoneOffsetPeriod> zoneOffsetPeriods) { 46 if (priority < MIN_PRIORITY) { 47 throw new IllegalArgumentException("priority must be >=" + MIN_PRIORITY); 48 } 49 this.zoneOffsetPeriods = zoneOffsetPeriods; 50 this.priority = priority; 51 this.zoneId = zoneId; 52 } 53 54 /** 55 * Creates a ZoneInfo using the supplied ICU data and metadata. 56 * 57 * <p>The priority must be >= 1, and startInclusive is expected to be before endExclusive. 58 * 59 * <p>The returned {@link ZoneInfo} will be populated with {@link ZoneOffsetPeriod}s using 60 * the ICU time zone rules and names supplied in the specified period. 61 */ create(TimeZoneNames timeZoneNames, BasicTimeZone timeZone, int priority, Instant startInclusive, Instant endExclusive)62 public static ZoneInfo create(TimeZoneNames timeZoneNames, BasicTimeZone timeZone, int priority, 63 Instant startInclusive, Instant endExclusive) { 64 List<ZoneOffsetPeriod> zoneOffsetPeriods = new ArrayList<>(); 65 66 // Start off the zone key with an artificial entry at startInclusive. 67 Instant start = startInclusive; 68 do { 69 ZoneOffsetPeriod zoneOffsetPeriod = 70 ZoneOffsetPeriod.create(timeZoneNames, timeZone, start, endExclusive); 71 zoneOffsetPeriods.add(zoneOffsetPeriod); 72 start = zoneOffsetPeriod.getEndInstant(); 73 } while (start.isBefore(endExclusive)); 74 75 return new ZoneInfo(timeZone.getID(), priority, zoneOffsetPeriods); 76 } 77 78 /** 79 * Splits the final {@link ZoneOffsetPeriod} at the specified time and replaces it with two 80 * {@link ZoneOffsetPeriod}s instead, using the supplied ICU names information to help obtain 81 * the name for the later of the two periods. 82 */ splitZoneOffsetPeriodAtTime( TimeZoneNames timeZoneNames, ZoneInfo zoneInfo, int index, Instant partitionInstant)83 public static void splitZoneOffsetPeriodAtTime( 84 TimeZoneNames timeZoneNames, ZoneInfo zoneInfo, int index, Instant partitionInstant) { 85 ZoneOffsetPeriod oldZoneOffsetPeriod = zoneInfo.zoneOffsetPeriods.get(index); 86 BasicTimeZone timeZone = (BasicTimeZone) TimeZone.getTimeZone(zoneInfo.getZoneId()); 87 ZoneOffsetPeriod[] newPeriods = ZoneOffsetPeriod.splitAtTime( 88 oldZoneOffsetPeriod, timeZoneNames, timeZone, partitionInstant); 89 zoneInfo.zoneOffsetPeriods.set(index, newPeriods[0]); 90 zoneInfo.zoneOffsetPeriods.add(index + 1, newPeriods[1]); 91 } 92 getZoneId()93 public String getZoneId() { 94 return zoneId; 95 } 96 getPriority()97 public int getPriority() { 98 return priority; 99 } 100 101 @Override toString()102 public String toString() { 103 return "ZoneInfo{" + 104 "priority=" + priority + 105 ", zoneId=" + zoneId + 106 '}'; 107 } 108 109 /** 110 * Creates a hashable key object that can be used to tell if the zone is the same for a range 111 * of periods. 112 * 113 * @param zonePeriodStartIndex the start index (inclusive) 114 * @param zonePeriodEndIndex the end index (exclusive) 115 */ createZonePeriodsKey( int zonePeriodStartIndex, int zonePeriodEndIndex)116 public ZoneOffsetPeriod.ZonePeriodsKey createZonePeriodsKey( 117 int zonePeriodStartIndex, int zonePeriodEndIndex) { 118 List<ZoneOffsetPeriod> periodsSubList = 119 zoneOffsetPeriods.subList(zonePeriodStartIndex, zonePeriodEndIndex); 120 return new ZoneOffsetPeriod.ZonePeriodsKey(periodsSubList); 121 } 122 123 /** 124 * Returns a single {@link ZoneOffsetPeriod}. 125 */ getZoneOffsetPeriod(int index)126 public ZoneOffsetPeriod getZoneOffsetPeriod(int index) { 127 return zoneOffsetPeriods.get(index); 128 } 129 130 /** 131 * Returns the total number of {@link ZoneOffsetPeriod}s associated with the zone. 132 */ getZoneOffsetPeriodCount()133 public int getZoneOffsetPeriodCount() { 134 return zoneOffsetPeriods.size(); 135 } 136 } 137