Skip to content

Commit 54b3d1c

Browse files
authored
Fix TZDB compiler %z parsing (#787)
* Needs to take account of standard millis * Update separate code path to allow %z to flow through
1 parent 0a264f1 commit 54b3d1c

File tree

2 files changed

+51
-30
lines changed

2 files changed

+51
-30
lines changed

src/main/java/org/joda/time/tz/ZoneInfoCompiler.java

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ static boolean test(String id, DateTimeZone tz) {
366366
return false;
367367
}
368368

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

504-
// store "back" links as aliases (where name is permanently mapped
504+
// store "back" links as aliases (where name is permanently mapped)
505505
for (int pass = 0; pass < 2; pass++) {
506506
for (int i = 0; i < iBackLinks.size(); i += 2) {
507507
String id = iBackLinks.get(i);
@@ -840,17 +840,23 @@ static class Rule {
840840
* Adds a recurring savings rule to the builder.
841841
*
842842
* @param builder the builder
843+
* @param standardMillis the standard millis, pre-adjusted to the negativeSave value
843844
* @param negativeSave the negative save value
844845
* @param nameFormat the name format
845846
*/
846-
public void addRecurring(DateTimeZoneBuilder builder, int negativeSave, String nameFormat) {
847+
public void addRecurring(DateTimeZoneBuilder builder, int standardMillis, int negativeSave, String nameFormat) {
847848
int saveMillis = iSaveMillis + -negativeSave;
848-
String nameKey = formatName(nameFormat, saveMillis, iLetterS);
849+
String nameKey = formatName(nameFormat, standardMillis, saveMillis, iLetterS);
849850
iDateTimeOfYear.addRecurring(builder, nameKey, saveMillis, iFromYear, iToYear);
850851
}
851852

852853
// ScopedForTesting
853-
static String formatName(String nameFormat, int saveMillis, String letterS) {
854+
static String formatName(String nameFormat, int standardMillis, int saveMillis, String letterS) {
855+
// this method is called while adding rules to the builder
856+
// the input parameters give the context as to whether the input is standard or 'summer' time
857+
// saveMillis == 0 in 'winter' time, and != 0 in 'summer' time
858+
// (negative save millis have been applied before this method is called)
859+
854860
// SPEC: Alternatively, a slash (/) separates standard and daylight abbreviations.
855861
int index = nameFormat.indexOf('/');
856862
if (index > 0) {
@@ -876,22 +882,30 @@ static String formatName(String nameFormat, int saveMillis, String letterS) {
876882
// offset in the form ±hh, ±hhmm, or ±hhmmss, using the shortest form that does not lose information,
877883
// where hh, mm, and ss are the hours, minutes, and seconds east (+) or west (-) of UT.
878884
if (nameFormat.equals("%z")) {
879-
String sign = saveMillis < 0 ? "-" : "+";
880-
int saveSecs = Math.abs(saveMillis) / 1000;
881-
int hours = saveSecs / 3600;
882-
int mins = ((saveSecs / 60) % 60);
883-
int secs = (saveSecs % 60);
884-
if (secs == 0) {
885-
if (mins == 0) {
886-
return sign + twoDigitString(hours);
887-
}
888-
return sign + twoDigitString(hours) + twoDigitString(mins);
885+
if (saveMillis == 0) {
886+
return formatOffset(standardMillis).intern();
887+
} else {
888+
return formatOffset(standardMillis + saveMillis).intern();
889889
}
890-
return sign + twoDigitString(hours) + twoDigitString(mins) + twoDigitString(secs);
891890
}
892891
return nameFormat;
893892
}
894893

894+
private static String formatOffset(int millis) {
895+
String sign = millis < 0 ? "-" : "+";
896+
int saveSecs = Math.abs(millis) / 1000;
897+
int hours = saveSecs / 3600;
898+
int mins = ((saveSecs / 60) % 60);
899+
int secs = (saveSecs % 60);
900+
if (secs == 0) {
901+
if (mins == 0) {
902+
return sign + twoDigitString(hours);
903+
}
904+
return sign + twoDigitString(hours) + twoDigitString(mins);
905+
}
906+
return sign + twoDigitString(hours) + twoDigitString(mins) + twoDigitString(secs);
907+
}
908+
895909
private static String twoDigitString(int value) {
896910
return Integer.toString(value + 100).substring(1);
897911
}
@@ -960,13 +974,13 @@ public void addRecurring(DateTimeZoneBuilder builder, int standardMillis, String
960974
// add a fake rule that predates all other rules to ensure standard=summer (see Namibia)
961975
if (negativeSave < 0) {
962976
Rule rule = new Rule(iRules.get(0));
963-
rule.addRecurring(builder, negativeSave, nameFormat);
977+
rule.addRecurring(builder, standardMillis, negativeSave, nameFormat);
964978
}
965979

966980
// add each rule, passing through the negative save to alter the actual iSaveMillis value that is used
967981
for (int i = 0; i < iRules.size(); i++) {
968982
Rule rule = iRules.get(i);
969-
rule.addRecurring(builder, negativeSave, nameFormat);
983+
rule.addRecurring(builder, standardMillis, negativeSave, nameFormat);
970984
}
971985
}
972986
}

src/test/java/org/joda/time/tz/TestCompiler.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -217,18 +217,19 @@ private void deleteOnExit(File tempFile) {
217217

218218
//-----------------------------------------------------------------------
219219
public void test_formatName() {
220-
assertEquals("PST", ZoneInfoCompiler.Rule.formatName("PST/PDT", 0, null));
221-
assertEquals("PDT", ZoneInfoCompiler.Rule.formatName("PST/PDT", 7200000, null));
222-
assertEquals("PST", ZoneInfoCompiler.Rule.formatName("P%sT", 7200000, "S"));
223-
assertEquals("PDT", ZoneInfoCompiler.Rule.formatName("P%sT", 7200000, "D"));
224-
assertEquals("PT", ZoneInfoCompiler.Rule.formatName("P%sT", 7200000, null));
225-
assertEquals("+00", ZoneInfoCompiler.Rule.formatName("%z", 0, null));
226-
assertEquals("+02", ZoneInfoCompiler.Rule.formatName("%z", 7200000, null));
227-
assertEquals("+020030", ZoneInfoCompiler.Rule.formatName("%z", 7230000, null));
228-
assertEquals("+0201", ZoneInfoCompiler.Rule.formatName("%z", 7260000, null));
229-
assertEquals("+020101", ZoneInfoCompiler.Rule.formatName("%z", 7261000, null));
230-
assertEquals("-02", ZoneInfoCompiler.Rule.formatName("%z", -7200000, null));
231-
assertEquals("-020030", ZoneInfoCompiler.Rule.formatName("%z", -7230000, null));
220+
assertEquals("PST", ZoneInfoCompiler.Rule.formatName("PST/PDT", 14400000, 0, null));
221+
assertEquals("PDT", ZoneInfoCompiler.Rule.formatName("PST/PDT", 14400000, 7200000, null));
222+
assertEquals("PST", ZoneInfoCompiler.Rule.formatName("P%sT", 14400000, 7200000, "S"));
223+
assertEquals("PDT", ZoneInfoCompiler.Rule.formatName("P%sT", 14400000, 7200000, "D"));
224+
assertEquals("PT", ZoneInfoCompiler.Rule.formatName("P%sT", 14400000, 7200000, null));
225+
assertEquals("+00", ZoneInfoCompiler.Rule.formatName("%z", 0, 0, null));
226+
assertEquals("+04", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 0, null));
227+
assertEquals("+06", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 7200000, null));
228+
assertEquals("+060030", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 7230000, null));
229+
assertEquals("+0601", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 7260000, null));
230+
assertEquals("+060101", ZoneInfoCompiler.Rule.formatName("%z", 14400000, 7261000, null));
231+
assertEquals("+02", ZoneInfoCompiler.Rule.formatName("%z", 14400000, -7200000, null));
232+
assertEquals("+020030", ZoneInfoCompiler.Rule.formatName("%z", 14400000, -7170000, null));
232233
}
233234

234235
//-----------------------------------------------------------------------
@@ -299,4 +300,10 @@ public void test_Tokyo_1949() {
299300
assertEquals(expected.getMillis(), next);
300301
}
301302

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

0 commit comments

Comments
 (0)