First version of time zone detector

This commit is contained in:
Alexey Andreev 2015-05-16 20:50:16 +03:00
parent 05c74a2d8a
commit 46da627318
6 changed files with 103 additions and 3 deletions

View File

@ -1169,6 +1169,7 @@ public class DateTimeZoneBuilder {
DSTZone tailZone; DSTZone tailZone;
if (flow.characters[flow.pointer++] == 'y') { if (flow.characters[flow.pointer++] == 'y') {
flow.pointer++;
tailZone = DSTZone.readZone(id, flow); tailZone = DSTZone.readZone(id, flow);
} else { } else {
tailZone = null; tailZone = null;

View File

@ -16,11 +16,16 @@
package org.teavm.classlib.impl.tz; package org.teavm.classlib.impl.tz;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import org.teavm.classlib.impl.Base46; import org.teavm.classlib.impl.Base46;
import org.teavm.classlib.impl.CharFlow; import org.teavm.classlib.impl.CharFlow;
import org.teavm.jso.JSBody;
import org.teavm.platform.metadata.MetadataProvider; import org.teavm.platform.metadata.MetadataProvider;
import org.teavm.platform.metadata.ResourceMap; import org.teavm.platform.metadata.ResourceMap;
@ -67,6 +72,96 @@ public class DateTimeZoneProvider {
return ids.toArray(new String[ids.size()]); return ids.toArray(new String[ids.size()]);
} }
public static DateTimeZone detectTimezone() {
List<Score> 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<Score> scoreTable = new ArrayList<>();
scoreTable.addAll(zones);
Map<Long, List<Score>> zoneMap = new HashMap<>();
PriorityQueue<Long> queue = new PriorityQueue<>(zones.size(), new Comparator<Long>() {
@Override public int compare(Long o1, Long o2) {
return o2.compareTo(o1);
}
});
Set<Long> 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<Score> 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) { private static TimeZoneResource getTimeZoneResource(String id) {
String areaName; String areaName;
String locationName; String locationName;
@ -85,6 +180,9 @@ public class DateTimeZoneProvider {
return area.get(locationName); return area.get(locationName);
} }
@JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();")
private static native int getNativeOffset(double instant);
@MetadataProvider(TimeZoneGenerator.class) @MetadataProvider(TimeZoneGenerator.class)
private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource(); private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource();
} }

View File

@ -120,7 +120,7 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
public static StorableDateTimeZone read(String id, String text) { public static StorableDateTimeZone read(String id, String text) {
CharFlow flow = new CharFlow(text.toCharArray()); CharFlow flow = new CharFlow(text.toCharArray());
int type = Base46.decode(flow); int type = Base46.decodeUnsigned(flow);
switch (type) { switch (type) {
case PRECALCULATED: case PRECALCULATED:
return PrecalculatedZone.readZone(id, flow); return PrecalculatedZone.readZone(id, flow);

View File

@ -30,7 +30,7 @@ public class LongNativeGenerator implements Generator {
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
switch (methodRef.getName()) { switch (methodRef.getName()) {
case "compare": 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(); .append(context.getParameterName(2)).append(");").softNewLine();
break; break;
case "hashCode": case "hashCode":

View File

@ -205,7 +205,6 @@ public class TPriorityQueue<E> extends TAbstractQueue<E> implements TSerializabl
private void removeAt(int index) { private void removeAt(int index) {
++version; ++version;
Object item = data[size - 1]; Object item = data[size - 1];
data[--size] = null;
while (true) { while (true) {
int left = index * 2 + 1; int left = index * 2 + 1;
int right = left + 1; int right = left + 1;
@ -224,6 +223,7 @@ public class TPriorityQueue<E> extends TAbstractQueue<E> implements TSerializabl
index = next; index = next;
} }
data[index] = item; data[index] = item;
data[--size] = null;
} }
private void ensureCapacity(int capacity) { private void ensureCapacity(int capacity) {

View File

@ -16,5 +16,6 @@ public class TimeZoneTest {
assertEquals(1414274399999L, tz.previousTransition(1431781727159L)); assertEquals(1414274399999L, tz.previousTransition(1431781727159L));
assertEquals(1301183999999L, tz.previousTransition(1414274399999L)); assertEquals(1301183999999L, tz.previousTransition(1414274399999L));
assertEquals(1288479599999L, tz.previousTransition(1301183999999L)); assertEquals(1288479599999L, tz.previousTransition(1301183999999L));
System.out.println(DateTimeZoneProvider.detectTimezone().getID());
} }
} }