Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix TZDB compiler %z parsing #787

Merged
merged 1 commit into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 32 additions & 18 deletions src/main/java/org/joda/time/tz/ZoneInfoCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ static boolean test(String id, DateTimeZone tz) {
return false;
}

if (nextKey == null || (nextKey.length() < 3 && !"??".equals(nextKey))) {
if (nextKey == null || (nextKey.length() < 3 && !"??".equals(nextKey) && !"%z".equals(nextKey))) {
System.out.println("*s* Error in " + tz.getID() + " "
+ new DateTime(millis,
ISOChronology.getInstanceUTC())
Expand Down Expand Up @@ -501,7 +501,7 @@ public Map<String, DateTimeZone> compile(File outputDir, File[] sources) throws
}
}

// store "back" links as aliases (where name is permanently mapped
// store "back" links as aliases (where name is permanently mapped)
for (int pass = 0; pass < 2; pass++) {
for (int i = 0; i < iBackLinks.size(); i += 2) {
String id = iBackLinks.get(i);
Expand Down Expand Up @@ -840,17 +840,23 @@ static class Rule {
* Adds a recurring savings rule to the builder.
*
* @param builder the builder
* @param standardMillis the standard millis, pre-adjusted to the negativeSave value
* @param negativeSave the negative save value
* @param nameFormat the name format
*/
public void addRecurring(DateTimeZoneBuilder builder, int negativeSave, String nameFormat) {
public void addRecurring(DateTimeZoneBuilder builder, int standardMillis, int negativeSave, String nameFormat) {
int saveMillis = iSaveMillis + -negativeSave;
String nameKey = formatName(nameFormat, saveMillis, iLetterS);
String nameKey = formatName(nameFormat, standardMillis, saveMillis, iLetterS);
iDateTimeOfYear.addRecurring(builder, nameKey, saveMillis, iFromYear, iToYear);
}

// ScopedForTesting
static String formatName(String nameFormat, int saveMillis, String letterS) {
static String formatName(String nameFormat, int standardMillis, int saveMillis, String letterS) {
// this method is called while adding rules to the builder
// the input parameters give the context as to whether the input is standard or 'summer' time
// saveMillis == 0 in 'winter' time, and != 0 in 'summer' time
// (negative save millis have been applied before this method is called)

// SPEC: Alternatively, a slash (/) separates standard and daylight abbreviations.
int index = nameFormat.indexOf('/');
if (index > 0) {
Expand All @@ -876,22 +882,30 @@ static String formatName(String nameFormat, int saveMillis, String letterS) {
// offset in the form ±hh, ±hhmm, or ±hhmmss, using the shortest form that does not lose information,
// where hh, mm, and ss are the hours, minutes, and seconds east (+) or west (-) of UT.
if (nameFormat.equals("%z")) {
String sign = saveMillis < 0 ? "-" : "+";
int saveSecs = Math.abs(saveMillis) / 1000;
int hours = saveSecs / 3600;
int mins = ((saveSecs / 60) % 60);
int secs = (saveSecs % 60);
if (secs == 0) {
if (mins == 0) {
return sign + twoDigitString(hours);
}
return sign + twoDigitString(hours) + twoDigitString(mins);
if (saveMillis == 0) {
return formatOffset(standardMillis).intern();
} else {
return formatOffset(standardMillis + saveMillis).intern();
}
return sign + twoDigitString(hours) + twoDigitString(mins) + twoDigitString(secs);
}
return nameFormat;
}

private static String formatOffset(int millis) {
String sign = millis < 0 ? "-" : "+";
int saveSecs = Math.abs(millis) / 1000;
int hours = saveSecs / 3600;
int mins = ((saveSecs / 60) % 60);
int secs = (saveSecs % 60);
if (secs == 0) {
if (mins == 0) {
return sign + twoDigitString(hours);
}
return sign + twoDigitString(hours) + twoDigitString(mins);
}
return sign + twoDigitString(hours) + twoDigitString(mins) + twoDigitString(secs);
}

private static String twoDigitString(int value) {
return Integer.toString(value + 100).substring(1);
}
Expand Down Expand Up @@ -960,13 +974,13 @@ public void addRecurring(DateTimeZoneBuilder builder, int standardMillis, String
// add a fake rule that predates all other rules to ensure standard=summer (see Namibia)
if (negativeSave < 0) {
Rule rule = new Rule(iRules.get(0));
rule.addRecurring(builder, negativeSave, nameFormat);
rule.addRecurring(builder, standardMillis, negativeSave, nameFormat);
}

// add each rule, passing through the negative save to alter the actual iSaveMillis value that is used
for (int i = 0; i < iRules.size(); i++) {
Rule rule = iRules.get(i);
rule.addRecurring(builder, negativeSave, nameFormat);
rule.addRecurring(builder, standardMillis, negativeSave, nameFormat);
}
}
}
Expand Down
31 changes: 19 additions & 12 deletions src/test/java/org/joda/time/tz/TestCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,18 +217,19 @@ private void deleteOnExit(File tempFile) {

//-----------------------------------------------------------------------
public void test_formatName() {
assertEquals("PST", ZoneInfoCompiler.Rule.formatName("PST/PDT", 0, null));
assertEquals("PDT", ZoneInfoCompiler.Rule.formatName("PST/PDT", 7200000, null));
assertEquals("PST", ZoneInfoCompiler.Rule.formatName("P%sT", 7200000, "S"));
assertEquals("PDT", ZoneInfoCompiler.Rule.formatName("P%sT", 7200000, "D"));
assertEquals("PT", ZoneInfoCompiler.Rule.formatName("P%sT", 7200000, null));
assertEquals("+00", ZoneInfoCompiler.Rule.formatName("%z", 0, null));
assertEquals("+02", ZoneInfoCompiler.Rule.formatName("%z", 7200000, null));
assertEquals("+020030", ZoneInfoCompiler.Rule.formatName("%z", 7230000, null));
assertEquals("+0201", ZoneInfoCompiler.Rule.formatName("%z", 7260000, null));
assertEquals("+020101", ZoneInfoCompiler.Rule.formatName("%z", 7261000, null));
assertEquals("-02", ZoneInfoCompiler.Rule.formatName("%z", -7200000, null));
assertEquals("-020030", ZoneInfoCompiler.Rule.formatName("%z", -7230000, null));
assertEquals("PST", ZoneInfoCompiler.Rule.formatName("PST/PDT", 14400000, 0, null));
assertEquals("PDT", ZoneInfoCompiler.Rule.formatName("PST/PDT", 14400000, 7200000, null));
assertEquals("PST", ZoneInfoCompiler.Rule.formatName("P%sT", 14400000, 7200000, "S"));
assertEquals("PDT", ZoneInfoCompiler.Rule.formatName("P%sT", 14400000, 7200000, "D"));
assertEquals("PT", ZoneInfoCompiler.Rule.formatName("P%sT", 14400000, 7200000, null));
assertEquals("+00", ZoneInfoCompiler.Rule.formatName("%z", 0, 0, null));
assertEquals("+04", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 0, null));
assertEquals("+06", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 7200000, null));
assertEquals("+060030", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 7230000, null));
assertEquals("+0601", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 7260000, null));
assertEquals("+060101", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 7261000, null));
assertEquals("+02", ZoneInfoCompiler.Rule.formatName("%z", 14400000, -7200000, null));
assertEquals("+020030", ZoneInfoCompiler.Rule.formatName("%z", 14400000, -7170000, null));
}

//-----------------------------------------------------------------------
Expand Down Expand Up @@ -299,4 +300,10 @@ public void test_Tokyo_1949() {
assertEquals(expected.getMillis(), next);
}

public void test_Azores() {
DateTimeZone zone = DateTimeZone.forID("Atlantic/Azores");
assertEquals("-01", zone.getNameKey(new DateTime(2000, 1, 1, 0, 0, zone).getMillis()));
assertEquals("+00", zone.getNameKey(new DateTime(2000, 7, 1, 0, 0, zone).getMillis()));
}

}