ICU-5990 metazoneInfo.res support.  Fixed a bug for detecting daylight saving time amount.  Updated icudata.jar to include metazoneInfo.res.

X-SVN-Rev: 22968
diff --git a/icu4j/src/com/ibm/icu/dev/test/format/TimeZoneFormatTest.java b/icu4j/src/com/ibm/icu/dev/test/format/TimeZoneFormatTest.java
index b7920d3..d4fc5c4 100644
--- a/icu4j/src/com/ibm/icu/dev/test/format/TimeZoneFormatTest.java
+++ b/icu4j/src/com/ibm/icu/dev/test/format/TimeZoneFormatTest.java
@@ -208,6 +208,7 @@
             };
         }
 
+        long testCounts = 0;
         long[] testTimes = new long[4];
         boolean[] expectedRoundTrip = new boolean[4];
         int testLen = 0;
@@ -219,7 +220,6 @@
                 SimpleDateFormat sdf = new SimpleDateFormat(pattern, LOCALES[locidx]);
 
                 String[] ids = TimeZone.getAvailableIDs();
-                timer = System.currentTimeMillis();
                 for (int zidx = 0; zidx < ids.length; zidx++) {
                     if(!ids[zidx].equals(ZoneMeta.getCanonicalID(ids[zidx]))) {
                         // Skip aliases
@@ -262,6 +262,8 @@
                             }
                         }
                         for (int testidx = 0; testidx < testLen; testidx++) {
+                            testCounts++;
+                            timer = System.currentTimeMillis();
                             String text = sdf.format(new Date(testTimes[testidx]));
                             try {
                                 Date parsedDate = sdf.parse(text);
@@ -285,6 +287,7 @@
                             } catch (ParseException pe) {
                                 errln("FAIL: " + pe.getMessage());
                             }
+                            times[patidx] += System.currentTimeMillis() - timer;
                         }
                         tzt = tz.getNextTransition(t, false);
                         if (tzt == null) {
@@ -300,7 +303,6 @@
                         }
                     }
                 }
-                times[patidx] += System.currentTimeMillis() - timer;
             }
         }
 
@@ -311,5 +313,6 @@
             total += times[i];
         }
         logln("Total: " + total + "ms");
+        logln("Iteration: " + testCounts);
     }
 }
