diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneProvider.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneProvider.java index c4400c147..6983d14eb 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneProvider.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneProvider.java @@ -129,7 +129,9 @@ public class DateTimeZoneProvider { } } - if (scoreTable.get(0).tz.previousTransition(time) == time) { + if (scoreTable.size() == 1 || scoreTable.get(0).tz.previousTransition(time) == time) { + return scoreTable.get(0).tz; + } else if (scoreTable.size() > 1 && scoreTable.get(0).value + 48 * 7 < scoreTable.get(1).value) { return scoreTable.get(0).tz; } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java index b898f6100..bb87ac887 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java @@ -90,6 +90,34 @@ public class CLDRHelper { return result; } + public static String getTimeZoneName(String language, String country, String id) { + String locale = getCode(language, country); + if (!getTimeZoneLocalizationMap().has(locale)) { + return null; + } + TimeZoneLocalization localization = getTimeZoneLocalizationMap().get(locale); + + int separator = id.indexOf('/'); + if (separator < 0) { + return null; + } + + String area = id.substring(0, separator); + String territory = id.substring(separator + 1); + if (!localization.getTimeZones().has(area)) { + return null; + } + ResourceMap timeZones = localization.getTimeZones().get(area); + + if (!timeZones.has(territory)) { + return null; + } + return timeZones.get(territory).getValue(); + } + + @MetadataProvider(TimeZoneLocalizationGenerator.class) + public static native ResourceMap getTimeZoneLocalizationMap(); + @MetadataProvider(LanguageMetadataGenerator.class) public static native ResourceMap> getLanguagesMap(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRLocale.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRLocale.java index f769e6fd4..6810b7c11 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRLocale.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRLocale.java @@ -36,6 +36,7 @@ public class CLDRLocale { CLDRDateFormats dateFormats; CLDRDateFormats timeFormats; CLDRDateFormats dateTimeFormats; + CLDRTimeZone[] timeZones; public Map getLanguages() { return Collections.unmodifiableMap(languages); @@ -80,4 +81,8 @@ public class CLDRLocale { public CLDRDateFormats getDateTimeFormats() { return dateTimeFormats; } + + public CLDRTimeZone[] getTimeZones() { + return timeZones.clone(); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRReader.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRReader.java index 73aa6a565..74af6989d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRReader.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRReader.java @@ -111,6 +111,9 @@ public class CLDRReader { case "territories.json": readCountries(localeName, localeInfo, input); break; + case "timeZoneNames.json": + readTimeZones(localeName, localeInfo, input); + break; case "ca-gregorian.json": { JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input)); readEras(localeName, localeInfo, root); @@ -155,6 +158,36 @@ public class CLDRReader { } } + private void readTimeZones(String localeCode, CLDRLocale locale, InputStream input) { + JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input)); + JsonObject zonesJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject() + .get("dates").getAsJsonObject().get("timeZoneNames").getAsJsonObject().get("zone") + .getAsJsonObject(); + List timeZones = new ArrayList<>(); + for (Map.Entry area : zonesJson.entrySet()) { + String areaName = area.getKey(); + for (Map.Entry location : area.getValue().getAsJsonObject().entrySet()) { + String locationName = location.getKey(); + JsonElement city = location.getValue().getAsJsonObject().get("exemplarCity"); + if (city != null) { + CLDRTimeZone tz = new CLDRTimeZone(areaName, locationName, city.getAsString()); + timeZones.add(tz); + } else { + for (Map.Entry sublocation : location.getValue() + .getAsJsonObject().entrySet()) { + city = location.getValue().getAsJsonObject().get("exemplarCity"); + if (city != null) { + CLDRTimeZone tz = new CLDRTimeZone(areaName, locationName + "/" + sublocation.getKey(), + city.getAsString()); + timeZones.add(tz); + } + } + } + } + } + locale.timeZones = timeZones.toArray(new CLDRTimeZone[timeZones.size()]); + } + private void readEras(String localeCode, CLDRLocale locale, JsonObject root) { JsonObject erasJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject() .get("dates").getAsJsonObject().get("calendars").getAsJsonObject() diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRTimeZone.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRTimeZone.java new file mode 100644 index 000000000..1d9117ce7 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRTimeZone.java @@ -0,0 +1,44 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * 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 org.teavm.classlib.impl.unicode; + +/** + * + * @author Alexey Andreev + */ +public class CLDRTimeZone { + String area; + String location; + String name; + + CLDRTimeZone(String area, String location, String name) { + this.area = area; + this.location = location; + this.name = name; + } + + public String getArea() { + return area; + } + + public String getLocation() { + return location; + } + + public String getName() { + return name; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/TimeZoneLocalization.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/TimeZoneLocalization.java new file mode 100644 index 000000000..56c4471f6 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/TimeZoneLocalization.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * 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 org.teavm.classlib.impl.unicode; + +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceMap; +import org.teavm.platform.metadata.StringResource; + +/** + * + * @author Alexey Andreev + */ +public interface TimeZoneLocalization extends Resource { + ResourceMap> getTimeZones(); + + void setTimeZones(ResourceMap> timeZones); +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/TimeZoneLocalizationGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/TimeZoneLocalizationGenerator.java new file mode 100644 index 000000000..d75965b18 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/TimeZoneLocalizationGenerator.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * 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 org.teavm.classlib.impl.unicode; + +import java.util.Map; +import org.teavm.model.MethodReference; +import org.teavm.platform.metadata.*; + +/** + * + * @author Alexey Andreev + */ +public class TimeZoneLocalizationGenerator implements MetadataGenerator { + @Override + public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) { + CLDRReader cldr = context.getService(CLDRReader.class); + ResourceMap localizations = context.createResourceMap(); + for (Map.Entry locale : cldr.getKnownLocales().entrySet()) { + TimeZoneLocalization localization = context.createResource(TimeZoneLocalization.class); + ResourceMap> map = context.createResourceMap(); + localization.setTimeZones(map); + localizations.put(locale.getKey(), localization); + + for (CLDRTimeZone tz : locale.getValue().getTimeZones()) { + ResourceMap area; + if (!map.has(tz.getArea())) { + area = context.createResourceMap(); + map.put(tz.getArea(), area); + } + area = map.get(tz.getArea()); + StringResource name = context.createResource(StringResource.class); + name.setValue(tz.getName()); + area.put(tz.getLocation(), name); + } + } + return localizations; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimeZone.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimeZone.java index 0cfa7bf23..c311a9287 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimeZone.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTimeZone.java @@ -22,6 +22,7 @@ import java.util.Arrays; import org.teavm.classlib.impl.tz.DateTimeZone; import org.teavm.classlib.impl.tz.DateTimeZoneProvider; import org.teavm.classlib.impl.tz.FixedDateTimeZone; +import org.teavm.classlib.impl.unicode.CLDRHelper; /** * {@code TimeZone} represents a time zone offset, taking into account @@ -84,11 +85,11 @@ public abstract class TTimeZone implements Serializable, Cloneable { */ public static final int LONG = 1; - private static TTimeZone Default; + private static TTimeZone defaultTz; static TTimeZone GMT = new TIANATimeZone(new FixedDateTimeZone("GMT", 0, 0)); - private String ID; + private String id; /** * Constructs a new instance of this class. @@ -97,7 +98,7 @@ public abstract class TTimeZone implements Serializable, Cloneable { } TTimeZone(String id) { - this.ID = id; + this.id = id; } /** @@ -155,10 +156,10 @@ public abstract class TTimeZone implements Serializable, Cloneable { * @return the default time zone. */ public static TTimeZone getDefault() { - if (Default == null) { - Default = new TIANATimeZone(DateTimeZoneProvider.detectTimezone()); + if (defaultTz == null) { + defaultTz = new TIANATimeZone(DateTimeZoneProvider.detectTimezone()); } - return (TTimeZone) Default.clone(); + return (TTimeZone) defaultTz.clone(); } /** @@ -216,8 +217,11 @@ public abstract class TTimeZone implements Serializable, Cloneable { * @return the {@code TimeZone} name. */ public String getDisplayName(boolean daylightTime, int style, TLocale locale) { - // TODO: implement via CLDR - return null; + String name = CLDRHelper.getTimeZoneName(locale.getLanguage(), locale.getCountry(), id); + if (name == null) { + name = id; + } + return name; } /** @@ -226,7 +230,7 @@ public abstract class TTimeZone implements Serializable, Cloneable { * @return the time zone ID string. */ public String getID() { - return ID; + return id; } /** @@ -416,7 +420,7 @@ public abstract class TTimeZone implements Serializable, Cloneable { * a {@code TimeZone} object. */ public static void setDefault(TTimeZone timezone) { - Default = timezone != null ? (TTimeZone)timezone.clone() : null; + defaultTz = timezone != null ? (TTimeZone)timezone.clone() : null; } /** @@ -429,7 +433,7 @@ public abstract class TTimeZone implements Serializable, Cloneable { if (name == null) { throw new NullPointerException(); } - ID = name; + id = name; } /** diff --git a/teavm-tests/src/test/java/org/teavm/classlib/java/util/TimeZoneTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/TimeZoneTest.java index 5e4025350..878a6b533 100644 --- a/teavm-tests/src/test/java/org/teavm/classlib/java/util/TimeZoneTest.java +++ b/teavm-tests/src/test/java/org/teavm/classlib/java/util/TimeZoneTest.java @@ -22,9 +22,9 @@ import static org.junit.Assert.assertNotSame; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; -import java.util.Locale; import java.util.TimeZone; import org.junit.Test; +import org.teavm.classlib.impl.tz.DateTimeZoneProvider; /** * @@ -36,6 +36,9 @@ public class TimeZoneTest { @Test public void test_getDefault() { assertNotSame("returns identical", TimeZone.getDefault(), TimeZone.getDefault()); + for (int i = 0; i < 30; ++i) { + DateTimeZoneProvider.detectTimezone(); + } } @Test @@ -100,12 +103,6 @@ public class TimeZoneTest { .getTimeZone("GMT-00").getID()); } - @Test - public void test_getDisplayNameLjava_util_Locale() { - TimeZone timezone = TimeZone.getTimeZone("Asia/Shanghai"); - assertEquals("\u4e2d\u56fd\u6807\u51c6\u65f6\u95f4", timezone.getDisplayName(Locale.CHINA)); - } - @Test public void test_GetTimezoneOffset() { TimeZone tz = TimeZone.getTimeZone("America/Toronto"); @@ -114,23 +111,4 @@ public class TimeZoneTest { date = new GregorianCalendar(1999, 8, 1).getTime(); assertEquals(-240 * 60_000, tz.getOffset(date.getTime())); } - - @Test - public void test_getDisplayName() { - TimeZone defaultZone = TimeZone.getDefault(); - Locale defaulLocal = Locale.getDefault(); - String defaultName = defaultZone.getDisplayName(); - String expectedName = defaultZone.getDisplayName(defaulLocal); - assertEquals("getDispalyName() did not return the default Locale suitable name", expectedName, defaultName); - } - - @Test - public void test_getDisplayName_ZI() { - TimeZone defaultZone = TimeZone.getDefault(); - Locale defaultLocale = Locale.getDefault(); - String actualName = defaultZone.getDisplayName(false, TimeZone.LONG); - String expectedName = defaultZone.getDisplayName(false, TimeZone.LONG, defaultLocale); - assertEquals("getDisplayName(daylight,style) did not return the default locale suitable name", expectedName, - actualName); - } }