mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Stdlib: save tzdb as set of rules instead of set of transitions
Generate transitions at run time
This commit is contained in:
parent
71f87d79a5
commit
3cfe4644cd
|
@ -193,9 +193,10 @@ public class DateTimeZoneBuilder {
|
||||||
int saveMillis = 0;
|
int saveMillis = 0;
|
||||||
|
|
||||||
int ruleSetCount = iRuleSets.size();
|
int ruleSetCount = iRuleSets.size();
|
||||||
|
GregorianCalendar calendar = new GregorianCalendar(getGMT());
|
||||||
for (int i = 0; i < ruleSetCount; i++) {
|
for (int i = 0; i < ruleSetCount; i++) {
|
||||||
RuleSet rs = iRuleSets.get(i);
|
RuleSet rs = iRuleSets.get(i);
|
||||||
Transition next = rs.firstTransition(millis);
|
Transition next = rs.firstTransition(millis, calendar);
|
||||||
if (next == null) {
|
if (next == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -206,7 +207,7 @@ public class DateTimeZoneBuilder {
|
||||||
// Copy it since we're going to destroy it.
|
// Copy it since we're going to destroy it.
|
||||||
rs = new RuleSet(rs);
|
rs = new RuleSet(rs);
|
||||||
|
|
||||||
while ((next = rs.nextTransition(millis, saveMillis)) != null) {
|
while ((next = rs.nextTransition(millis, saveMillis, calendar)) != null) {
|
||||||
if (addTransition(transitions, next)) {
|
if (addTransition(transitions, next)) {
|
||||||
if (tailZone != null) {
|
if (tailZone != null) {
|
||||||
// Got the extra transition before DSTZone.
|
// Got the extra transition before DSTZone.
|
||||||
|
@ -305,20 +306,52 @@ public class DateTimeZoneBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(StringBuilder sb) {
|
public void write(StringBuilder sb) {
|
||||||
sb.append(iMode);
|
int flags = 0;
|
||||||
|
if (iAdvance) {
|
||||||
|
flags |= 1;
|
||||||
|
}
|
||||||
|
if (iDayOfWeek != 0) {
|
||||||
|
flags |= 2;
|
||||||
|
}
|
||||||
|
switch (iMode) {
|
||||||
|
case 'w':
|
||||||
|
flags |= 4;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
flags |= 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Base46.encodeUnsigned(sb, flags);
|
||||||
Base46.encodeUnsigned(sb, iMonthOfYear);
|
Base46.encodeUnsigned(sb, iMonthOfYear);
|
||||||
Base46.encodeUnsigned(sb, iDayOfMonth);
|
Base46.encodeUnsigned(sb, iDayOfMonth);
|
||||||
Base46.encode(sb, iDayOfWeek);
|
if (iDayOfWeek != 0) {
|
||||||
sb.append(iAdvance ? 'y' : 'n');
|
Base46.encode(sb, iDayOfWeek);
|
||||||
|
}
|
||||||
StorableDateTimeZone.writeUnsignedTime(sb, iMillisOfDay);
|
StorableDateTimeZone.writeUnsignedTime(sb, iMillisOfDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OfYear read(CharFlow flow) {
|
public static OfYear read(CharFlow flow) {
|
||||||
char mode = flow.characters[flow.pointer++];
|
int flags = Base46.decodeUnsigned(flow);
|
||||||
|
boolean advance = (flags & 1) != 0;
|
||||||
|
boolean hasDayOfWeek = (flags & 2) != 0;
|
||||||
|
|
||||||
|
int modeBits = (flags >>> 2) & 3;
|
||||||
|
char mode;
|
||||||
|
switch (modeBits) {
|
||||||
|
case 1:
|
||||||
|
mode = 'w';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
mode = 's';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode = 'u';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
int monthOfYear = Base46.decodeUnsigned(flow);
|
int monthOfYear = Base46.decodeUnsigned(flow);
|
||||||
int dayOfMonth = Base46.decodeUnsigned(flow);
|
int dayOfMonth = Base46.decodeUnsigned(flow);
|
||||||
int dayOfWeek = Base46.decode(flow);
|
int dayOfWeek = hasDayOfWeek ? Base46.decode(flow) : 0;
|
||||||
boolean advance = flow.characters[flow.pointer++] == 'y';
|
|
||||||
int millisOfDay = (int) StorableDateTimeZone.readUnsignedTime(flow);
|
int millisOfDay = (int) StorableDateTimeZone.readUnsignedTime(flow);
|
||||||
return new OfYear(mode, monthOfYear, dayOfMonth, dayOfWeek, advance, millisOfDay);
|
return new OfYear(mode, monthOfYear, dayOfMonth, dayOfWeek, advance, millisOfDay);
|
||||||
}
|
}
|
||||||
|
@ -358,7 +391,7 @@ public class DateTimeZoneBuilder {
|
||||||
/**
|
/**
|
||||||
* @param standardOffset standard offset just before next recurrence
|
* @param standardOffset standard offset just before next recurrence
|
||||||
*/
|
*/
|
||||||
public long next(long instant, int standardOffset, int saveMillis) {
|
public long next(long instant, int standardOffset, int saveMillis, GregorianCalendar calendar) {
|
||||||
int offset;
|
int offset;
|
||||||
if (iMode == 'w') {
|
if (iMode == 'w') {
|
||||||
offset = standardOffset + saveMillis;
|
offset = standardOffset + saveMillis;
|
||||||
|
@ -371,7 +404,6 @@ public class DateTimeZoneBuilder {
|
||||||
// Convert from UTC to local time.
|
// Convert from UTC to local time.
|
||||||
instant += offset;
|
instant += offset;
|
||||||
|
|
||||||
GregorianCalendar calendar = new GregorianCalendar(getGMT());
|
|
||||||
calendar.setTimeInMillis(instant);
|
calendar.setTimeInMillis(instant);
|
||||||
calendar.set(Calendar.MONTH, iMonthOfYear - 1);
|
calendar.set(Calendar.MONTH, iMonthOfYear - 1);
|
||||||
calendar.set(Calendar.DATE, 1);
|
calendar.set(Calendar.DATE, 1);
|
||||||
|
@ -505,7 +537,7 @@ public class DateTimeZoneBuilder {
|
||||||
*/
|
*/
|
||||||
static final class Recurrence {
|
static final class Recurrence {
|
||||||
final OfYear iOfYear;
|
final OfYear iOfYear;
|
||||||
final int iSaveMillis;
|
int iSaveMillis;
|
||||||
|
|
||||||
Recurrence(OfYear ofYear, int saveMillis) {
|
Recurrence(OfYear ofYear, int saveMillis) {
|
||||||
iOfYear = ofYear;
|
iOfYear = ofYear;
|
||||||
|
@ -519,8 +551,8 @@ public class DateTimeZoneBuilder {
|
||||||
/**
|
/**
|
||||||
* @param standardOffset standard offset just before next recurrence
|
* @param standardOffset standard offset just before next recurrence
|
||||||
*/
|
*/
|
||||||
public long next(long instant, int standardOffset, int saveMillis) {
|
public long next(long instant, int standardOffset, int saveMillis, GregorianCalendar calendar) {
|
||||||
return iOfYear.next(instant, standardOffset, saveMillis);
|
return iOfYear.next(instant, standardOffset, saveMillis, calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -578,8 +610,7 @@ public class DateTimeZoneBuilder {
|
||||||
return iRecurrence.getSaveMillis();
|
return iRecurrence.getSaveMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long next(final long instant, int standardOffset, int saveMillis) {
|
public long next(final long instant, int standardOffset, int saveMillis, GregorianCalendar calendar) {
|
||||||
Calendar calendar = Calendar.getInstance(getGMT());
|
|
||||||
final int wallOffset = standardOffset + saveMillis;
|
final int wallOffset = standardOffset + saveMillis;
|
||||||
long testInstant = instant;
|
long testInstant = instant;
|
||||||
|
|
||||||
|
@ -591,6 +622,10 @@ public class DateTimeZoneBuilder {
|
||||||
year = calendar.get(Calendar.YEAR);
|
year = calendar.get(Calendar.YEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (year > iToYear) {
|
||||||
|
return instant;
|
||||||
|
}
|
||||||
|
|
||||||
if (year < iFromYear) {
|
if (year < iFromYear) {
|
||||||
calendar.setTimeInMillis(0);
|
calendar.setTimeInMillis(0);
|
||||||
calendar.set(Calendar.YEAR, iFromYear);
|
calendar.set(Calendar.YEAR, iFromYear);
|
||||||
|
@ -601,7 +636,7 @@ public class DateTimeZoneBuilder {
|
||||||
testInstant -= 1;
|
testInstant -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
long next = iRecurrence.next(testInstant, standardOffset, saveMillis);
|
long next = iRecurrence.next(testInstant, standardOffset, saveMillis, calendar);
|
||||||
|
|
||||||
if (next > instant) {
|
if (next > instant) {
|
||||||
calendar.setTimeInMillis(next + wallOffset);
|
calendar.setTimeInMillis(next + wallOffset);
|
||||||
|
@ -662,7 +697,8 @@ public class DateTimeZoneBuilder {
|
||||||
if (other == null) {
|
if (other == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return iMillis > other.iMillis && iWallOffset != other.iWallOffset;
|
return iMillis > other.iMillis
|
||||||
|
&& (iWallOffset != other.iWallOffset || iStandardOffset != other.iStandardOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,7 +773,7 @@ public class DateTimeZoneBuilder {
|
||||||
*
|
*
|
||||||
* @param firstMillis millis of first transition
|
* @param firstMillis millis of first transition
|
||||||
*/
|
*/
|
||||||
public Transition firstTransition(final long firstMillis) {
|
public Transition firstTransition(final long firstMillis, GregorianCalendar calendar) {
|
||||||
if (iInitialNameKey != null) {
|
if (iInitialNameKey != null) {
|
||||||
// Initial zone info explicitly set, so don't search the rules.
|
// Initial zone info explicitly set, so don't search the rules.
|
||||||
return new Transition(firstMillis, iStandardOffset + iInitialSaveMillis, iStandardOffset);
|
return new Transition(firstMillis, iStandardOffset + iInitialSaveMillis, iStandardOffset);
|
||||||
|
@ -755,7 +791,7 @@ public class DateTimeZoneBuilder {
|
||||||
Transition first = null;
|
Transition first = null;
|
||||||
|
|
||||||
Transition next;
|
Transition next;
|
||||||
while ((next = nextTransition(millis, saveMillis)) != null) {
|
while ((next = nextTransition(millis, saveMillis, calendar)) != null) {
|
||||||
millis = next.getMillis();
|
millis = next.getMillis();
|
||||||
|
|
||||||
if (millis == firstMillis) {
|
if (millis == firstMillis) {
|
||||||
|
@ -768,7 +804,7 @@ public class DateTimeZoneBuilder {
|
||||||
// Find first rule without savings. This way a more
|
// Find first rule without savings. This way a more
|
||||||
// accurate nameKey is found even though no rule
|
// accurate nameKey is found even though no rule
|
||||||
// extends to the RuleSet's lower limit.
|
// extends to the RuleSet's lower limit.
|
||||||
for (Rule rule : copy) {
|
for (Rule rule : iRules) {
|
||||||
if (rule.getSaveMillis() == 0) {
|
if (rule.getSaveMillis() == 0) {
|
||||||
first = new Transition(firstMillis, rule, iStandardOffset);
|
first = new Transition(firstMillis, rule, iStandardOffset);
|
||||||
break;
|
break;
|
||||||
|
@ -806,15 +842,20 @@ public class DateTimeZoneBuilder {
|
||||||
*
|
*
|
||||||
* @param saveMillis savings before next transition
|
* @param saveMillis savings before next transition
|
||||||
*/
|
*/
|
||||||
public Transition nextTransition(final long instant, final int saveMillis) {
|
public Transition nextTransition(long instant, int saveMillis, GregorianCalendar calendar) {
|
||||||
// Find next matching rule.
|
// Find next matching rule.
|
||||||
Rule nextRule = null;
|
Rule nextRule = null;
|
||||||
long nextMillis = Long.MAX_VALUE;
|
long nextMillis = Long.MAX_VALUE;
|
||||||
|
int nextYear = Integer.MAX_VALUE;
|
||||||
|
|
||||||
Iterator<Rule> it = iRules.iterator();
|
Iterator<Rule> it = iRules.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Rule rule = it.next();
|
Rule rule = it.next();
|
||||||
long next = rule.next(instant, iStandardOffset, saveMillis);
|
if (rule.iFromYear > nextYear) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
long next = rule.next(instant, iStandardOffset, saveMillis, calendar);
|
||||||
if (next <= instant) {
|
if (next <= instant) {
|
||||||
it.remove();
|
it.remove();
|
||||||
continue;
|
continue;
|
||||||
|
@ -825,6 +866,7 @@ public class DateTimeZoneBuilder {
|
||||||
// Found a better match.
|
// Found a better match.
|
||||||
nextRule = rule;
|
nextRule = rule;
|
||||||
nextMillis = next;
|
nextMillis = next;
|
||||||
|
nextYear = calendar.get(Calendar.YEAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -833,16 +875,14 @@ public class DateTimeZoneBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop precalculating if year reaches some arbitrary limit.
|
// Stop precalculating if year reaches some arbitrary limit.
|
||||||
Calendar c = Calendar.getInstance(getGMT());
|
calendar.setTimeInMillis(nextMillis);
|
||||||
c.setTimeInMillis(nextMillis);
|
if (calendar.get(Calendar.YEAR) >= YEAR_LIMIT) {
|
||||||
if (c.get(Calendar.YEAR) >= YEAR_LIMIT) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if upper limit reached or passed.
|
// Check if upper limit reached or passed.
|
||||||
if (iUpperYear < Integer.MAX_VALUE) {
|
if (iUpperYear < Integer.MAX_VALUE) {
|
||||||
long upperMillis =
|
long upperMillis = iUpperOfYear.setInstant(iUpperYear, iStandardOffset, saveMillis);
|
||||||
iUpperOfYear.setInstant(iUpperYear, iStandardOffset, saveMillis);
|
|
||||||
if (nextMillis >= upperMillis) {
|
if (nextMillis >= upperMillis) {
|
||||||
// At or after upper limit.
|
// At or after upper limit.
|
||||||
return null;
|
return null;
|
||||||
|
@ -890,6 +930,7 @@ public class DateTimeZoneBuilder {
|
||||||
final int iStandardOffset;
|
final int iStandardOffset;
|
||||||
final Recurrence iStartRecurrence;
|
final Recurrence iStartRecurrence;
|
||||||
final Recurrence iEndRecurrence;
|
final Recurrence iEndRecurrence;
|
||||||
|
final GregorianCalendar calendar = new GregorianCalendar(getGMT());
|
||||||
|
|
||||||
DSTZone(String id, int standardOffset, Recurrence startRecurrence, Recurrence endRecurrence) {
|
DSTZone(String id, int standardOffset, Recurrence startRecurrence, Recurrence endRecurrence) {
|
||||||
super(id);
|
super(id);
|
||||||
|
@ -923,7 +964,7 @@ public class DateTimeZoneBuilder {
|
||||||
long end;
|
long end;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
start = startRecurrence.next(instant, standardOffset, endRecurrence.getSaveMillis());
|
start = startRecurrence.next(instant, standardOffset, endRecurrence.getSaveMillis(), calendar);
|
||||||
if (instant > 0 && start < 0) {
|
if (instant > 0 && start < 0) {
|
||||||
// Overflowed.
|
// Overflowed.
|
||||||
start = instant;
|
start = instant;
|
||||||
|
@ -934,7 +975,7 @@ public class DateTimeZoneBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
end = endRecurrence.next(instant, standardOffset, startRecurrence.getSaveMillis());
|
end = endRecurrence.next(instant, standardOffset, startRecurrence.getSaveMillis(), calendar);
|
||||||
if (instant > 0 && end < 0) {
|
if (instant > 0 && end < 0) {
|
||||||
// Overflowed.
|
// Overflowed.
|
||||||
end = instant;
|
end = instant;
|
||||||
|
@ -994,14 +1035,14 @@ public class DateTimeZoneBuilder {
|
||||||
long end;
|
long end;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
start = startRecurrence.next(instant, standardOffset, endRecurrence.getSaveMillis());
|
start = startRecurrence.next(instant, standardOffset, endRecurrence.getSaveMillis(), calendar);
|
||||||
} catch (IllegalArgumentException | ArithmeticException e) {
|
} catch (IllegalArgumentException | ArithmeticException e) {
|
||||||
// Overflowed.
|
// Overflowed.
|
||||||
start = instant;
|
start = instant;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
end = endRecurrence.next(instant, standardOffset, startRecurrence.getSaveMillis());
|
end = endRecurrence.next(instant, standardOffset, startRecurrence.getSaveMillis(), calendar);
|
||||||
} catch (IllegalArgumentException | ArithmeticException e) {
|
} catch (IllegalArgumentException | ArithmeticException e) {
|
||||||
// Overflowed.
|
// Overflowed.
|
||||||
end = instant;
|
end = instant;
|
||||||
|
@ -1092,17 +1133,38 @@ public class DateTimeZoneBuilder {
|
||||||
++start;
|
++start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (start > 1) {
|
||||||
|
--start;
|
||||||
|
iTransitions[start] = iTransitions[0];
|
||||||
|
iWallOffsets[start] = iWallOffsets[0];
|
||||||
|
iStandardOffsets[start] = iStandardOffsets[0];
|
||||||
|
}
|
||||||
|
|
||||||
Base46.encodeUnsigned(sb, PRECALCULATED);
|
Base46.encodeUnsigned(sb, PRECALCULATED);
|
||||||
Base46.encodeUnsigned(sb, iTransitions.length - start);
|
|
||||||
|
int lengthEncoded = (iTransitions.length - start) << 1;
|
||||||
|
if (iTransitions[start] == Long.MIN_VALUE) {
|
||||||
|
lengthEncoded |= 1;
|
||||||
|
}
|
||||||
|
Base46.encodeUnsigned(sb, lengthEncoded);
|
||||||
|
|
||||||
long[] transitions = iTransitions.clone();
|
long[] transitions = iTransitions.clone();
|
||||||
for (int i = 0; i < transitions.length; ++i) {
|
for (int i = 0; i < transitions.length; ++i) {
|
||||||
transitions[i] = (transitions[i] / 60_000) * 60_000;
|
transitions[i] = (transitions[i] / 60_000) * 60_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeTime(sb, transitions[start]);
|
if (iTransitions[start] == Long.MIN_VALUE) {
|
||||||
for (int i = start + 1; i < transitions.length; ++i) {
|
if (start + 1 < transitions.length) {
|
||||||
writeTime(sb, transitions[i] - transitions[i - 1] - (365 * 3600 * 1000 / 2));
|
writeTime(sb, transitions[start + 1]);
|
||||||
|
for (int i = start + 2; i < transitions.length; ++i) {
|
||||||
|
writeTime(sb, transitions[i] - transitions[i - 1] - (365 * 3600 * 1000 / 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
writeTime(sb, transitions[start]);
|
||||||
|
for (int i = start + 1; i < transitions.length; ++i) {
|
||||||
|
writeTime(sb, transitions[i] - transitions[i - 1] - (365 * 3600 * 1000 / 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeTimeArray(sb, Arrays.copyOfRange(iWallOffsets, start, transitions.length));
|
writeTimeArray(sb, Arrays.copyOfRange(iWallOffsets, start, transitions.length));
|
||||||
|
@ -1118,13 +1180,25 @@ public class DateTimeZoneBuilder {
|
||||||
|
|
||||||
public static StorableDateTimeZone readZone(String id, CharFlow flow) {
|
public static StorableDateTimeZone readZone(String id, CharFlow flow) {
|
||||||
int length = Base46.decodeUnsigned(flow);
|
int length = Base46.decodeUnsigned(flow);
|
||||||
|
boolean firstLongIsMin = (length & 1) != 0;
|
||||||
|
length >>>= 1;
|
||||||
long[] transitions = new long[length];
|
long[] transitions = new long[length];
|
||||||
int[] wallOffsets = new int[length];
|
int[] wallOffsets = new int[length];
|
||||||
int[] standardOffsets = new int[length];
|
int[] standardOffsets = new int[length];
|
||||||
|
|
||||||
transitions[0] = readTime(flow);
|
if (firstLongIsMin) {
|
||||||
for (int i = 1; i < length; ++i) {
|
transitions[0] = Long.MIN_VALUE;
|
||||||
transitions[i] = transitions[i - 1] + readTime(flow) + 365 * 3600 * 1000 / 2;
|
if (transitions.length > 1) {
|
||||||
|
transitions[1] = readTime(flow);
|
||||||
|
for (int i = 2; i < length; ++i) {
|
||||||
|
transitions[i] = transitions[i - 1] + readTime(flow) + 365 * 3600 * 1000 / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
transitions[0] = readTime(flow);
|
||||||
|
for (int i = 1; i < length; ++i) {
|
||||||
|
transitions[i] = transitions[i - 1] + readTime(flow) + 365 * 3600 * 1000 / 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readTimeArray(flow, wallOffsets);
|
readTimeArray(flow, wallOffsets);
|
||||||
|
@ -1277,4 +1351,175 @@ public class DateTimeZoneBuilder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class RuleBasedZone extends StorableDateTimeZone {
|
||||||
|
private DateTimeZoneBuilder builder;
|
||||||
|
private DateTimeZone zone;
|
||||||
|
|
||||||
|
RuleBasedZone(String id, DateTimeZoneBuilder builder) {
|
||||||
|
super(id);
|
||||||
|
this.builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initZone() {
|
||||||
|
if (zone == null) {
|
||||||
|
zone = builder.toDateTimeZone(getID(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOffset(long instant) {
|
||||||
|
initZone();
|
||||||
|
return zone.getOffset(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStandardOffset(long instant) {
|
||||||
|
initZone();
|
||||||
|
return zone.getStandardOffset(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFixed() {
|
||||||
|
initZone();
|
||||||
|
return zone.isFixed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long nextTransition(long instant) {
|
||||||
|
initZone();
|
||||||
|
return zone.nextTransition(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long previousTransition(long instant) {
|
||||||
|
initZone();
|
||||||
|
return zone.previousTransition(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(StringBuilder sb) {
|
||||||
|
Base46.encodeUnsigned(sb, RULE_BASED);
|
||||||
|
Base46.encodeUnsigned(sb, builder.iRuleSets.size());
|
||||||
|
int currentYear = Integer.MIN_VALUE;
|
||||||
|
int fromYear = currentYear;
|
||||||
|
for (RuleSet rs : builder.iRuleSets) {
|
||||||
|
writeTime(sb, rs.iInitialSaveMillis);
|
||||||
|
writeTime(sb, rs.iStandardOffset);
|
||||||
|
List<Rule> rules = filterRules(fromYear, rs.iUpperYear, rs.iRules);
|
||||||
|
Base46.encodeUnsigned(sb, (rules.size() << 1) | (rs.iInitialNameKey != null ? 1 : 0));
|
||||||
|
for (Rule rule : rules) {
|
||||||
|
if (currentYear == Integer.MIN_VALUE) {
|
||||||
|
Base46.encode(sb, rule.getFromYear());
|
||||||
|
} else {
|
||||||
|
Base46.encode(sb, rule.getFromYear() - currentYear);
|
||||||
|
}
|
||||||
|
currentYear = rule.getFromYear();
|
||||||
|
if (rule.getToYear() != Integer.MAX_VALUE) {
|
||||||
|
int yearDelta = rule.getToYear() - currentYear;
|
||||||
|
if (yearDelta < 0) {
|
||||||
|
yearDelta = (-yearDelta - 1) << 1 | 1;
|
||||||
|
} else {
|
||||||
|
yearDelta = yearDelta << 1;
|
||||||
|
}
|
||||||
|
Base46.encodeUnsigned(sb, yearDelta + 1);
|
||||||
|
currentYear = rule.getToYear();
|
||||||
|
} else {
|
||||||
|
Base46.encodeUnsigned(sb, 0);
|
||||||
|
}
|
||||||
|
rule.iRecurrence.write(sb);
|
||||||
|
}
|
||||||
|
if (rs.iUpperYear == Integer.MAX_VALUE) {
|
||||||
|
Base46.encodeUnsigned(sb, 0);
|
||||||
|
} else {
|
||||||
|
int encodedYear;
|
||||||
|
if (currentYear == Integer.MIN_VALUE) {
|
||||||
|
encodedYear = rs.iUpperYear;
|
||||||
|
} else {
|
||||||
|
encodedYear = rs.iUpperYear - currentYear;
|
||||||
|
}
|
||||||
|
encodedYear = encodedYear < 0 ? ((-encodedYear - 1) << 1) | 1 : encodedYear << 1;
|
||||||
|
Base46.encodeUnsigned(sb, encodedYear + 1);
|
||||||
|
rs.iUpperOfYear.write(sb);
|
||||||
|
currentYear = rs.iUpperYear;
|
||||||
|
}
|
||||||
|
fromYear = rs.iUpperYear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StorableDateTimeZone readZone(String id, CharFlow flow) {
|
||||||
|
DateTimeZoneBuilder builder = new DateTimeZoneBuilder();
|
||||||
|
int ruleSetCount = Base46.decodeUnsigned(flow);
|
||||||
|
int currentYear = Integer.MIN_VALUE;
|
||||||
|
for (int i = 0; i < ruleSetCount; ++i) {
|
||||||
|
RuleSet rs = new RuleSet();
|
||||||
|
rs.iInitialSaveMillis = (int) readTime(flow);
|
||||||
|
rs.iStandardOffset = (int) readTime(flow);
|
||||||
|
int ruleCount = Base46.decodeUnsigned(flow);
|
||||||
|
if ((ruleCount & 1) != 0) {
|
||||||
|
rs.iInitialNameKey = "*";
|
||||||
|
}
|
||||||
|
ruleCount >>>= 1;
|
||||||
|
for (int j = 0; j < ruleCount; ++j) {
|
||||||
|
int fromYear = Base46.decode(flow);
|
||||||
|
if (currentYear != Integer.MIN_VALUE) {
|
||||||
|
fromYear += currentYear;
|
||||||
|
}
|
||||||
|
currentYear = fromYear;
|
||||||
|
int toYear = Base46.decodeUnsigned(flow);
|
||||||
|
if (toYear == 0) {
|
||||||
|
toYear = Integer.MAX_VALUE;
|
||||||
|
} else {
|
||||||
|
toYear--;
|
||||||
|
toYear = (toYear & 1) == 0 ? toYear >>> 1 : -(toYear >>> 1) - 1;
|
||||||
|
if (currentYear != Integer.MIN_VALUE) {
|
||||||
|
toYear += currentYear;
|
||||||
|
}
|
||||||
|
currentYear = toYear;
|
||||||
|
}
|
||||||
|
Recurrence recurrence = Recurrence.read(flow);
|
||||||
|
rs.iRules.add(new Rule(recurrence, fromYear, toYear));
|
||||||
|
}
|
||||||
|
int year = Base46.decodeUnsigned(flow);
|
||||||
|
if (year == 0) {
|
||||||
|
rs.iUpperYear = Integer.MAX_VALUE;
|
||||||
|
} else {
|
||||||
|
year--;
|
||||||
|
if ((year & 1) == 0) {
|
||||||
|
year >>>= 1;
|
||||||
|
} else {
|
||||||
|
year = -(year >>> 1) - 1;
|
||||||
|
}
|
||||||
|
if (currentYear != Integer.MIN_VALUE) {
|
||||||
|
year += currentYear;
|
||||||
|
}
|
||||||
|
rs.iUpperYear = year;
|
||||||
|
rs.iUpperOfYear = OfYear.read(flow);
|
||||||
|
currentYear = year;
|
||||||
|
}
|
||||||
|
builder.iRuleSets.add(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RuleBasedZone(id, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Rule> filterRules(int fromYear, int toYear, List<Rule> rules) {
|
||||||
|
List<Rule> filtered = new ArrayList<>();
|
||||||
|
if (fromYear != Integer.MIN_VALUE) {
|
||||||
|
int bestYear = Integer.MIN_VALUE;
|
||||||
|
for (Rule rule : rules) {
|
||||||
|
if (rule.getToYear() < fromYear && rule.getToYear() > bestYear) {
|
||||||
|
bestYear = rule.getToYear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fromYear = bestYear;
|
||||||
|
}
|
||||||
|
for (Rule rule : rules) {
|
||||||
|
if (rule.getToYear() >= fromYear && rule.getFromYear() <= toYear) {
|
||||||
|
filtered.add(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,12 +19,14 @@ import org.teavm.classlib.impl.Base46;
|
||||||
import org.teavm.classlib.impl.CharFlow;
|
import org.teavm.classlib.impl.CharFlow;
|
||||||
import org.teavm.classlib.impl.tz.DateTimeZoneBuilder.DSTZone;
|
import org.teavm.classlib.impl.tz.DateTimeZoneBuilder.DSTZone;
|
||||||
import org.teavm.classlib.impl.tz.DateTimeZoneBuilder.PrecalculatedZone;
|
import org.teavm.classlib.impl.tz.DateTimeZoneBuilder.PrecalculatedZone;
|
||||||
|
import org.teavm.classlib.impl.tz.DateTimeZoneBuilder.RuleBasedZone;
|
||||||
|
|
||||||
public abstract class StorableDateTimeZone extends DateTimeZone {
|
public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
public static final int PRECALCULATED = 0;
|
public static final int PRECALCULATED = 0;
|
||||||
public static final int FIXED = 1;
|
public static final int FIXED = 1;
|
||||||
public static final int DST = 3;
|
public static final int DST = 3;
|
||||||
public static final int ALIAS = 4;
|
public static final int ALIAS = 4;
|
||||||
|
public static final int RULE_BASED = 5;
|
||||||
|
|
||||||
public StorableDateTimeZone(String id) {
|
public StorableDateTimeZone(String id) {
|
||||||
super(id);
|
super(id);
|
||||||
|
@ -34,9 +36,9 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
|
|
||||||
public static void writeTime(StringBuilder sb, long time) {
|
public static void writeTime(StringBuilder sb, long time) {
|
||||||
if (time % 1800_000 == 0) {
|
if (time % 1800_000 == 0) {
|
||||||
Base46.encode(sb, (int) ((time / 1800_000) << 1));
|
Base46.encode(sb, (time / 1800_000) << 1);
|
||||||
} else {
|
} else {
|
||||||
Base46.encode(sb, (int) (((time / 60_000) << 1) | 1));
|
Base46.encode(sb, (time << 1) | 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +47,7 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
if ((value & 1) == 0) {
|
if ((value & 1) == 0) {
|
||||||
return (value >> 1) * 1800_000;
|
return (value >> 1) * 1800_000;
|
||||||
} else {
|
} else {
|
||||||
return (value >> 1) * 60_000;
|
return value >> 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +55,7 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
if (time % 1800_000 == 0) {
|
if (time % 1800_000 == 0) {
|
||||||
Base46.encodeUnsigned(sb, (int) ((time / 1800_000) << 1));
|
Base46.encodeUnsigned(sb, (int) ((time / 1800_000) << 1));
|
||||||
} else {
|
} else {
|
||||||
Base46.encodeUnsigned(sb, (int) (((time / 60_000) << 1) | 1));
|
Base46.encodeUnsigned(sb, (int) ((time << 1) | 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
if ((value & 1) == 0) {
|
if ((value & 1) == 0) {
|
||||||
return (value >>> 1) * 1800_000;
|
return (value >>> 1) * 1800_000;
|
||||||
} else {
|
} else {
|
||||||
return (value >>> 1) * 60_000;
|
return value >>> 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +126,8 @@ public abstract class StorableDateTimeZone extends DateTimeZone {
|
||||||
return DSTZone.readZone(id, flow);
|
return DSTZone.readZone(id, flow);
|
||||||
case FIXED:
|
case FIXED:
|
||||||
return FixedDateTimeZone.readZone(id, flow);
|
return FixedDateTimeZone.readZone(id, flow);
|
||||||
|
case RULE_BASED:
|
||||||
|
return RuleBasedZone.readZone(id, flow);
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown zone type: " + type);
|
throw new IllegalArgumentException("Unknown zone type: " + type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class TimeZoneCache {
|
||||||
String data = aliasLine.substring(index + 1);
|
String data = aliasLine.substring(index + 1);
|
||||||
CharFlow flow = new CharFlow(data.toCharArray());
|
CharFlow flow = new CharFlow(data.toCharArray());
|
||||||
Base46.decode(flow);
|
Base46.decode(flow);
|
||||||
result.put(id, result.get(data.substring(flow.pointer)));
|
result.put(id, new AliasDateTimeZone(id, result.get(data.substring(flow.pointer))));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,7 @@ public class ZoneInfoCompiler {
|
||||||
long end = ISOChronology.getInstanceUTC().year().set(0, 2050);
|
long end = ISOChronology.getInstanceUTC().year().set(0, 2050);
|
||||||
|
|
||||||
int offset = tz.getOffset(millis);
|
int offset = tz.getOffset(millis);
|
||||||
|
int stdOffset = tz.getStandardOffset(millis);
|
||||||
|
|
||||||
List<Long> transitions = new ArrayList<>();
|
List<Long> transitions = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -140,16 +141,18 @@ public class ZoneInfoCompiler {
|
||||||
millis = next;
|
millis = next;
|
||||||
|
|
||||||
int nextOffset = tz.getOffset(millis);
|
int nextOffset = tz.getOffset(millis);
|
||||||
|
int nextStdOffset = tz.getStandardOffset(millis);
|
||||||
|
|
||||||
if (offset == nextOffset) {
|
if (offset == nextOffset && stdOffset == nextStdOffset) {
|
||||||
System.out.println("*d* Error in " + tz.getID() + " "
|
System.out.println("*d* Error in " + tz.getID() + " "
|
||||||
+ new DateTime(millis, ISOChronology.getInstanceUTC()));
|
+ new DateTime(millis, ISOChronology.getInstanceUTC()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
transitions.add(Long.valueOf(millis));
|
transitions.add(millis);
|
||||||
|
|
||||||
offset = nextOffset;
|
offset = nextOffset;
|
||||||
|
stdOffset = nextStdOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now verify that reverse transitions match up.
|
// Now verify that reverse transitions match up.
|
||||||
|
@ -165,7 +168,7 @@ public class ZoneInfoCompiler {
|
||||||
|
|
||||||
millis = prev;
|
millis = prev;
|
||||||
|
|
||||||
long trans = transitions.get(i).longValue();
|
long trans = transitions.get(i);
|
||||||
|
|
||||||
if (trans - 1 != millis) {
|
if (trans - 1 != millis) {
|
||||||
System.out.println("*r* Error in " + tz.getID() + " "
|
System.out.println("*r* Error in " + tz.getID() + " "
|
||||||
|
@ -203,11 +206,10 @@ public class ZoneInfoCompiler {
|
||||||
Map<String, Zone> sourceMap = new TreeMap<>();
|
Map<String, Zone> sourceMap = new TreeMap<>();
|
||||||
|
|
||||||
// write out the standard entries
|
// write out the standard entries
|
||||||
for (int i = 0; i < iZones.size(); i++) {
|
for (Zone zone : iZones) {
|
||||||
Zone zone = iZones.get(i);
|
|
||||||
DateTimeZoneBuilder builder = new DateTimeZoneBuilder();
|
DateTimeZoneBuilder builder = new DateTimeZoneBuilder();
|
||||||
zone.addToBuilder(builder, iRuleSets);
|
zone.addToBuilder(builder, iRuleSets);
|
||||||
StorableDateTimeZone tz = builder.toDateTimeZone(zone.iName, true);
|
StorableDateTimeZone tz = new DateTimeZoneBuilder.RuleBasedZone(zone.iName, builder);
|
||||||
if (test(tz.getID(), tz)) {
|
if (test(tz.getID(), tz)) {
|
||||||
map.put(tz.getID(), tz);
|
map.put(tz.getID(), tz);
|
||||||
sourceMap.put(tz.getID(), zone);
|
sourceMap.put(tz.getID(), zone);
|
||||||
|
@ -225,7 +227,7 @@ public class ZoneInfoCompiler {
|
||||||
} else {
|
} else {
|
||||||
DateTimeZoneBuilder builder = new DateTimeZoneBuilder();
|
DateTimeZoneBuilder builder = new DateTimeZoneBuilder();
|
||||||
sourceZone.addToBuilder(builder, iRuleSets);
|
sourceZone.addToBuilder(builder, iRuleSets);
|
||||||
StorableDateTimeZone revived = builder.toDateTimeZone(alias, true);
|
StorableDateTimeZone revived = new DateTimeZoneBuilder.RuleBasedZone(alias, builder);
|
||||||
if (test(revived.getID(), revived)) {
|
if (test(revived.getID(), revived)) {
|
||||||
map.put(revived.getID(), revived);
|
map.put(revived.getID(), revived);
|
||||||
}
|
}
|
||||||
|
@ -604,7 +606,7 @@ public class ZoneInfoCompiler {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
return str + "...\n" + iNext.toString();
|
return str + "...\n" + iNext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user