\ No newline at end of file
diff --git a/icu4j/src/com/ibm/icu/impl/ZoneMeta.java b/icu4j/src/com/ibm/icu/impl/ZoneMeta.java
index aaa855f..80850d3 100644
--- a/icu4j/src/com/ibm/icu/impl/ZoneMeta.java
+++ b/icu4j/src/com/ibm/icu/impl/ZoneMeta.java
@@ -747,66 +747,20 @@
     }
 
     static Map getOlsonToMetaMap() {
-        HashMap olsonToMeta = null;
+        Map olsonToMeta = null;
         synchronized(ZoneMeta.class) {
             if (OLSON_TO_META_REF != null) {
                 olsonToMeta = (HashMap)OLSON_TO_META_REF.get();
             }
             if (olsonToMeta == null) {
-                // Create olson id to metazone mapping table
-                olsonToMeta = new HashMap();
-                UResourceBundle zoneStringsBundle = null;
-                try {
-                    UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "root");
-                    zoneStringsBundle = bundle.get("zoneStrings");
-                } catch (MissingResourceException mre) {
-                    // do nothing
+                olsonToMeta = createOlsonToMetaMap();
+                if (olsonToMeta == null) {
+                    // We may not need this code for ICU4J...
+                    olsonToMeta = createOlsonToMetaMapOld();
                 }
-                if (zoneStringsBundle != null) {
-                    // DateFormat to be used for parsing metazone mapping range
-                    SimpleDateFormat mzdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-                    mzdf.setTimeZone(TimeZone.getTimeZone("UTC"));
-                    
-                    String[] tzids = getAvailableIDs();
-                    for (int i = 0; i < tzids.length; i++) {
-                        // Skip aliases
-                        if (!tzids[i].equals(getCanonicalID(tzids[i]))) {
-                            continue;
-                        }
-                        String tzkey = tzids[i].replace('/', ':');
-                        try {
-                            UResourceBundle zoneBundle = zoneStringsBundle.get(tzkey);
-                            UResourceBundle useMZ = zoneBundle.get("um");
-                            LinkedList mzMappings = new LinkedList();
-                            for (int idx = 0; ; idx++) {
-                                try {
-                                    UResourceBundle mz = useMZ.get("mz" + idx);
-                                    String[] mzstr = mz.getStringArray();
-                                    if (mzstr == null || mzstr.length != 3) {
-                                        continue;
-                                    }
-                                    OlsonToMetaMappingEntry mzmap = new OlsonToMetaMappingEntry();
-                                    mzmap.mzid = mzstr[0].intern();
-                                    mzmap.from = mzdf.parse(mzstr[1]).getTime();
-                                    mzmap.to = mzdf.parse(mzstr[2]).getTime();
-
-                                    // Add this mapping to the list
-                                    mzMappings.add(mzmap);
-                                } catch (MissingResourceException nomz) {
-                                    // we're done
-                                    break;
-                                } catch (ParseException baddate) {
-                                    // skip this
-                                }
-                            }
-                            if (mzMappings.size() != 0) {
-                                // Add to the olson-to-meta map
-                                olsonToMeta.put(tzids[i], mzMappings);
-                            }
-                        } catch (MissingResourceException noum) {
-                            // Does not use metazone, just skip this.
-                        }
-                    }
+                if (olsonToMeta == null) {
+                    // We need to return non-null Map to avoid disaster
+                    olsonToMeta = new HashMap();
                 }
                 OLSON_TO_META_REF = new SoftReference(olsonToMeta);
             }
@@ -814,6 +768,127 @@
         return olsonToMeta;
     }
 
+    /*
+     * Create olson tzid to metazone mappings from metazoneInfo.res (3.8.1 or later)
+     */
+    private static Map createOlsonToMetaMap() {
+        // Create olson id to metazone mapping table
+        HashMap olsonToMeta = null;
+        UResourceBundle metazoneMappingsBundle = null;
+        try {
+            UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "metazoneInfo");
+            metazoneMappingsBundle = bundle.get("metazoneMappings");
+        } catch (MissingResourceException mre) {
+            // do nothing
+        }
+        if (metazoneMappingsBundle != null) {
+            String[] tzids = getAvailableIDs();
+            for (int i = 0; i < tzids.length; i++) {
+                // Skip aliases
+                if (!tzids[i].equals(getCanonicalID(tzids[i]))) {
+                    continue;
+                }
+                String tzkey = tzids[i].replace('/', ':');
+                try {
+                    UResourceBundle zoneBundle = metazoneMappingsBundle.get(tzkey);
+                    LinkedList mzMappings = new LinkedList();
+                    for (int idx = 0; ; idx++) {
+                        try {
+                            UResourceBundle mz = zoneBundle.get("mz" + idx);
+                            String[] mzstr = mz.getStringArray();
+                            if (mzstr == null || mzstr.length != 3) {
+                                continue;
+                            }
+                            OlsonToMetaMappingEntry mzmap = new OlsonToMetaMappingEntry();
+                            mzmap.mzid = mzstr[0].intern();
+                            mzmap.from = parseDate(mzstr[1]);
+                            mzmap.to = parseDate(mzstr[2]);
+
+                            // Add this mapping to the list
+                            mzMappings.add(mzmap);
+                        } catch (MissingResourceException nomz) {
+                            // we're done
+                            break;
+                        } catch (IllegalArgumentException baddate) {
+                            // skip this
+                        }
+                    }
+                    if (mzMappings.size() != 0) {
+                        // Add to the olson-to-meta map
+                        if (olsonToMeta == null) {
+                            olsonToMeta = new HashMap();
+                        }
+                        olsonToMeta.put(tzids[i], mzMappings);
+                    }
+                } catch (MissingResourceException noum) {
+                    // Does not use metazone, just skip this.
+                }
+            }
+        }
+        return olsonToMeta;
+    }
+
+    /*
+     * Create olson tzid to metazone mappings from root.res (3.8)
+     */
+    private static Map createOlsonToMetaMapOld() {
+        // Create olson id to metazone mapping table
+        HashMap olsonToMeta = null;
+        UResourceBundle zoneStringsBundle = null;
+        try {
+            UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "root");
+            zoneStringsBundle = bundle.get("zoneStrings");
+        } catch (MissingResourceException mre) {
+            // do nothing
+        }
+        if (zoneStringsBundle != null) {
+            String[] tzids = getAvailableIDs();
+            for (int i = 0; i < tzids.length; i++) {
+                // Skip aliases
+                if (!tzids[i].equals(getCanonicalID(tzids[i]))) {
+                    continue;
+                }
+                String tzkey = tzids[i].replace('/', ':');
+                try {
+                    UResourceBundle zoneBundle = zoneStringsBundle.get(tzkey);
+                    UResourceBundle useMZ = zoneBundle.get("um");
+                    LinkedList mzMappings = new LinkedList();
+                    for (int idx = 0; ; idx++) {
+                        try {
+                            UResourceBundle mz = useMZ.get("mz" + idx);
+                            String[] mzstr = mz.getStringArray();
+                            if (mzstr == null || mzstr.length != 3) {
+                                continue;
+                            }
+                            OlsonToMetaMappingEntry mzmap = new OlsonToMetaMappingEntry();
+                            mzmap.mzid = mzstr[0].intern();
+                            mzmap.from = parseDate(mzstr[1]);
+                            mzmap.to = parseDate(mzstr[2]);
+
+                            // Add this mapping to the list
+                            mzMappings.add(mzmap);
+                        } catch (MissingResourceException nomz) {
+                            // we're done
+                            break;
+                        } catch (IllegalArgumentException baddate) {
+                            // skip this
+                        }
+                    }
+                    if (mzMappings.size() != 0) {
+                        // Add to the olson-to-meta map
+                        if (olsonToMeta == null) {
+                            olsonToMeta = new HashMap();
+                        }
+                        olsonToMeta.put(tzids[i], mzMappings);
+                    }
+                } catch (MissingResourceException noum) {
+                    // Does not use metazone, just skip this.
+                }
+            }
+        }
+        return olsonToMeta;
+    }
+    
     /**
      * Returns a CLDR metazone ID for the given Olson tzid and time.
      */
