Home | History | Annotate | Download | only in zonetree
      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 
     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      */
     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      */
     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 
     93     public String getZoneId() {
     94         return zoneId;
     95     }
     96 
     97     public int getPriority() {
     98         return priority;
     99     }
    100 
    101     @Override
    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      */
    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      */
    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      */
    133     public int getZoneOffsetPeriodCount() {
    134         return zoneOffsetPeriods.size();
    135     }
    136 }
    137