mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
Start to implement time zones
This commit is contained in:
parent
7277696870
commit
b264e34ef8
|
@ -69,6 +69,11 @@
|
||||||
<artifactId>jzlib</artifactId>
|
<artifactId>jzlib</artifactId>
|
||||||
<version>1.1.3</version>
|
<version>1.1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>joda-time</groupId>
|
||||||
|
<artifactId>joda-time</artifactId>
|
||||||
|
<version>2.7</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
125
teavm-classlib/src/main/java/org/teavm/classlib/impl/Base46.java
Normal file
125
teavm-classlib/src/main/java/org/teavm/classlib/impl/Base46.java
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Base47 encoding is best fit for encoding varible length numbers in JavaScript strings.</p>
|
||||||
|
*
|
||||||
|
* <p>47 = (int)(93 / 2), where 94 is the number of ASCII characters representable in JavaScript string
|
||||||
|
* without escaping. These characters are encoded by one byte in UTF-8 charset. All other character require
|
||||||
|
* either escaping or two or more bytes in UTF-8.</p>
|
||||||
|
*
|
||||||
|
* <p>We divide 93 by 2 for the following trick. Representing integers takes 5 bytes in Base93. However,
|
||||||
|
* we often need smaller integers that might be represented by one or two bytes. By each Base93 digit we
|
||||||
|
* can encode both part of the number and a flag indicating whether the number contains one more digit.</p>
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public class Base46 {
|
||||||
|
public static void encodeUnsigned(StringBuilder sb, int number) {
|
||||||
|
boolean hasMore;
|
||||||
|
do {
|
||||||
|
int digit = number % 46;
|
||||||
|
number /= 46;
|
||||||
|
hasMore = number > 0;
|
||||||
|
digit = digit * 2 + (hasMore ? 1 : 0);
|
||||||
|
sb.append(encodeDigit(digit));
|
||||||
|
} while (hasMore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void encode(StringBuilder sb, int number) {
|
||||||
|
encodeUnsigned(sb, Math.abs(number) * 2 + (number >= 0 ? 0 : 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void encodeUnsigned(StringBuilder sb, long number) {
|
||||||
|
boolean hasMore;
|
||||||
|
do {
|
||||||
|
int digit = (int)(number % 46);
|
||||||
|
number /= 46;
|
||||||
|
hasMore = number > 0;
|
||||||
|
digit = digit * 2 + (hasMore ? 1 : 0);
|
||||||
|
sb.append(encodeDigit(digit));
|
||||||
|
} while (hasMore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void encode(StringBuilder sb, long number) {
|
||||||
|
encodeUnsigned(sb, Math.abs(number) * 2 + (number >= 0 ? 0 : 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int decodeUnsigned(CharFlow seq) {
|
||||||
|
int number = 0;
|
||||||
|
int pos = 1;
|
||||||
|
boolean hasMore;
|
||||||
|
do {
|
||||||
|
int digit = decodeDigit(seq.characters[seq.pointer++]);
|
||||||
|
hasMore = digit % 2 == 1;
|
||||||
|
number += pos * (digit / 2);
|
||||||
|
pos *= 46;
|
||||||
|
} while (hasMore);
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int decode(CharFlow seq) {
|
||||||
|
int number = decodeUnsigned(seq);
|
||||||
|
int result = number / 2;
|
||||||
|
if (number % 2 != 0) {
|
||||||
|
result = -result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long decodeUnsignedLong(CharFlow seq) {
|
||||||
|
long number = 0;
|
||||||
|
long pos = 1;
|
||||||
|
boolean hasMore;
|
||||||
|
do {
|
||||||
|
int digit = decodeDigit(seq.characters[seq.pointer++]);
|
||||||
|
hasMore = digit % 2 == 1;
|
||||||
|
number += pos * (digit / 2);
|
||||||
|
pos *= 46;
|
||||||
|
} while (hasMore);
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long decodeLong(CharFlow seq) {
|
||||||
|
long number = decodeUnsigned(seq);
|
||||||
|
long result = number / 2;
|
||||||
|
if (number % 2 != 0) {
|
||||||
|
result = -result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static char encodeDigit(int digit) {
|
||||||
|
if (digit < 2) {
|
||||||
|
return (char)(digit + ' ');
|
||||||
|
} else if (digit < 59) {
|
||||||
|
return (char)(digit + 1 + ' ');
|
||||||
|
} else {
|
||||||
|
return (char)(digit + 2 + ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int decodeDigit(char c) {
|
||||||
|
if (c < '"') {
|
||||||
|
return c - ' ';
|
||||||
|
} else if (c < '\\') {
|
||||||
|
return c - ' ' - 1;
|
||||||
|
} else {
|
||||||
|
return c - ' ' - 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public class CharFlow {
|
||||||
|
public final char[] characters;
|
||||||
|
public int pointer;
|
||||||
|
|
||||||
|
public CharFlow(char[] characters) {
|
||||||
|
this.characters = characters;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* 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.tz;
|
||||||
|
|
||||||
|
import org.joda.time.DateTimeZone;
|
||||||
|
import org.teavm.classlib.impl.Base46;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.platform.metadata.MetadataGenerator;
|
||||||
|
import org.teavm.platform.metadata.MetadataGeneratorContext;
|
||||||
|
import org.teavm.platform.metadata.ResourceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public class TimeZoneGenerator implements MetadataGenerator {
|
||||||
|
private static int[] divisors = { 60_000, 300_000, 1800_000, 3600_000 };
|
||||||
|
public static int RESOLUTION_MINUTE = 0;
|
||||||
|
public static int RESOLUTION_5_MINUTES = 1;
|
||||||
|
public static int RESOLUTION_HALF_HOUR = 2;
|
||||||
|
public static int RESOLUTION_HOUR = 3;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceMap<ResourceMap<TimeZoneResource>> generateMetadata(
|
||||||
|
MetadataGeneratorContext context, MethodReference method) {
|
||||||
|
ResourceMap<ResourceMap<TimeZoneResource>> result = context.createResourceMap();
|
||||||
|
for (String id : DateTimeZone.getAvailableIDs()) {
|
||||||
|
int sepIndex = id.indexOf('/');
|
||||||
|
String areaName = id.substring(0, sepIndex);
|
||||||
|
String locationName = id.substring(sepIndex + 1);
|
||||||
|
ResourceMap<TimeZoneResource> area = result.get(areaName);
|
||||||
|
if (area == null) {
|
||||||
|
area = context.createResourceMap();
|
||||||
|
result.put(areaName, area);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTimeZone tz = DateTimeZone.forID(id);
|
||||||
|
TimeZoneResource tzRes = context.createResource(TimeZoneResource.class);
|
||||||
|
tzRes.setAbbreviation(locationName);
|
||||||
|
tzRes.setData(encodeData(tz));
|
||||||
|
area.put(locationName, tzRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encodeData(DateTimeZone tz) {
|
||||||
|
// Find resolution
|
||||||
|
int resolution = RESOLUTION_HOUR;
|
||||||
|
long current = 0;
|
||||||
|
long offset = tz.getOffset(0);
|
||||||
|
while (true) {
|
||||||
|
long next = tz.nextTransition(current);
|
||||||
|
if (next == current) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
|
||||||
|
int nextOffset = tz.getOffset(next);
|
||||||
|
if (nextOffset == offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = nextOffset;
|
||||||
|
resolution = getResolution(resolution, current);
|
||||||
|
resolution = getResolution(resolution, offset);
|
||||||
|
if (resolution == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
Base46.encode(sb, resolution);
|
||||||
|
|
||||||
|
current = 0;
|
||||||
|
offset = tz.getOffset(0);
|
||||||
|
int divisor = divisors[resolution];
|
||||||
|
long last = 0;
|
||||||
|
long lastOffset = offset / divisor;
|
||||||
|
Base46.encode(sb, lastOffset);
|
||||||
|
while (true) {
|
||||||
|
long next = tz.nextTransition(current);
|
||||||
|
if (next == current) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
|
||||||
|
int nextOffset = tz.getOffset(next);
|
||||||
|
if (nextOffset == offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = nextOffset;
|
||||||
|
long newTime = current / divisor;
|
||||||
|
long newOffset = offset / divisor;
|
||||||
|
Base46.encodeUnsigned(sb, newTime - last);
|
||||||
|
Base46.encode(sb, newOffset - lastOffset);
|
||||||
|
last = newTime;
|
||||||
|
lastOffset = newOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getResolution(int currentResolution, long value) {
|
||||||
|
while (currentResolution > 0 && value % divisors[currentResolution] != 0) {
|
||||||
|
--currentResolution;
|
||||||
|
}
|
||||||
|
return currentResolution;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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.tz;
|
||||||
|
|
||||||
|
import org.teavm.platform.metadata.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public interface TimeZoneResource extends Resource {
|
||||||
|
String getAbbreviation();
|
||||||
|
|
||||||
|
void setAbbreviation(String abbreviation);
|
||||||
|
|
||||||
|
String getData();
|
||||||
|
|
||||||
|
void setData(String data);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* 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.tz;
|
||||||
|
|
||||||
|
import org.teavm.platform.metadata.MetadataProvider;
|
||||||
|
import org.teavm.platform.metadata.ResourceMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public class TimeZoneResourceProvider {
|
||||||
|
public static TimeZoneResource getTimeZone(String id) {
|
||||||
|
int sepIndex = id.indexOf('/');
|
||||||
|
String areaName = id.substring(0, sepIndex);
|
||||||
|
String locationName = id.substring(sepIndex + 1);
|
||||||
|
ResourceMap<TimeZoneResource> area = getResource().get(areaName);
|
||||||
|
if (area == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return area.get(locationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@MetadataProvider(TimeZoneGenerator.class)
|
||||||
|
private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource();
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public class Base46Test {
|
||||||
|
@Test
|
||||||
|
public void encode() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = -65536; i <= 65536; ++i) {
|
||||||
|
sb.setLength(0);
|
||||||
|
Base46.encode(sb, i);
|
||||||
|
System.out.println(i + " - " + sb);
|
||||||
|
CharFlow flow = new CharFlow(sb.toString().toCharArray());
|
||||||
|
int num = Base46.decode(flow);
|
||||||
|
assertEquals(i, num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.teavm.classlib.java.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.teavm.classlib.impl.tz.TimeZoneResourceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
public class TimeZoneTest {
|
||||||
|
@Test
|
||||||
|
public void resourceProvided() {
|
||||||
|
assertNotNull(TimeZoneResourceProvider.getTimeZone("Europe/Moscow"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user