@@ -920,4 +995,67 @@
 //        }
 //        return getZoneIdByMetazone(metazoneID, region);
 //    }
+
+    /*
+     * Convert a date string used by metazone mappings to long.
+     * The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
+     * We do not want to use SimpleDateFormat to parse the metazone
+     * mapping range strings in createOlsonToMeta, because it might be
+     * called from SimpleDateFormat initialization code.
+     */
+     static long parseDate (String text) throws IllegalArgumentException {
+        int year = 0, month = 0, day = 0, hour = 0, min = 0;
+        int idx;
+        int n;
+
+        // "yyyy" (0 - 3)
+        for (idx = 0; idx <= 3; idx++) {
+            n = text.charAt(idx) - '0';
+            if (n >= 0 && n < 10) {
+                year = 10*year + n;
+            } else {
+                throw new IllegalArgumentException("Bad year");
+            }
+        }
+        // "MM" (5 - 6)
+        for (idx = 5; idx <= 6; idx++) {
+            n = text.charAt(idx) - '0';
+            if (n >= 0 && n < 10) {
+                month = 10*month + n;
+            } else {
+                throw new IllegalArgumentException("Bad month");
+            }
+        }
+        // "dd" (8 - 9)
+        for (idx = 8; idx <= 9; idx++) {
+            n = text.charAt(idx) - '0';
+            if (n >= 0 && n < 10) {
+                day = 10*day + n;
+            } else {
+                throw new IllegalArgumentException("Bad day");
+            }
+        }
+        // "HH" (11 - 12)
+        for (idx = 11; idx <= 12; idx++) {
+            n = text.charAt(idx) - '0';
+            if (n >= 0 && n < 10) {
+                hour = 10*hour + n;
+            } else {
+                throw new IllegalArgumentException("Bad hour");
+            }
+        }
+        // "mm" (14 - 15)
+        for (idx = 14; idx <= 15; idx++) {
+            n = text.charAt(idx) - '0';
+            if (n >= 0 && n < 10) {
+                min = 10*min + n;
+            } else {
+                throw new IllegalArgumentException("Bad minute");
+            }
+        }
+
+        long date = Grego.fieldsToDay(year, month - 1, day) * Grego.MILLIS_PER_DAY
+                    + hour * Grego.MILLIS_PER_HOUR + min * Grego.MILLIS_PER_MINUTE;
+        return date;
+     }
 }
