• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.icu.text;
18 
19 import android.icu.text.TimeZoneNames;
20 import android.icu.util.TimeZone;
21 import android.icu.util.ULocale;
22 
23 import libcore.api.IntraCoreApi;
24 import libcore.util.NonNull;
25 import libcore.util.Nullable;
26 
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.EnumSet;
31 import java.util.List;
32 import java.util.Set;
33 
34 /**
35  * Provide extra functionalities on top of {@link TimeZoneNames} public APIs.
36  *
37  * @hide
38  */
39 @IntraCoreApi
40 public class ExtendedTimeZoneNames {
41 
42     private static final Set<TimeZoneNames.NameType> DST_NAME_TYPES =
43             Collections.unmodifiableSet(
44                     EnumSet.of(
45                             TimeZoneNames.NameType.LONG_DAYLIGHT,
46                             TimeZoneNames.NameType.SHORT_DAYLIGHT));
47 
48     private static final EnumSet<TimeZoneNames.NameType> STANDARD_AND_DST_TYPES =
49             EnumSet.of(
50                     TimeZoneNames.NameType.SHORT_STANDARD,
51                     TimeZoneNames.NameType.LONG_STANDARD,
52                     TimeZoneNames.NameType.SHORT_DAYLIGHT,
53                     TimeZoneNames.NameType.LONG_DAYLIGHT);
54 
55     private final ULocale locale;
56     private final TimeZoneNames timeZoneNames;
57 /**
58      * A class representing the return result of {@link #matchName(CharSequence, int, String)}
59      *
60      * @hide
61      */
62     @IntraCoreApi
63     public static final class Match {
64 
65         private final int matchLength;
66         private final @NonNull String tzId;
67         private final boolean isDst;
68 
Match(int matchLength, @NonNull String tzId, boolean isDst)69         private Match(int matchLength, @NonNull String tzId, boolean isDst) {
70             this.matchLength = matchLength;
71             this.tzId = tzId;
72             this.isDst = isDst;
73         }
74 
75         /**
76          * Returns the number of chars in the matched name.
77          *
78          * @hide
79          */
80         @IntraCoreApi
getMatchLength()81         public int getMatchLength() {
82             return matchLength;
83         }
84 
85         /**
86          * Returns the time zone id associated with the matched name.
87          *
88          * @hide
89          */
90         @IntraCoreApi
getTzId()91         public @NonNull String getTzId() {
92             return tzId;
93         }
94 
95         /**
96          * Returns true if the matched name is a display name for daylight saving time. For example,
97          * returns true for "Pacific Daylight Time", but false for "Pacific Standard Time".
98          *
99          * @hide
100          */
101         @IntraCoreApi
isDst()102         public boolean isDst() {
103             return isDst;
104         }
105     }
106 
ExtendedTimeZoneNames(ULocale locale)107     private ExtendedTimeZoneNames(ULocale locale) {
108         this.locale = locale;
109         this.timeZoneNames = TimeZoneNames.getInstance(locale);
110     }
111 
112     /**
113      * Returns an instance of {@link ExtendedTimeZoneNames}.
114      *
115      * @hide
116      */
117     @IntraCoreApi
getInstance(@onNull ULocale locale)118     public static @NonNull ExtendedTimeZoneNames getInstance(@NonNull ULocale locale) {
119         return new ExtendedTimeZoneNames(locale);
120     }
121 
122     /**
123      * Returns the underlying {@link TimeZoneNames} instance.
124      *
125      * @hide
126      */
127     @IntraCoreApi
getTimeZoneNames()128     public @NonNull TimeZoneNames getTimeZoneNames() {
129         return timeZoneNames;
130     }
131 
132     /**
133      * Returns {@link Match} if a time zone name in ICU can be matched against the input
134      * CharSequence {@code s}.
135      * The best match is found by the following principles:
136      * <ul>
137      * <li>Length of the matched name. Longest name matched to the given {@code s} has the
138      * highest priority.</li>
139      * <li>The current time zone and meta zones possible in the current country have higher
140      * priority than other zones.</li>
141      * <li>If only meta zones are matched, the country/region in the locale is used to select
142      * a reference time zone. For example, if the name is "Pacific Standard Time" and the country
143      * is US, America/Los_Angeles is returned.</li>
144      * </ul>
145      *
146      * @param text input string to be matched against time zone names in ICU
147      * @param start the begin index in the CharSequence {@code s}
148      * @param currentTzId the time zone ID prioritized to be matched if multiple time zone IDs can
149      *                    be matched and this is one of the matched IDs.
150      * @return null if no match is found
151      *
152      * @hide
153      */
154     @IntraCoreApi
matchName(@onNull CharSequence text, int start, @NonNull String currentTzId)155     public @Nullable Match matchName(@NonNull CharSequence text, int start,
156             @NonNull String currentTzId) {
157         currentTzId = TimeZone.getCanonicalID(currentTzId);
158 
159         Collection<TimeZoneNames.MatchInfo> matchedInfos =
160                 timeZoneNames.find(text, start, STANDARD_AND_DST_TYPES);
161 
162         if (matchedInfos.isEmpty()) {
163             return null;
164         }
165 
166         List<TimeZoneNames.MatchInfo> maxLengthMatchInfos = new ArrayList<>();
167         int maxMatchedInfoLength = 0;
168 
169         for (TimeZoneNames.MatchInfo matchInfo : matchedInfos) {
170             if (matchInfo.matchLength() > maxMatchedInfoLength) {
171                 maxMatchedInfoLength = matchInfo.matchLength();
172                 maxLengthMatchInfos.clear();
173             }
174 
175             if (matchInfo.matchLength() >= maxMatchedInfoLength) {
176                 maxLengthMatchInfos.add(matchInfo);
177             }
178         }
179 
180         Set<String> metaZonesInCurrentZone = timeZoneNames.getAvailableMetaZoneIDs(currentTzId);
181 
182         TimeZoneNames.MatchInfo tzMatchInfo = null;
183 
184         for (TimeZoneNames.MatchInfo matchInfo : maxLengthMatchInfos) {
185             if (matchInfo.tzID() != null && matchInfo.tzID().equals(currentTzId)) {
186                 return matchedTimeZone(matchInfo.tzID(), matchInfo);
187             }
188             if (matchInfo.mzID() != null && metaZonesInCurrentZone.contains(matchInfo.mzID())) {
189                 return matchedTimeZone(currentTzId, matchInfo);
190             }
191 
192             if (matchInfo.tzID() != null) {
193                 tzMatchInfo = matchInfo;
194             }
195         }
196 
197         if (tzMatchInfo != null) {
198             return matchedTimeZone(tzMatchInfo.tzID(), tzMatchInfo);
199         }
200 
201         String region = locale.getCountry();
202         if (region == null || region.isEmpty()) {
203             // An UN M49 code to represent the world. See TimeZoneNames#getReferenceZoneID().
204             region = "001";
205         }
206 
207         for (TimeZoneNames.MatchInfo matchInfo : maxLengthMatchInfos) {
208             if (matchInfo.mzID() != null) {
209                 String timeZoneId = timeZoneNames.getReferenceZoneID(matchInfo.mzID(), region);
210                 if (timeZoneId != null) {
211                     return matchedTimeZone(timeZoneId, matchInfo);
212                 }
213             }
214         }
215 
216         return null;
217     }
218 
matchedTimeZone( String timeZoneId, TimeZoneNames.MatchInfo matchInfo)219     private static Match matchedTimeZone(
220             String timeZoneId, TimeZoneNames.MatchInfo matchInfo) {
221         return new Match(
222                 matchInfo.matchLength(),
223                 timeZoneId,
224                 DST_NAME_TYPES.contains(matchInfo.nameType()));
225 
226     }
227 
228 }
229