-//##header\r
-//#if defined(FOUNDATION10) || defined(J2SE13)\r
-//#else\r
-/*\r
- *******************************************************************************\r
- * Copyright (C) 2002-2009, International Business Machines Corporation and *\r
- * others. All Rights Reserved. *\r
- *******************************************************************************\r
-*/\r
-package com.ibm.icu.dev.test.timezone;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Date;\r
-import java.util.Calendar;\r
-import java.util.GregorianCalendar;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Locale;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.TreeSet;\r
-import java.util.Iterator;\r
-\r
-import java.text.DateFormat;\r
-import java.text.NumberFormat;\r
-\r
-import com.ibm.icu.dev.test.*;\r
-import com.ibm.icu.dev.test.util.BagFormatter;\r
-import com.ibm.icu.util.TimeZone;\r
-\r
-\r
-/**\r
- * Class for testing TimeZones for consistency\r
- * @author Davis\r
- * \r
- */\r
-public class TimeZoneAliasTest extends TestFmwk {\r
- \r
- public static void main(String[] args) throws Exception {\r
- new TimeZoneAliasTest().run(args);\r
- }\r
- \r
- /**\r
- * There are two things to check aliases for:<br>\r
- * 1. the alias set must be uniform: if a isAlias b, then aliasSet(a) == aliasSet(b)<br>\r
- * 2. all aliases must have the same offsets\r
- */\r
- public void TestAliases() {\r
- Zone.Seconds seconds = new Zone.Seconds();\r
- for (Iterator it = Zone.getZoneSet().iterator(); it.hasNext(); ) {\r
- Zone zone = (Zone)it.next();\r
- String id = zone.id;\r
- if (id.indexOf('/') < 0 && (id.endsWith("ST") || id.endsWith("DT"))) {\r
- if (zone.minRecentOffset != zone.maxRecentOffset) {\r
- errln(\r
- "Standard or Daylight Time not constant: " + id \r
- + ": " + Zone.formatHours(zone.minRecentOffset)\r
- + " != " + Zone.formatHours(zone.maxRecentOffset));\r
- }\r
- }\r
- Set aliases = zone.getPurportedAliases();\r
- Set aliasesSet = new TreeSet(aliases);\r
- aliasesSet.add(id); // for comparison\r
- Iterator aliasIterator = aliases.iterator();\r
- while (aliasIterator.hasNext()) {\r
- String otherId = (String)aliasIterator.next();\r
- Zone otherZone = Zone.make(otherId);\r
- Set otherAliases = otherZone.getPurportedAliases();\r
- otherAliases.add(otherId); // for comparison\r
- if (!aliasesSet.equals(otherAliases)) {\r
- errln(\r
- "Aliases Unsymmetric: "\r
- + id + " => " + Zone.bf.join(aliasesSet)\r
- + "; " \r
- + otherId + " => " + Zone.bf.join(otherAliases));\r
- }\r
- if (zone.findOffsetOrdering(otherZone, seconds) != 0) {\r
- errln("Aliases differ: " + id + ", " + otherId\r
- + " differ at " + seconds);\r
- }\r
- }\r
- }\r
- }\r
- \r
- /**\r
- * We check to see that every timezone that is not an alias is actually different!\r
- */\r
- public void TestDifferences() {\r
- Zone last = null;\r
- Zone.Seconds diffDate = new Zone.Seconds(); \r
- for (Iterator it = Zone.getZoneSet().iterator(); it.hasNext();) {\r
- Zone testZone = (Zone)it.next();\r
- if (last != null) {\r
- String common = testZone + "\tvs " + last + ":\t";\r
- int diff = testZone.findOffsetOrdering(last, diffDate);\r
- if (diff != 0) {\r
- logln("\t" + common + "difference at: " + diffDate \r
- + ", " + Zone.formatHours(diff) + "hr");\r
- } else if (testZone.isRealAlias(last)) {\r
- logln("\t" + common + "alias, no difference");\r
- } else {\r
- errln(common + "NOT ALIAS BUT NO DIFFERENCE!");\r
- }\r
- }\r
- last = testZone;\r
- }\r
- }\r
- \r
- /**\r
- * Utility for printing out zones to be translated.\r
- */\r
- public static void TestGenerateZones() {\r
- int count = 1;\r
- for (Iterator it = Zone.getUniqueZoneSet().iterator(); it.hasNext();) {\r
- Zone zone = (Zone)it.next();\r
- System.out.println(zone.toString(count++));\r
- }\r
- }\r
- \r
- /** Utility; ought to be someplace common\r
- */\r
- /*\r
- static String join(Collection c, String separator) {\r
- StringBuffer result = new StringBuffer();\r
- boolean isFirst = true;\r
- for (Iterator it = c.iterator(); it.hasNext(); ) {\r
- if (!isFirst) result.append(separator);\r
- else isFirst = false;\r
- result.append(it.next().toString());\r
- }\r
- return result.toString();\r
- }\r
- */\r
- \r
- /**\r
- * The guts is in this subclass. It sucks in all the data from the zones,\r
- * and analyses it. It constructs some mappings for the unique ids,\r
- * etc.<br>\r
- * The main tricky bit is that for performance it pre-analyses all zones\r
- * for inflections points; the points in time where the offset changes.\r
- * The zones can then be sorted by those points, which allows us to\r
- * avoid expensive comparisons.\r
- * @author Davis\r
- */\r
- static class Zone implements Comparable {\r
- // class fields\r
- static private final BagFormatter bf = new BagFormatter().setSeparator(", ");\r
- static private final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.US);\r
- static private final NumberFormat nf = NumberFormat.getInstance(Locale.US);\r
- static private final long HOUR = 1000*60*60;\r
- static private final double DHOUR = HOUR;\r
- static private final long DAY = 24*HOUR;\r
- static private final long GROSS_PERIOD = 30*DAY;\r
- static private final long EPSILON = HOUR/4;\r
- static private final int currentYear = new GregorianCalendar().get(Calendar.YEAR);\r
- static private final long endDate = getDate((currentYear+1),0,1).getTime();\r
- static private final long endDate2 = getDate((currentYear+1),6,1).getTime();\r
- static private final long recentLimit = getDate((currentYear-1),6,1).getTime();\r
- static private final long startDate = getDate(1905,0,1).getTime();\r
- \r
- static private final Map idToZone = new HashMap();\r
- static private final Set zoneSet = new TreeSet();\r
- static private final Set uniqueZoneSet = new TreeSet();\r
- static private final Map idToRealAliases = new HashMap();\r
-\r
- // build everything once.\r
- static {\r
- String [] foo = TimeZone.getAvailableIDs();\r
- for (int i = 0; i < foo.length; ++i) {\r
- zoneSet.add(Zone.make(foo[i]));\r
- }\r
- Zone last = null;\r
- Zone.Seconds diffDate = new Zone.Seconds();\r
- String lastUnique = ""; \r
- for (Iterator it = Zone.getZoneSet().iterator(); it.hasNext();) {\r
- Zone testZone = (Zone)it.next();\r
- if (last == null) {\r
- uniqueZoneSet.add(testZone);\r
- lastUnique = testZone.id;\r
- } else {\r
- int diff = testZone.findOffsetOrdering(last, diffDate);\r
- if (diff != 0) {\r
- uniqueZoneSet.add(testZone);\r
- lastUnique = testZone.id;\r
- } else {\r
- Set aliases = (Set)idToRealAliases.get(lastUnique);\r
- if (aliases == null) {\r
- aliases = new TreeSet();\r
- idToRealAliases.put(lastUnique, aliases);\r
- }\r
- aliases.add(testZone.id);\r
- }\r
- }\r
- last = testZone;\r
- }\r
- }\r
- \r
- static public Set getZoneSet() {\r
- return zoneSet;\r
- }\r
- \r
- public static Set getUniqueZoneSet() {\r
- return uniqueZoneSet;\r
- }\r
-\r
- static public Zone make(String id) {\r
- Zone result = (Zone)idToZone.get(id);\r
- if (result != null) return result;\r
- result = new Zone(id);\r
- idToZone.put(id, result);\r
- return result;\r
- }\r
- \r
- static public String formatHours(int hours) {\r
- return nf.format(hours/DHOUR);\r
- }\r
- \r
- // utility class for date return, because Date is clunky.\r
- public static class Seconds {\r
- public long seconds = Long.MIN_VALUE;\r
- public String toString() {\r
- if (seconds == Long.MIN_VALUE) return "n/a";\r
- return df.format(new Date(seconds));\r
- }\r
- }\r
- \r
- // instance fields\r
- // we keep min/max offsets not only over all time (that we care about)\r
- // but also separate ones for recent years.\r
- private String id;\r
- private TimeZone zone;\r
- // computed below\r
- private int minOffset;\r
- private int maxOffset;\r
- private int minRecentOffset;\r
- private int maxRecentOffset;\r
- private List inflectionPoints = new ArrayList();\r
- private Set purportedAliases = new TreeSet();\r
- \r
- private Zone(String id) { // for interal use only; use make instead!\r
- zone = TimeZone.getTimeZone(id);\r
- this.id = id;\r
- \r
- // get aliases\r
- int equivCount = TimeZone.countEquivalentIDs(id);\r
- for (int j = 0; j < equivCount; ++j) {\r
- String altID = TimeZone.getEquivalentID(id, j);\r
- if (altID.equals(id)) continue;\r
- purportedAliases.add(altID);\r
- }\r
-\r
- // find inflexion points; times where the offset changed\r
- long lastDate = endDate;\r
- if (zone.getOffset(lastDate) < zone.getOffset(endDate2)) lastDate = endDate2;\r
- maxRecentOffset = minRecentOffset = minOffset = maxOffset = zone.getOffset(lastDate);\r
-\r
- inflectionPoints.add(new Long(lastDate));\r
- int lastOffset = zone.getOffset(endDate);\r
- long lastInflection = endDate;\r
- \r
- // we do a gross search, then narrow in when we find a difference from the last one\r
- for (long currentDate = endDate; currentDate >= startDate; currentDate -= GROSS_PERIOD) {\r
- int currentOffset = zone.getOffset(currentDate);\r
- if (currentOffset != lastOffset) { // Binary Search\r
- if (currentOffset < minOffset) minOffset = currentOffset;\r
- if (currentOffset > maxOffset) maxOffset = currentOffset;\r
- if (lastInflection >= recentLimit) {\r
- if (currentOffset < minRecentOffset) minRecentOffset = currentOffset;\r
- if (currentOffset > maxRecentOffset) maxRecentOffset = currentOffset;\r
- }\r
- long low = currentDate;\r
- long high = lastDate;\r
- while (low - high > EPSILON) {\r
- long mid = (high + low)/2;\r
- int midOffset = zone.getOffset(mid);\r
- if (midOffset == low) {\r
- low = mid;\r
- } else {\r
- high = mid;\r
- }\r
- }\r
- inflectionPoints.add(new Long(low));\r
- lastInflection = low;\r
- }\r
- lastOffset = currentOffset;\r
- }\r
- inflectionPoints.add(new Long(startDate)); // just to cap it off for comparisons.\r
- }\r
- \r
- // we assume that places will not convert time zones then back within one day\r
- // so we go first by half\r
- public int findOffsetOrdering(Zone other, Seconds dateDiffFound) {\r
- //System.out.println("-diff: " + id + "\t" + other.id);\r
- int result = 0;\r
- long seconds = 0;\r
- int min = inflectionPoints.size();\r
- if (other.inflectionPoints.size() < min) min = other.inflectionPoints.size();\r
- main:\r
- {\r
- for (int i = 0; i < min; ++i) {\r
- long myIP = ((Long)inflectionPoints.get(i)).longValue();\r
- long otherIP = ((Long)other.inflectionPoints.get(i)).longValue();\r
- if (myIP > otherIP) { // take lowest, for transitivity (semi)\r
- long temp = myIP;\r
- myIP = otherIP;\r
- otherIP = temp;\r
- }\r
- result = zone.getOffset(myIP) - other.zone.getOffset(myIP);\r
- if (result != 0) {\r
- seconds = myIP;\r
- break main;\r
- } \r
- if (myIP == otherIP) continue; // test other if different\r
- myIP = otherIP;\r
- result = zone.getOffset(myIP) - other.zone.getOffset(myIP);\r
- if (result != 0) {\r
- seconds = myIP;\r
- break main;\r
- } \r
- }\r
- // if they are equal so far, we don't care about the rest\r
- result = 0;\r
- seconds = Long.MIN_VALUE;\r
- break main;\r
- }\r
- //System.out.println("+diff: " + (result/HOUR) + "\t" + dateDiffFound);\r
- if (dateDiffFound != null) dateDiffFound.seconds = seconds;\r
- return result;\r
- }\r
- \r
- // internal buffer to avoid creation all the time.\r
- private Seconds diffDateReturn = new Seconds();\r
- \r
- public int compareTo(Object o) {\r
- Zone other = (Zone)o;\r
- // first order by max and min offsets\r
- // min will usually correspond to standard time, max to daylight\r
- // unless there have been historical shifts\r
- if (minRecentOffset < other.minRecentOffset) return -1;\r
- if (minRecentOffset > other.minRecentOffset) return 1;\r
- if (maxRecentOffset < other.maxRecentOffset) return -1;\r
- if (maxRecentOffset > other.maxRecentOffset) return 1;\r
- // now check that all offsets are the same over history\r
- int diffDate = findOffsetOrdering(other, diffDateReturn);\r
- if (diffDate != 0) return diffDate;\r
- // choose longer name first!!\r
- if (id.length() != other.id.length()) {\r
- if (id.length() < other.id.length()) return 1;\r
- return -1;\r
- }\r
- return id.compareTo(other.id);\r
- }\r
- \r
- public Set getPurportedAliases() {\r
- return new TreeSet(purportedAliases); // clone for safety\r
- }\r
- \r
- public boolean isPurportedAlias(String zoneID) {\r
- return purportedAliases.contains(zoneID);\r
- }\r
- \r
- public boolean isRealAlias(Zone z) {\r
- return purportedAliases.contains(z.id);\r
- }\r
- \r
- public String getPurportedAliasesAsString() {\r
- Set s = getPurportedAliases();\r
- if (s.size() == 0) return "";\r
- return " " + bf.join(s);\r
- }\r
- \r
- public String getRealAliasesAsString() {\r
- Set s = (Set)idToRealAliases.get(id);\r
- if (s == null) return "";\r
- return " *" + bf.join(s);\r
- }\r
- \r
- public String getCity() {\r
- int pos = id.lastIndexOf(('/'));\r
- String city = id.substring(pos+1);\r
- return city.replace('_',' ');\r
- }\r
-\r
- public String toString() {\r
- return toString(-1);\r
- }\r
- \r
- /**\r
- * Where count > 0, returns string that is set up for translation\r
- */\r
- public String toString(int count) {\r
- String city = getCity();\r
- String hours = formatHours(minRecentOffset)\r
- + (minRecentOffset != maxRecentOffset \r
- ? "," + formatHours(maxRecentOffset) \r
- : "");\r
- if (count < 0) {\r
- return id + getPurportedAliasesAsString() + " (" + hours + ")";\r
- } \r
- // for getting template for translation\r
- return "\t{\t\"" + id + "\"\t// [" + count + "] " + hours \r
- + getRealAliasesAsString() + "\r\n"\r
- + "\t\t// translate the following!!\r\n"\r
- + (minRecentOffset != maxRecentOffset\r
- ? "\t\t\"" + city + " Standard Time\"\r\n"\r
- + "\t\t\"" + city + "-ST\"\r\n"\r
- + "\t\t\"" + city + " Daylight Time\"\r\n"\r
- + "\t\t\"" + city + "-DT\"\r\n"\r
- : "\t\t\"\"\r\n"\r
- + "\t\t\"\"\r\n"\r
- + "\t\t\"\"\r\n"\r
- + "\t\t\"\"\r\n")\r
- + "\t\t\"" + city + " Time\"\r\n"\r
- + "\t\t\"" + city + "-T\"\r\n"\r
- + "\t\t\"" + city + "\"\r\n"\r
- + "\t}";\r
- }\r
- }\r
-}\r
-\r
-//#endif\r
+//##header J2SE15
+//#if defined(FOUNDATION10) || defined(J2SE13)
+//#else
+/*
+ *******************************************************************************
+ * Copyright (C) 2002-2009, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+*/
+package com.ibm.icu.dev.test.timezone;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Iterator;
+
+import java.text.DateFormat;
+import java.text.NumberFormat;
+
+import com.ibm.icu.dev.test.*;
+import com.ibm.icu.dev.test.util.BagFormatter;
+import com.ibm.icu.util.TimeZone;
+
+
+/**
+ * Class for testing TimeZones for consistency
+ * @author Davis
+ *
+ */
+public class TimeZoneAliasTest extends TestFmwk {
+
+ public static void main(String[] args) throws Exception {
+ new TimeZoneAliasTest().run(args);
+ }
+
+ /**
+ * There are two things to check aliases for:<br>
+ * 1. the alias set must be uniform: if a isAlias b, then aliasSet(a) == aliasSet(b)<br>
+ * 2. all aliases must have the same offsets
+ */
+ public void TestAliases() {
+ Zone.Seconds seconds = new Zone.Seconds();
+ for (Iterator it = Zone.getZoneSet().iterator(); it.hasNext(); ) {
+ Zone zone = (Zone)it.next();
+ String id = zone.id;
+ if (id.indexOf('/') < 0 && (id.endsWith("ST") || id.endsWith("DT"))) {
+ if (zone.minRecentOffset != zone.maxRecentOffset) {
+ errln(
+ "Standard or Daylight Time not constant: " + id
+ + ": " + Zone.formatHours(zone.minRecentOffset)
+ + " != " + Zone.formatHours(zone.maxRecentOffset));
+ }
+ }
+ Set aliases = zone.getPurportedAliases();
+ Set aliasesSet = new TreeSet(aliases);
+ aliasesSet.add(id); // for comparison
+ Iterator aliasIterator = aliases.iterator();
+ while (aliasIterator.hasNext()) {
+ String otherId = (String)aliasIterator.next();
+ Zone otherZone = Zone.make(otherId);
+ Set otherAliases = otherZone.getPurportedAliases();
+ otherAliases.add(otherId); // for comparison
+ if (!aliasesSet.equals(otherAliases)) {
+ errln(
+ "Aliases Unsymmetric: "
+ + id + " => " + Zone.bf.join(aliasesSet)
+ + "; "
+ + otherId + " => " + Zone.bf.join(otherAliases));
+ }
+ if (zone.findOffsetOrdering(otherZone, seconds) != 0) {
+ errln("Aliases differ: " + id + ", " + otherId
+ + " differ at " + seconds);
+ }
+ }
+ }
+ }
+
+ /**
+ * We check to see that every timezone that is not an alias is actually different!
+ */
+ public void TestDifferences() {
+ Zone last = null;
+ Zone.Seconds diffDate = new Zone.Seconds();
+ for (Iterator it = Zone.getZoneSet().iterator(); it.hasNext();) {
+ Zone testZone = (Zone)it.next();
+ if (last != null) {
+ String common = testZone + "\tvs " + last + ":\t";
+ int diff = testZone.findOffsetOrdering(last, diffDate);
+ if (diff != 0) {
+ logln("\t" + common + "difference at: " + diffDate
+ + ", " + Zone.formatHours(diff) + "hr");
+ } else if (testZone.isRealAlias(last)) {
+ logln("\t" + common + "alias, no difference");
+ } else {
+ errln(common + "NOT ALIAS BUT NO DIFFERENCE!");
+ }
+ }
+ last = testZone;
+ }
+ }
+
+ /**
+ * Utility for printing out zones to be translated.
+ */
+ public static void TestGenerateZones() {
+ int count = 1;
+ for (Iterator it = Zone.getUniqueZoneSet().iterator(); it.hasNext();) {
+ Zone zone = (Zone)it.next();
+ System.out.println(zone.toString(count++));
+ }
+ }
+
+ /** Utility; ought to be someplace common
+ */
+ /*
+ static String join(Collection c, String separator) {
+ StringBuffer result = new StringBuffer();
+ boolean isFirst = true;
+ for (Iterator it = c.iterator(); it.hasNext(); ) {
+ if (!isFirst) result.append(separator);
+ else isFirst = false;
+ result.append(it.next().toString());
+ }
+ return result.toString();
+ }
+ */
+
+ /**
+ * The guts is in this subclass. It sucks in all the data from the zones,
+ * and analyses it. It constructs some mappings for the unique ids,
+ * etc.<br>
+ * The main tricky bit is that for performance it pre-analyses all zones
+ * for inflections points; the points in time where the offset changes.
+ * The zones can then be sorted by those points, which allows us to
+ * avoid expensive comparisons.
+ * @author Davis
+ */
+ static class Zone implements Comparable {
+ // class fields
+ static private final BagFormatter bf = new BagFormatter().setSeparator(", ");
+ static private final DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.US);
+ static private final NumberFormat nf = NumberFormat.getInstance(Locale.US);
+ static private final long HOUR = 1000*60*60;
+ static private final double DHOUR = HOUR;
+ static private final long DAY = 24*HOUR;
+ static private final long GROSS_PERIOD = 30*DAY;
+ static private final long EPSILON = HOUR/4;
+ static private final int currentYear = new GregorianCalendar().get(Calendar.YEAR);
+ static private final long endDate = getDate((currentYear+1),0,1).getTime();
+ static private final long endDate2 = getDate((currentYear+1),6,1).getTime();
+ static private final long recentLimit = getDate((currentYear-1),6,1).getTime();
+ static private final long startDate = getDate(1905,0,1).getTime();
+
+ static private final Map idToZone = new HashMap();
+ static private final Set zoneSet = new TreeSet();
+ static private final Set uniqueZoneSet = new TreeSet();
+ static private final Map idToRealAliases = new HashMap();
+
+ // build everything once.
+ static {
+ String [] foo = TimeZone.getAvailableIDs();
+ for (int i = 0; i < foo.length; ++i) {
+ zoneSet.add(Zone.make(foo[i]));
+ }
+ Zone last = null;
+ Zone.Seconds diffDate = new Zone.Seconds();
+ String lastUnique = "";
+ for (Iterator it = Zone.getZoneSet().iterator(); it.hasNext();) {
+ Zone testZone = (Zone)it.next();
+ if (last == null) {
+ uniqueZoneSet.add(testZone);
+ lastUnique = testZone.id;
+ } else {
+ int diff = testZone.findOffsetOrdering(last, diffDate);
+ if (diff != 0) {
+ uniqueZoneSet.add(testZone);
+ lastUnique = testZone.id;
+ } else {
+ Set aliases = (Set)idToRealAliases.get(lastUnique);
+ if (aliases == null) {
+ aliases = new TreeSet();
+ idToRealAliases.put(lastUnique, aliases);
+ }
+ aliases.add(testZone.id);
+ }
+ }
+ last = testZone;
+ }
+ }
+
+ static public Set getZoneSet() {
+ return zoneSet;
+ }
+
+ public static Set getUniqueZoneSet() {
+ return uniqueZoneSet;
+ }
+
+ static public Zone make(String id) {
+ Zone result = (Zone)idToZone.get(id);
+ if (result != null) return result;
+ result = new Zone(id);
+ idToZone.put(id, result);
+ return result;
+ }
+
+ static public String formatHours(int hours) {
+ return nf.format(hours/DHOUR);
+ }
+
+ // utility class for date return, because Date is clunky.
+ public static class Seconds {
+ public long seconds = Long.MIN_VALUE;
+ public String toString() {
+ if (seconds == Long.MIN_VALUE) return "n/a";
+ return df.format(new Date(seconds));
+ }
+ }
+
+ // instance fields
+ // we keep min/max offsets not only over all time (that we care about)
+ // but also separate ones for recent years.
+ private String id;
+ private TimeZone zone;
+ // computed below
+ private int minOffset;
+ private int maxOffset;
+ private int minRecentOffset;
+ private int maxRecentOffset;
+ private List inflectionPoints = new ArrayList();
+ private Set purportedAliases = new TreeSet();
+
+ private Zone(String id) { // for interal use only; use make instead!
+ zone = TimeZone.getTimeZone(id);
+ this.id = id;
+
+ // get aliases
+ int equivCount = TimeZone.countEquivalentIDs(id);
+ for (int j = 0; j < equivCount; ++j) {
+ String altID = TimeZone.getEquivalentID(id, j);
+ if (altID.equals(id)) continue;
+ purportedAliases.add(altID);
+ }
+
+ // find inflexion points; times where the offset changed
+ long lastDate = endDate;
+ if (zone.getOffset(lastDate) < zone.getOffset(endDate2)) lastDate = endDate2;
+ maxRecentOffset = minRecentOffset = minOffset = maxOffset = zone.getOffset(lastDate);
+
+ inflectionPoints.add(new Long(lastDate));
+ int lastOffset = zone.getOffset(endDate);
+ long lastInflection = endDate;
+
+ // we do a gross search, then narrow in when we find a difference from the last one
+ for (long currentDate = endDate; currentDate >= startDate; currentDate -= GROSS_PERIOD) {
+ int currentOffset = zone.getOffset(currentDate);
+ if (currentOffset != lastOffset) { // Binary Search
+ if (currentOffset < minOffset) minOffset = currentOffset;
+ if (currentOffset > maxOffset) maxOffset = currentOffset;
+ if (lastInflection >= recentLimit) {
+ if (currentOffset < minRecentOffset) minRecentOffset = currentOffset;
+ if (currentOffset > maxRecentOffset) maxRecentOffset = currentOffset;
+ }
+ long low = currentDate;
+ long high = lastDate;
+ while (low - high > EPSILON) {
+ long mid = (high + low)/2;
+ int midOffset = zone.getOffset(mid);
+ if (midOffset == low) {
+ low = mid;
+ } else {
+ high = mid;
+ }
+ }
+ inflectionPoints.add(new Long(low));
+ lastInflection = low;
+ }
+ lastOffset = currentOffset;
+ }
+ inflectionPoints.add(new Long(startDate)); // just to cap it off for comparisons.
+ }
+
+ // we assume that places will not convert time zones then back within one day
+ // so we go first by half
+ public int findOffsetOrdering(Zone other, Seconds dateDiffFound) {
+ //System.out.println("-diff: " + id + "\t" + other.id);
+ int result = 0;
+ long seconds = 0;
+ int min = inflectionPoints.size();
+ if (other.inflectionPoints.size() < min) min = other.inflectionPoints.size();
+ main:
+ {
+ for (int i = 0; i < min; ++i) {
+ long myIP = ((Long)inflectionPoints.get(i)).longValue();
+ long otherIP = ((Long)other.inflectionPoints.get(i)).longValue();
+ if (myIP > otherIP) { // take lowest, for transitivity (semi)
+ long temp = myIP;
+ myIP = otherIP;
+ otherIP = temp;
+ }
+ result = zone.getOffset(myIP) - other.zone.getOffset(myIP);
+ if (result != 0) {
+ seconds = myIP;
+ break main;
+ }
+ if (myIP == otherIP) continue; // test other if different
+ myIP = otherIP;
+ result = zone.getOffset(myIP) - other.zone.getOffset(myIP);
+ if (result != 0) {
+ seconds = myIP;
+ break main;
+ }
+ }
+ // if they are equal so far, we don't care about the rest
+ result = 0;
+ seconds = Long.MIN_VALUE;
+ break main;
+ }
+ //System.out.println("+diff: " + (result/HOUR) + "\t" + dateDiffFound);
+ if (dateDiffFound != null) dateDiffFound.seconds = seconds;
+ return result;
+ }
+
+ // internal buffer to avoid creation all the time.
+ private Seconds diffDateReturn = new Seconds();
+
+ public int compareTo(Object o) {
+ Zone other = (Zone)o;
+ // first order by max and min offsets
+ // min will usually correspond to standard time, max to daylight
+ // unless there have been historical shifts
+ if (minRecentOffset < other.minRecentOffset) return -1;
+ if (minRecentOffset > other.minRecentOffset) return 1;
+ if (maxRecentOffset < other.maxRecentOffset) return -1;
+ if (maxRecentOffset > other.maxRecentOffset) return 1;
+ // now check that all offsets are the same over history
+ int diffDate = findOffsetOrdering(other, diffDateReturn);
+ if (diffDate != 0) return diffDate;
+ // choose longer name first!!
+ if (id.length() != other.id.length()) {
+ if (id.length() < other.id.length()) return 1;
+ return -1;
+ }
+ return id.compareTo(other.id);
+ }
+
+ public Set getPurportedAliases() {
+ return new TreeSet(purportedAliases); // clone for safety
+ }
+
+ public boolean isPurportedAlias(String zoneID) {
+ return purportedAliases.contains(zoneID);
+ }
+
+ public boolean isRealAlias(Zone z) {
+ return purportedAliases.contains(z.id);
+ }
+
+ public String getPurportedAliasesAsString() {
+ Set s = getPurportedAliases();
+ if (s.size() == 0) return "";
+ return " " + bf.join(s);
+ }
+
+ public String getRealAliasesAsString() {
+ Set s = (Set)idToRealAliases.get(id);
+ if (s == null) return "";
+ return " *" + bf.join(s);
+ }
+
+ public String getCity() {
+ int pos = id.lastIndexOf(('/'));
+ String city = id.substring(pos+1);
+ return city.replace('_',' ');
+ }
+
+ public String toString() {
+ return toString(-1);
+ }
+
+ /**
+ * Where count > 0, returns string that is set up for translation
+ */
+ public String toString(int count) {
+ String city = getCity();
+ String hours = formatHours(minRecentOffset)
+ + (minRecentOffset != maxRecentOffset
+ ? "," + formatHours(maxRecentOffset)
+ : "");
+ if (count < 0) {
+ return id + getPurportedAliasesAsString() + " (" + hours + ")";
+ }
+ // for getting template for translation
+ return "\t{\t\"" + id + "\"\t// [" + count + "] " + hours
+ + getRealAliasesAsString() + "\r\n"
+ + "\t\t// translate the following!!\r\n"
+ + (minRecentOffset != maxRecentOffset
+ ? "\t\t\"" + city + " Standard Time\"\r\n"
+ + "\t\t\"" + city + "-ST\"\r\n"
+ + "\t\t\"" + city + " Daylight Time\"\r\n"
+ + "\t\t\"" + city + "-DT\"\r\n"
+ : "\t\t\"\"\r\n"
+ + "\t\t\"\"\r\n"
+ + "\t\t\"\"\r\n"
+ + "\t\t\"\"\r\n")
+ + "\t\t\"" + city + " Time\"\r\n"
+ + "\t\t\"" + city + "-T\"\r\n"
+ + "\t\t\"" + city + "\"\r\n"
+ + "\t}";
+ }
+ }
+}
+
+//#endif