mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +08:00
Start to implement time zones
This commit is contained in:
parent
7277696870
commit
b264e34ef8
@ -69,6 +69,11 @@
|
||||
<artifactId>jzlib</artifactId>
|
||||
<version>1.1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>2.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<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