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 bb87ac887..7c2c9a395 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 @@ -92,6 +92,9 @@ public class CLDRHelper { public static String getTimeZoneName(String language, String country, String id) { String locale = getCode(language, country); + if (!getTimeZoneLocalizationMap().has(locale)) { + locale = language; + } if (!getTimeZoneLocalizationMap().has(locale)) { return null; } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java index 958fc7693..280338b3e 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormat.java @@ -48,6 +48,7 @@ public abstract class TDateFormat extends TFormat { public final static int TIMEZONE_FIELD = 17; protected TDateFormat() { + calendar = TCalendar.getInstance(); } @Override @@ -96,6 +97,14 @@ public abstract class TDateFormat extends TFormat { return calendar; } + public TTimeZone getTimeZone() { + return calendar.getTimeZone(); + } + + public void setTimeZone(TTimeZone timeZone) { + calendar.setTimeZone(timeZone); + } + public static TDateFormat getDateInstance() { return getDateInstance(DEFAULT); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java index 3bf38d843..6b4860f43 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TDateFormatElement.java @@ -345,6 +345,7 @@ abstract class TDateFormatElement { public static class GeneralTimezone extends TDateFormatElement { private static Map cache; + private static TrieNode idSearchTrie; private TLocale locale; private TrieNode searchTrie; @@ -370,7 +371,7 @@ abstract class TDateFormatElement { if (tz.getID().startsWith("GMT")) { int minutes = tz.getRawOffset() / 60_000; buffer.append("GMT"); - if (minutes > 0) { + if (minutes >= 0) { buffer.append('+'); } else { minutes = -minutes; @@ -399,23 +400,24 @@ abstract class TDateFormatElement { if (position.getIndex() + 1 < text.length()) { } - TTimeZone tz = match(text, position); + TTimeZone tz = match(searchTrie, text, position); if (tz != null) { date.setTimeZone(tz); } else { - position.setErrorIndex(position.getIndex()); + prepareIdTrie(); + tz = match(idSearchTrie, text, position); + if (tz != null) { + date.setTimeZone(tz); + } else { + position.setErrorIndex(position.getIndex()); + } } } - private void parseHoursMinutes(String text, TCalendar date, TParsePosition position) { - - } - - public TTimeZone match(String text, TParsePosition position) { + public TTimeZone match(TrieNode node, String text, TParsePosition position) { prepareTrie(); int start = position.getIndex(); int index = start; - TrieNode node = searchTrie; int lastMatch = start; TTimeZone tz = null; while (node.childNodes.length > 0) { @@ -426,7 +428,7 @@ abstract class TDateFormatElement { if (index >= text.length()) { break; } - int next = Arrays.binarySearch(node.chars, text.charAt(index++)); + int next = Arrays.binarySearch(node.chars, Character.toLowerCase(text.charAt(index++))); if (next < 0) { return null; } @@ -445,7 +447,58 @@ abstract class TDateFormatElement { TTimeZone tz = TTimeZone.getTimeZone(tzId); builder.add(tz.getDisplayName(locale), tz); } + searchTrie = builder.build(); } + + private static void prepareIdTrie() { + if (idSearchTrie != null) { + return; + } + TrieBuilder builder = new TrieBuilder(); + for (String tzId : TTimeZone.getAvailableIDs()) { + TTimeZone tz = TTimeZone.getTimeZone(tzId); + builder.add(tz.getID(), tz); + } + } + } + + static void parseHoursMinutes(String text, TCalendar date, TParsePosition position) { + int index = position.getIndex() + 3; + int sign = text.charAt(index++) == '-' ? -1 : 1; + if (index >= text.length() || !Character.isDigit(text.charAt(index))) { + position.setErrorIndex(index); + return; + } + int hours = Character.digit(text.charAt(index++), 10); + if (index >= text.length()) { + position.setErrorIndex(index); + return; + } + if (text.charAt(index) != ':') { + if (!Character.isDigit(text.charAt(index))) { + position.setErrorIndex(index); + return; + } + hours = 10 * hours + Character.digit(text.charAt(index), 10); + } + if (index >= text.length() || text.charAt(index) != ':') { + position.setErrorIndex(index); + return; + } + + if (index + 2 > text.length() || !Character.isDigit(text.charAt(index)) || + !Character.isDigit(text.charAt(index + 1))) { + position.setErrorIndex(index); + return; + } + int minutes = Character.digit(text.charAt(index), 10) * 10 + Character.digit(text.charAt(index), 10); + position.setIndex(index + 2); + TTimeZone tz = getStaticTimeZone(sign * hours, minutes); + date.setTimeZone(tz); + } + + static TTimeZone getStaticTimeZone(int hours, int minutes) { + return TTimeZone.getTimeZone("GMT" + (hours) + ":" + (minutes / 10) + (minutes % 10)); } static class TrieNode { @@ -461,7 +514,7 @@ abstract class TDateFormatElement { int index = 0; TrieNodeBuilder node = root; while (index < text.length()) { - char c = text.charAt(index); + char c = Character.toLowerCase(text.charAt(index)); while (node.ch != c) { if (node.ch == '\0') { node.ch = c; @@ -478,7 +531,11 @@ abstract class TDateFormatElement { node.tz = tz; } - public TrieNode build(TrieNodeBuilder builder) { + public TrieNode build() { + return build(root); + } + + TrieNode build(TrieNodeBuilder builder) { TrieNode node = new TrieNode(); node.tz = builder.tz; List builders = new ArrayList<>(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDateFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDateFormat.java index 3775405b3..331c3aeec 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDateFormat.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDateFormat.java @@ -59,6 +59,7 @@ public class TSimpleDateFormat extends TDateFormat { public StringBuffer format(TDate date, StringBuffer buffer, TFieldPosition field) { TCalendar calendar = new TGregorianCalendar(locale); calendar.setTime(date); + calendar.setTimeZone(this.calendar.getTimeZone()); for (TDateFormatElement element : elements) { element.format(calendar, buffer); } @@ -71,7 +72,7 @@ public class TSimpleDateFormat extends TDateFormat { } private void reparsePattern() { - TSimpleDatePatternParser parser = new TSimpleDatePatternParser(dateFormatSymbols); + TSimpleDatePatternParser parser = new TSimpleDatePatternParser(dateFormatSymbols, locale); parser.parsePattern(pattern); elements = parser.getElements().toArray(new TDateFormatElement[0]); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDatePatternParser.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDatePatternParser.java index a0f27576f..13b6faaf3 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDatePatternParser.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/TSimpleDatePatternParser.java @@ -18,6 +18,7 @@ package org.teavm.classlib.java.text; import java.util.ArrayList; import java.util.List; import org.teavm.classlib.java.util.TCalendar; +import org.teavm.classlib.java.util.TLocale; /** * @@ -25,12 +26,14 @@ import org.teavm.classlib.java.util.TCalendar; */ class TSimpleDatePatternParser { private TDateFormatSymbols symbols; + private TLocale locale; private List elements = new ArrayList<>(); private int index; private String pattern; - public TSimpleDatePatternParser(TDateFormatSymbols symbols) { + public TSimpleDatePatternParser(TDateFormatSymbols symbols, TLocale locale) { this.symbols = symbols; + this.locale = locale; } public List getElements() { @@ -148,6 +151,11 @@ class TSimpleDatePatternParser { elements.add(new TDateFormatElement.Numeric(TCalendar.MILLISECOND, rep)); break; } + case 'z': { + parseRepetitions(); + elements.add(TDateFormatElement.GeneralTimezone.get(locale)); + break; + } default: if (isControl(c)) { parseRepetitions(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TIANATimeZone.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TIANATimeZone.java index dd17a7161..d9e757378 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TIANATimeZone.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TIANATimeZone.java @@ -26,10 +26,13 @@ class TIANATimeZone extends TTimeZone { private static final long serialVersionUID = -8196006595542230951L; private DateTimeZone underlyingZone; private int rawOffset; + private int diff; public TIANATimeZone(DateTimeZone underlyingZone) { super(underlyingZone.getID()); this.underlyingZone = underlyingZone; + rawOffset = underlyingZone.getStandardOffset(System.currentTimeMillis()); + diff = -rawOffset; } @Override @@ -43,7 +46,7 @@ class TIANATimeZone extends TTimeZone { @Override public int getOffset(long time) { - return rawOffset + underlyingZone.getOffset(time); + return rawOffset + diff + underlyingZone.getOffset(time); } @Override @@ -63,7 +66,7 @@ class TIANATimeZone extends TTimeZone { @Override public boolean useDaylightTime() { - return underlyingZone.isFixed(); + return !underlyingZone.isFixed(); } @Override diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java index 2c0aa68e3..392693641 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java @@ -69,7 +69,7 @@ public final class TLocale implements TCloneable, TSerializable { static { String localeName = CLDRHelper.getDefaultLocale().getValue(); int countryIndex = localeName.indexOf('_'); - defaultLocale = new TLocale(localeName.substring(0, countryIndex), localeName.substring(countryIndex) + 1, ""); + defaultLocale = new TLocale(localeName.substring(0, countryIndex), localeName.substring(countryIndex + 1), ""); } private transient String countryCode;