/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.timezone; import android.annotation.NonNull; import android.annotation.Nullable; import android.icu.util.TimeZone; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Information about a country's time zones. * * @hide */ public final class CountryTimeZones { /** * A mapping to a time zone ID with some associated metadata. * * @hide */ public static final class TimeZoneMapping { @NonNull private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate; TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) { this.mDelegate = Objects.requireNonNull(delegate); } /** * Returns the ID for this mapping. The ID is a tzdb time zone identifier like * "America/Los_Angeles" that can be used with methods such as {@link * TimeZone#getFrozenTimeZone(String)}. See {@link #getTimeZone()} which returns a frozen * {@link TimeZone} object. */ @NonNull public String getTimeZoneId() { return mDelegate.getTimeZoneId(); } /** * Returns a frozen {@link TimeZone} object for this mapping. */ @NonNull public TimeZone getTimeZone() { return mDelegate.getTimeZone(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TimeZoneMapping that = (TimeZoneMapping) o; return this.mDelegate.equals(that.mDelegate); } @Override public int hashCode() { return this.mDelegate.hashCode(); } @Override public String toString() { return mDelegate.toString(); } } /** * The result of lookup up a time zone using offset information (and possibly more). * * @hide */ public static final class OffsetResult { private final TimeZone mTimeZone; private final boolean mIsOnlyMatch; /** Creates an instance with the supplied information. */ public OffsetResult(@NonNull TimeZone timeZone, boolean isOnlyMatch) { mTimeZone = Objects.requireNonNull(timeZone); mIsOnlyMatch = isOnlyMatch; } /** * Returns a time zone that matches the supplied criteria. */ @NonNull public TimeZone getTimeZone() { return mTimeZone; } /** * Returns {@code true} if there is only one matching time zone for the supplied criteria. */ public boolean isOnlyMatch() { return mIsOnlyMatch; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } OffsetResult that = (OffsetResult) o; return mIsOnlyMatch == that.mIsOnlyMatch && mTimeZone.getID().equals(that.mTimeZone.getID()); } @Override public int hashCode() { return Objects.hash(mTimeZone, mIsOnlyMatch); } @Override public String toString() { return "OffsetResult{" + "mTimeZone(ID)=" + mTimeZone.getID() + ", mIsOnlyMatch=" + mIsOnlyMatch + '}'; } } @NonNull private final libcore.timezone.CountryTimeZones mDelegate; CountryTimeZones(libcore.timezone.CountryTimeZones delegate) { mDelegate = delegate; } /** * Returns true if the ISO code for the country is a case-insensitive match for the one * supplied. */ public boolean matchesCountryCode(@NonNull String countryIso) { return mDelegate.isForCountryCode(countryIso); } /** * Returns the default time zone ID for the country. Can return {@code null} in cases when no * data is available or the time zone ID was not recognized. */ @Nullable public String getDefaultTimeZoneId() { return mDelegate.getDefaultTimeZoneId(); } /** * Returns the default time zone for the country. Can return {@code null} in cases when no data * is available or the time zone ID was not recognized. */ @Nullable public TimeZone getDefaultTimeZone() { return mDelegate.getDefaultTimeZone(); } /** * Qualifier for a country's default time zone. {@code true} indicates that the country's * default time zone would be a good choice generally when there's no UTC offset * information available. This will only be {@code true} in countries with multiple zones where * a large majority of the population is covered by only one of them. */ public boolean isDefaultTimeZoneBoosted() { return mDelegate.isDefaultTimeZoneBoosted(); } /** * Returns {@code true} if the country has at least one time zone that uses UTC at the given * time. This is an efficient check when trying to validate received UTC offset information. * For example, there are situations when a detected zero UTC offset cannot be distinguished * from "no information available" or a corrupted signal. This method is useful because checking * offset information for large countries is relatively expensive but it is generally only the * countries close to the prime meridian that use UTC at any time of the year. * * @param whenMillis the time the offset information is for in milliseconds since the beginning * of the Unix epoch */ public boolean hasUtcZone(long whenMillis) { return mDelegate.hasUtcZone(whenMillis); } /** * Returns a time zone for the country, if there is one, that matches the supplied properties. * If there are multiple matches and the {@code bias} is one of them then it is returned, * otherwise an arbitrary match is returned based on the {@link * #getEffectiveTimeZoneMappingsAt(long)} ordering. * * @param whenMillis the UTC time to match against * @param bias the time zone to prefer, can be {@code null} to indicate there is no preference * @param totalOffsetMillis the offset from UTC at {@code whenMillis} * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST, * {@code false} means not DST * @return an {@link OffsetResult} with information about a matching zone, or {@code null} if * there is no match */ @Nullable public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias, int totalOffsetMillis, boolean isDst) { libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = mDelegate.lookupByOffsetWithBias( whenMillis, bias, totalOffsetMillis, isDst); return delegateOffsetResult == null ? null : new OffsetResult( delegateOffsetResult.getTimeZone(), delegateOffsetResult.isOnlyMatch()); } /** * Returns a time zone for the country, if there is one, that matches the supplied properties. * If there are multiple matches and the {@code bias} is one of them then it is returned, * otherwise an arbitrary match is returned based on the {@link * #getEffectiveTimeZoneMappingsAt(long)} ordering. * * @param whenMillis the UTC time to match against * @param bias the time zone to prefer, can be {@code null} to indicate there is no preference * @param totalOffsetMillis the offset from UTC at {@code whenMillis} * @return an {@link OffsetResult} with information about a matching zone, or {@code null} if * there is no match */ @Nullable public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias, int totalOffsetMillis) { libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = mDelegate.lookupByOffsetWithBias(whenMillis, bias, totalOffsetMillis); return delegateOffsetResult == null ? null : new OffsetResult( delegateOffsetResult.getTimeZone(), delegateOffsetResult.isOnlyMatch()); } /** * Returns an immutable, ordered list of time zone mappings for the country in an undefined but * "priority" order, filtered so that only "effective" time zone IDs are returned. An * "effective" time zone is one that differs from another time zone used in the country after * {@code whenMillis}. The list can be empty if there were no zones configured or the configured * zone IDs were not recognized. */ @NonNull public List getEffectiveTimeZoneMappingsAt(long whenMillis) { List delegateList = mDelegate.getEffectiveTimeZoneMappingsAt(whenMillis); List toReturn = new ArrayList<>(delegateList.size()); for (libcore.timezone.CountryTimeZones.TimeZoneMapping delegateMapping : delegateList) { toReturn.add(new TimeZoneMapping(delegateMapping)); } return Collections.unmodifiableList(toReturn); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CountryTimeZones that = (CountryTimeZones) o; return mDelegate.equals(that.mDelegate); } @Override public int hashCode() { return Objects.hash(mDelegate); } @Override public String toString() { return mDelegate.toString(); } }