Merge "Add more tzlookup.xml time zone metadata" into pi-dev
diff --git a/luni/src/main/java/libcore/util/CountryTimeZones.java b/luni/src/main/java/libcore/util/CountryTimeZones.java
index 048e551..f05bf28 100644
--- a/luni/src/main/java/libcore/util/CountryTimeZones.java
+++ b/luni/src/main/java/libcore/util/CountryTimeZones.java
@@ -62,16 +62,18 @@
public final static class TimeZoneMapping {
public final String timeZoneId;
public final boolean showInPicker;
+ public final Long notUsedAfter;
- TimeZoneMapping(String timeZoneId, boolean showInPicker) {
+ TimeZoneMapping(String timeZoneId, boolean showInPicker, Long notUsedAfter) {
this.timeZoneId = timeZoneId;
this.showInPicker = showInPicker;
+ this.notUsedAfter = notUsedAfter;
}
// VisibleForTesting
- public static TimeZoneMapping createForTests(String timeZoneId,
- boolean showInPicker) {
- return new TimeZoneMapping(timeZoneId, showInPicker);
+ public static TimeZoneMapping createForTests(
+ String timeZoneId, boolean showInPicker, Long notUsedAfter) {
+ return new TimeZoneMapping(timeZoneId, showInPicker, notUsedAfter);
}
@Override
@@ -84,12 +86,13 @@
}
TimeZoneMapping that = (TimeZoneMapping) o;
return showInPicker == that.showInPicker &&
- Objects.equals(timeZoneId, that.timeZoneId);
+ Objects.equals(timeZoneId, that.timeZoneId) &&
+ Objects.equals(notUsedAfter, that.notUsedAfter);
}
@Override
public int hashCode() {
- return Objects.hash(timeZoneId, showInPicker);
+ return Objects.hash(timeZoneId, showInPicker, notUsedAfter);
}
@Override
@@ -97,6 +100,7 @@
return "TimeZoneMapping{"
+ "timeZoneId='" + timeZoneId + '\''
+ ", showInPicker=" + showInPicker
+ + ", notUsedAfter=" + notUsedAfter
+ '}';
}
diff --git a/luni/src/main/java/libcore/util/TimeZoneFinder.java b/luni/src/main/java/libcore/util/TimeZoneFinder.java
index 55e25c7..7c682d0 100644
--- a/luni/src/main/java/libcore/util/TimeZoneFinder.java
+++ b/luni/src/main/java/libcore/util/TimeZoneFinder.java
@@ -60,10 +60,15 @@
private static final String DEFAULT_TIME_ZONE_ID_ATTRIBUTE = "default";
private static final String EVER_USES_UTC_ATTRIBUTE = "everutc";
- // Country -> Time zone mapping. e.g. <id picker="n">
+ // Country -> Time zone mapping. e.g. <id>ZoneId</id>, <id picker="n">ZoneId</id>,
+ // <id notafter={timestamp}>ZoneId</id>
// The default for the picker attribute when unspecified is "y".
+ // The notafter attribute is optional. It specifies a timestamp (time in milliseconds from Unix
+ // epoch start) after which the zone is not (effectively) in use. If unspecified the zone is in
+ // use forever.
private static final String ZONE_ID_ELEMENT = "id";
private static final String ZONE_SHOW_IN_PICKER_ATTRIBUTE = "picker";
+ private static final String ZONE_NOT_USED_AFTER_ATTRIBUTE = "notafter";
private static final String TRUE_ATTRIBUTE_VALUE = "y";
private static final String FALSE_ATTRIBUTE_VALUE = "n";
@@ -395,6 +400,8 @@
// The picker attribute is optional and defaulted to true.
boolean showInPicker = parseBooleanAttribute(
parser, ZONE_SHOW_IN_PICKER_ATTRIBUTE, true /* defaultValue */);
+ Long notUsedAfter = parseLongAttribute(
+ parser, ZONE_NOT_USED_AFTER_ATTRIBUTE, null /* defaultValue */);
String zoneIdString = consumeText(parser);
// Make sure we are on the </id> element.
@@ -406,7 +413,8 @@
+ parser.getPositionDescription());
}
- TimeZoneMapping timeZoneMapping = new TimeZoneMapping(zoneIdString, showInPicker);
+ TimeZoneMapping timeZoneMapping =
+ new TimeZoneMapping(zoneIdString, showInPicker, notUsedAfter);
timeZoneMappings.add(timeZoneMapping);
}
@@ -415,6 +423,25 @@
}
/**
+ * Parses an attribute value, which must be either {@code null} or a valid signed long value.
+ * If the attribute value is {@code null} then {@code defaultValue} is returned. If the
+ * attribute is present but not a valid long value then an XmlPullParserException is thrown.
+ */
+ private static Long parseLongAttribute(XmlPullParser parser, String attributeName,
+ Long defaultValue) throws XmlPullParserException {
+ String attributeValueString = parser.getAttributeValue(null /* namespace */, attributeName);
+ if (attributeValueString == null) {
+ return defaultValue;
+ }
+ try {
+ return Long.parseLong(attributeValueString);
+ } catch (NumberFormatException e) {
+ throw new XmlPullParserException("Attribute \"" + attributeName
+ + "\" is not a long value: " + parser.getPositionDescription());
+ }
+ }
+
+ /**
* Parses an attribute value, which must be either {@code null}, {@code "y"} or {@code "n"}.
* If the attribute value is {@code null} then {@code defaultValue} is returned. If the
* attribute is present but not "y" or "n" then an XmlPullParserException is thrown.
diff --git a/luni/src/test/java/libcore/libcore/util/CountryTimeZonesTest.java b/luni/src/test/java/libcore/libcore/util/CountryTimeZonesTest.java
index aeb9c00..3466e7b 100644
--- a/luni/src/test/java/libcore/libcore/util/CountryTimeZonesTest.java
+++ b/luni/src/test/java/libcore/libcore/util/CountryTimeZonesTest.java
@@ -722,7 +722,8 @@
*/
private static List<TimeZoneMapping> timeZoneMappings(String... timeZoneIds) {
return Arrays.stream(timeZoneIds)
- .map(x -> TimeZoneMapping.createForTests(x, true))
+ .map(x -> TimeZoneMapping.createForTests(
+ x, true /* picker */, null /* notUsedAfter */))
.collect(Collectors.toList());
}
diff --git a/luni/src/test/java/libcore/libcore/util/CountryZonesFinderTest.java b/luni/src/test/java/libcore/libcore/util/CountryZonesFinderTest.java
index d9e2287..eca59a0 100644
--- a/luni/src/test/java/libcore/libcore/util/CountryZonesFinderTest.java
+++ b/luni/src/test/java/libcore/libcore/util/CountryZonesFinderTest.java
@@ -107,7 +107,8 @@
*/
private static List<TimeZoneMapping> timeZoneMappings(String... timeZoneIds) {
return Arrays.stream(timeZoneIds)
- .map(x -> TimeZoneMapping.createForTests(x, true))
+ .map(x -> TimeZoneMapping.createForTests(
+ x, true /* picker */, null /* notUsedAfter */))
.collect(Collectors.toList());
}
}
diff --git a/luni/src/test/java/libcore/libcore/util/TimeZoneFinderTest.java b/luni/src/test/java/libcore/libcore/util/TimeZoneFinderTest.java
index d4663b5..d3a0e03 100644
--- a/luni/src/test/java/libcore/libcore/util/TimeZoneFinderTest.java
+++ b/luni/src/test/java/libcore/libcore/util/TimeZoneFinderTest.java
@@ -462,10 +462,48 @@
CountryTimeZones usTimeZones = finder.lookupCountryTimeZones("us");
List<TimeZoneMapping> actualTimeZoneMappings = usTimeZones.getTimeZoneMappings();
List<TimeZoneMapping> expectedTimeZoneMappings = list(
- TimeZoneMapping.createForTests("America/New_York", true /* shownInPicker */),
- TimeZoneMapping.createForTests("America/Los_Angeles", true /* shownInPicker */),
TimeZoneMapping.createForTests(
- "America/Indiana/Vincennes", false /* shownInPicker */)
+ "America/New_York", true /* shownInPicker */, null /* notUsedAfter */),
+ TimeZoneMapping.createForTests(
+ "America/Los_Angeles", true /* shownInPicker */, null /* notUsedAfter */),
+ TimeZoneMapping.createForTests(
+ "America/Indiana/Vincennes", false /* shownInPicker */,
+ null /* notUsedAfter */)
+ );
+ assertEquals(expectedTimeZoneMappings, actualTimeZoneMappings);
+ }
+
+ @Test
+ public void xmlParsing_badTimeZoneMappingNotAfter() throws Exception {
+ checkValidateThrowsParserException("<timezones ianaversion=\"2017b\">\n"
+ + " <countryzones>\n"
+ + " <country code=\"gb\" default=\"Europe/London\" everutc=\"y\">\n"
+ + " <id notafter=\"sometimes\">Europe/London</id>\n"
+ + " </country>\n"
+ + " </countryzones>\n"
+ + "</timezones>\n");
+ }
+
+ @Test
+ public void xmlParsing_timeZoneMappingNotAfter() throws Exception {
+ TimeZoneFinder finder = validate("<timezones ianaversion=\"2017b\">\n"
+ + " <countryzones>\n"
+ + " <country code=\"us\" default=\"America/New_York\" everutc=\"n\">\n"
+ + " <!-- Explicit notafter -->\n"
+ + " <id notafter=\"1234\">America/New_York</id>\n"
+ + " <!-- Missing notafter -->\n"
+ + " <id>America/Indiana/Vincennes</id>\n"
+ + " </country>\n"
+ + " </countryzones>\n"
+ + "</timezones>\n");
+ CountryTimeZones usTimeZones = finder.lookupCountryTimeZones("us");
+ List<TimeZoneMapping> actualTimeZoneMappings = usTimeZones.getTimeZoneMappings();
+ List<TimeZoneMapping> expectedTimeZoneMappings = list(
+ TimeZoneMapping.createForTests(
+ "America/New_York", true /* shownInPicker */, 1234L /* notUsedAfter */),
+ TimeZoneMapping.createForTests(
+ "America/Indiana/Vincennes", true /* shownInPicker */,
+ null /* notUsedAfter */)
);
assertEquals(expectedTimeZoneMappings, actualTimeZoneMappings);
}
@@ -908,7 +946,7 @@
assertEquals(expected, actual);
}
- private static void checkValidateThrowsParserException(String xml) throws Exception {
+ private static void checkValidateThrowsParserException(String xml) {
try {
validate(xml);
fail();
@@ -927,7 +965,8 @@
*/
private static List<TimeZoneMapping> timeZoneMappings(String... timeZoneIds) {
return Arrays.stream(timeZoneIds)
- .map(x -> TimeZoneMapping.createForTests(x, true))
+ .map(x -> TimeZoneMapping.createForTests(
+ x, true /* showInPicker */, null /* notUsedAfter */))
.collect(Collectors.toList());
}