Loading src/java/com/android/internal/telephony/NitzData.java +25 −3 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ import java.util.Calendar; import java.util.TimeZone; /** * Represents NITZ data. Various static methods are provided to help with parsing and intepretation * Represents NITZ data. Various static methods are provided to help with parsing and interpretation * of NITZ data. * * {@hide} Loading Loading @@ -153,7 +153,11 @@ public final class NitzData { /** * Returns the total offset to apply to the {@link #getCurrentTimeInMillis()} to arrive at a * local time. * local time. NITZ is limited in only being able to express total offsets in multiples of 15 * minutes. * * <p>Note that some time zones change offset during the year for reasons other than "daylight * savings", e.g. for Ramadan. This is not well handled by most date / time APIs. */ public int getLocalOffsetMillis() { return mZoneOffset; Loading @@ -162,7 +166,25 @@ public final class NitzData { /** * Returns the offset (already included in {@link #getLocalOffsetMillis()}) associated with * Daylight Savings Time (DST). This field is optional: {@code null} means the DST offset is * unknown. * unknown. NITZ is limited in only being able to express DST offsets in positive multiples of * one or two hours. * * <p>Callers should remember that standard time / DST is a matter of convention: it has * historically been assumed by NITZ and many date/time APIs that DST happens in the summer and * the "raw" offset will increase during this time, usually by one hour. However, the tzdb * maintainers have moved to different conventions on a country-by-country basis so that some * summer times are considered the "standard" time (i.e. in this model winter time is the "DST" * and a negative adjustment, usually of (negative) one hour. * * <p>There is nothing that says NITZ and tzdb need to treat DST conventions the same. * * <p>At the time of writing Android date/time APIs are sticking with the historic tzdb * convention that DST is used in summer time and is <em>always</em> a positive offset but this * could change in future. If Android or carriers change the conventions used then it might make * NITZ comparisons with tzdb information more error-prone. * * <p>See also {@link #getLocalOffsetMillis()} for other reasons besides DST that a local offset * may change. */ public Integer getDstAdjustmentMillis() { return mDstOffset; Loading src/java/com/android/internal/telephony/NitzStateMachineImpl.java +1 −1 Original line number Diff line number Diff line Loading @@ -263,7 +263,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { } NitzData newNitzData = nitzSignal.getValue(); boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst(); boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0; return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal); } Loading src/java/com/android/internal/telephony/TimeZoneLookupHelper.java +36 −13 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import libcore.timezone.CountryTimeZones; import libcore.timezone.CountryTimeZones.TimeZoneMapping; import libcore.timezone.TimeZoneFinder; import java.util.Date; import java.util.List; import java.util.Objects; Loading Loading @@ -314,30 +313,36 @@ public class TimeZoneLookupHelper { private static OffsetResult lookupByNitzStatic(NitzData nitzData) { int utcOffsetMillis = nitzData.getLocalOffsetMillis(); boolean isDst = nitzData.isDst(); long timeMillis = nitzData.getCurrentTimeInMillis(); // Android NITZ time zone matching doesn't try to do a precise match using the DST offset // supplied by the carrier. It only considers whether or not the carrier suggests local time // is DST (if known). NITZ is limited in only being able to express DST offsets in whole // hours and the DST info is optional. Integer dstAdjustmentMillis = nitzData.getDstAdjustmentMillis(); Boolean isDst = dstAdjustmentMillis == null ? null : dstAdjustmentMillis != 0; OffsetResult match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst); if (match == null) { // Couldn't find a proper timezone. Perhaps the DST data is wrong. match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst); if (match == null && isDst != null) { // This branch is extremely unlikely and could probably be removed. The match above will // have searched the entire tzdb for a zone with the same total offset and isDst state. // Here we try another match but use "null" for isDst to indicate that only the total // offset should be considered. If, by the end of this, there isn't a match then the // current offset suggested by the carrier must be highly unusual. match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, null /* isDst */); } return match; } private static OffsetResult lookupByInstantOffsetDst(long timeMillis, int utcOffsetMillis, boolean isDst) { int rawOffset = utcOffsetMillis; if (isDst) { rawOffset -= MS_PER_HOUR; } String[] zones = TimeZone.getAvailableIDs(rawOffset); Boolean isDst) { String[] zones = TimeZone.getAvailableIDs(); TimeZone match = null; Date d = new Date(timeMillis); boolean isOnlyMatch = true; for (String zone : zones) { TimeZone tz = TimeZone.getFrozenTimeZone(zone); if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) { if (offsetMatchesAtTime(tz, utcOffsetMillis, isDst, timeMillis)) { if (match == null) { match = tz; } else { Loading @@ -353,6 +358,24 @@ public class TimeZoneLookupHelper { return new OffsetResult(match, isOnlyMatch); } /** * Returns {@code true} if the specified {@code totalOffset} and {@code isDst} would be valid in * the {@code timeZone} at time {@code whenMillis}. {@code totalOffetMillis} is always matched. * If {@code isDst} is {@code null} this means the DST state is unknown so DST state is ignored. * If {@code isDst} is not {@code null} then it is also matched. */ private static boolean offsetMatchesAtTime(TimeZone timeZone, int totalOffsetMillis, Boolean isDst, long whenMillis) { int[] offsets = new int[2]; timeZone.getOffset(whenMillis, false /* local */, offsets); if (totalOffsetMillis != (offsets[0] + offsets[1])) { return false; } return isDst == null || isDst == (offsets[1] != 0); } /** * Returns {@code true} if the supplied (lower-case) ISO country code is for a country known to * use a raw offset of zero from UTC at the time specified. Loading Loading
src/java/com/android/internal/telephony/NitzData.java +25 −3 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ import java.util.Calendar; import java.util.TimeZone; /** * Represents NITZ data. Various static methods are provided to help with parsing and intepretation * Represents NITZ data. Various static methods are provided to help with parsing and interpretation * of NITZ data. * * {@hide} Loading Loading @@ -153,7 +153,11 @@ public final class NitzData { /** * Returns the total offset to apply to the {@link #getCurrentTimeInMillis()} to arrive at a * local time. * local time. NITZ is limited in only being able to express total offsets in multiples of 15 * minutes. * * <p>Note that some time zones change offset during the year for reasons other than "daylight * savings", e.g. for Ramadan. This is not well handled by most date / time APIs. */ public int getLocalOffsetMillis() { return mZoneOffset; Loading @@ -162,7 +166,25 @@ public final class NitzData { /** * Returns the offset (already included in {@link #getLocalOffsetMillis()}) associated with * Daylight Savings Time (DST). This field is optional: {@code null} means the DST offset is * unknown. * unknown. NITZ is limited in only being able to express DST offsets in positive multiples of * one or two hours. * * <p>Callers should remember that standard time / DST is a matter of convention: it has * historically been assumed by NITZ and many date/time APIs that DST happens in the summer and * the "raw" offset will increase during this time, usually by one hour. However, the tzdb * maintainers have moved to different conventions on a country-by-country basis so that some * summer times are considered the "standard" time (i.e. in this model winter time is the "DST" * and a negative adjustment, usually of (negative) one hour. * * <p>There is nothing that says NITZ and tzdb need to treat DST conventions the same. * * <p>At the time of writing Android date/time APIs are sticking with the historic tzdb * convention that DST is used in summer time and is <em>always</em> a positive offset but this * could change in future. If Android or carriers change the conventions used then it might make * NITZ comparisons with tzdb information more error-prone. * * <p>See also {@link #getLocalOffsetMillis()} for other reasons besides DST that a local offset * may change. */ public Integer getDstAdjustmentMillis() { return mDstOffset; Loading
src/java/com/android/internal/telephony/NitzStateMachineImpl.java +1 −1 Original line number Diff line number Diff line Loading @@ -263,7 +263,7 @@ public final class NitzStateMachineImpl implements NitzStateMachine { } NitzData newNitzData = nitzSignal.getValue(); boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0 && !newNitzData.isDst(); boolean zeroOffsetNitz = newNitzData.getLocalOffsetMillis() == 0; return zeroOffsetNitz && !countryUsesUtc(isoCountryCode, nitzSignal); } Loading
src/java/com/android/internal/telephony/TimeZoneLookupHelper.java +36 −13 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import libcore.timezone.CountryTimeZones; import libcore.timezone.CountryTimeZones.TimeZoneMapping; import libcore.timezone.TimeZoneFinder; import java.util.Date; import java.util.List; import java.util.Objects; Loading Loading @@ -314,30 +313,36 @@ public class TimeZoneLookupHelper { private static OffsetResult lookupByNitzStatic(NitzData nitzData) { int utcOffsetMillis = nitzData.getLocalOffsetMillis(); boolean isDst = nitzData.isDst(); long timeMillis = nitzData.getCurrentTimeInMillis(); // Android NITZ time zone matching doesn't try to do a precise match using the DST offset // supplied by the carrier. It only considers whether or not the carrier suggests local time // is DST (if known). NITZ is limited in only being able to express DST offsets in whole // hours and the DST info is optional. Integer dstAdjustmentMillis = nitzData.getDstAdjustmentMillis(); Boolean isDst = dstAdjustmentMillis == null ? null : dstAdjustmentMillis != 0; OffsetResult match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, isDst); if (match == null) { // Couldn't find a proper timezone. Perhaps the DST data is wrong. match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, !isDst); if (match == null && isDst != null) { // This branch is extremely unlikely and could probably be removed. The match above will // have searched the entire tzdb for a zone with the same total offset and isDst state. // Here we try another match but use "null" for isDst to indicate that only the total // offset should be considered. If, by the end of this, there isn't a match then the // current offset suggested by the carrier must be highly unusual. match = lookupByInstantOffsetDst(timeMillis, utcOffsetMillis, null /* isDst */); } return match; } private static OffsetResult lookupByInstantOffsetDst(long timeMillis, int utcOffsetMillis, boolean isDst) { int rawOffset = utcOffsetMillis; if (isDst) { rawOffset -= MS_PER_HOUR; } String[] zones = TimeZone.getAvailableIDs(rawOffset); Boolean isDst) { String[] zones = TimeZone.getAvailableIDs(); TimeZone match = null; Date d = new Date(timeMillis); boolean isOnlyMatch = true; for (String zone : zones) { TimeZone tz = TimeZone.getFrozenTimeZone(zone); if (tz.getOffset(timeMillis) == utcOffsetMillis && tz.inDaylightTime(d) == isDst) { if (offsetMatchesAtTime(tz, utcOffsetMillis, isDst, timeMillis)) { if (match == null) { match = tz; } else { Loading @@ -353,6 +358,24 @@ public class TimeZoneLookupHelper { return new OffsetResult(match, isOnlyMatch); } /** * Returns {@code true} if the specified {@code totalOffset} and {@code isDst} would be valid in * the {@code timeZone} at time {@code whenMillis}. {@code totalOffetMillis} is always matched. * If {@code isDst} is {@code null} this means the DST state is unknown so DST state is ignored. * If {@code isDst} is not {@code null} then it is also matched. */ private static boolean offsetMatchesAtTime(TimeZone timeZone, int totalOffsetMillis, Boolean isDst, long whenMillis) { int[] offsets = new int[2]; timeZone.getOffset(whenMillis, false /* local */, offsets); if (totalOffsetMillis != (offsets[0] + offsets[1])) { return false; } return isDst == null || isDst == (offsets[1] != 0); } /** * Returns {@code true} if the supplied (lower-case) ISO country code is for a country known to * use a raw offset of zero from UTC at the time specified. Loading