Stdlib: save tzdb as set of rules instead of set of transitions

Generate transitions at run time
This commit is contained in:
Alexey Andreev 2021-03-16 19:53:29 +03:00
parent 71f87d79a5
commit 3cfe4644cd
4 changed files with 303 additions and 52 deletions

View File

@ -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;
}
}
} }

View File

@ -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);
} }

View File

@ -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;
} }

View File

@ -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;
} }
} }
} }