diff --git a/java/build.xml b/java/build.xml index a0553cd77..18e30b26f 100644 --- a/java/build.xml +++ b/java/build.xml @@ -127,6 +127,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -153,7 +173,7 @@ + depends="build-phone-metadata,build-short-metadata,build-alternate-metadata,build-carrier-data,build-geo-data,build-timezones-data"> @@ -169,7 +189,7 @@ - + @@ -192,17 +212,19 @@ + + + depends="compile,build-test-metadata,build-carrier-test-data,build-geo-test-data,build-timezones-test-data"> @@ -222,6 +244,7 @@ + diff --git a/java/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java b/java/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java index 9344fbbaf..77fa2a96e 100644 --- a/java/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java +++ b/java/demo/src/com/google/phonenumbers/PhoneNumberParserServlet.java @@ -21,6 +21,7 @@ package com.google.phonenumbers; import com.google.i18n.phonenumbers.AsYouTypeFormatter; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberToCarrierMapper; +import com.google.i18n.phonenumbers.PhoneNumberToTimeZonesMapper; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType; @@ -264,6 +265,16 @@ public class PhoneNumberParserServlet extends HttpServlet { output.append(""); output.append(""); + output.append("
"); + output.append(""); + output.append(""); + appendLine( + "Time zone(s)", + PhoneNumberToTimeZonesMapper.getInstance().getTimeZonesForNumber(number).toString(), + output); + output.append("
PhoneNumberToTimeZonesMapper Results
"); + output.append("
"); + if (numberType == PhoneNumberType.MOBILE || numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE || numberType == PhoneNumberType.PAGER) { diff --git a/java/geocoder/pom.xml b/java/geocoder/pom.xml index 3f5635a52..72a212c7a 100644 --- a/java/geocoder/pom.xml +++ b/java/geocoder/pom.xml @@ -21,12 +21,20 @@ src/com/google/i18n/phonenumbers/geocoding/data com/google/i18n/phonenumbers/geocoding/data + + src/com/google/i18n/phonenumbers/timezones/data + com/google/i18n/phonenumbers/timezones/data + test/com/google/i18n/phonenumbers/geocoding/testing_data com/google/i18n/phonenumbers/geocoding/testing_data + + test/com/google/i18n/phonenumbers/timezones/testing_data + com/google/i18n/phonenumbers/timezones/testing_data + diff --git a/java/geocoder/src/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapper.java b/java/geocoder/src/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapper.java new file mode 100644 index 000000000..d1d4c4486 --- /dev/null +++ b/java/geocoder/src/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapper.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2012 The Libphonenumber Authors + * + * 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 com.google.i18n.phonenumbers; + +import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; +import com.google.i18n.phonenumbers.prefixmapper.PrefixTimeZonesMap; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * An offline mapper from phone numbers to time zones. + * + * @author Walter Erquinigo + */ +public class PhoneNumberToTimeZonesMapper { + private static PhoneNumberToTimeZonesMapper instance = null; + private static final String MAPPING_DATA_DIRECTORY = + "/com/google/i18n/phonenumbers/timezones/data/"; + private static final String MAPPING_DATA_FILE_NAME = "map_data"; + // This is defined by ICU as the unknown time zone. + private static final String UNKNOWN_TIMEZONE = "Etc/Unknown"; + // A list with the ICU unknown time zone as single element. + // @VisibleForTesting + static final List UNKNOWN_TIME_ZONE_LIST = new ArrayList(1); + static { + UNKNOWN_TIME_ZONE_LIST.add(UNKNOWN_TIMEZONE); + } + + private static final Logger LOGGER = + Logger.getLogger(PhoneNumberToTimeZonesMapper.class.getName()); + + private PrefixTimeZonesMap prefixTimeZonesMap = null; + + // @VisibleForTesting + PhoneNumberToTimeZonesMapper(String prefixTimeZonesMapDataDirectory) { + this.prefixTimeZonesMap = loadPrefixTimeZonesMapFromFile( + prefixTimeZonesMapDataDirectory + MAPPING_DATA_FILE_NAME); + } + + private PhoneNumberToTimeZonesMapper(PrefixTimeZonesMap prefixTimeZonesMap) { + this.prefixTimeZonesMap = prefixTimeZonesMap; + } + + private static PrefixTimeZonesMap loadPrefixTimeZonesMapFromFile(String path) { + InputStream source = PhoneNumberToTimeZonesMapper.class.getResourceAsStream(path); + ObjectInputStream in = null; + PrefixTimeZonesMap map = new PrefixTimeZonesMap(); + try { + in = new ObjectInputStream(source); + map.readExternal(in); + } catch (IOException e) { + LOGGER.log(Level.WARNING, e.toString()); + } finally { + close(in); + } + return map; + } + + private static void close(InputStream in) { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + LOGGER.log(Level.WARNING, e.toString()); + } + } + } + + /** + * Helper class used for lazy instantiation of a PhoneNumberToTimeZonesMapper. This also loads the + * map data in a thread-safe way. + */ + private static class LazyHolder { + private static final PhoneNumberToTimeZonesMapper INSTANCE; + static { + PrefixTimeZonesMap map = + loadPrefixTimeZonesMapFromFile(MAPPING_DATA_DIRECTORY + MAPPING_DATA_FILE_NAME); + INSTANCE = new PhoneNumberToTimeZonesMapper(map); + } + } + + /** + * Gets a {@link PhoneNumberToTimeZonesMapper} instance. + * + *

The {@link PhoneNumberToTimeZonesMapper} is implemented as a singleton. Therefore, calling + * this method multiple times will only result in one instance being created. + * + * @return a {@link PhoneNumberToTimeZonesMapper} instance + */ + public static synchronized PhoneNumberToTimeZonesMapper getInstance() { + return LazyHolder.INSTANCE; + } + + /** + * Returns a list of time zones to which a phone number belongs. + * + *

This method assumes the validity of the number passed in has already been checked, and that + * the number is geo-localizable. We consider fixed-line and mobile numbers possible candidates + * for geo-localization. + * + * @param number a valid phone number for which we want to get the time zones to which it belongs + * @return a list of the corresponding time zones or a single element list with the default + * unknown time zone if no other time zone was found or if the number was invalid + */ + public List getTimeZonesForGeographicalNumber(PhoneNumber number) { + return getTimeZonesForGeocodableNumber(number); + } + + /** + * As per {@link #getTimeZonesForGeographicalNumber(PhoneNumber)} but explicitly checks + * the validity of the number passed in. + * + * @param number the phone number for which we want to get the time zones to which it belongs + * @return a list of the corresponding time zones or a single element list with the default + * unknown time zone if no other time zone was found or if the number was invalid + */ + public List getTimeZonesForNumber(PhoneNumber number) { + PhoneNumberType numberType = PhoneNumberUtil.getInstance().getNumberType(number); + if (numberType == PhoneNumberType.UNKNOWN) { + return UNKNOWN_TIME_ZONE_LIST; + } else if (!canBeGeocoded(numberType)) { + return getCountryLevelTimeZonesforNumber(number); + } + return getTimeZonesForGeographicalNumber(number); + } + + /** + * A similar method is implemented as PhoneNumberUtil.isNumberGeographical, which performs a + * stricter check, as it determines if a number has a geographical association. Also, if new + * phone number types were added, we should check if this other method should be updated too. + * TODO: Remove duplication by completing the logic in the method in PhoneNumberUtil. + * For more information, see the comments in that method. + */ + private boolean canBeGeocoded(PhoneNumberType numberType) { + return (numberType == PhoneNumberType.FIXED_LINE || + numberType == PhoneNumberType.MOBILE || + numberType == PhoneNumberType.FIXED_LINE_OR_MOBILE); + } + + /** + * Returns a String with the ICU unknown time zone. + */ + public static String getUnknownTimeZone() { + return UNKNOWN_TIMEZONE; + } + + /** + * Returns a list of time zones to which a geocodable phone number belongs. + * + * @param number the phone number for which we want to get the time zones to which it belongs + * @return the list of corresponding time zones or a single element list with the default + * unknown time zone if no other time zone was found or if the number was invalid + */ + private List getTimeZonesForGeocodableNumber(PhoneNumber number) { + List timezones = prefixTimeZonesMap.lookupTimeZonesForNumber(number); + return Collections.unmodifiableList(timezones.isEmpty() ? UNKNOWN_TIME_ZONE_LIST + : timezones); + } + + /** + * Returns the list of time zones corresponding to the country calling code of {@code number}. + * + * @param number the phone number to look up + * @return the list of corresponding time zones or a single element list with the default + * unknown time zone if no other time zone was found + */ + private List getCountryLevelTimeZonesforNumber(PhoneNumber number) { + List timezones = prefixTimeZonesMap.lookupCountryLevelTimeZonesForNumber(number); + return Collections.unmodifiableList(timezones.isEmpty() ? UNKNOWN_TIME_ZONE_LIST + : timezones); + } +} diff --git a/java/geocoder/src/com/google/i18n/phonenumbers/timezones/data/map_data b/java/geocoder/src/com/google/i18n/phonenumbers/timezones/data/map_data new file mode 100644 index 000000000..aa574da08 Binary files /dev/null and b/java/geocoder/src/com/google/i18n/phonenumbers/timezones/data/map_data differ diff --git a/java/geocoder/test/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapperTest.java b/java/geocoder/test/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapperTest.java new file mode 100644 index 000000000..8bb3423da --- /dev/null +++ b/java/geocoder/test/com/google/i18n/phonenumbers/PhoneNumberToTimeZonesMapperTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 The Libphonenumber Authors + * + * 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 com.google.i18n.phonenumbers; + +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit tests for PhoneNumberToTimeZonesMapper.java + * + * @author Walter Erquinigo + */ +public class PhoneNumberToTimeZonesMapperTest extends TestCase { + private final PhoneNumberToTimeZonesMapper prefixTimeZonesMapper = + new PhoneNumberToTimeZonesMapper(TEST_MAPPING_DATA_DIRECTORY); + private static final String TEST_MAPPING_DATA_DIRECTORY = + "/com/google/i18n/phonenumbers/timezones/testing_data/"; + // Set up some test numbers to re-use. + private static final PhoneNumber AU_NUMBER = + new PhoneNumber().setCountryCode(61).setNationalNumber(236618300L); + private static final PhoneNumber CA_NUMBER = + new PhoneNumber().setCountryCode(1).setNationalNumber(6048406565L); + private static final PhoneNumber KO_NUMBER = + new PhoneNumber().setCountryCode(82).setNationalNumber(22123456L); + private static final PhoneNumber KO_INVALID_NUMBER = + new PhoneNumber().setCountryCode(82).setNationalNumber(1234L); + private static final PhoneNumber US_NUMBER1 = + new PhoneNumber().setCountryCode(1).setNationalNumber(6509600000L); + private static final PhoneNumber US_NUMBER2 = + new PhoneNumber().setCountryCode(1).setNationalNumber(2128120000L); + private static final PhoneNumber US_NUMBER3 = + new PhoneNumber().setCountryCode(1).setNationalNumber(6174240000L); + private static final PhoneNumber US_INVALID_NUMBER = + new PhoneNumber().setCountryCode(1).setNationalNumber(123456789L); + private static final PhoneNumber NUMBER_WITH_INVALID_COUNTRY_CODE = + new PhoneNumber().setCountryCode(999).setNationalNumber(2423651234L); + private static final PhoneNumber INTERNATIONAL_TOLL_FREE = + new PhoneNumber().setCountryCode(800).setNationalNumber(12345678L); + + // NANPA time zones. + private static final String CHICAGO_TZ = "America/Chicago"; + private static final String LOS_ANGELES_TZ = "America/Los_Angeles"; + private static final String NEW_YORK_TZ = "America/New_York"; + private static final String WINNIPEG_TZ = "America/Winnipeg"; + // Non NANPA time zones. + private static final String SEOUL_TZ = "Asia/Seoul"; + private static final String SYDNEY_TZ = "Australia/Sydney"; + + static List buildListOfTimeZones(String ... timezones) { + ArrayList timezonesList = new ArrayList(timezones.length); + for (String timezone : timezones) { + timezonesList.add(timezone); + } + return timezonesList; + } + + private static List getNanpaTimeZonesList() { + return buildListOfTimeZones(NEW_YORK_TZ, CHICAGO_TZ, WINNIPEG_TZ, LOS_ANGELES_TZ); + } + + public void testGetTimeZonesForNumber() { + // Test with invalid numbers even when their country code prefixes exist in the mapper. + assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, + prefixTimeZonesMapper.getTimeZonesForNumber(US_INVALID_NUMBER)); + assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, + prefixTimeZonesMapper.getTimeZonesForNumber(KO_INVALID_NUMBER)); + // Test with valid prefixes. + assertEquals(buildListOfTimeZones(SYDNEY_TZ), + prefixTimeZonesMapper.getTimeZonesForNumber(AU_NUMBER)); + assertEquals(buildListOfTimeZones(SEOUL_TZ), + prefixTimeZonesMapper.getTimeZonesForNumber(KO_NUMBER)); + assertEquals(buildListOfTimeZones(WINNIPEG_TZ), + prefixTimeZonesMapper.getTimeZonesForNumber(CA_NUMBER)); + assertEquals(buildListOfTimeZones(LOS_ANGELES_TZ), + prefixTimeZonesMapper.getTimeZonesForNumber(US_NUMBER1)); + assertEquals(buildListOfTimeZones(NEW_YORK_TZ), + prefixTimeZonesMapper.getTimeZonesForNumber(US_NUMBER2)); + // Test with an invalid country code. + assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, + prefixTimeZonesMapper.getTimeZonesForNumber(NUMBER_WITH_INVALID_COUNTRY_CODE)); + // Test with a non geographical phone number. + assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, + prefixTimeZonesMapper.getTimeZonesForNumber(INTERNATIONAL_TOLL_FREE)); + } + + public void testGetTimeZonesForValidNumber() { + // Test with invalid numbers even when their country code prefixes exist in the mapper. + assertEquals(getNanpaTimeZonesList(), + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(US_INVALID_NUMBER)); + assertEquals(buildListOfTimeZones(SEOUL_TZ), + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(KO_INVALID_NUMBER)); + // Test with valid prefixes. + assertEquals(buildListOfTimeZones(SYDNEY_TZ), + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(AU_NUMBER)); + assertEquals(buildListOfTimeZones(SEOUL_TZ), + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(KO_NUMBER)); + assertEquals(buildListOfTimeZones(WINNIPEG_TZ), + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(CA_NUMBER)); + assertEquals(buildListOfTimeZones(LOS_ANGELES_TZ), + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(US_NUMBER1)); + assertEquals(buildListOfTimeZones(NEW_YORK_TZ), + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber(US_NUMBER2)); + // Test with an invalid country code. + assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber( + NUMBER_WITH_INVALID_COUNTRY_CODE)); + // Test with a non geographical phone number. + assertEquals(PhoneNumberToTimeZonesMapper.UNKNOWN_TIME_ZONE_LIST, + prefixTimeZonesMapper.getTimeZonesForGeographicalNumber( + INTERNATIONAL_TOLL_FREE)); + } + + public void testGetTimeZonesForValidNumberSearchingAtCountryCodeLevel() { + // Test that the country level time zones are returned when the number passed in is valid but + // not covered by any non-country level prefixes in the mapper. + assertEquals(prefixTimeZonesMapper.getTimeZonesForNumber(US_NUMBER3), + getNanpaTimeZonesList()); + } +} diff --git a/java/geocoder/test/com/google/i18n/phonenumbers/timezones/testing_data/map_data b/java/geocoder/test/com/google/i18n/phonenumbers/timezones/testing_data/map_data new file mode 100644 index 000000000..0150711ea Binary files /dev/null and b/java/geocoder/test/com/google/i18n/phonenumbers/timezones/testing_data/map_data differ diff --git a/java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PhonePrefixMap.java b/java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PhonePrefixMap.java index 0d65e078f..fdab3ec4a 100644 --- a/java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PhonePrefixMap.java +++ b/java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PhonePrefixMap.java @@ -144,13 +144,12 @@ public class PhonePrefixMap implements Externalizable { * @param number the phone number to look up * @return the description of the number */ - String lookup(PhoneNumber number) { - int numOfEntries = phonePrefixMapStorage.getNumOfEntries(); + String lookup(long number) { + int numOfEntries = phonePrefixMapStorage.getNumOfEntries(); if (numOfEntries == 0) { return null; } - long phonePrefix = - Long.parseLong(number.getCountryCode() + phoneUtil.getNationalSignificantNumber(number)); + long phonePrefix = number; int currentIndex = numOfEntries - 1; SortedSet currentSetOfLengths = phonePrefixMapStorage.getPossibleLengths(); while (currentSetOfLengths.size() > 0) { @@ -172,6 +171,18 @@ public class PhonePrefixMap implements Externalizable { return null; } + /** + * As per {@link #lookup(long)}, but receives the number as a PhoneNumber instead of a long. + * + * @param number the phone number to look up + * @return the description corresponding to the prefix that best matches this phone number + */ + public String lookup(PhoneNumber number) { + long phonePrefix = + Long.parseLong(number.getCountryCode() + phoneUtil.getNationalSignificantNumber(number)); + return lookup(phonePrefix); + } + /** * Does a binary search for {@code value} in the provided array from {@code start} to {@code end} * (inclusive). Returns the position if {@code value} is found; otherwise, returns the diff --git a/java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMap.java b/java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMap.java new file mode 100644 index 000000000..8a2ce029c --- /dev/null +++ b/java/internal/prefixmapper/src/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMap.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 The Libphonenumber Authors + * + * 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 com.google.i18n.phonenumbers.prefixmapper; + +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.LinkedList; +import java.util.List; +import java.util.SortedMap; +import java.util.StringTokenizer; + +/** + * A utility that maps phone number prefixes to a list of strings describing the time zones to + * which each prefix belongs. + * + * @author Walter Erquinigo + */ +public class PrefixTimeZonesMap implements Externalizable { + private final PhonePrefixMap phonePrefixMap = new PhonePrefixMap(); + private static final String RAW_STRING_TIMEZONES_SEPARATOR = "&"; + + /** + * Creates a {@link PrefixTimeZoneMap} initialized with {@code sortedPrefixTimeZoneMap}. Note + * that the underlying implementation of this method is expensive thus should not be called by + * time-critical applications. + * + * @param sortedPrefixTimeZoneMap a map from phone number prefixes to their corresponding time + * zones, sorted in ascending order of the phone number prefixes as integers. + */ + public void readPrefixTimeZonesMap(SortedMap sortedPrefixTimeZoneMap) { + phonePrefixMap.readPhonePrefixMap(sortedPrefixTimeZoneMap); + } + + /** + * Supports Java Serialization. + */ + public void writeExternal(ObjectOutput objectOutput) throws IOException { + phonePrefixMap.writeExternal(objectOutput); + } + + public void readExternal(ObjectInput objectInput) throws IOException { + phonePrefixMap.readExternal(objectInput); + } + + /** + * Returns the list of time zones {@code key} corresponds to. + * + *

{@code key} could be the calling country code and the full significant number of a + * certain number, or it could be just a phone-number prefix. + * For example, the full number 16502530000 (from the phone-number +1 650 253 0000) is a valid + * input. Also, any of its prefixes, such as 16502, is also valid. + * + * @param key the key to look up + * @return the list of corresponding time zones + */ + private List lookupTimeZonesForNumber(long key) { + // Lookup in the map data. The returned String may consist of several time zones, so it must be + // split. + String timezonesString = phonePrefixMap.lookup(key); + if (timezonesString == null) { + return new LinkedList(); + } + return tokenizeRawOutputString(timezonesString); + } + + /** + * As per {@link #lookupTimeZonesForNumber(long)}, but receives the number as a PhoneNumber + * instead of a long. + * + * @param number the phone number to look up + * @return the list of corresponding time zones + */ + public List lookupTimeZonesForNumber(PhoneNumber number) { + long phonePrefix = Long.parseLong(number.getCountryCode() + + PhoneNumberUtil.getInstance().getNationalSignificantNumber(number)); + return lookupTimeZonesForNumber(phonePrefix); + } + + /** + * Returns the list of time zones {@code number}'s calling country code corresponds to. + * + * @param number the phone number to look up + * @return the list of corresponding time zones + */ + public List lookupCountryLevelTimeZonesForNumber(PhoneNumber number) { + return lookupTimeZonesForNumber(number.getCountryCode()); + } + + /** + * Split {@code timezonesString} into all the time zones that are part of it. + */ + private List tokenizeRawOutputString(String timezonesString) { + StringTokenizer tokenizer = new StringTokenizer(timezonesString, + RAW_STRING_TIMEZONES_SEPARATOR); + LinkedList timezonesList = new LinkedList(); + while (tokenizer.hasMoreTokens()) { + timezonesList.add(tokenizer.nextToken()); + } + return timezonesList; + } + + /** + * Dumps the mappings contained in the phone prefix map. + */ + @Override + public String toString() { + return phonePrefixMap.toString(); + } +} diff --git a/java/internal/prefixmapper/test/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMapTest.java b/java/internal/prefixmapper/test/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMapTest.java new file mode 100644 index 000000000..c6ac492ba --- /dev/null +++ b/java/internal/prefixmapper/test/com/google/i18n/phonenumbers/prefixmapper/PrefixTimeZonesMapTest.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2012 The Libphonenumber Authors + * + * 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 com.google.i18n.phonenumbers.prefixmapper; + +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Unittests for PrefixTimeZonesMap.java + * + * @author Walter Erquinigo + */ +public class PrefixTimeZonesMapTest extends TestCase { + private final PrefixTimeZonesMap prefixTimeZonesMapForUS = new PrefixTimeZonesMap(); + private final PrefixTimeZonesMap prefixTimeZonesMapForRU = new PrefixTimeZonesMap(); + + // US time zones + private static final String CHICAGO_TZ = "America/Chicago"; + private static final String DENVER_TZ = "America/Denver"; + private static final String LOS_ANGELES_TZ = "America/Los_Angeles"; + private static final String NEW_YORK_TZ = "America/New_York"; + + // Russian time zones + private static final String IRKUTSK_TZ = "Asia/Irkutsk"; + private static final String MOSCOW_TZ = "Europe/Moscow"; + private static final String VLADIVOSTOK_TZ = "Asia/Vladivostok"; + private static final String YEKATERINBURG_TZ = "Asia/Yekaterinburg"; + + public PrefixTimeZonesMapTest() { + SortedMap sortedMapForUS = new TreeMap(); + sortedMapForUS.put(1, NEW_YORK_TZ + "&" + CHICAGO_TZ + "&" + LOS_ANGELES_TZ + "&" + DENVER_TZ); + sortedMapForUS.put(1201, NEW_YORK_TZ); + sortedMapForUS.put(1205, CHICAGO_TZ); + sortedMapForUS.put(1208292, LOS_ANGELES_TZ); + sortedMapForUS.put(1208234, DENVER_TZ); + sortedMapForUS.put(1541367, LOS_ANGELES_TZ); + sortedMapForUS.put(1423843, NEW_YORK_TZ); + sortedMapForUS.put(1402721, CHICAGO_TZ); + sortedMapForUS.put(1208888, DENVER_TZ); + + prefixTimeZonesMapForUS.readPrefixTimeZonesMap(sortedMapForUS); + + SortedMap sortedMapForRU = new TreeMap(); + sortedMapForRU.put(7421, VLADIVOSTOK_TZ); + sortedMapForRU.put(7879, MOSCOW_TZ); + sortedMapForRU.put(7342, YEKATERINBURG_TZ); + sortedMapForRU.put(7395, IRKUTSK_TZ); + + prefixTimeZonesMapForRU.readPrefixTimeZonesMap(sortedMapForRU); + } + + static List buildListOfTimeZones(String ... timezones) { + ArrayList timezonesList = new ArrayList(timezones.length); + for (String timezone : timezones) { + timezonesList.add(timezone); + } + return timezonesList; + } + + private static SortedMap createMapCandidate() { + SortedMap sortedMap = new TreeMap(); + sortedMap.put(1212, NEW_YORK_TZ); + sortedMap.put(1213, NEW_YORK_TZ); + sortedMap.put(1214, NEW_YORK_TZ); + sortedMap.put(1480, CHICAGO_TZ); + return sortedMap; + } + + public void testLookupTimeZonesForNumberCountryLevel_US() { + PhoneNumber number = new PhoneNumber(); + number.setCountryCode(1).setNationalNumber(1000000000L); + assertEquals(buildListOfTimeZones(NEW_YORK_TZ, CHICAGO_TZ, LOS_ANGELES_TZ, DENVER_TZ), + prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); + } + + public void testLookupTimeZonesForNumber_ValidNumber_Chicago() { + PhoneNumber number = new PhoneNumber(); + number.setCountryCode(1).setNationalNumber(2051235458L); + assertEquals(buildListOfTimeZones(CHICAGO_TZ), + prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); + } + + public void testLookupTimeZonesForNumber_LA() { + PhoneNumber number = new PhoneNumber(); + number.setCountryCode(1).setNationalNumber(2082924565L); + assertEquals(buildListOfTimeZones(LOS_ANGELES_TZ), + prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); + } + + public void testLookupTimeZonesForNumber_NY() { + PhoneNumber number = new PhoneNumber(); + number.setCountryCode(1).setNationalNumber(2016641234L); + assertEquals(buildListOfTimeZones(NEW_YORK_TZ), + prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); + } + + public void testLookupTimeZonesForNumber_CH() { + PhoneNumber number = new PhoneNumber(); + number.setCountryCode(41).setNationalNumber(446681300L); + assertEquals(buildListOfTimeZones(), + prefixTimeZonesMapForUS.lookupTimeZonesForNumber(number)); + } + + public void testLookupTimeZonesForNumber_RU() { + PhoneNumber number = new PhoneNumber(); + number.setCountryCode(7).setNationalNumber(87945154L); + assertEquals(buildListOfTimeZones(MOSCOW_TZ), + prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); + + number.setNationalNumber(421548578L); + assertEquals(buildListOfTimeZones(VLADIVOSTOK_TZ), + prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); + + number.setNationalNumber(342457897L); + assertEquals(buildListOfTimeZones(YEKATERINBURG_TZ), + prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); + + // A mobile number + number.setNationalNumber(9342457897L); + assertEquals(buildListOfTimeZones(), + prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); + + // An invalid number (too short) + number.setNationalNumber(3951L); + assertEquals(buildListOfTimeZones(IRKUTSK_TZ), + prefixTimeZonesMapForRU.lookupTimeZonesForNumber(number)); + } + + /** + * Creates a new PrefixTimeZonesMap serializing the provided map to a stream and then reading + * this stream. The resulting PrefixTimeZonesMap is expected to be strictly equal to the provided + * one from which it was generated. + */ + private static PrefixTimeZonesMap createNewPrefixTimeZonesMap( + PrefixTimeZonesMap prefixTimeZonesMap) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + prefixTimeZonesMap.writeExternal(objectOutputStream); + objectOutputStream.flush(); + + PrefixTimeZonesMap newPrefixTimeZonesMap = new PrefixTimeZonesMap(); + newPrefixTimeZonesMap.readExternal( + new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()))); + return newPrefixTimeZonesMap; + } + + public void testReadWriteExternal() throws IOException { + PrefixTimeZonesMap localPrefixTimeZonesMap = new PrefixTimeZonesMap(); + localPrefixTimeZonesMap.readPrefixTimeZonesMap(createMapCandidate()); + + PrefixTimeZonesMap newPrefixTimeZonesMap = createNewPrefixTimeZonesMap(localPrefixTimeZonesMap); + assertEquals(localPrefixTimeZonesMap.toString(), newPrefixTimeZonesMap.toString()); + } +} diff --git a/java/release_notes.txt b/java/release_notes.txt index a387e972a..880b55d00 100644 --- a/java/release_notes.txt +++ b/java/release_notes.txt @@ -1,3 +1,11 @@ +Oct 2, 2013: +* Code changes: + - Added PhoneNumberToTimeZonesMapper including unittests to the geocoder maven project. + - Added build rules for generating the binary time zones mapping file from the text file. + - Modified PhoneNumberParserServlet.java (the appengine demo) to incorporate time zone mapping. +* Metadata changes: + - Added the time zone mapping file. + Sep 30, 2013: * Code changes: - Added PhoneNumberToCarrierMapper including unittests. diff --git a/resources/test/timezones/map_data.txt b/resources/test/timezones/map_data.txt new file mode 100644 index 000000000..3a438a6e4 --- /dev/null +++ b/resources/test/timezones/map_data.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2012 The Libphonenumber Authors + +# 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. + +1|America/New_York&America/Chicago&America/Winnipeg&America/Los_Angeles +1201|America/New_York +1212812|America/New_York +1234|America/New_York +1604|America/Winnipeg +1617423|America/Chicago +1650960|America/Los_Angeles +1989|Ameriac/Los_Angeles +612|Australia/Sydney +82|Asia/Seoul diff --git a/resources/timezones/map_data.txt b/resources/timezones/map_data.txt new file mode 100644 index 000000000..c0798c132 --- /dev/null +++ b/resources/timezones/map_data.txt @@ -0,0 +1,1996 @@ +# Copyright (C) 2012 The Libphonenumber Authors + +# 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. + +# Generated from: +# Internal statistics data (2013-06-12). + +# TODO: Solve these current issues: +# -Australian prefix 618 has almost as many establishments from +# Australia/Adelaide as from Australia/Perth. This is due to the short +# length of the phone prefix. Thus, a custom config should be used to +# get a longer prefix. +# -Add prefixes for America/Noronha and Pacific/Easter. + +# List of deleted prefixes from the original data: +# -Prefixes 9761, 9762, since they just indicate the carrier. +# -Prefix 9765, since it is just a wireless local loop prefix. +# -Prefix 5905. It is the only prefix for 590 country code, which has three +# timezones (America/Guadeloupe, America/Halifax and America/Marigot), +# whereas 5905 has only one (America/Guadeloupe). +# TODO: Investigate where the time zones America/Halifax and America/Marigot +# should be used. + +1|America/Anguilla&America/Antigua&America/Barbados&America/Cayman&America/Chicago&America/Denver&America/Dominica&America/Edmonton&America/Grand_Turk&America/Grenada&America/Halifax&America/Jamaica&America/Juneau&America/Los_Angeles&America/Lower_Princes&America/Montserrat&America/Nassau&America/New_York&America/Port_of_Spain&America/Puerto_Rico&America/St_Johns&America/St_Kitts&America/St_Lucia&America/St_Thomas&America/St_Vincent&America/Toronto&America/Tortola&America/Vancouver&America/Winnipeg&Atlantic/Bermuda&Pacific/Guam&Pacific/Honolulu&Pacific/Pago_Pago&Pacific/Saipan +1201|America/New_York +1202|America/New_York +1203|America/New_York +1204|America/Winnipeg +1205|America/Chicago +1206|America/Los_Angeles +1207|America/New_York +120822|America/Denver +120823|America/Denver +120824|America/Los_Angeles +1208253|America/Denver +1208255|America/Los_Angeles +120826|America/Los_Angeles +120827|America/Denver +120828|America/Denver +1208292|America/Los_Angeles +1208297|America/Denver +12083|America/Denver +120841|America/Denver +120842|America/Denver +1208433|America/Denver +1208436|America/Denver +1208437|America/Los_Angeles +1208438|America/Denver +1208442|America/Denver +1208448|America/Los_Angeles +1208452|America/Denver +1208453|America/Denver +1208454|America/Denver +1208455|America/Denver +1208457|America/Los_Angeles +1208459|America/Denver +120846|America/Denver +1208475|America/Denver +1208476|America/Los_Angeles +1208478|America/Denver +120848|America/Denver +120849|America/Denver +12085|America/Denver +120861|America/Los_Angeles +1208622|America/Denver +1208623|America/Los_Angeles +1208624|America/Denver +1208628|America/Los_Angeles +1208629|America/Denver +120863|America/Denver +120864|America/Denver +120865|America/Denver +120866|America/Los_Angeles +1208672|America/Denver +1208676|America/Los_Angeles +1208677|America/Denver +1208678|America/Denver +1208682|America/Los_Angeles +1208683|America/Los_Angeles +1208684|America/Denver +1208686|America/Los_Angeles +1208687|America/Los_Angeles +120870|America/Denver +120871|America/Denver +120872|America/Denver +120873|America/Denver +1208743|America/Los_Angeles +1208745|America/Denver +1208746|America/Los_Angeles +1208755|America/Los_Angeles +1208756|America/Denver +1208762|America/Los_Angeles +1208765|America/Los_Angeles +1208766|America/Denver +1208769|America/Los_Angeles +120877|America/Los_Angeles +1208782|America/Denver +1208783|America/Los_Angeles +1208785|America/Denver +1208787|America/Denver +1208788|America/Denver +1208794|America/Denver +1208798|America/Los_Angeles +1208799|America/Los_Angeles +1208835|America/Los_Angeles +1208837|America/Denver +120884|America/Denver +120885|America/Denver +120886|America/Denver +1208870|America/Denver +1208875|America/Los_Angeles +1208878|America/Denver +1208879|America/Denver +1208880|America/Denver +1208882|America/Los_Angeles +1208883|America/Los_Angeles +1208884|America/Denver +1208885|America/Los_Angeles +1208886|America/Denver +1208887|America/Denver +1208888|America/Denver +120889|America/Denver +1208922|America/Denver +1208926|America/Los_Angeles +1208934|America/Denver +1208935|America/Los_Angeles +1208938|America/Denver +1208939|America/Denver +120894|America/Denver +120896|America/Los_Angeles +120898|America/Los_Angeles +120899|America/Denver +1209|America/Los_Angeles +1210|America/Chicago +1212|America/New_York +1213|America/Los_Angeles +1214|America/Chicago +1215|America/New_York +1216|America/New_York +1217|America/Chicago +1218|America/Chicago +121922|America/Chicago +121925|America/New_York +121926|America/Chicago +121927|America/New_York +121928|America/Chicago +12193|America/Chicago +12194|America/Chicago +12195|America/Chicago +12196|America/Chicago +12197|America/Chicago +12198|America/Chicago +12199|America/Chicago +1224|America/Chicago +1225|America/Chicago +1226|America/Toronto +1228|America/Chicago +1229|America/New_York +123|America/New_York +1240|America/New_York +1242|America/Nassau +1246|America/Barbados +1248|America/New_York +125021|America/Vancouver +1250242|America/Edmonton +1250245|America/Vancouver +1250246|America/Vancouver +1250247|America/Vancouver +1250248|America/Vancouver +125025|America/Vancouver +1250260|America/Vancouver +1250262|America/Edmonton +1250265|America/Vancouver +125028|America/Vancouver +125029|America/Vancouver +125031|America/Vancouver +125033|America/Vancouver +125034|America/Edmonton +125035|America/Vancouver +125036|America/Vancouver +125037|America/Vancouver +125038|America/Vancouver +125039|America/Vancouver +1250412|America/Vancouver +1250417|America/Edmonton +125042|America/Edmonton +125044|America/Vancouver +125046|America/Vancouver +125047|America/Vancouver +1250480|America/Vancouver +1250483|America/Vancouver +1250487|America/Vancouver +1250489|America/Edmonton +125049|America/Vancouver +12505|America/Vancouver +12506|America/Vancouver +125070|America/Vancouver +125071|America/Vancouver +125072|America/Vancouver +125074|America/Vancouver +125075|America/Vancouver +125076|America/Vancouver +125077|America/Vancouver +125078|America/Edmonton +12508|America/Vancouver +12509|America/Vancouver +1251|America/Chicago +1252|America/New_York +1253|America/Los_Angeles +1254|America/Chicago +1256|America/Chicago +1260|America/New_York +1262|America/Chicago +1264|America/Anguilla +1267|America/New_York +1268|America/Antigua +1269|America/New_York +1270230|America/Chicago +1270234|America/New_York +1270236|America/Chicago +1270237|America/Chicago +127024|America/Chicago +127025|America/Chicago +127026|America/Chicago +127027|America/Chicago +127029|America/Chicago +127033|America/Chicago +127034|America/Chicago +127035|America/New_York +1270360|America/New_York +1270362|America/Chicago +1270365|America/Chicago +1270369|America/New_York +127038|America/Chicago +127039|America/Chicago +127041|America/Chicago +127042|America/New_York +127043|America/Chicago +127044|America/Chicago +127046|America/New_York +127047|America/Chicago +127048|America/Chicago +12705|America/Chicago +127062|America/Chicago +127063|America/Chicago +127064|America/Chicago +127065|America/Chicago +127066|America/Chicago +127067|America/Chicago +127068|America/Chicago +1270691|America/Chicago +1270692|America/New_York +1270699|America/New_York +127070|America/Chicago +127072|America/Chicago +127073|America/New_York +127074|America/Chicago +127075|America/Chicago +1270761|America/Chicago +1270762|America/Chicago +1270763|America/New_York +1270765|America/New_York +1270766|America/New_York +1270769|America/New_York +127077|America/Chicago +1270780|America/Chicago +1270781|America/Chicago +1270782|America/Chicago +1270783|America/Chicago +1270786|America/Chicago +1270789|America/New_York +127079|America/Chicago +1270821|America/Chicago +1270824|America/Chicago +1270825|America/Chicago +1270826|America/Chicago +1270827|America/Chicago +1270828|America/New_York +127083|America/Chicago +127084|America/Chicago +127085|America/Chicago +1270862|America/New_York +1270864|America/Chicago +1270866|America/Chicago +1270877|America/New_York +1270879|America/Chicago +127088|America/Chicago +127089|America/Chicago +127090|America/Chicago +127092|America/Chicago +127093|America/Chicago +127096|America/Chicago +1270982|America/New_York +1270988|America/Chicago +1276|America/New_York +1281|America/Chicago +1284|America/Tortola +1289|America/Toronto +1301|America/New_York +1302|America/New_York +1303|America/Denver +1304|America/New_York +1305|America/New_York +13062|America/Winnipeg +13063|America/Winnipeg +13064|America/Winnipeg +13065|America/Winnipeg +13066|America/Winnipeg +13067|America/Winnipeg +130682|America/Edmonton +130683|America/Winnipeg +130684|America/Winnipeg +130686|America/Winnipeg +130687|America/Winnipeg +130688|America/Winnipeg +130689|America/Edmonton +13069|America/Winnipeg +1307|America/Denver +1308233|America/Chicago +1308234|America/Chicago +1308235|America/Denver +1308236|America/Chicago +1308237|America/Chicago +130825|America/Denver +130826|America/Denver +130828|America/Denver +1308324|America/Chicago +1308327|America/Denver +130834|America/Chicago +130835|America/Denver +130836|America/Chicago +130838|America/Chicago +130839|America/Chicago +1308423|America/Denver +1308425|America/Chicago +130843|America/Denver +130845|America/Chicago +130846|America/Chicago +13085|America/Chicago +130862|America/Denver +130863|America/Denver +130866|America/Denver +130869|America/Chicago +130872|America/Chicago +130874|America/Chicago +130875|America/Chicago +130876|America/Denver +130877|America/Denver +130878|America/Chicago +130883|America/Chicago +130886|America/Chicago +1308872|America/Chicago +1308874|America/Denver +130888|America/Denver +13089|America/Chicago +1309|America/Chicago +1310|America/Los_Angeles +1312|America/Chicago +1313|America/New_York +1314|America/Chicago +1315|America/New_York +1316|America/Chicago +1317|America/New_York +1318|America/Chicago +1319|America/Chicago +1320|America/Chicago +1321|America/New_York +1323|America/Los_Angeles +1325|America/Chicago +1330|America/New_York +1334|America/Chicago +1336|America/New_York +1337|America/Chicago +1340|America/St_Thomas +1345|America/Cayman +1347|America/New_York +135|America/New_York +1360|America/Los_Angeles +1361|America/Chicago +1385|America/Denver +1386|America/New_York +1401|America/New_York +14022|America/Chicago +140232|America/Chicago +140233|America/Chicago +140234|America/Chicago +140235|America/Chicago +140236|America/Chicago +1402370|America/Chicago +1402371|America/Chicago +1402372|America/Chicago +1402373|America/Chicago +1402374|America/Chicago +1402375|America/Chicago +1402376|America/Denver +1402379|America/Chicago +140238|America/Chicago +140239|America/Chicago +14024|America/Chicago +14025|America/Chicago +14026|America/Chicago +14027|America/Chicago +14028|America/Chicago +14029|America/Chicago +1403|America/Edmonton +1404|America/New_York +1405|America/Chicago +1406|America/Denver +1407|America/New_York +1408|America/Los_Angeles +1409|America/Chicago +1410|America/New_York +1412|America/New_York +1413|America/New_York +1414|America/Chicago +1415|America/Los_Angeles +1416|America/Toronto +1417|America/Chicago +1418|America/Toronto +1419|America/New_York +14232|America/New_York +14233|America/New_York +142342|America/New_York +142343|America/New_York +1423442|America/New_York +1423447|America/Chicago +142346|America/New_York +142347|America/New_York +142348|America/New_York +142349|America/New_York +14235|America/New_York +142361|America/New_York +142362|America/New_York +142363|America/New_York +142364|America/New_York +1423652|America/New_York +1423658|America/Chicago +142366|America/New_York +142369|America/New_York +14237|America/New_York +142382|America/New_York +142383|America/Chicago +142384|America/New_York +142385|America/New_York +142386|America/New_York +142387|America/New_York +142388|America/New_York +142389|America/New_York +142391|America/New_York +142392|America/New_York +142394|America/Chicago +142395|America/New_York +142396|America/New_York +142397|America/New_York +142398|America/New_York +1425|America/Los_Angeles +1432|America/Chicago +1434|America/New_York +1435|America/Denver +1438|America/Toronto +1440|America/New_York +1441|Atlantic/Bermuda +1443|America/New_York +145|America/Toronto +146|America/Chicago +1473|America/Grenada +1478|America/New_York +1479|America/Chicago +1480|America/Denver +1484|America/New_York +1501|America/Chicago +1502|America/New_York +1503|America/Los_Angeles +1504|America/Chicago +1505|America/Denver +1506|America/Halifax +1507|America/Chicago +1508|America/New_York +1509|America/Los_Angeles +1510|America/Los_Angeles +1512|America/Chicago +1513|America/New_York +1514|America/Toronto +1515|America/Chicago +1516|America/New_York +1517|America/New_York +1518|America/New_York +1519|America/Toronto +152|America/Denver +153|America/Los_Angeles +1540|America/New_York +15412|America/Los_Angeles +154130|America/Los_Angeles +154131|America/Los_Angeles +154132|America/Los_Angeles +154133|America/Los_Angeles +154134|America/Los_Angeles +154135|America/Los_Angeles +154136|America/Los_Angeles +154137|America/Denver +154138|America/Los_Angeles +154139|America/Los_Angeles +154140|America/Los_Angeles +154141|America/Los_Angeles +154142|America/Los_Angeles +154143|America/Los_Angeles +154144|America/Los_Angeles +154145|America/Los_Angeles +154146|America/Los_Angeles +1541471|America/Los_Angeles +1541472|America/Los_Angeles +1541473|America/Denver +1541474|America/Los_Angeles +1541475|America/Los_Angeles +1541476|America/Los_Angeles +1541479|America/Los_Angeles +154148|America/Los_Angeles +154149|America/Los_Angeles +15415|America/Los_Angeles +15416|America/Los_Angeles +15417|America/Los_Angeles +154181|America/Los_Angeles +154182|America/Los_Angeles +154183|America/Los_Angeles +154184|America/Los_Angeles +154185|America/Los_Angeles +154186|America/Los_Angeles +154187|America/Los_Angeles +1541881|America/Denver +1541882|America/Los_Angeles +1541883|America/Los_Angeles +1541884|America/Los_Angeles +1541885|America/Los_Angeles +1541888|America/Los_Angeles +1541889|America/Denver +154189|America/Los_Angeles +15419|America/Los_Angeles +155|America/Los_Angeles +1561|America/New_York +1562|America/Los_Angeles +1563|America/Chicago +1570|America/New_York +1571|America/New_York +1573|America/Chicago +15742|America/New_York +15743|America/New_York +15744|America/New_York +15745|America/New_York +15746|America/New_York +157472|America/New_York +157475|America/New_York +1574772|America/Chicago +1574773|America/New_York +157478|America/New_York +157482|America/New_York +157483|America/New_York +157484|America/New_York +157485|America/New_York +157486|America/New_York +157487|America/New_York +1574892|America/New_York +1574893|America/New_York +1574896|America/Chicago +15749|America/New_York +1575|America/Denver +1580|America/Chicago +1585|America/New_York +1586|America/New_York +1601|America/Chicago +1602|America/Denver +1603|America/New_York +1604|America/Vancouver +160521|America/Chicago +1605223|America/Denver +1605224|America/Chicago +1605225|America/Chicago +1605226|America/Chicago +1605229|America/Chicago +160523|America/Chicago +160525|America/Chicago +160526|America/Chicago +1605271|America/Chicago +1605274|America/Chicago +1605275|America/Chicago +1605279|America/Denver +160529|America/Chicago +160532|America/Chicago +160533|America/Chicago +1605341|America/Denver +1605342|America/Denver +1605343|America/Denver +1605345|America/Chicago +1605347|America/Denver +1605348|America/Denver +1605352|America/Chicago +1605353|America/Chicago +1605355|America/Denver +1605356|America/Chicago +1605357|America/Chicago +160536|America/Chicago +1605371|America/Chicago +1605373|America/Chicago +1605374|America/Denver +1605384|America/Chicago +1605387|America/Chicago +1605388|America/Denver +1605393|America/Denver +1605394|America/Denver +1605397|America/Chicago +1605399|America/Denver +160542|America/Chicago +160543|America/Chicago +160544|America/Chicago +160545|America/Denver +160547|America/Chicago +160548|America/Chicago +160549|America/Chicago +160552|America/Chicago +160553|America/Chicago +160554|America/Chicago +160557|America/Denver +1605582|America/Chicago +1605584|America/Denver +1605589|America/Chicago +160559|America/Chicago +160562|America/Chicago +1605642|America/Denver +1605644|America/Denver +1605647|America/Chicago +1605649|America/Chicago +160566|America/Chicago +160567|America/Denver +160568|America/Denver +160569|America/Chicago +160571|America/Denver +1605720|America/Denver +1605721|America/Denver +1605722|America/Denver +1605723|America/Denver +1605724|America/Chicago +1605725|America/Chicago +160573|America/Chicago +1605745|America/Denver +1605747|America/Chicago +160575|America/Chicago +160576|America/Chicago +160577|America/Chicago +160578|America/Denver +1605791|America/Denver +1605796|America/Chicago +160582|America/Denver +1605835|America/Chicago +1605837|America/Denver +160584|America/Chicago +1605852|America/Chicago +1605853|America/Chicago +1605854|America/Chicago +1605856|America/Chicago +1605859|America/Denver +160586|America/Denver +160587|America/Chicago +160588|America/Chicago +160589|America/Denver +1605923|America/Denver +1605925|America/Chicago +1605928|America/Chicago +160594|America/Chicago +160596|America/Denver +160597|America/Chicago +160598|America/Chicago +160599|America/Chicago +16062|America/New_York +160632|America/New_York +160633|America/New_York +160634|America/New_York +160635|America/New_York +160636|America/New_York +160637|America/New_York +160638|America/Chicago +16064|America/New_York +16065|America/New_York +16066|America/New_York +16067|America/New_York +16068|America/New_York +16069|America/New_York +1607|America/New_York +1608|America/Chicago +1609|America/New_York +1610|America/New_York +1612|America/Chicago +1613|America/Toronto +1614|America/New_York +1615|America/Chicago +1616|America/New_York +1617|America/New_York +1618|America/Chicago +1619|America/Los_Angeles +16202|America/Chicago +162032|America/Chicago +162033|America/Chicago +162034|America/Chicago +162035|America/Chicago +162036|America/Chicago +1620375|America/Chicago +1620376|America/Denver +1620378|America/Chicago +1620382|America/Chicago +1620384|America/Denver +162039|America/Chicago +16204|America/Chicago +16205|America/Chicago +16206|America/Chicago +16207|America/Chicago +16208|America/Chicago +16209|America/Chicago +1623|America/Denver +1626|America/Los_Angeles +1630|America/Chicago +1631|America/New_York +1636|America/Chicago +1641|America/Chicago +1646|America/New_York +1647|America/Toronto +1649|America/Grand_Turk +1650|America/Los_Angeles +1651|America/Chicago +1660|America/Chicago +1661|America/Los_Angeles +1662|America/Chicago +1664|America/Montserrat +1670|Pacific/Saipan +1671|Pacific/Guam +1678|America/New_York +1682|America/Chicago +1684|Pacific/Pago_Pago +1701221|America/Chicago +1701222|America/Chicago +1701223|America/Chicago +1701224|America/Chicago +1701225|America/Denver +1701227|America/Denver +1701228|America/Chicago +170123|America/Chicago +170124|America/Chicago +170125|America/Chicago +170126|America/Chicago +170127|America/Chicago +170128|America/Chicago +170129|America/Chicago +17013|America/Chicago +170140|America/Chicago +170143|America/Chicago +170144|America/Chicago +1701452|America/Chicago +1701454|America/Chicago +1701456|America/Denver +170146|America/Chicago +170147|America/Chicago +170148|America/Denver +170149|America/Chicago +170152|America/Denver +170153|America/Chicago +170154|America/Chicago +170156|America/Denver +1701572|America/Chicago +1701575|America/Denver +1701577|America/Chicago +170158|America/Denver +17016|America/Chicago +170172|America/Chicago +170173|America/Chicago +1701742|America/Chicago +1701746|America/Chicago +1701748|America/Denver +170175|America/Chicago +1701764|America/Denver +1701766|America/Chicago +170177|America/Chicago +170178|America/Chicago +170179|America/Chicago +170183|America/Chicago +1701842|America/Denver +1701843|America/Chicago +1701845|America/Chicago +1701852|America/Chicago +1701854|America/Denver +1701857|America/Chicago +1701858|America/Chicago +170187|America/Denver +170188|America/Chicago +17019|America/Chicago +1702|America/Los_Angeles +1703|America/New_York +1704|America/New_York +1705|America/Toronto +1706|America/New_York +1707|America/Los_Angeles +1708|America/Chicago +170922|America/St_Johns +170923|America/St_Johns +170925|America/St_Johns +170927|America/St_Johns +170928|America/Halifax +17093|America/St_Johns +17094|America/St_Johns +17095|America/St_Johns +17096|America/St_Johns +17097|America/St_Johns +170983|America/St_Johns +1709895|America/St_Johns +1709896|America/Halifax +17099|America/Halifax +1712|America/Chicago +1713|America/Chicago +1714|America/Los_Angeles +1715|America/Chicago +1716|America/New_York +1717|America/New_York +1718|America/New_York +1719|America/Denver +1720|America/Denver +1721|America/Lower_Princes +1724|America/New_York +1727|America/New_York +1731|America/Chicago +1732|America/New_York +1734|America/New_York +174|America/New_York +1754|America/New_York +1757|America/New_York +1758|America/St_Lucia +1760|America/Los_Angeles +1763|America/Chicago +1765|America/New_York +1767|America/Dominica +1769|America/Chicago +1770|America/New_York +1772|America/New_York +1773|America/Chicago +1774|America/New_York +1775|America/Los_Angeles +1778|America/Vancouver +1779|America/Chicago +1780|America/Edmonton +1781|America/New_York +1784|America/St_Vincent +17852|America/Chicago +17853|America/Chicago +17854|America/Chicago +17855|America/Chicago +17856|America/Chicago +17857|America/Chicago +178582|America/Chicago +178583|America/Chicago +178584|America/Chicago +1785852|America/Denver +1785856|America/Chicago +178586|America/Chicago +178587|America/Chicago +178588|America/Chicago +178589|America/Denver +17859|America/Chicago +1786|America/New_York +1787|America/Puerto_Rico +1801|America/Denver +1802|America/New_York +1803|America/New_York +1804|America/New_York +1805|America/Los_Angeles +1806|America/Chicago +1807223|America/Winnipeg +1807229|America/Toronto +180727|America/Winnipeg +180728|America/Toronto +18073|America/Toronto +180746|America/Winnipeg +180747|America/Toronto +180748|America/Winnipeg +180754|America/Winnipeg +180757|America/Toronto +180759|America/Toronto +18076|America/Toronto +180772|America/Winnipeg +180773|America/Winnipeg +180776|America/Toronto +18078|America/Toronto +1807934|America/Winnipeg +1807937|America/Winnipeg +1807939|America/Toronto +1808|Pacific/Honolulu +1809|America/Halifax +1810|America/New_York +18122|America/New_York +181231|America/New_York +181232|America/New_York +181233|America/New_York +181234|America/New_York +181235|America/New_York +181236|America/New_York +181237|America/New_York +1812384|America/New_York +1812385|America/Chicago +1812386|America/Chicago +181240|America/Chicago +1812421|America/Chicago +1812422|America/Chicago +1812423|America/Chicago +1812424|America/Chicago +1812425|America/Chicago +1812426|America/Chicago +1812427|America/New_York +1812428|America/Chicago +1812432|America/New_York +1812435|America/Chicago +1812437|America/Chicago +1812438|America/New_York +181244|America/New_York +181245|America/Chicago +1812462|America/New_York +1812464|America/Chicago +1812466|America/New_York +1812471|America/Chicago +1812473|America/Chicago +1812474|America/Chicago +1812475|America/Chicago +1812476|America/Chicago +1812477|America/Chicago +1812478|America/New_York +1812479|America/Chicago +1812481|America/New_York +1812482|America/New_York +1812485|America/Chicago +1812486|America/New_York +181249|America/Chicago +181252|America/New_York +181253|America/New_York +1812546|America/New_York +1812547|America/Chicago +181259|America/New_York +181262|America/New_York +181263|America/New_York +181264|America/Chicago +181265|America/New_York +181266|America/New_York +1812682|America/Chicago +1812683|America/New_York +1812689|America/New_York +181272|America/New_York +181273|America/New_York +181274|America/Chicago +1812752|America/New_York +1812753|America/Chicago +181276|America/Chicago +181279|America/New_York +181282|America/New_York +181283|America/Chicago +1812842|America/Chicago +1812847|America/New_York +1812849|America/New_York +1812853|America/Chicago +1812855|America/New_York +1812858|America/Chicago +1812865|America/New_York +1812866|America/New_York +1812867|America/Chicago +1812874|America/Chicago +1812875|America/New_York +1812876|America/New_York +1812877|America/New_York +181288|America/New_York +181289|America/Chicago +181291|America/New_York +1812923|America/New_York +1812925|America/Chicago +1812926|America/New_York +1812932|America/New_York +1812933|America/New_York +1812934|America/New_York +1812936|America/New_York +1812937|America/Chicago +1812939|America/New_York +181294|America/New_York +181295|America/New_York +1812963|America/Chicago +1812967|America/New_York +1812985|America/Chicago +1812988|America/New_York +1813|America/New_York +1814|America/New_York +1815|America/Chicago +1816|America/Chicago +1817|America/Chicago +1818|America/Los_Angeles +1819|America/Toronto +1828|America/New_York +1829|America/Halifax +1830|America/Chicago +1831|America/Los_Angeles +1832|America/Chicago +1843|America/New_York +1845|America/New_York +1847|America/Chicago +1849|America/Halifax +1850210|America/New_York +1850215|America/Chicago +1850216|America/New_York +1850219|America/New_York +1850222|America/New_York +1850223|America/New_York +1850224|America/New_York +1850226|America/Chicago +1850227|America/Chicago +1850229|America/Chicago +185023|America/Chicago +185024|America/Chicago +1850251|America/New_York +1850256|America/Chicago +1850258|America/Chicago +1850259|America/Chicago +185026|America/Chicago +185027|America/Chicago +185028|America/Chicago +185029|America/New_York +1850301|America/Chicago +1850309|America/New_York +185031|America/Chicago +1850325|America/New_York +1850327|America/Chicago +1850329|America/New_York +185033|America/Chicago +185036|America/Chicago +185038|America/New_York +1850391|America/New_York +1850398|America/Chicago +185040|America/New_York +185041|America/Chicago +1850421|America/New_York +1850422|America/New_York +1850423|America/Chicago +1850424|America/Chicago +1850425|America/New_York +1850429|America/Chicago +1850431|America/New_York +1850432|America/Chicago +1850433|America/Chicago +1850434|America/Chicago +1850435|America/Chicago +1850436|America/Chicago +1850437|America/Chicago +1850438|America/Chicago +1850439|America/Chicago +185044|America/Chicago +185045|America/Chicago +185046|America/Chicago +185047|America/Chicago +1850481|America/Chicago +1850482|America/Chicago +1850484|America/Chicago +1850488|America/New_York +185049|America/Chicago +1850505|America/Chicago +1850508|America/New_York +185051|America/New_York +1850522|America/Chicago +1850523|America/New_York +1850526|America/Chicago +1850527|America/Chicago +1850535|America/Chicago +1850537|America/Chicago +1850539|America/New_York +1850545|America/New_York +1850547|America/Chicago +185056|America/New_York +185057|America/New_York +1850580|America/New_York +1850581|America/Chicago +1850584|America/New_York +1850585|America/Chicago +1850587|America/Chicago +1850588|America/Chicago +1850592|America/Chicago +1850593|America/Chicago +1850595|America/Chicago +1850597|America/New_York +185060|America/Chicago +1850622|America/Chicago +1850623|America/Chicago +1850626|America/Chicago +1850627|America/New_York +185063|America/Chicago +1850640|America/Chicago +1850643|America/New_York +1850644|America/New_York +1850650|America/Chicago +1850651|America/Chicago +1850653|America/New_York +1850654|America/Chicago +1850656|America/New_York +1850663|America/New_York +1850664|America/Chicago +1850668|America/New_York +1850670|America/New_York +1850671|America/New_York +1850674|America/Chicago +1850675|America/Chicago +1850678|America/Chicago +1850681|America/New_York +1850682|America/Chicago +1850683|America/Chicago +1850685|America/Chicago +1850689|America/Chicago +1850696|America/Chicago +1850697|America/New_York +1850722|America/Chicago +1850727|America/New_York +1850729|America/Chicago +185074|America/Chicago +185076|America/Chicago +185078|America/Chicago +185079|America/Chicago +1850833|America/Chicago +1850835|America/Chicago +1850837|America/Chicago +1850838|America/New_York +185085|America/Chicago +185086|America/Chicago +1850871|America/Chicago +1850872|America/Chicago +1850874|America/Chicago +1850875|America/New_York +1850877|America/New_York +1850878|America/New_York +185088|America/Chicago +1850891|America/New_York +1850892|America/Chicago +1850893|America/New_York +1850894|America/New_York +1850897|America/Chicago +185091|America/Chicago +185092|America/New_York +185093|America/Chicago +1850941|America/Chicago +1850942|America/New_York +1850944|America/Chicago +185095|America/Chicago +185096|America/Chicago +185097|America/New_York +185098|America/Chicago +1850994|America/Chicago +1850995|America/Chicago +1850997|America/New_York +1856|America/New_York +1857|America/New_York +1858|America/Los_Angeles +1859|America/New_York +1860|America/New_York +1862|America/New_York +1863|America/New_York +1864|America/New_York +1865|America/New_York +18673|America/Vancouver +18674|America/Vancouver +18675|America/Vancouver +186763|America/Vancouver +186764|America/Winnipeg +1867667|America/Vancouver +1867668|America/Vancouver +1867669|America/Edmonton +186769|America/Edmonton +18677|America/Edmonton +18678|America/Edmonton +186792|America/Edmonton +186797|America/Toronto +186799|America/Vancouver +1868|America/Port_of_Spain +1869|America/St_Kitts +1870|America/Chicago +1876|America/Jamaica +1901|America/Chicago +1902|America/Halifax +1903|America/Chicago +1904|America/New_York +1905|America/Toronto +190622|America/New_York +190623|America/New_York +190624|America/New_York +190625|America/New_York +190626|America/Chicago +190629|America/New_York +19063|America/New_York +19064|America/New_York +190652|America/New_York +190656|America/Chicago +190658|America/New_York +19066|America/New_York +190675|America/Chicago +190677|America/Chicago +190678|America/New_York +190684|America/New_York +190686|America/Chicago +190687|America/Chicago +190688|America/New_York +19069|America/Chicago +1907|America/Juneau +1908|America/New_York +1909|America/Los_Angeles +1910|America/New_York +1912|America/New_York +1913|America/Chicago +1914|America/New_York +1915|America/Denver +1916|America/Los_Angeles +1917|America/New_York +1918|America/Chicago +1919|America/New_York +1920|America/Chicago +1925|America/Los_Angeles +1928|America/Denver +1931|America/Chicago +1936|America/Chicago +1937|America/New_York +1939|America/Puerto_Rico +1940|America/Chicago +1941|America/New_York +1949|America/Los_Angeles +1951|America/Los_Angeles +1952|America/Chicago +1954|America/New_York +1956|America/Chicago +1970|America/Denver +1971|America/Los_Angeles +1972|America/Chicago +1973|America/New_York +1978|America/New_York +1979|America/Chicago +1980|America/New_York +1985|America/Chicago +1989|America/New_York +20|Africa/Cairo +211|Africa/Nairobi +212|Atlantic/Canary +213|Europe/Paris +216|Africa/Tunis +218|Europe/Paris +220|Africa/Banjul +221|Africa/Dakar +222|Africa/Nouakchott +223|Africa/Bamako +224|Africa/Conakry +225|Africa/Abidjan +226|Africa/Ouagadougou +227|Africa/Niamey +228|Africa/Lome +229|Africa/Porto-Novo +230|Indian/Mauritius +231|Atlantic/Reykjavik +232|Africa/Freetown +233|Africa/Accra +234|Africa/Lagos +235|Africa/Ndjamena +236|Africa/Bangui +237|Africa/Douala +238|Atlantic/Cape_Verde +239|Africa/Sao_Tome +240|Africa/Malabo +241|Africa/Libreville +242|Africa/Brazzaville +243|Africa/Kinshasa&Africa/Lubumbashi +2431|Africa/Kinshasa +2435|Africa/Kinshasa +244|Africa/Luanda +245|Atlantic/Reykjavik +246|Indian/Chagos +247|Atlantic/St_Helena +248|Indian/Mahe +249|Africa/Nairobi +250|Africa/Kigali +251|Africa/Addis_Ababa +252|Africa/Mogadishu +253|Africa/Djibouti +254|Africa/Nairobi +255|Africa/Dar_es_Salaam +256|Africa/Kampala +257|Africa/Bujumbura +258|Africa/Maputo +260|Africa/Lusaka +261|Indian/Antananarivo +262|Indian/Mayotte&Indian/Reunion +262262|Indian/Reunion +262269|Indian/Mayotte +263|Africa/Harare +264|Africa/Lagos +265|Africa/Blantyre +266|Africa/Maseru +267|Africa/Gaborone +268|Africa/Mbabane +269|Indian/Comoro +27|Africa/Johannesburg +290|Atlantic/St_Helena +291|Africa/Asmera +297|America/Aruba +298|Atlantic/Faeroe +299|America/Godthab&America/Scoresbysund&America/Thule&Atlantic/Reykjavik +2993|America/Godthab +2998|America/Godthab +2999|America/Godthab +30|Europe/Athens +31|Europe/Amsterdam +32|Europe/Brussels +33|Europe/Paris +34|Atlantic/Canary&Europe/Madrid +3481|Europe/Madrid +34821|Europe/Madrid +34822|Atlantic/Canary +34823|Europe/Madrid +34824|Europe/Madrid +34825|Europe/Madrid +34826|Europe/Madrid +34827|Europe/Madrid +34828|Atlantic/Canary +3483|Europe/Madrid +3484|Europe/Madrid +3485|Europe/Madrid +34860|Europe/Madrid +34865|Europe/Madrid +34868|Europe/Madrid +34869|Atlantic/Canary&Europe/Madrid +3487|Europe/Madrid +3488|Europe/Madrid +3491|Europe/Madrid +34920|Europe/Madrid +34921|Europe/Madrid +34922|Atlantic/Canary +34923|Europe/Madrid +34924|Europe/Madrid +34925|Europe/Madrid +34926|Europe/Madrid +34927|Europe/Madrid +34928|Atlantic/Canary +3493|Europe/Madrid +3494|Europe/Madrid +3495|Europe/Madrid +3496|Europe/Madrid +3497|Europe/Madrid +3498|Europe/Madrid +350|Europe/Gibraltar +351|Atlantic/Azores&Atlantic/Canary +35121|Atlantic/Canary +35122|Atlantic/Canary +35123|Atlantic/Canary +35124|Atlantic/Canary +35125|Atlantic/Canary +35126|Atlantic/Canary +35127|Atlantic/Canary +35128|Atlantic/Canary +351291|Atlantic/Canary +351292|Atlantic/Azores +351295|Atlantic/Azores +351296|Atlantic/Azores +352|Europe/Luxembourg +353|Europe/Dublin +354|Atlantic/Reykjavik +355|Europe/Tirane +356|Europe/Malta +357|Asia/Nicosia +358|Europe/Helsinki&Europe/Mariehamn +35813|Europe/Helsinki +35814|Europe/Helsinki +35815|Europe/Helsinki +35816|Europe/Helsinki +35817|Europe/Helsinki +35818|Europe/Mariehamn +35819|Europe/Helsinki +3582|Europe/Helsinki +3583|Europe/Helsinki +3585|Europe/Helsinki +3586|Europe/Helsinki +3588|Europe/Helsinki +3589|Europe/Helsinki +359|Europe/Sofia +36|Europe/Budapest +370|Europe/Bucharest +371|Europe/Bucharest +372|Europe/Bucharest +373|Europe/Bucharest +374|Asia/Yerevan +375|Europe/Belarus +376|Europe/Andorra +377|Europe/Monaco +378|Europe/San_Marino +379|Europe/Vatican +380|Europe/Bucharest +381|Europe/Belgrade +382|Europe/Podgorica +385|Europe/Zagreb +386|Europe/Ljubljana +387|Europe/Sarajevo +389|Europe/Skopje +39|Europe/Rome +40|Europe/Bucharest +41|Europe/Zurich +420|Europe/Prague +421|Europe/Bratislava +423|Europe/Vaduz +43|Europe/Vienna +44|Atlantic/Reykjavik&Europe/London +4411|Europe/London +4412|Europe/London +44130|Europe/London +44131|Europe/London +44132|Europe/London +44133|Europe/London +44134|Europe/London +44135|Europe/London +44136|Europe/London +44137|Europe/London +441380|Europe/London +441381|Europe/London +441382|Europe/London +441383|Europe/London +441384|Europe/London +441386|Europe/London +441387|Europe/London +4413873|Europe/London +441388|Europe/London +441389|Europe/London +44139|Europe/London +44140|Europe/London +44141|Europe/London +44142|Europe/London +44143|Europe/London +44144|Europe/London +44145|Europe/London +44146|Europe/London +44147|Europe/London +441480|Europe/London +441481|Atlantic/Reykjavik +441482|Europe/London +441483|Europe/London +441484|Europe/London +441485|Europe/London +441487|Europe/London +441488|Europe/London +441489|Europe/London +44149|Europe/London +44150|Europe/London +44151|Europe/London +441520|Europe/London +441522|Europe/London +441524|Europe/London +4415242|Europe/London +441525|Europe/London +441526|Europe/London +441527|Europe/London +441528|Europe/London +441529|Europe/London +441530|Europe/London +441531|Europe/London +441534|Atlantic/Reykjavik +441535|Europe/London +441536|Europe/London +441538|Europe/London +441539|Europe/London +4415394|Europe/London +4415395|Europe/London +4415396|Europe/London +44154|Europe/London +44155|Europe/London +44156|Europe/London +44157|Europe/London +44158|Europe/London +44159|Europe/London +44160|Europe/London +44161|Europe/London +441620|Europe/London +441621|Europe/London +441622|Europe/London +441623|Europe/London +441624|Atlantic/Reykjavik +441625|Europe/London +441626|Europe/London +441628|Europe/London +441629|Europe/London +44163|Europe/London +44164|Europe/London +44165|Europe/London +44166|Europe/London +44167|Europe/London +44168|Europe/London +441690|Europe/London +441691|Europe/London +441692|Europe/London +441694|Europe/London +441695|Europe/London +441697|Europe/London +4416973|Europe/London +4416974|Europe/London +4416977|Europe/London +441698|Europe/London +44170|Europe/London +44172|Europe/London +44173|Europe/London +44174|Europe/London +44175|Europe/London +441760|Europe/London +441761|Europe/London +441763|Europe/London +441764|Europe/London +441765|Europe/London +441766|Europe/London +441767|Europe/London +441768|Europe/London +4417683|Europe/London +4417684|Europe/London +4417687|Europe/London +441769|Europe/London +44177|Europe/London +44178|Europe/London +44179|Europe/London +4418|Europe/London +44190|Europe/London +44191|Europe/London +44192|Europe/London +44193|Europe/London +441942|Europe/London +441943|Europe/London +441944|Europe/London +441945|Europe/London +441946|Europe/London +4419467|Europe/London +441947|Europe/London +441948|Europe/London +441949|Europe/London +44195|Europe/London +44196|Europe/London +44197|Europe/London +44198|Europe/London +44199|Europe/London +442|Europe/London +45|Europe/Copenhagen +46|Europe/Stockholm +47|Europe/Oslo&Europe/Paris +472|Europe/Oslo +473|Europe/Oslo +475|Europe/Oslo +476|Europe/Oslo +4770|Europe/Oslo +4771|Europe/Oslo +4772|Europe/Oslo +4773|Europe/Oslo +4774|Europe/Oslo +4775|Europe/Oslo +4776|Europe/Oslo +4777|Europe/Oslo +4778|Europe/Oslo +4779|Europe/Paris +48|Europe/Warsaw +49|Europe/Berlin +500|Atlantic/Stanley +501|America/Belize +502|America/Guatemala +503|America/El_Salvador +504|America/Tegucigalpa +505|America/Chicago +506|America/Costa_Rica +507|America/Panama +508|America/Miquelon +509|America/Port-au-Prince +51|America/Lima +52|America/Hermosillo&America/Mexico_City&America/Tijuana +522|America/Mexico_City +52311|America/Hermosillo +52312|America/Mexico_City +52313|America/Mexico_City +52314|America/Mexico_City +52315|America/Mexico_City +52316|America/Mexico_City +52317|America/Mexico_City +52319|America/Hermosillo +52321|America/Mexico_City +52322|America/Mexico_City +52323|America/Hermosillo +52324|America/Hermosillo +52325|America/Hermosillo +52326|America/Mexico_City +52327|America/Hermosillo +52328|America/Mexico_City +52329|America/Hermosillo&America/Mexico_City +5233|America/Mexico_City +5234|America/Mexico_City +5235|America/Mexico_City +5237|America/Mexico_City +52381|America/Mexico_City +52382|America/Mexico_City +52383|America/Mexico_City +52384|America/Mexico_City +52385|America/Mexico_City +52386|America/Mexico_City +52387|America/Mexico_City +52388|America/Mexico_City +52389|America/Hermosillo +5239|America/Mexico_City +524|America/Mexico_City +525|America/Mexico_City +52612|America/Hermosillo +52613|America/Hermosillo +52614|America/Hermosillo +52615|America/Hermosillo +52616|America/Hermosillo&America/Tijuana +52618|America/Mexico_City +5262|America/Hermosillo +5263|America/Hermosillo +52641|America/Hermosillo +52642|America/Hermosillo +52643|America/Hermosillo +52644|America/Hermosillo +52645|America/Hermosillo +52646|America/Tijuana +52647|America/Hermosillo +52648|America/Hermosillo +52649|America/Hermosillo&America/Mexico_City +52651|America/Hermosillo +52652|America/Hermosillo +52653|America/Hermosillo +52656|America/Hermosillo +52658|America/Tijuana +52659|America/Hermosillo +52661|America/Tijuana +52662|America/Hermosillo +52664|America/Tijuana +52665|America/Tijuana +52667|America/Hermosillo +52668|America/Hermosillo +52669|America/Hermosillo +52671|America/Mexico_City +52672|America/Hermosillo +52673|America/Hermosillo +52674|America/Mexico_City +52675|America/Mexico_City +52676|America/Mexico_City +52677|America/Mexico_City +52686|America/Tijuana +52687|America/Hermosillo +5269|America/Hermosillo +527|America/Mexico_City +528|America/Mexico_City +529|America/Mexico_City +53|America/Havana +54|America/Buenos_Aires +55|America/Manaus&America/Noronha&America/Sao_Paulo +551|America/Sao_Paulo +552|America/Sao_Paulo +553|America/Sao_Paulo +554|America/Sao_Paulo +555|America/Sao_Paulo +5561|America/Sao_Paulo +5562|America/Sao_Paulo +5563|America/Sao_Paulo +5564|America/Sao_Paulo +5565|America/Manaus +5566|America/Manaus +5567|America/Manaus +5568|America/Manaus +5569|America/Manaus +557|America/Sao_Paulo +558|America/Sao_Paulo +5591|America/Manaus +5592|America/Manaus +5593|America/Manaus +5594|America/Manaus +5595|America/Manaus +5596|America/Sao_Paulo +5597|America/Manaus +5598|America/Sao_Paulo +5599|America/Sao_Paulo +56|America/Santiago&Pacific/Easter +562|America/Santiago +563|America/Santiago +564|America/Santiago +565|America/Santiago +566|America/Santiago +567|America/Santiago +57|America/Bogota +58|America/Caracas +590|America/Guadeloupe&America/Halifax&America/Marigot +591|America/La_Paz +592|America/Guyana +593|America/Guayaquil&Pacific/Galapagos +5932|America/Guayaquil +5933|America/Guayaquil +5934|America/Guayaquil +5935|America/Guayaquil&Pacific/Galapagos +5936|America/Guayaquil +5937|America/Guayaquil +594|America/Cayenne +595|America/Asuncion +596|America/Martinique +597|America/Paramaribo +598|America/Montevideo +599|America/Curacao&America/Kralendijk +5993|America/Kralendijk +5994|America/Kralendijk +5997|America/Kralendijk +5999|America/Curacao +60|Asia/Kuching +61|Antarctica/Macquarie&Australia/Adelaide&Australia/Eucla&Australia/Lord_Howe&Australia/Perth&Australia/Sydney&Indian/Christmas&Indian/Cocos +612|Australia/Sydney +613|Australia/Sydney +617|Australia/Sydney +618|Australia/Adelaide&Australia/Perth +62|Asia/Jakarta&Asia/Jayapura&Asia/Makassar +622|Asia/Jakarta +6231|Asia/Jakarta +62321|Asia/Jakarta +62322|Asia/Jakarta +62323|Asia/Jakarta +62324|Asia/Jakarta +62327|Asia/Jakarta&Asia/Makassar +62328|Asia/Jakarta +6233|Asia/Jakarta +6234|Asia/Jakarta +6235|Asia/Jakarta +6236|Asia/Makassar +62370|Asia/Makassar +62371|Asia/Makassar +62372|Asia/Makassar +62373|Asia/Jakarta&Asia/Makassar +62374|Asia/Jakarta&Asia/Makassar +62376|Asia/Makassar +62380|Asia/Makassar +62381|Asia/Jakarta&Asia/Makassar +62382|Asia/Makassar +62384|Asia/Jakarta&Asia/Makassar +62385|Asia/Makassar +62386|Asia/Makassar +62388|Asia/Jakarta&Asia/Makassar +62389|Asia/Makassar +62401|Asia/Makassar +62402|Asia/Makassar +62403|Asia/Makassar +62405|Asia/Jakarta&Asia/Makassar +62408|Asia/Makassar +62411|Asia/Jakarta +62413|Asia/Makassar +62414|Asia/Jakarta&Asia/Makassar +62417|Asia/Jakarta&Asia/Makassar +62419|Asia/Jakarta +62420|Asia/Jakarta +62422|Asia/Makassar +62423|Asia/Jakarta&Asia/Makassar +62426|Asia/Makassar +62428|Asia/Makassar +6243|Asia/Makassar +62451|Asia/Makassar +62452|Asia/Makassar +62453|Asia/Jakarta&Asia/Makassar +62457|Asia/Makassar +62458|Asia/Jakarta +62461|Asia/Makassar +62462|Asia/Jakarta +62464|Asia/Makassar +6247|Asia/Jakarta +62481|Asia/Makassar +62484|Asia/Makassar +62485|Asia/Jakarta +62511|Asia/Makassar +62512|Asia/Makassar +62513|Asia/Jakarta +62516|Asia/Jakarta +62517|Asia/Makassar +62518|Asia/Jakarta&Asia/Makassar +62519|Asia/Jakarta +62525|Asia/Jakarta +62526|Asia/Makassar +62527|Asia/Makassar +62528|Asia/Jakarta&Asia/Makassar +6253|Asia/Jakarta +6254|Asia/Makassar +62551|Asia/Makassar +62552|Asia/Jakarta +62553|Asia/Makassar +62554|Asia/Makassar +62556|Asia/Jakarta&Asia/Makassar +6256|Asia/Jakarta +6261|Asia/Jakarta +62620|Asia/Jakarta +62621|Asia/Jakarta +62622|Asia/Jakarta +62623|Asia/Jakarta&Asia/Makassar +62624|Asia/Jakarta +62625|Asia/Jakarta +62626|Asia/Jakarta +62627|Asia/Jakarta +62628|Asia/Jakarta +62629|Asia/Jakarta +6263|Asia/Jakarta +62641|Asia/Jakarta +62642|Asia/Jakarta&Asia/Makassar +62643|Asia/Jakarta +62644|Asia/Jakarta +62645|Asia/Jakarta +62646|Asia/Jakarta +6265|Asia/Jakarta +62711|Asia/Makassar +62712|Asia/Makassar +62713|Asia/Makassar +62714|Asia/Makassar +62716|Asia/Jakarta +62717|Asia/Jakarta +62718|Asia/Jakarta&Asia/Makassar +62719|Asia/Jakarta +6272|Asia/Jakarta +62730|Asia/Makassar +62731|Asia/Makassar +62732|Asia/Jakarta +62733|Asia/Makassar +62734|Asia/Makassar +62735|Asia/Makassar +62736|Asia/Jakarta +62737|Asia/Jakarta +62738|Asia/Jakarta +62739|Asia/Jakarta +6274|Asia/Jakarta +6275|Asia/Jakarta +6276|Asia/Jakarta +6277|Asia/Jakarta +62901|Asia/Jayapura +62902|Asia/Jakarta&Asia/Makassar +62910|Asia/Jakarta +62911|Asia/Jayapura +62913|Asia/Jakarta&Asia/Jayapura +62915|Asia/Jakarta&Asia/Makassar +62916|Asia/Jakarta&Asia/Jayapura +62917|Asia/Jakarta +62918|Asia/Jakarta&Asia/Makassar +62921|Asia/Jayapura +62929|Asia/Jakarta +62951|Asia/Jayapura +62955|Asia/Jakarta&Asia/Jayapura +62956|Asia/Jakarta&Asia/Jayapura +62957|Asia/Jayapura +62958|Asia/Jakarta +62966|Asia/Jakarta +62967|Asia/Jayapura +6297|Asia/Jayapura +62981|Asia/Jayapura +62983|Asia/Jakarta +62984|Asia/Jayapura +62986|Asia/Jayapura +63|Asia/Manila +64|Pacific/Auckland&Pacific/Chatham +643|Pacific/Auckland +643305|Pacific/Chatham +644|Pacific/Auckland +646|Pacific/Auckland +647|Pacific/Auckland +649|Pacific/Auckland +65|Asia/Singapore +66|Asia/Bangkok +670|Asia/Dili +672|Pacific/Norfolk +673|Asia/Brunei +674|Pacific/Nauru +675|Pacific/Port_Moresby +676|Pacific/Tongatapu +677|Pacific/Guadalcanal +678|Pacific/Efate +679|Pacific/Fiji +680|Pacific/Palau +681|Pacific/Wallis +682|Pacific/Rarotonga +683|Pacific/Niue +685|Pacific/Apia +686|Pacific/Enderbury&Pacific/Kiritimati&Pacific/Tarawa +687|Pacific/Noumea +688|Pacific/Funafuti +689|Pacific/Gambier&Pacific/Marquesas&Pacific/Tahiti +6894|Pacific/Tahiti +6895|Pacific/Tahiti +6896|Pacific/Tahiti +6898|Pacific/Tahiti +68990|Pacific/Tahiti +68991|Pacific/Marquesas +68992|Pacific/Marquesas +68993|Pacific/Tahiti +68994|Pacific/Tahiti +68995|Pacific/Tahiti +68996|Pacific/Tahiti +68997|Pacific/Gambier +68998|Pacific/Tahiti +690|Pacific/Fakaofo +691|Pacific/Kosrae&Pacific/Ponape&Pacific/Truk +69132|Pacific/Ponape +69133|Pacific/Truk +69135|Pacific/Truk +69137|Pacific/Kosrae +6919|Pacific/Ponape +692|Pacific/Majuro +7|Asia/Almaty&Asia/Aqtobe&Asia/Irkutsk&Asia/Krasnoyarsk&Asia/Magadan&Asia/Novosibirsk&Asia/Omsk&Asia/Sakhalin&Asia/Vladivostok&Asia/Yakutsk&Asia/Yekaterinburg&Europe/Moscow&Europe/Volgograd +7301|Asia/Irkutsk +7302|Asia/Yakutsk +7341|Europe/Moscow +7342|Asia/Yekaterinburg +7343|Asia/Yekaterinburg +7345|Asia/Yekaterinburg +7346|Asia/Yekaterinburg +7347|Asia/Yekaterinburg +7349|Asia/Yekaterinburg +735|Asia/Yekaterinburg +738|Asia/Omsk +7390|Asia/Krasnoyarsk +7391|Asia/Krasnoyarsk +7394|Asia/Krasnoyarsk +7395|Asia/Irkutsk +7411|Asia/Yakutsk +7413|Asia/Magadan +7415|Asia/Magadan +7416|Asia/Yakutsk +7421|Asia/Vladivostok +7423|Asia/Vladivostok +7424|Asia/Vladivostok +7426|Asia/Vladivostok +7427|Asia/Magadan +747|Europe/Moscow +748|Europe/Moscow +749|Europe/Moscow +7710|Asia/Almaty +7711|Asia/Aqtobe +7712|Asia/Aqtobe +7713|Asia/Aqtobe +7714|Asia/Almaty +7715|Asia/Almaty +7716|Asia/Almaty +7717|Asia/Almaty +7718|Asia/Almaty +7721|Asia/Almaty +7722|Asia/Almaty +7723|Asia/Almaty +7724|Asia/Almaty +7725|Asia/Almaty +7726|Asia/Almaty +7727|Asia/Almaty +7728|Asia/Almaty +7729|Asia/Aqtobe +78|Europe/Moscow +81|Asia/Tokyo +82|Asia/Seoul +84|Asia/Saigon +850|Asia/Pyongyang +852|Asia/Hong_Kong +853|Asia/Shanghai +855|Asia/Phnom_Penh +856|Asia/Vientiane +86|Asia/Shanghai +880|Asia/Dhaka +886|Asia/Taipei +90|Europe/Bucharest +91|Asia/Calcutta +92|Asia/Karachi +93|Asia/Kabul +94|Asia/Colombo +95|Asia/Rangoon +960|Indian/Maldives +961|Asia/Beirut +962|Asia/Amman +963|Asia/Damascus +964|Asia/Baghdad +965|Asia/Kuwait +966|Asia/Riyadh +967|Asia/Aden +968|Asia/Muscat +970|Europe/Bucharest +971|Asia/Dubai +972|Asia/Jerusalem +973|Asia/Bahrain +974|Asia/Qatar +975|Asia/Thimphu +976|Asia/Choibalsan&Asia/Hovd&Asia/Ulaanbaatar +977|Asia/Katmandu +98|Asia/Tehran +992|Asia/Dushanbe +993|Asia/Ashgabat +994|Asia/Baku +995|Asia/Tbilisi +996|Asia/Bishkek +998|Asia/Tashkent diff --git a/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar b/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar index 5a039c8d9..061b7e8b5 100644 Binary files a/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar and b/tools/java/cpp-build/target/cpp-build-1.0-SNAPSHOT-jar-with-dependencies.jar differ diff --git a/tools/java/java-build/src/com/google/i18n/phonenumbers/EntryPoint.java b/tools/java/java-build/src/com/google/i18n/phonenumbers/EntryPoint.java index f7eec1b03..63b8f1b14 100644 --- a/tools/java/java-build/src/com/google/i18n/phonenumbers/EntryPoint.java +++ b/tools/java/java-build/src/com/google/i18n/phonenumbers/EntryPoint.java @@ -17,6 +17,7 @@ package com.google.i18n.phonenumbers; import com.google.i18n.phonenumbers.buildtools.GeneratePhonePrefixDataEntryPoint; +import com.google.i18n.phonenumbers.buildtools.GenerateTimeZonesMapDataEntryPoint; /** * Entry point class for Java and JavaScript build tools. @@ -30,6 +31,7 @@ public class EntryPoint { new BuildMetadataJsonFromXml(), new BuildMetadataProtoFromXml(), new GeneratePhonePrefixDataEntryPoint(), + new GenerateTimeZonesMapDataEntryPoint(), }).start(); System.exit(status ? 0 : 1); diff --git a/tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapData.java b/tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapData.java new file mode 100644 index 000000000..b07a187cf --- /dev/null +++ b/tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapData.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2012 The Libphonenumber Authors + * + * 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 com.google.i18n.phonenumbers.buildtools; + +import com.google.i18n.phonenumbers.prefixmapper.PrefixTimeZonesMap; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A utility that generates the binary serialization of the prefix/time zones mappings from + * a human-readable text file. + * + * @author Walter Erquinigo + */ +public class GenerateTimeZonesMapData { + private final File inputTextFile; + private static final String MAPPING_DATA_FILE_NAME = "map_data"; + // The IO Handler used to output the generated binary file. + private final AbstractPhonePrefixDataIOHandler ioHandler; + + private static final Logger LOGGER = Logger.getLogger(GenerateTimeZonesMapData.class.getName()); + + public GenerateTimeZonesMapData(File inputTextFile, AbstractPhonePrefixDataIOHandler ioHandler) + throws IOException { + this.inputTextFile = inputTextFile; + if (!inputTextFile.isFile()) { + throw new IOException("The provided input text file does not exist."); + } + this.ioHandler = ioHandler; + } + + /** + * Reads phone prefix data from the provided input stream and returns a SortedMap with the + * prefix to time zones mappings. + */ + // @VisibleForTesting + static SortedMap parseTextFile(InputStream input) + throws IOException, RuntimeException { + final SortedMap timeZoneMap = new TreeMap(); + BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader( + new BufferedInputStream(input), Charset.forName("UTF-8"))); + int lineNumber = 1; + + for (String line; (line = bufferedReader.readLine()) != null; lineNumber++) { + line = line.trim(); + if (line.length() == 0 || line.startsWith("#")) { + continue; + } + int indexOfPipe = line.indexOf('|'); + if (indexOfPipe == -1) { + throw new RuntimeException(String.format("line %d: malformatted data, expected '|'", + lineNumber)); + } + Integer prefix = Integer.parseInt(line.substring(0, indexOfPipe)); + String timezones = line.substring(indexOfPipe + 1); + if (timezones.isEmpty()) { + throw new RuntimeException(String.format("line %d: missing time zones", lineNumber)); + } + if (timeZoneMap.put(prefix, timezones) != null) { + throw new RuntimeException(String.format("duplicated prefix %d", prefix)); + } + } + return timeZoneMap; + } + + /** + * Writes the provided phone prefix/time zones map to the provided output stream. + * + * @throws IOException + */ + // @VisibleForTesting + static void writeToBinaryFile(SortedMap sortedMap, OutputStream output) + throws IOException { + // Build the corresponding PrefixTimeZonesMap and serialize it to the binary format. + PrefixTimeZonesMap prefixTimeZonesMap = new PrefixTimeZonesMap(); + prefixTimeZonesMap.readPrefixTimeZonesMap(sortedMap); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(output); + prefixTimeZonesMap.writeExternal(objectOutputStream); + objectOutputStream.flush(); + } + + /** + * Runs the prefix to time zones map data generator. + * + * @throws IOException + */ + public void run() throws IOException { + FileInputStream fileInputStream = null; + FileOutputStream fileOutputStream = null; + try { + fileInputStream = new FileInputStream(inputTextFile); + SortedMap mappings = parseTextFile(fileInputStream); + File outputBinaryFile = ioHandler.createFile(MAPPING_DATA_FILE_NAME); + try { + fileOutputStream = new FileOutputStream(outputBinaryFile); + writeToBinaryFile(mappings, fileOutputStream); + ioHandler.addFileToOutput(outputBinaryFile); + } finally { + ioHandler.closeFile(fileOutputStream); + } + } catch (RuntimeException e) { + LOGGER.log(Level.SEVERE, + "Error processing file " + inputTextFile.getAbsolutePath()); + throw e; + } catch (IOException e) { + LOGGER.log(Level.SEVERE, e.getMessage()); + } finally { + ioHandler.closeFile(fileInputStream); + ioHandler.closeFile(fileOutputStream); + ioHandler.close(); + } + LOGGER.log(Level.INFO, "Time zone data successfully generated."); + } +} diff --git a/tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataEntryPoint.java b/tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataEntryPoint.java new file mode 100644 index 000000000..355c9fa5e --- /dev/null +++ b/tools/java/java-build/src/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataEntryPoint.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012 The Libphonenumber Authors + * + * 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 com.google.i18n.phonenumbers.buildtools; + +import com.google.i18n.phonenumbers.Command; + +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Entry point class used to invoke the generation of the binary time zone data files. + * + * @author Walter Erquinigo + * @author Philippe Liard + */ +public class GenerateTimeZonesMapDataEntryPoint extends Command { + private static final Logger LOGGER = Logger.getLogger(GenerateTimeZonesMapData.class.getName()); + + @Override + public String getCommandName() { + return "GenerateTimeZonesMapData"; + } + + @Override + public boolean start() { + String[] args = getArgs(); + + if (args.length != 3) { + LOGGER.log(Level.SEVERE, + "usage: GenerateTimeZonesMapData /path/to/input/text_file " + + "/path/to/output/directory"); + return false; + } + try { + GenerateTimeZonesMapData generateTimeZonesMapData = new GenerateTimeZonesMapData( + new File(args[1]), new PhonePrefixDataIOHandler(new File(args[2]))); + generateTimeZonesMapData.run(); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, e.getMessage()); + return false; + } + return true; + } +} diff --git a/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar b/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar index 1e971e49f..305b87d1e 100644 Binary files a/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar and b/tools/java/java-build/target/java-build-1.0-SNAPSHOT-jar-with-dependencies.jar differ diff --git a/tools/java/java-build/test/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataTest.java b/tools/java/java-build/test/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataTest.java new file mode 100644 index 000000000..4ca397cfe --- /dev/null +++ b/tools/java/java-build/test/com/google/i18n/phonenumbers/buildtools/GenerateTimeZonesMapDataTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 The Libphonenumber Authors + * + * 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 com.google.i18n.phonenumbers.buildtools; + +import com.google.i18n.phonenumbers.prefixmapper.PrefixTimeZonesMap; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.Map; +import java.util.SortedMap; + +/** + * Unittests for GenerateTimeZonesMapData.java + * + * @author Walter Erquinigo + */ +public class GenerateTimeZonesMapDataTest extends TestCase { + private static final String BRUSSELS_TZ = "Europe/Brussels"; + private static final String PARIS_TZ = "Europe/Paris"; + private static final String PARIS_BRUSSELS_LINES = + "322|" + BRUSSELS_TZ + "\n331|" + PARIS_TZ + "\n"; + + private static SortedMap parseTextFileHelper(String input) throws IOException { + return GenerateTimeZonesMapData.parseTextFile(new ByteArrayInputStream(input.getBytes())); + } + + public void testParseTextFile() throws IOException { + Map result = parseTextFileHelper(PARIS_BRUSSELS_LINES); + assertEquals(2, result.size()); + assertEquals(PARIS_TZ, result.get(331)); + assertEquals(BRUSSELS_TZ, result.get(322)); + } + + public void testParseTextFileIgnoresComments() throws IOException { + Map result = parseTextFileHelper("# Hello\n" + PARIS_BRUSSELS_LINES); + assertEquals(2, result.size()); + assertEquals(PARIS_TZ, result.get(331)); + assertEquals(BRUSSELS_TZ, result.get(322)); + } + + public void testParseTextFileIgnoresBlankLines() throws IOException { + Map result = parseTextFileHelper("\n" + PARIS_BRUSSELS_LINES); + assertEquals(2, result.size()); + assertEquals(PARIS_TZ, result.get(331)); + assertEquals(BRUSSELS_TZ, result.get(322)); + } + + public void testParseTextFileIgnoresTrailingWhitespaces() throws IOException { + Map result = parseTextFileHelper( + "331|" + PARIS_TZ + "\n322|" + BRUSSELS_TZ + " \n"); + assertEquals(2, result.size()); + assertEquals(PARIS_TZ, result.get(331)); + assertEquals(BRUSSELS_TZ, result.get(322)); + } + + public void testParseTextFileThrowsExceptionWithMalformattedData() throws IOException { + try { + parseTextFileHelper("331"); + fail(); + } catch (RuntimeException e) { + // Expected. + } + } + + public void testParseTextFileThrowsExceptionWithMissingTimeZone() throws IOException { + try { + parseTextFileHelper("331|"); + fail(); + } catch (RuntimeException e) { + // Expected. + } + } + + // Returns a String representing the input after serialization and deserialization by + // PrefixTimeZonesMap. + private static String convertDataHelper(String input) throws IOException { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(input.getBytes()); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + + SortedMap prefixTimeZonesMapping = parseTextFileHelper(input); + GenerateTimeZonesMapData.writeToBinaryFile(prefixTimeZonesMapping, byteArrayOutputStream); + // The byte array output stream now contains the corresponding serialized prefix to time zones + // map. Try to deserialize it and compare it with the initial input. + PrefixTimeZonesMap prefixTimeZonesMap = new PrefixTimeZonesMap(); + prefixTimeZonesMap.readExternal( + new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()))); + + return prefixTimeZonesMap.toString(); + } + + public void testConvertData() throws IOException { + String input = PARIS_BRUSSELS_LINES; + + String dataAfterDeserialization = convertDataHelper(input); + assertEquals(input, dataAfterDeserialization); + } + + public void testConvertThrowsExceptionWithMissingTimeZone() throws IOException { + String input = PARIS_BRUSSELS_LINES + "3341|\n"; + + try { + String dataAfterDeserialization = convertDataHelper(input); + } catch (RuntimeException e) { + // Expected. + } + } + + public void testConvertDataThrowsExceptionWithDuplicatedPrefixes() throws IOException { + String input = "331|" + PARIS_TZ + "\n331|" + BRUSSELS_TZ + "\n"; + + try { + convertDataHelper(input); + fail(); + } catch (RuntimeException e) { + // Expected. + } + } +}