diff --git a/icu4j/src/com/ibm/icu/impl/data/icudata.jar b/icu4j/src/com/ibm/icu/impl/data/icudata.jar
index bcd5c59..414c266 100755
--- a/icu4j/src/com/ibm/icu/impl/data/icudata.jar
+++ b/icu4j/src/com/ibm/icu/impl/data/icudata.jar
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:0e01a9a1a779d74792f4ae0391b586695f6dd280d7d972fd1e9919bbe74905fe
-size 5391701
+oid sha256:d50452d34dbff6e92dbe1d2985eb62696e35bafd40914a57afd0ba2edc8c8876
+size 5409244
diff --git a/icu4j/src/com/ibm/icu/text/SimpleDateFormat.java b/icu4j/src/com/ibm/icu/text/SimpleDateFormat.java
index 8edc3df..68752f7 100644
--- a/icu4j/src/com/ibm/icu/text/SimpleDateFormat.java
+++ b/icu4j/src/com/ibm/icu/text/SimpleDateFormat.java
@@ -1648,7 +1648,6 @@
                         }
                     } else { // tztype == TZTYPE_DST
                         if (offsets[1] == 0) {
-                            int savings = 60*60*1000; // default DST savings
                             if (btz != null) {
                                 long time = localMillis + offsets[0];
                                 // We use the nearest daylight saving time rule.
@@ -1684,16 +1683,23 @@
 
                                 if (beforeTrs != null && afterTrs != null) {
                                     if (time - beforeT > afterT - time) {
-                                        savings = afterSav;
+                                        resolvedSavings = afterSav;
                                     } else {
-                                        savings = beforeSav;
+                                        resolvedSavings = beforeSav;
                                     }
-                                } else if (beforeTrs != null) {
-                                    savings = beforeSav;
-                                } else if (afterTrs != null) {
-                                    savings = afterSav;
+                                } else if (beforeTrs != null && beforeSav != 0) {
+                                    resolvedSavings = beforeSav;
+                                } else if (afterTrs != null && afterSav != 0) {
+                                    resolvedSavings = afterSav;
+                                } else {
+                                    resolvedSavings = btz.getDSTSavings();
                                 }
-                                resolvedSavings = savings;
+                            } else {
+                                resolvedSavings = tz.getDSTSavings();
+                            }
+                            if (resolvedSavings == 0) {
+                                // Final fallback
+                                resolvedSavings = millisPerHour;
                             }
                         }
                     }
diff --git a/icu4j/src/com/ibm/icu/util/Calendar.java b/icu4j/src/com/ibm/icu/util/Calendar.java
index 8dfcaf9..779b12a 100644
--- a/icu4j/src/com/ibm/icu/util/Calendar.java
+++ b/icu4j/src/com/ibm/icu/util/Calendar.java
@@ -4041,8 +4041,6 @@
 
         fields[JULIAN_DAY] = (int) days + EPOCH_JULIAN_DAY;
 
-        // In some cases we will have to call this method again below to
-        // adjust for DST pushing us into the next Julian day.
         computeGregorianAndDOWFields(fields[JULIAN_DAY]);
 
         // Call framework method to have subclass compute its fields.