diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneBuilder.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneBuilder.java index f7ba08073..9ea54ca2d 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneBuilder.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneBuilder.java @@ -1169,6 +1169,7 @@ public class DateTimeZoneBuilder { DSTZone tailZone; if (flow.characters[flow.pointer++] == 'y') { + flow.pointer++; tailZone = DSTZone.readZone(id, flow); } else { tailZone = null; 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 7382a3e3e..5bb18c521 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 @@ -16,11 +16,16 @@ package org.teavm.classlib.impl.tz; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.PriorityQueue; +import java.util.Set; import org.teavm.classlib.impl.Base46; import org.teavm.classlib.impl.CharFlow; +import org.teavm.jso.JSBody; import org.teavm.platform.metadata.MetadataProvider; import org.teavm.platform.metadata.ResourceMap; @@ -67,6 +72,96 @@ public class DateTimeZoneProvider { return ids.toArray(new String[ids.size()]); } + public static DateTimeZone detectTimezone() { + List zones = new ArrayList<>(); + long time = System.currentTimeMillis(); + int offset = -getNativeOffset(System.currentTimeMillis()); + for (String id : getIds()) { + DateTimeZone tz = getTimeZone(id); + int tzOffset = tz.getOffset(time) / 60_000; + if (Math.abs(tzOffset - offset) > 120) { + continue; + } + zones.add(new Score(tz)); + } + + List scoreTable = new ArrayList<>(); + scoreTable.addAll(zones); + Map> zoneMap = new HashMap<>(); + PriorityQueue queue = new PriorityQueue<>(zones.size(), new Comparator() { + @Override public int compare(Long o1, Long o2) { + return o2.compareTo(o1); + } + }); + Set timeInQueue = new HashSet<>(); + long last = time; + queue.add(time); + zoneMap.put(time, new ArrayList<>(zones)); + + while (!queue.isEmpty() && scoreTable.size() > 1) { + time = queue.remove(); + timeInQueue.remove(time); + zones = zoneMap.remove(time); + offset = -getNativeOffset(time); + + for (Score score : zones) { + long prev = score.tz.previousTransition(time); + if (prev == time) { + if (scoreTable.get(0) == score) { + return score.tz; + } + scoreTable.remove(score); + } else { + int tzOffset = score.tz.getOffset(time) / 60_000; + if (Math.abs(tzOffset - offset) > 120) { + scoreTable.remove(score); + continue; + } + List prevZones = zoneMap.get(prev); + if (prevZones == null) { + prevZones = new ArrayList<>(); + zoneMap.put(prev, prevZones); + } + prevZones.add(score); + if (timeInQueue.add(prev)) { + queue.add(prev); + } + } + } + + if (scoreTable.get(0).tz.previousTransition(time) == time) { + return scoreTable.get(0).tz; + } + + for (int i = scoreTable.size() - 1; i >= 0; --i) { + Score score = scoreTable.get(i); + int tzOffset = score.tz.getOffset(time) / 60_000; + if (tzOffset != offset) { + score.value += (int)((last - time) / 60_000) * (Math.abs(tzOffset - offset)) / 30; + } + int j = i + 1; + while (j < scoreTable.size() && score.value > scoreTable.get(j).value) { + scoreTable.set(j - 1, scoreTable.get(j)); + ++j; + } + scoreTable.set(j - 1, score); + } + + last = time; + } + + return scoreTable.get(0).tz; + } + + static class Score { + DateTimeZone tz; + int value; + + public Score(DateTimeZone tz) { + this.tz = tz; + } + } + private static TimeZoneResource getTimeZoneResource(String id) { String areaName; String locationName; @@ -85,6 +180,9 @@ public class DateTimeZoneProvider { return area.get(locationName); } + @JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();") + private static native int getNativeOffset(double instant); + @MetadataProvider(TimeZoneGenerator.class) private static native ResourceMap> getResource(); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/StorableDateTimeZone.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/StorableDateTimeZone.java index 65a82db5f..025d8c905 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/StorableDateTimeZone.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/tz/StorableDateTimeZone.java @@ -120,7 +120,7 @@ public abstract class StorableDateTimeZone extends DateTimeZone { public static StorableDateTimeZone read(String id, String text) { CharFlow flow = new CharFlow(text.toCharArray()); - int type = Base46.decode(flow); + int type = Base46.decodeUnsigned(flow); switch (type) { case PRECALCULATED: return PrecalculatedZone.readZone(id, flow); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java index 2af7de222..4ac039270 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/LongNativeGenerator.java @@ -30,7 +30,7 @@ public class LongNativeGenerator implements Generator { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { case "compare": - writer.append("Long_compare(").append(context.getParameterName(1)).append(", ") + writer.append("return Long_compare(").append(context.getParameterName(1)).append(", ") .append(context.getParameterName(2)).append(");").softNewLine(); break; case "hashCode": diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TPriorityQueue.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TPriorityQueue.java index 2ae5f83e8..4462196af 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TPriorityQueue.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TPriorityQueue.java @@ -205,7 +205,6 @@ public class TPriorityQueue extends TAbstractQueue implements TSerializabl private void removeAt(int index) { ++version; Object item = data[size - 1]; - data[--size] = null; while (true) { int left = index * 2 + 1; int right = left + 1; @@ -224,6 +223,7 @@ public class TPriorityQueue extends TAbstractQueue implements TSerializabl index = next; } data[index] = item; + data[--size] = null; } private void ensureCapacity(int capacity) { 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 fab5ec2df..ab677dbcf 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 @@ -16,5 +16,6 @@ public class TimeZoneTest { assertEquals(1414274399999L, tz.previousTransition(1431781727159L)); assertEquals(1301183999999L, tz.previousTransition(1414274399999L)); assertEquals(1288479599999L, tz.previousTransition(1301183999999L)); + System.out.println(DateTimeZoneProvider.detectTimezone().getID()); } }