Merge "Making the clipboard work across users." into lmp-dev
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index f34e746..aa6ad20 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -16,19 +16,38 @@
 
 package android.text.format;
 
-import android.content.res.Resources;
+import android.util.TimeFormatException;
 
+import java.io.IOException;
 import java.util.Locale;
 import java.util.TimeZone;
 
-import libcore.icu.LocaleData;
+import libcore.util.ZoneInfo;
+import libcore.util.ZoneInfoDB;
 
 /**
  * An alternative to the {@link java.util.Calendar} and
  * {@link java.util.GregorianCalendar} classes. An instance of the Time class represents
  * a moment in time, specified with second precision. It is modelled after
- * struct tm, and in fact, uses struct tm to implement most of the
- * functionality.
+ * struct tm. This class is not thread-safe and does not consider leap seconds.
+ *
+ * <p>This class has a number of issues and it is recommended that
+ * {@link java.util.GregorianCalendar} is used instead.
+ *
+ * <p>Known issues:
+ * <ul>
+ *     <li>For historical reasons when performing time calculations all arithmetic currently takes
+ *     place using 32-bit integers. This limits the reliable time range representable from 1902
+ *     until 2037.See the wikipedia article on the
+ *     <a href="http://en.wikipedia.org/wiki/Year_2038_problem">Year 2038 problem</a> for details.
+ *     Do not rely on this behavior; it may change in the future.
+ *     </li>
+ *     <li>Calling {@link #switchTimezone(String)} on a date that cannot exist, such as a wall time
+ *     that was skipped due to a DST transition, will result in a date in 1969 (i.e. -1, or 1 second
+ *     before 1st Jan 1970 UTC).</li>
+ *     <li>Much of the formatting / parsing assumes ASCII text and is therefore not suitable for
+ *     use with non-ASCII scripts.</li>
+ * </ul>
  */
 public class Time {
     private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000";
@@ -106,7 +125,7 @@
     public int isDst;
 
     /**
-     * Offset from UTC (in seconds).
+     * Offset in seconds from UTC including any DST offset.
      */
     public long gmtoff;
 
@@ -137,41 +156,20 @@
     public static final int FRIDAY = 5;
     public static final int SATURDAY = 6;
 
-    /*
-     * The Locale for which date formatting strings have been loaded.
-     */
-    private static Locale sLocale;
-    private static String[] sShortMonths;
-    private static String[] sLongMonths;
-    private static String[] sLongStandaloneMonths;
-    private static String[] sShortWeekdays;
-    private static String[] sLongWeekdays;
-    private static String sTimeOnlyFormat;
-    private static String sDateOnlyFormat;
-    private static String sDateTimeFormat;
-    private static String sAm;
-    private static String sPm;
-    private static char sZeroDigit;
-
-    // Referenced by native code.
-    private static String sDateCommand = "%a %b %e %H:%M:%S %Z %Y";
+    // An object that is reused for date calculations.
+    private TimeCalculator calculator;
 
     /**
      * Construct a Time object in the timezone named by the string
      * argument "timezone". The time is initialized to Jan 1, 1970.
-     * @param timezone string containing the timezone to use.
+     * @param timezoneId string containing the timezone to use.
      * @see TimeZone
      */
-    public Time(String timezone) {
-        if (timezone == null) {
-            throw new NullPointerException("timezone is null!");
+    public Time(String timezoneId) {
+        if (timezoneId == null) {
+            throw new NullPointerException("timezoneId is null!");
         }
-        this.timezone = timezone;
-        this.year = 1970;
-        this.monthDay = 1;
-        // Set the daylight-saving indicator to the unknown value -1 so that
-        // it will be recomputed.
-        this.isDst = -1;
+        initialize(timezoneId);
     }
 
     /**
@@ -179,7 +177,7 @@
      * Jan 1, 1970.
      */
     public Time() {
-        this(TimeZone.getDefault().getID());
+        initialize(TimeZone.getDefault().getID());
     }
 
     /**
@@ -189,9 +187,23 @@
      * @param other
      */
     public Time(Time other) {
+        initialize(other.timezone);
         set(other);
     }
 
+    /** Initialize the Time to 00:00:00 1/1/1970 in the specified timezone. */
+    private void initialize(String timezoneId) {
+        this.timezone = timezoneId;
+        this.year = 1970;
+        this.monthDay = 1;
+        // Set the daylight-saving indicator to the unknown value -1 so that
+        // it will be recomputed.
+        this.isDst = -1;
+
+        // A reusable object that performs the date/time calculations.
+        calculator = new TimeCalculator(timezoneId);
+    }
+
     /**
      * Ensures the values in each field are in range. For example if the
      * current value of this calendar is March 32, normalize() will convert it
@@ -208,14 +220,26 @@
      *
      * @return the UTC milliseconds since the epoch
      */
-    native public long normalize(boolean ignoreDst);
+    public long normalize(boolean ignoreDst) {
+        calculator.copyFieldsFromTime(this);
+        long timeInMillis = calculator.toMillis(ignoreDst);
+        calculator.copyFieldsToTime(this);
+        return timeInMillis;
+    }
 
     /**
      * Convert this time object so the time represented remains the same, but is
      * instead located in a different timezone. This method automatically calls
-     * normalize() in some cases
+     * normalize() in some cases.
+     *
+     * <p>This method can return incorrect results if the date / time cannot be normalized.
      */
-    native public void switchTimezone(String timezone);
+    public void switchTimezone(String timezone) {
+        calculator.copyFieldsFromTime(this);
+        calculator.switchTimeZone(timezone);
+        calculator.copyFieldsToTime(this);
+        this.timezone = timezone;
+    }
 
     private static final int[] DAYS_PER_MONTH = { 31, 28, 31, 30, 31, 30, 31,
             31, 30, 31, 30, 31 };
@@ -265,13 +289,13 @@
     /**
      * Clears all values, setting the timezone to the given timezone. Sets isDst
      * to a negative value to mean "unknown".
-     * @param timezone the timezone to use.
+     * @param timezoneId the timezone to use.
      */
-    public void clear(String timezone) {
-        if (timezone == null) {
+    public void clear(String timezoneId) {
+        if (timezoneId == null) {
             throw new NullPointerException("timezone is null!");
         }
-        this.timezone = timezone;
+        this.timezone = timezoneId;
         this.allDay = false;
         this.second = 0;
         this.minute = 0;
@@ -304,12 +328,12 @@
         } else if (b == null) {
             throw new NullPointerException("b == null");
         }
+        a.calculator.copyFieldsFromTime(a);
+        b.calculator.copyFieldsFromTime(b);
 
-        return nativeCompare(a, b);
+        return TimeCalculator.compare(a.calculator, b.calculator);
     }
 
-    private static native int nativeCompare(Time a, Time b);
-
     /**
      * Print the current value given the format string provided. See man
      * strftime for what means what. The final string must be less than 256
@@ -318,61 +342,21 @@
      * @return a String containing the current time expressed in the current locale.
      */
     public String format(String format) {
-        synchronized (Time.class) {
-            Locale locale = Locale.getDefault();
-
-            if (sLocale == null || locale == null || !(locale.equals(sLocale))) {
-                LocaleData localeData = LocaleData.get(locale);
-
-                sAm = localeData.amPm[0];
-                sPm = localeData.amPm[1];
-                sZeroDigit = localeData.zeroDigit;
-
-                sShortMonths = localeData.shortMonthNames;
-                sLongMonths = localeData.longMonthNames;
-                sLongStandaloneMonths = localeData.longStandAloneMonthNames;
-                sShortWeekdays = localeData.shortWeekdayNames;
-                sLongWeekdays = localeData.longWeekdayNames;
-
-                Resources r = Resources.getSystem();
-                sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
-                sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
-                sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
-
-                sLocale = locale;
-            }
-
-            String result = format1(format);
-            if (sZeroDigit != '0') {
-                result = localizeDigits(result);
-            }
-            return result;
-        }
+        calculator.copyFieldsFromTime(this);
+        return calculator.format(format);
     }
 
-    native private String format1(String format);
-
-    // TODO: unify this with java.util.Formatter's copy.
-    private String localizeDigits(String s) {
-        int length = s.length();
-        int offsetToLocalizedDigits = sZeroDigit - '0';
-        StringBuilder result = new StringBuilder(length);
-        for (int i = 0; i < length; ++i) {
-            char ch = s.charAt(i);
-            if (ch >= '0' && ch <= '9') {
-                ch += offsetToLocalizedDigits;
-            }
-            result.append(ch);
-        }
-        return result.toString();
-    }
-
-
     /**
      * Return the current time in YYYYMMDDTHHMMSS<tz> format
      */
     @Override
-    native public String toString();
+    public String toString() {
+        // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+        // happens during debugging when the debugger calls toString().
+        TimeCalculator calculator = new TimeCalculator(this.timezone);
+        calculator.copyFieldsFromTime(this);
+        return calculator.toStringInternal();
+    }
 
     /**
      * Parses a date-time string in either the RFC 2445 format or an abbreviated
@@ -414,7 +398,7 @@
         if (s == null) {
             throw new NullPointerException("time string is null");
         }
-        if (nativeParse(s)) {
+        if (parseInternal(s)) {
             timezone = TIMEZONE_UTC;
             return true;
         }
@@ -424,7 +408,94 @@
     /**
      * Parse a time in the current zone in YYYYMMDDTHHMMSS format.
      */
-    native private boolean nativeParse(String s);
+    private boolean parseInternal(String s) {
+        int len = s.length();
+        if (len < 8) {
+            throw new TimeFormatException("String is too short: \"" + s +
+                    "\" Expected at least 8 characters.");
+        }
+
+        boolean inUtc = false;
+
+        // year
+        int n = getChar(s, 0, 1000);
+        n += getChar(s, 1, 100);
+        n += getChar(s, 2, 10);
+        n += getChar(s, 3, 1);
+        year = n;
+
+        // month
+        n = getChar(s, 4, 10);
+        n += getChar(s, 5, 1);
+        n--;
+        month = n;
+
+        // day of month
+        n = getChar(s, 6, 10);
+        n += getChar(s, 7, 1);
+        monthDay = n;
+
+        if (len > 8) {
+            if (len < 15) {
+                throw new TimeFormatException(
+                        "String is too short: \"" + s
+                                + "\" If there are more than 8 characters there must be at least"
+                                + " 15.");
+            }
+            checkChar(s, 8, 'T');
+            allDay = false;
+
+            // hour
+            n = getChar(s, 9, 10);
+            n += getChar(s, 10, 1);
+            hour = n;
+
+            // min
+            n = getChar(s, 11, 10);
+            n += getChar(s, 12, 1);
+            minute = n;
+
+            // sec
+            n = getChar(s, 13, 10);
+            n += getChar(s, 14, 1);
+            second = n;
+
+            if (len > 15) {
+                // Z
+                checkChar(s, 15, 'Z');
+                inUtc = true;
+            }
+        } else {
+            allDay = true;
+            hour = 0;
+            minute = 0;
+            second = 0;
+        }
+
+        weekDay = 0;
+        yearDay = 0;
+        isDst = -1;
+        gmtoff = 0;
+        return inUtc;
+    }
+
+    private void checkChar(String s, int spos, char expected) {
+        char c = s.charAt(spos);
+        if (c != expected) {
+            throw new TimeFormatException(String.format(
+                    "Unexpected character 0x%02d at pos=%d.  Expected 0x%02d (\'%c\').",
+                    (int) c, spos, (int) expected, expected));
+        }
+    }
+
+    private static int getChar(String s, int spos, int mul) {
+        char c = s.charAt(spos);
+        if (Character.isDigit(c)) {
+            return Character.getNumericValue(c) * mul;
+        } else {
+            throw new TimeFormatException("Parse error at pos=" + spos);
+        }
+    }
 
     /**
      * Parse a time in RFC 3339 format.  This method also parses simple dates
@@ -461,14 +532,140 @@
          if (s == null) {
              throw new NullPointerException("time string is null");
          }
-         if (nativeParse3339(s)) {
+         if (parse3339Internal(s)) {
              timezone = TIMEZONE_UTC;
              return true;
          }
          return false;
      }
 
-     native private boolean nativeParse3339(String s);
+     private boolean parse3339Internal(String s) {
+         int len = s.length();
+         if (len < 10) {
+             throw new TimeFormatException("String too short --- expected at least 10 characters.");
+         }
+         boolean inUtc = false;
+
+         // year
+         int n = getChar(s, 0, 1000);
+         n += getChar(s, 1, 100);
+         n += getChar(s, 2, 10);
+         n += getChar(s, 3, 1);
+         year = n;
+
+         checkChar(s, 4, '-');
+
+         // month
+         n = getChar(s, 5, 10);
+         n += getChar(s, 6, 1);
+         --n;
+         month = n;
+
+         checkChar(s, 7, '-');
+
+         // day
+         n = getChar(s, 8, 10);
+         n += getChar(s, 9, 1);
+         monthDay = n;
+
+         if (len >= 19) {
+             // T
+             checkChar(s, 10, 'T');
+             allDay = false;
+
+             // hour
+             n = getChar(s, 11, 10);
+             n += getChar(s, 12, 1);
+
+             // Note that this.hour is not set here. It is set later.
+             int hour = n;
+
+             checkChar(s, 13, ':');
+
+             // minute
+             n = getChar(s, 14, 10);
+             n += getChar(s, 15, 1);
+             // Note that this.minute is not set here. It is set later.
+             int minute = n;
+
+             checkChar(s, 16, ':');
+
+             // second
+             n = getChar(s, 17, 10);
+             n += getChar(s, 18, 1);
+             second = n;
+
+             // skip the '.XYZ' -- we don't care about subsecond precision.
+
+             int tzIndex = 19;
+             if (tzIndex < len && s.charAt(tzIndex) == '.') {
+                 do {
+                     tzIndex++;
+                 } while (tzIndex < len && Character.isDigit(s.charAt(tzIndex)));
+             }
+
+             int offset = 0;
+             if (len > tzIndex) {
+                 char c = s.charAt(tzIndex);
+                 // NOTE: the offset is meant to be subtracted to get from local time
+                 // to UTC.  we therefore use 1 for '-' and -1 for '+'.
+                 switch (c) {
+                     case 'Z':
+                         // Zulu time -- UTC
+                         offset = 0;
+                         break;
+                     case '-':
+                         offset = 1;
+                         break;
+                     case '+':
+                         offset = -1;
+                         break;
+                     default:
+                         throw new TimeFormatException(String.format(
+                                 "Unexpected character 0x%02d at position %d.  Expected + or -",
+                                 (int) c, tzIndex));
+                 }
+                 inUtc = true;
+
+                 if (offset != 0) {
+                     if (len < tzIndex + 6) {
+                         throw new TimeFormatException(
+                                 String.format("Unexpected length; should be %d characters",
+                                         tzIndex + 6));
+                     }
+
+                     // hour
+                     n = getChar(s, tzIndex + 1, 10);
+                     n += getChar(s, tzIndex + 2, 1);
+                     n *= offset;
+                     hour += n;
+
+                     // minute
+                     n = getChar(s, tzIndex + 4, 10);
+                     n += getChar(s, tzIndex + 5, 1);
+                     n *= offset;
+                     minute += n;
+                 }
+             }
+             this.hour = hour;
+             this.minute = minute;
+
+             if (offset != 0) {
+                 normalize(false);
+             }
+         } else {
+             allDay = true;
+             this.hour = 0;
+             this.minute = 0;
+             this.second = 0;
+         }
+
+         this.weekDay = 0;
+         this.yearDay = 0;
+         this.isDst = -1;
+         this.gmtoff = 0;
+         return inUtc;
+     }
 
     /**
      * Returns the timezone string that is currently set for the device.
@@ -480,7 +677,9 @@
     /**
      * Sets the time of the given Time object to the current time.
      */
-    native public void setToNow();
+    public void setToNow() {
+        set(System.currentTimeMillis());
+    }
 
     /**
      * Converts this time to milliseconds. Suitable for interacting with the
@@ -530,7 +729,10 @@
      * to read back the same milliseconds that you set with {@link #set(long)}
      * or {@link #set(Time)} or after parsing a date string.
      */
-    native public long toMillis(boolean ignoreDst);
+    public long toMillis(boolean ignoreDst) {
+        calculator.copyFieldsFromTime(this);
+        return calculator.toMillis(ignoreDst);
+    }
 
     /**
      * Sets the fields in this Time object given the UTC milliseconds.  After
@@ -539,15 +741,23 @@
      *
      * @param millis the time in UTC milliseconds since the epoch.
      */
-    native public void set(long millis);
+    public void set(long millis) {
+        allDay = false;
+        calculator.timezone = timezone;
+        calculator.setTimeInMillis(millis);
+        calculator.copyFieldsToTime(this);
+    }
 
     /**
-     * Format according to RFC 2445 DATETIME type.
+     * Format according to RFC 2445 DATE-TIME type.
      *
-     * <p>
-     * The same as format("%Y%m%dT%H%M%S").
+     * <p>The same as format("%Y%m%dT%H%M%S"), or format("%Y%m%dT%H%M%SZ") for a Time with a
+     * timezone set to "UTC".
      */
-    native public String format2445();
+    public String format2445() {
+        calculator.copyFieldsFromTime(this);
+        return calculator.format2445(!allDay);
+    }
 
     /**
      * Copy the value of that to this Time object. No normalization happens.
@@ -682,7 +892,6 @@
      * Otherwise, if the timezone is UTC, expresses the time as Y-M-D-T-H-M-S UTC</p>
      * <p>
      * Otherwise the time is expressed the time as Y-M-D-T-H-M-S +- GMT</p>
-     * @param allDay
      * @return string in the RFC 3339 format.
      */
     public String format3339(boolean allDay) {
@@ -693,7 +902,7 @@
         } else {
             String base = format(Y_M_D_T_H_M_S_000);
             String sign = (gmtoff < 0) ? "-" : "+";
-            int offset = (int)Math.abs(gmtoff);
+            int offset = (int) Math.abs(gmtoff);
             int minutes = (offset % 3600) / 60;
             int hours = offset / 3600;
 
@@ -714,16 +923,18 @@
     }
 
     /**
-     * Computes the Julian day number, given the UTC milliseconds
-     * and the offset (in seconds) from UTC.  The Julian day for a given
-     * date will be the same for every timezone.  For example, the Julian
-     * day for July 1, 2008 is 2454649.  This is the same value no matter
-     * what timezone is being used.  The Julian day is useful for testing
-     * if two events occur on the same day and for determining the relative
-     * time of an event from the present ("yesterday", "3 days ago", etc.).
+     * Computes the Julian day number for a point in time in a particular
+     * timezone. The Julian day for a given date is the same for every
+     * timezone. For example, the Julian day for July 1, 2008 is 2454649.
      *
-     * <p>
-     * Use {@link #toMillis(boolean)} to get the milliseconds.
+     * <p>Callers must pass the time in UTC millisecond (as can be returned
+     * by {@link #toMillis(boolean)} or {@link #normalize(boolean)})
+     * and the offset from UTC of the timezone in seconds (as might be in
+     * {@link #gmtoff}).
+     *
+     * <p>The Julian day is useful for testing if two events occur on the
+     * same calendar date and for determining the relative time of an event
+     * from the present ("yesterday", "3 days ago", etc.).
      *
      * @param millis the time in UTC milliseconds
      * @param gmtoff the offset from UTC in seconds
@@ -810,4 +1021,240 @@
     public static int getJulianMondayFromWeeksSinceEpoch(int week) {
         return MONDAY_BEFORE_JULIAN_EPOCH + week * 7;
     }
+
+    /**
+     * A class that handles date/time calculations.
+     *
+     * This class originated as a port of a native C++ class ("android.Time") to pure Java. It is
+     * separate from the enclosing class because some methods copy the result of calculations back
+     * to the enclosing object, but others do not: thus separate state is retained.
+     */
+    private static class TimeCalculator {
+        public final ZoneInfo.WallTime wallTime;
+        public String timezone;
+
+        // Information about the current timezone.
+        private ZoneInfo zoneInfo;
+
+        public TimeCalculator(String timezoneId) {
+            this.zoneInfo = lookupZoneInfo(timezoneId);
+            this.wallTime = new ZoneInfo.WallTime();
+        }
+
+        public long toMillis(boolean ignoreDst) {
+            if (ignoreDst) {
+                wallTime.setIsDst(-1);
+            }
+
+            int r = wallTime.mktime(zoneInfo);
+            if (r == -1) {
+                return -1;
+            }
+            return r * 1000L;
+        }
+
+        public void setTimeInMillis(long millis) {
+            // Preserve old 32-bit Android behavior.
+            int intSeconds = (int) (millis / 1000);
+
+            updateZoneInfoFromTimeZone();
+            wallTime.localtime(intSeconds, zoneInfo);
+        }
+
+        public String format(String format) {
+            if (format == null) {
+                format = "%c";
+            }
+            TimeFormatter formatter = new TimeFormatter();
+            return formatter.format(format, wallTime, zoneInfo);
+        }
+
+        private void updateZoneInfoFromTimeZone() {
+            if (!zoneInfo.getID().equals(timezone)) {
+                this.zoneInfo = lookupZoneInfo(timezone);
+            }
+        }
+
+        private static ZoneInfo lookupZoneInfo(String timezoneId) {
+            try {
+                ZoneInfo zoneInfo = ZoneInfoDB.getInstance().makeTimeZone(timezoneId);
+                if (zoneInfo == null) {
+                    zoneInfo = ZoneInfoDB.getInstance().makeTimeZone("GMT");
+                }
+                if (zoneInfo == null) {
+                    throw new AssertionError("GMT not found: \"" + timezoneId + "\"");
+                }
+                return zoneInfo;
+            } catch (IOException e) {
+                // This should not ever be thrown.
+                throw new AssertionError("Error loading timezone: \"" + timezoneId + "\"", e);
+            }
+        }
+
+        public void switchTimeZone(String timezone) {
+            int seconds = wallTime.mktime(zoneInfo);
+            this.timezone = timezone;
+            updateZoneInfoFromTimeZone();
+            wallTime.localtime(seconds, zoneInfo);
+        }
+
+        public String format2445(boolean hasTime) {
+            char[] buf = new char[hasTime ? 16 : 8];
+            int n = wallTime.getYear();
+
+            buf[0] = toChar(n / 1000);
+            n %= 1000;
+            buf[1] = toChar(n / 100);
+            n %= 100;
+            buf[2] = toChar(n / 10);
+            n %= 10;
+            buf[3] = toChar(n);
+
+            n = wallTime.getMonth() + 1;
+            buf[4] = toChar(n / 10);
+            buf[5] = toChar(n % 10);
+
+            n = wallTime.getMonthDay();
+            buf[6] = toChar(n / 10);
+            buf[7] = toChar(n % 10);
+
+            if (!hasTime) {
+                return new String(buf, 0, 8);
+            }
+
+            buf[8] = 'T';
+
+            n = wallTime.getHour();
+            buf[9] = toChar(n / 10);
+            buf[10] = toChar(n % 10);
+
+            n = wallTime.getMinute();
+            buf[11] = toChar(n / 10);
+            buf[12] = toChar(n % 10);
+
+            n = wallTime.getSecond();
+            buf[13] = toChar(n / 10);
+            buf[14] = toChar(n % 10);
+
+            if (TIMEZONE_UTC.equals(timezone)) {
+                // The letter 'Z' is appended to the end.
+                buf[15] = 'Z';
+                return new String(buf, 0, 16);
+            } else {
+                return new String(buf, 0, 15);
+            }
+        }
+
+        private char toChar(int n) {
+            return (n >= 0 && n <= 9) ? (char) (n + '0') : ' ';
+        }
+
+        /**
+         * A method that will return the state of this object in string form. Note: it has side
+         * effects and so has deliberately not been made the default {@link #toString()}.
+         */
+        public String toStringInternal() {
+            // This implementation possibly displays the un-normalized fields because that is
+            // what it has always done.
+            return String.format("%04d%02d%02dT%02d%02d%02d%s(%d,%d,%d,%d,%d)",
+                    wallTime.getYear(),
+                    wallTime.getMonth() + 1,
+                    wallTime.getMonthDay(),
+                    wallTime.getHour(),
+                    wallTime.getMinute(),
+                    wallTime.getSecond(),
+                    timezone,
+                    wallTime.getWeekDay(),
+                    wallTime.getYearDay(),
+                    wallTime.getGmtOffset(),
+                    wallTime.getIsDst(),
+                    toMillis(false /* use isDst */) / 1000
+            );
+
+        }
+
+        public static int compare(TimeCalculator aObject, TimeCalculator bObject) {
+            if (aObject.timezone.equals(bObject.timezone)) {
+                // If the timezones are the same, we can easily compare the two times.
+                int diff = aObject.wallTime.getYear() - bObject.wallTime.getYear();
+                if (diff != 0) {
+                    return diff;
+                }
+
+                diff = aObject.wallTime.getMonth() - bObject.wallTime.getMonth();
+                if (diff != 0) {
+                    return diff;
+                }
+
+                diff = aObject.wallTime.getMonthDay() - bObject.wallTime.getMonthDay();
+                if (diff != 0) {
+                    return diff;
+                }
+
+                diff = aObject.wallTime.getHour() - bObject.wallTime.getHour();
+                if (diff != 0) {
+                    return diff;
+                }
+
+                diff = aObject.wallTime.getMinute() - bObject.wallTime.getMinute();
+                if (diff != 0) {
+                    return diff;
+                }
+
+                diff = aObject.wallTime.getSecond() - bObject.wallTime.getSecond();
+                if (diff != 0) {
+                    return diff;
+                }
+
+                return 0;
+            } else {
+                // Otherwise, convert to milliseconds and compare that. This requires that object be
+                // normalized. Note: For dates that do not exist: toMillis() can return -1, which
+                // can be confused with a valid time.
+                long am = aObject.toMillis(false /* use isDst */);
+                long bm = bObject.toMillis(false /* use isDst */);
+                long diff = am - bm;
+                return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
+            }
+
+        }
+
+        public void copyFieldsToTime(Time time) {
+            time.second = wallTime.getSecond();
+            time.minute = wallTime.getMinute();
+            time.hour = wallTime.getHour();
+            time.monthDay = wallTime.getMonthDay();
+            time.month = wallTime.getMonth();
+            time.year = wallTime.getYear();
+
+            // Read-only fields that are derived from other information above.
+            time.weekDay = wallTime.getWeekDay();
+            time.yearDay = wallTime.getYearDay();
+
+            // < 0: DST status unknown, 0: is not in DST, 1: is in DST
+            time.isDst = wallTime.getIsDst();
+            // This is in seconds and includes any DST offset too.
+            time.gmtoff = wallTime.getGmtOffset();
+        }
+
+        public void copyFieldsFromTime(Time time) {
+            wallTime.setSecond(time.second);
+            wallTime.setMinute(time.minute);
+            wallTime.setHour(time.hour);
+            wallTime.setMonthDay(time.monthDay);
+            wallTime.setMonth(time.month);
+            wallTime.setYear(time.year);
+            wallTime.setWeekDay(time.weekDay);
+            wallTime.setYearDay(time.yearDay);
+            wallTime.setIsDst(time.isDst);
+            wallTime.setGmtOffset((int) time.gmtoff);
+
+            if (time.allDay && (time.second != 0 || time.minute != 0 || time.hour != 0)) {
+                throw new IllegalArgumentException("allDay is true but sec, min, hour are not 0.");
+            }
+
+            timezone = time.timezone;
+            updateZoneInfoFromTimeZone();
+        }
+    }
 }
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
new file mode 100644
index 0000000..ec79b36
--- /dev/null
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -0,0 +1,519 @@
+/*
+ * Based on the UCB version of strftime.c with the copyright notice appearing below.
+ */
+
+/*
+** Copyright (c) 1989 The Regents of the University of California.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms are permitted
+** provided that the above copyright notice and this paragraph are
+** duplicated in all such forms and that any documentation,
+** advertising materials, and other materials related to such
+** distribution and use acknowledge that the software was developed
+** by the University of California, Berkeley. The name of the
+** University may not be used to endorse or promote products derived
+** from this software without specific prior written permission.
+** THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+package android.text.format;
+
+import android.content.res.Resources;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Formatter;
+import java.util.Locale;
+import java.util.TimeZone;
+import libcore.icu.LocaleData;
+import libcore.util.ZoneInfo;
+
+/**
+ * Formatting logic for {@link Time}. Contains a port of Bionic's broken strftime_tz to Java. The
+ * main issue with this implementation is the treatment of characters as ASCII, despite returning
+ * localized (UTF-16) strings from the LocaleData.
+ *
+ * <p>This class is not thread safe.
+ */
+class TimeFormatter {
+    // An arbitrary value outside the range representable by a byte / ASCII character code.
+    private static final int FORCE_LOWER_CASE = 0x100;
+
+    private static final int SECSPERMIN = 60;
+    private static final int MINSPERHOUR = 60;
+    private static final int DAYSPERWEEK = 7;
+    private static final int MONSPERYEAR = 12;
+    private static final int HOURSPERDAY = 24;
+    private static final int DAYSPERLYEAR = 366;
+    private static final int DAYSPERNYEAR = 365;
+
+    /**
+     * The Locale for which the cached LocaleData and formats have been loaded.
+     */
+    private static Locale sLocale;
+    private static LocaleData sLocaleData;
+    private static String sTimeOnlyFormat;
+    private static String sDateOnlyFormat;
+    private static String sDateTimeFormat;
+
+    private final LocaleData localeData;
+    private final String dateTimeFormat;
+    private final String timeOnlyFormat;
+    private final String dateOnlyFormat;
+    private final Locale locale;
+
+    private StringBuilder outputBuilder;
+    private Formatter outputFormatter;
+
+    public TimeFormatter() {
+        synchronized (TimeFormatter.class) {
+            Locale locale = Locale.getDefault();
+
+            if (sLocale == null || !(locale.equals(sLocale))) {
+                sLocale = locale;
+                sLocaleData = LocaleData.get(locale);
+
+                Resources r = Resources.getSystem();
+                sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day);
+                sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year);
+                sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time);
+            }
+
+            this.dateTimeFormat = sDateTimeFormat;
+            this.timeOnlyFormat = sTimeOnlyFormat;
+            this.dateOnlyFormat = sDateOnlyFormat;
+            this.locale = locale;
+            localeData = sLocaleData;
+        }
+    }
+
+    /**
+     * Format the specified {@code wallTime} using {@code pattern}. The output is returned.
+     */
+    public String format(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
+        try {
+            StringBuilder stringBuilder = new StringBuilder();
+
+            outputBuilder = stringBuilder;
+            outputFormatter = new Formatter(stringBuilder, locale);
+
+            formatInternal(pattern, wallTime, zoneInfo);
+            String result = stringBuilder.toString();
+            // This behavior is the source of a bug since some formats are defined as being
+            // in ASCII. Generally localization is very broken.
+            if (localeData.zeroDigit != '0') {
+                result = localizeDigits(result);
+            }
+            return result;
+        } finally {
+            outputBuilder = null;
+            outputFormatter = null;
+        }
+    }
+
+    private String localizeDigits(String s) {
+        int length = s.length();
+        int offsetToLocalizedDigits = localeData.zeroDigit - '0';
+        StringBuilder result = new StringBuilder(length);
+        for (int i = 0; i < length; ++i) {
+            char ch = s.charAt(i);
+            if (ch >= '0' && ch <= '9') {
+                ch += offsetToLocalizedDigits;
+            }
+            result.append(ch);
+        }
+        return result.toString();
+    }
+
+    /**
+     * Format the specified {@code wallTime} using {@code pattern}. The output is written to
+     * {@link #outputBuilder}.
+     */
+    private void formatInternal(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) {
+        // Convert to ASCII bytes to be compatible with old implementation behavior.
+        byte[] bytes = pattern.getBytes(StandardCharsets.US_ASCII);
+        if (bytes.length == 0) {
+            return;
+        }
+
+        ByteBuffer formatBuffer = ByteBuffer.wrap(bytes);
+        while (formatBuffer.remaining() > 0) {
+            boolean outputCurrentByte = true;
+            char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+            if (currentByteAsChar == '%') {
+                outputCurrentByte = handleToken(formatBuffer, wallTime, zoneInfo);
+            }
+            if (outputCurrentByte) {
+                currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+                outputBuilder.append(currentByteAsChar);
+            }
+
+            formatBuffer.position(formatBuffer.position() + 1);
+        }
+    }
+
+    private boolean handleToken(ByteBuffer formatBuffer, ZoneInfo.WallTime wallTime,
+            ZoneInfo zoneInfo) {
+
+        // The byte at formatBuffer.position() is expected to be '%' at this point.
+        int modifier = 0;
+        while (formatBuffer.remaining() > 1) {
+            // Increment the position then get the new current byte.
+            formatBuffer.position(formatBuffer.position() + 1);
+            char currentByteAsChar = convertToChar(formatBuffer.get(formatBuffer.position()));
+            switch (currentByteAsChar) {
+                case 'A':
+                    modifyAndAppend((wallTime.getWeekDay() < 0
+                                    || wallTime.getWeekDay() >= DAYSPERWEEK)
+                                    ? "?" : localeData.longWeekdayNames[wallTime.getWeekDay() + 1],
+                            modifier);
+                    return false;
+                case 'a':
+                    modifyAndAppend((wallTime.getWeekDay() < 0
+                                    || wallTime.getWeekDay() >= DAYSPERWEEK)
+                                    ? "?" : localeData.shortWeekdayNames[wallTime.getWeekDay() + 1],
+                            modifier);
+                    return false;
+                case 'B':
+                    if (modifier == '-') {
+                        modifyAndAppend((wallTime.getMonth() < 0
+                                        || wallTime.getMonth() >= MONSPERYEAR)
+                                        ? "?"
+                                        : localeData.longStandAloneMonthNames[wallTime.getMonth()],
+                                modifier);
+                    } else {
+                        modifyAndAppend((wallTime.getMonth() < 0
+                                        || wallTime.getMonth() >= MONSPERYEAR)
+                                        ? "?" : localeData.longMonthNames[wallTime.getMonth()],
+                                modifier);
+                    }
+                    return false;
+                case 'b':
+                case 'h':
+                    modifyAndAppend((wallTime.getMonth() < 0 || wallTime.getMonth() >= MONSPERYEAR)
+                                    ? "?" : localeData.shortMonthNames[wallTime.getMonth()],
+                            modifier);
+                    return false;
+                case 'C':
+                    outputYear(wallTime.getYear(), true, false, modifier);
+                    return false;
+                case 'c':
+                    formatInternal(dateTimeFormat, wallTime, zoneInfo);
+                    return false;
+                case 'D':
+                    formatInternal("%m/%d/%y", wallTime, zoneInfo);
+                    return false;
+                case 'd':
+                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                            wallTime.getMonthDay());
+                    return false;
+                case 'E':
+                case 'O':
+                    // C99 locale modifiers are not supported.
+                    continue;
+                case '_':
+                case '-':
+                case '0':
+                case '^':
+                case '#':
+                    modifier = currentByteAsChar;
+                    continue;
+                case 'e':
+                    outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+                            wallTime.getMonthDay());
+                    return false;
+                case 'F':
+                    formatInternal("%Y-%m-%d", wallTime, zoneInfo);
+                    return false;
+                case 'H':
+                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                            wallTime.getHour());
+                    return false;
+                case 'I':
+                    int hour = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
+                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), hour);
+                    return false;
+                case 'j':
+                    int yearDay = wallTime.getYearDay() + 1;
+                    outputFormatter.format(getFormat(modifier, "%03d", "%3d", "%d", "%03d"),
+                            yearDay);
+                    return false;
+                case 'k':
+                    outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"),
+                            wallTime.getHour());
+                    return false;
+                case 'l':
+                    int n2 = (wallTime.getHour() % 12 != 0) ? (wallTime.getHour() % 12) : 12;
+                    outputFormatter.format(getFormat(modifier, "%2d", "%2d", "%d", "%02d"), n2);
+                    return false;
+                case 'M':
+                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                            wallTime.getMinute());
+                    return false;
+                case 'm':
+                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                            wallTime.getMonth() + 1);
+                    return false;
+                case 'n':
+                    modifyAndAppend("\n", modifier);
+                    return false;
+                case 'p':
+                    modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
+                            : localeData.amPm[0], modifier);
+                    return false;
+                case 'P':
+                    modifyAndAppend((wallTime.getHour() >= (HOURSPERDAY / 2)) ? localeData.amPm[1]
+                            : localeData.amPm[0], FORCE_LOWER_CASE);
+                    return false;
+                case 'R':
+                    formatInternal("%H:%M", wallTime, zoneInfo);
+                    return false;
+                case 'r':
+                    formatInternal("%I:%M:%S %p", wallTime, zoneInfo);
+                    return false;
+                case 'S':
+                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                            wallTime.getSecond());
+                    return false;
+                case 's':
+                    int timeInSeconds = wallTime.mktime(zoneInfo);
+                    modifyAndAppend(Integer.toString(timeInSeconds), modifier);
+                    return false;
+                case 'T':
+                    formatInternal("%H:%M:%S", wallTime, zoneInfo);
+                    return false;
+                case 't':
+                    modifyAndAppend("\t", modifier);
+                    return false;
+                case 'U':
+                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"),
+                            (wallTime.getYearDay() + DAYSPERWEEK - wallTime.getWeekDay())
+                                    / DAYSPERWEEK);
+                    return false;
+                case 'u':
+                    int day = (wallTime.getWeekDay() == 0) ? DAYSPERWEEK : wallTime.getWeekDay();
+                    outputFormatter.format("%d", day);
+                    return false;
+                case 'V':   /* ISO 8601 week number */
+                case 'G':   /* ISO 8601 year (four digits) */
+                case 'g':   /* ISO 8601 year (two digits) */
+                {
+                    int year = wallTime.getYear();
+                    int yday = wallTime.getYearDay();
+                    int wday = wallTime.getWeekDay();
+                    int w;
+                    while (true) {
+                        int len = isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
+                        // What yday (-3 ... 3) does the ISO year begin on?
+                        int bot = ((yday + 11 - wday) % DAYSPERWEEK) - 3;
+                        // What yday does the NEXT ISO year begin on?
+                        int top = bot - (len % DAYSPERWEEK);
+                        if (top < -3) {
+                            top += DAYSPERWEEK;
+                        }
+                        top += len;
+                        if (yday >= top) {
+                            ++year;
+                            w = 1;
+                            break;
+                        }
+                        if (yday >= bot) {
+                            w = 1 + ((yday - bot) / DAYSPERWEEK);
+                            break;
+                        }
+                        --year;
+                        yday += isLeap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
+                    }
+                    if (currentByteAsChar == 'V') {
+                        outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), w);
+                    } else if (currentByteAsChar == 'g') {
+                        outputYear(year, false, true, modifier);
+                    } else {
+                        outputYear(year, true, true, modifier);
+                    }
+                    return false;
+                }
+                case 'v':
+                    formatInternal("%e-%b-%Y", wallTime, zoneInfo);
+                    return false;
+                case 'W':
+                    int n = (wallTime.getYearDay() + DAYSPERWEEK - (
+                                    wallTime.getWeekDay() != 0 ? (wallTime.getWeekDay() - 1)
+                                            : (DAYSPERWEEK - 1))) / DAYSPERWEEK;
+                    outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+                    return false;
+                case 'w':
+                    outputFormatter.format("%d", wallTime.getWeekDay());
+                    return false;
+                case 'X':
+                    formatInternal(timeOnlyFormat, wallTime, zoneInfo);
+                    return false;
+                case 'x':
+                    formatInternal(dateOnlyFormat, wallTime, zoneInfo);
+                    return false;
+                case 'y':
+                    outputYear(wallTime.getYear(), false, true, modifier);
+                    return false;
+                case 'Y':
+                    outputYear(wallTime.getYear(), true, true, modifier);
+                    return false;
+                case 'Z':
+                    if (wallTime.getIsDst() < 0) {
+                        return false;
+                    }
+                    boolean isDst = wallTime.getIsDst() != 0;
+                    modifyAndAppend(zoneInfo.getDisplayName(isDst, TimeZone.SHORT), modifier);
+                    return false;
+                case 'z': {
+                    if (wallTime.getIsDst() < 0) {
+                        return false;
+                    }
+                    int diff = wallTime.getGmtOffset();
+                    String sign;
+                    if (diff < 0) {
+                        sign = "-";
+                        diff = -diff;
+                    } else {
+                        sign = "+";
+                    }
+                    modifyAndAppend(sign, modifier);
+                    diff /= SECSPERMIN;
+                    diff = (diff / MINSPERHOUR) * 100 + (diff % MINSPERHOUR);
+                    outputFormatter.format(getFormat(modifier, "%04d", "%4d", "%d", "%04d"), diff);
+                    return false;
+                }
+                case '+':
+                    formatInternal("%a %b %e %H:%M:%S %Z %Y", wallTime, zoneInfo);
+                    return false;
+                case '%':
+                    // If conversion char is undefined, behavior is undefined. Print out the
+                    // character itself.
+                default:
+                    return true;
+            }
+        }
+        return true;
+    }
+
+    private void modifyAndAppend(CharSequence str, int modifier) {
+        switch (modifier) {
+            case FORCE_LOWER_CASE:
+                for (int i = 0; i < str.length(); i++) {
+                    outputBuilder.append(brokenToLower(str.charAt(i)));
+                }
+                break;
+            case '^':
+                for (int i = 0; i < str.length(); i++) {
+                    outputBuilder.append(brokenToUpper(str.charAt(i)));
+                }
+                break;
+            case '#':
+                for (int i = 0; i < str.length(); i++) {
+                    char c = str.charAt(i);
+                    if (brokenIsUpper(c)) {
+                        c = brokenToLower(c);
+                    } else if (brokenIsLower(c)) {
+                        c = brokenToUpper(c);
+                    }
+                    outputBuilder.append(c);
+                }
+                break;
+            default:
+                outputBuilder.append(str);
+
+        }
+    }
+
+    private void outputYear(int value, boolean outputTop, boolean outputBottom, int modifier) {
+        int lead;
+        int trail;
+
+        final int DIVISOR = 100;
+        trail = value % DIVISOR;
+        lead = value / DIVISOR + trail / DIVISOR;
+        trail %= DIVISOR;
+        if (trail < 0 && lead > 0) {
+            trail += DIVISOR;
+            --lead;
+        } else if (lead < 0 && trail > 0) {
+            trail -= DIVISOR;
+            ++lead;
+        }
+        if (outputTop) {
+            if (lead == 0 && trail < 0) {
+                modifyAndAppend("-0", modifier);
+            } else {
+                outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), lead);
+            }
+        }
+        if (outputBottom) {
+            int n = ((trail < 0) ? -trail : trail);
+            outputFormatter.format(getFormat(modifier, "%02d", "%2d", "%d", "%02d"), n);
+        }
+    }
+
+    private static String getFormat(int modifier, String normal, String underscore, String dash,
+            String zero) {
+        switch (modifier) {
+            case '_':
+                return underscore;
+            case '-':
+                return dash;
+            case '0':
+                return zero;
+        }
+        return normal;
+    }
+
+    private static boolean isLeap(int year) {
+        return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
+    }
+
+    /**
+     * A broken implementation of {@link Character#isUpperCase(char)} that assumes ASCII in order to
+     * be compatible with the old native implementation.
+     */
+    private static boolean brokenIsUpper(char toCheck) {
+        return toCheck >= 'A' && toCheck <= 'Z';
+    }
+
+    /**
+     * A broken implementation of {@link Character#isLowerCase(char)} that assumes ASCII in order to
+     * be compatible with the old native implementation.
+     */
+    private static boolean brokenIsLower(char toCheck) {
+        return toCheck >= 'a' && toCheck <= 'z';
+    }
+
+    /**
+     * A broken implementation of {@link Character#toLowerCase(char)} that assumes ASCII in order to
+     * be compatible with the old native implementation.
+     */
+    private static char brokenToLower(char input) {
+        if (input >= 'A' && input <= 'Z') {
+            return (char) (input - 'A' + 'a');
+        }
+        return input;
+    }
+
+    /**
+     * A broken implementation of {@link Character#toUpperCase(char)} that assumes ASCII in order to
+     * be compatible with the old native implementation.
+     */
+    private static char brokenToUpper(char input) {
+        if (input >= 'a' && input <= 'z') {
+            return (char) (input - 'a' + 'A');
+        }
+        return input;
+    }
+
+    /**
+     * Safely convert a byte containing an ASCII character to a char, even for character codes
+     * > 127.
+     */
+    private static char convertToChar(byte b) {
+        return (char) (b & 0xFF);
+    }
+}
diff --git a/core/java/android/util/TimeFormatException.java b/core/java/android/util/TimeFormatException.java
index d7a898b..f520523 100644
--- a/core/java/android/util/TimeFormatException.java
+++ b/core/java/android/util/TimeFormatException.java
@@ -18,7 +18,11 @@
 
 public class TimeFormatException extends RuntimeException
 {
-    TimeFormatException(String s)
+
+    /**
+     * @hide
+     */
+    public TimeFormatException(String s)
     {
         super(s);
     }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 7b5395b..480383b 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -23,7 +23,6 @@
 
 LOCAL_SRC_FILES:= \
 	AndroidRuntime.cpp \
-	Time.cpp \
 	com_android_internal_content_NativeLibraryHelper.cpp \
 	com_google_android_gles_jni_EGLImpl.cpp \
 	com_google_android_gles_jni_GLImpl.cpp.arm \
@@ -81,7 +80,6 @@
 	android_net_NetUtils.cpp \
 	android_net_TrafficStats.cpp \
 	android_nio_utils.cpp \
-	android_text_format_Time.cpp \
 	android_util_AssetManager.cpp \
 	android_util_Binder.cpp \
 	android_util_EventLog.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 92a8fca..3a02ab9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -144,7 +144,6 @@
 extern int register_android_database_SQLiteGlobal(JNIEnv* env);
 extern int register_android_database_SQLiteDebug(JNIEnv* env);
 extern int register_android_nio_utils(JNIEnv* env);
-extern int register_android_text_format_Time(JNIEnv* env);
 extern int register_android_os_Debug(JNIEnv* env);
 extern int register_android_os_MessageQueue(JNIEnv* env);
 extern int register_android_os_Parcel(JNIEnv* env);
@@ -1223,7 +1222,6 @@
     REG_JNI(register_android_util_EventLog),
     REG_JNI(register_android_util_Log),
     REG_JNI(register_android_util_FloatMath),
-    REG_JNI(register_android_text_format_Time),
     REG_JNI(register_android_content_AssetManager),
     REG_JNI(register_android_content_StringBlock),
     REG_JNI(register_android_content_XmlBlock),
diff --git a/core/jni/Time.cpp b/core/jni/Time.cpp
deleted file mode 100644
index f3037f3..0000000
--- a/core/jni/Time.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-#include "TimeUtils.h"
-#include <stdio.h>
-#include <cutils/tztime.h>
-
-namespace android {
-
-static void
-dump(const Time& t)
-{
-    #ifdef HAVE_TM_GMTOFF
-        long tm_gmtoff = t.t.tm_gmtoff;
-    #else
-        long tm_gmtoff = 0;
-    #endif
-    printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n",
-            t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday,
-            t.t.tm_hour, t.t.tm_min, t.t.tm_sec,
-            t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday);
-}
-
-Time::Time()
-{
-    t.tm_sec = 0;
-    t.tm_min = 0;
-    t.tm_hour = 0;
-    t.tm_mday = 0;
-    t.tm_mon = 0;
-    t.tm_year = 0;
-    t.tm_wday = 0;
-    t.tm_yday = 0;
-    t.tm_isdst = -1; // we don't know, so let the C library determine
-    #ifdef HAVE_TM_GMTOFF
-        t.tm_gmtoff = 0;
-    #endif
-}
-
-
-#define COMPARE_FIELD(field) do { \
-        int diff = a.t.field - b.t.field; \
-        if (diff != 0) return diff; \
-    } while(0)
-
-int
-Time::compare(Time& a, Time& b)
-{
-    if (0 == strcmp(a.timezone, b.timezone)) {
-        // if the timezones are the same, we can easily compare the two
-        // times.  Otherwise, convert to milliseconds and compare that.
-        // This requires that object be normalized.
-        COMPARE_FIELD(tm_year);
-        COMPARE_FIELD(tm_mon);
-        COMPARE_FIELD(tm_mday);
-        COMPARE_FIELD(tm_hour);
-        COMPARE_FIELD(tm_min);
-        COMPARE_FIELD(tm_sec);
-        return 0;
-    } else {
-        int64_t am = a.toMillis(false /* use isDst */);
-        int64_t bm = b.toMillis(false /* use isDst */);
-        int64_t diff = am-bm;
-        return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
-    }
-}
-
-static const int DAYS_PER_MONTH[] = {
-                        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-                    };
-
-static inline int days_this_month(int year, int month)
-{
-    int n = DAYS_PER_MONTH[month];
-    if (n != 28) {
-        return n;
-    } else {
-        int y = year;
-        return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28;
-    }
-}
-
-void 
-Time::switchTimezone(const char* timezone)
-{
-    time_t seconds = mktime_tz(&(this->t), this->timezone);
-    localtime_tz(&seconds, &(this->t), timezone);
-}
-
-String8 
-Time::format(const char *format, const struct strftime_locale *locale) const
-{
-    char buf[257];
-    int n = strftime_tz(buf, 257, format, &(this->t), locale);
-    if (n > 0) {
-        return String8(buf);
-    } else {
-        return String8();
-    }
-}
-
-static inline short
-tochar(int n)
-{
-    return (n >= 0 && n <= 9) ? ('0'+n) : ' ';
-}
-
-static inline short
-next_char(int *m, int k)
-{
-    int n = *m / k;
-    *m = *m % k;
-    return tochar(n);
-}
-
-void
-Time::format2445(short* buf, bool hasTime) const
-{
-    int n;
-
-    n = t.tm_year+1900;
-    buf[0] = next_char(&n, 1000);
-    buf[1] = next_char(&n, 100);
-    buf[2] = next_char(&n, 10);
-    buf[3] = tochar(n);
-
-    n = t.tm_mon+1;
-    buf[4] = next_char(&n, 10);
-    buf[5] = tochar(n);
-
-    n = t.tm_mday;
-    buf[6] = next_char(&n, 10);
-    buf[7] = tochar(n);
-
-    if (hasTime) {
-      buf[8] = 'T';
-
-      n = t.tm_hour;
-      buf[9] = next_char(&n, 10);
-      buf[10] = tochar(n);
-      
-      n = t.tm_min;
-      buf[11] = next_char(&n, 10);
-      buf[12] = tochar(n);
-      
-      n = t.tm_sec;
-      buf[13] = next_char(&n, 10);
-      buf[14] = tochar(n);
-      bool inUtc = strcmp("UTC", timezone) == 0;
-      if (inUtc) {
-          buf[15] = 'Z';
-      }
-    }
-}
-
-String8 
-Time::toString() const
-{
-    String8 str;
-    char* s = str.lockBuffer(150);
-    #ifdef HAVE_TM_GMTOFF
-        long tm_gmtoff = t.tm_gmtoff;
-    #else
-        long tm_gmtoff = 0;
-    #endif
-    sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)", 
-            t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min,
-            t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst,
-            (int)(((Time*)this)->toMillis(false /* use isDst */)/1000));
-    str.unlockBuffer();
-    return str;
-}
-
-void 
-Time::setToNow()
-{
-    time_t seconds;
-    time(&seconds);
-    localtime_tz(&seconds, &(this->t), this->timezone);
-}
-
-int64_t 
-Time::toMillis(bool ignoreDst)
-{
-    if (ignoreDst) {
-        this->t.tm_isdst = -1;
-    }
-    int64_t r = mktime_tz(&(this->t), this->timezone);
-    if (r == -1)
-        return -1;
-    return r * 1000;
-}
-
-void 
-Time::set(int64_t millis)
-{
-    time_t seconds = millis / 1000;
-    localtime_tz(&seconds, &(this->t), this->timezone);
-}
-
-}; // namespace android
-
diff --git a/core/jni/TimeUtils.h b/core/jni/TimeUtils.h
deleted file mode 100644
index b19e021..0000000
--- a/core/jni/TimeUtils.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_TIME_H
-#define ANDROID_TIME_H
-
-#include <time.h>
-#include <cutils/tztime.h>
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-
-namespace android {
-
-/*
- * This class is the core implementation of the android.util.Time java
- * class.  It doesn't implement some of the methods that are implemented
- * in Java.  They could be done here, but it's not expected that this class
- * will be used.  If that assumption is incorrect, feel free to update this
- * file.  The reason to do it here is to not mix the implementation of this
- * class and the jni glue code.
- */
-class Time
-{
-public:
-    struct tm t;
-
-    // this object doesn't own this string
-    const char *timezone;
-
-    enum {
-        SEC = 1,
-        MIN = 2,
-        HOUR = 3,
-        MDAY = 4,
-        MON = 5,
-        YEAR = 6,
-        WDAY = 7,
-        YDAY = 8
-    };
-
-    static int compare(Time& a, Time& b);
-
-    Time();
-
-    void switchTimezone(const char *timezone);
-    String8 format(const char *format, const struct strftime_locale *locale) const;
-    void format2445(short* buf, bool hasTime) const;
-    String8 toString() const;
-    void setToNow();
-    int64_t toMillis(bool ignoreDst);
-    void set(int64_t millis);
-
-    inline void set(int sec, int min, int hour, int mday, int mon, int year,
-            int isdst)
-    {
-        this->t.tm_sec = sec;
-        this->t.tm_min = min;
-        this->t.tm_hour = hour;
-        this->t.tm_mday = mday;
-        this->t.tm_mon = mon;
-        this->t.tm_year = year;
-        this->t.tm_isdst = isdst;
-#ifdef HAVE_TM_GMTOFF
-        this->t.tm_gmtoff = 0;
-#endif
-        this->t.tm_wday = 0;
-        this->t.tm_yday = 0;
-    }
-};
-
-}; // namespace android
-
-#endif // ANDROID_TIME_H
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
deleted file mode 100644
index 28a8a5d..0000000
--- a/core/jni/android_text_format_Time.cpp
+++ /dev/null
@@ -1,689 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-#define LOG_TAG "Log_println"
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <assert.h>
-
-#include "jni.h"
-#include "utils/misc.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "ScopedStringChars.h"
-#include "TimeUtils.h"
-#include <nativehelper/JNIHelp.h>
-#include <cutils/tztime.h>
-
-namespace android {
-
-static jfieldID g_allDayField = 0;
-static jfieldID g_secField = 0;
-static jfieldID g_minField = 0;
-static jfieldID g_hourField = 0;
-static jfieldID g_mdayField = 0;
-static jfieldID g_monField = 0;
-static jfieldID g_yearField = 0;
-static jfieldID g_wdayField = 0;
-static jfieldID g_ydayField = 0;
-static jfieldID g_isdstField = 0;
-static jfieldID g_gmtoffField = 0;
-static jfieldID g_timezoneField = 0;
-
-static jfieldID g_shortMonthsField = 0;
-static jfieldID g_longMonthsField = 0;
-static jfieldID g_longStandaloneMonthsField = 0;
-static jfieldID g_shortWeekdaysField = 0;
-static jfieldID g_longWeekdaysField = 0;
-static jfieldID g_timeOnlyFormatField = 0;
-static jfieldID g_dateOnlyFormatField = 0;
-static jfieldID g_dateTimeFormatField = 0;
-static jfieldID g_amField = 0;
-static jfieldID g_pmField = 0;
-static jfieldID g_dateCommandField = 0;
-static jfieldID g_localeField = 0;
-
-static jclass g_timeClass = NULL;
-
-static inline bool java2time(JNIEnv* env, Time* t, jobject o)
-{
-    t->t.tm_sec = env->GetIntField(o, g_secField);
-    t->t.tm_min = env->GetIntField(o, g_minField);
-    t->t.tm_hour = env->GetIntField(o, g_hourField);
-    t->t.tm_mday = env->GetIntField(o, g_mdayField);
-    t->t.tm_mon = env->GetIntField(o, g_monField);
-    t->t.tm_year = (env->GetIntField(o, g_yearField))-1900;
-    t->t.tm_wday = env->GetIntField(o, g_wdayField);
-    t->t.tm_yday = env->GetIntField(o, g_ydayField);
-    t->t.tm_isdst = env->GetIntField(o, g_isdstField);
-    t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField);
-    bool allDay = env->GetBooleanField(o, g_allDayField);
-    if (allDay &&
-       ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
-        jniThrowException(env, "java/lang/IllegalArgumentException",
-                          "allDay is true but sec, min, hour are not 0.");
-        return false;
-    }
-    return true;
-}
-
-static inline void time2java(JNIEnv* env, jobject o, const Time &t)
-{
-    env->SetIntField(o, g_secField, t.t.tm_sec);
-    env->SetIntField(o, g_minField, t.t.tm_min);
-    env->SetIntField(o, g_hourField, t.t.tm_hour);
-    env->SetIntField(o, g_mdayField, t.t.tm_mday);
-    env->SetIntField(o, g_monField, t.t.tm_mon);
-    env->SetIntField(o, g_yearField, t.t.tm_year+1900);
-    env->SetIntField(o, g_wdayField, t.t.tm_wday);
-    env->SetIntField(o, g_ydayField, t.t.tm_yday);
-    env->SetIntField(o, g_isdstField, t.t.tm_isdst);
-    env->SetLongField(o, g_gmtoffField, t.t.tm_gmtoff);
-}
-
-#define ACQUIRE_TIMEZONE(This, t) \
-    jstring timezoneString_##This \
-            = (jstring) env->GetObjectField(This, g_timezoneField); \
-    t.timezone = env->GetStringUTFChars(timezoneString_##This, NULL);
-
-#define RELEASE_TIMEZONE(This, t) \
-    env->ReleaseStringUTFChars(timezoneString_##This, t.timezone);
-
-
-// ============================================================================
-
-static jlong android_text_format_Time_normalize(JNIEnv* env, jobject This,
-                                           jboolean ignoreDst)
-{
-    Time t;
-    if (!java2time(env, &t, This)) return 0L;
-    ACQUIRE_TIMEZONE(This, t)
-
-    int64_t result = t.toMillis(ignoreDst != 0);
-
-    time2java(env, This, t);
-    RELEASE_TIMEZONE(This, t)
-
-    return static_cast<jlong>(result);
-}
-
-static void android_text_format_Time_switchTimezone(JNIEnv* env, jobject This,
-                            jstring timezoneObject)
-{
-    Time t;
-    if (!java2time(env, &t, This)) return;
-    ACQUIRE_TIMEZONE(This, t)
-
-    const char* timezone = env->GetStringUTFChars(timezoneObject, NULL);
-
-    t.switchTimezone(timezone);
-
-    time2java(env, This, t);
-    env->ReleaseStringUTFChars(timezoneObject, timezone);
-    RELEASE_TIMEZONE(This, t)
-
-    // we do this here because there's no point in reallocating the string
-    env->SetObjectField(This, g_timezoneField, timezoneObject);
-}
-
-static jint android_text_format_Time_compare(JNIEnv* env, jobject clazz,
-                            jobject aObject, jobject bObject)
-{
-    Time a, b;
-
-    if (!java2time(env, &a, aObject)) return 0;
-    ACQUIRE_TIMEZONE(aObject, a)
-
-    if (!java2time(env, &b, bObject)) return 0;
-    ACQUIRE_TIMEZONE(bObject, b)
-
-    int result = Time::compare(a, b);
-
-    RELEASE_TIMEZONE(aObject, a)
-    RELEASE_TIMEZONE(bObject, b)
-
-    return static_cast<jint>(result);
-}
-
-static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This)
-{
-    Time t;
-    if (!java2time(env, &t, This)) return env->NewStringUTF("");
-    bool allDay = env->GetBooleanField(This, g_allDayField);
-    
-    if (!allDay) {
-        ACQUIRE_TIMEZONE(This, t)
-        bool inUtc = strcmp("UTC", t.timezone) == 0;
-        short buf[16];
-        t.format2445(buf, true);
-        RELEASE_TIMEZONE(This, t)
-        if (inUtc) {
-            // The letter 'Z' is appended to the end so allow for one
-            // more character in the buffer.
-            return env->NewString((jchar*)buf, 16);
-        } else {
-            return env->NewString((jchar*)buf, 15);
-        }
-    } else {
-        short buf[8];
-        t.format2445(buf, false);
-        return env->NewString((jchar*)buf, 8);
-    }
-}
-
-static jstring android_text_format_Time_format(JNIEnv* env, jobject This,
-                            jstring formatObject)
-{
-    // We only teardown and setup our 'locale' struct and other state
-    // when the Java-side locale changed.  This is safe to do here
-    // without locking because we're always called from Java code
-    // synchronized on the class instance.
-    static jobject js_locale_previous = NULL;
-    static struct strftime_locale locale;
-    static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7];
-    static jstring js_standalone_month[12];
-    static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt;
-
-    Time t;
-    if (!java2time(env, &t, This)) return env->NewStringUTF("");
-
-    jclass timeClass = g_timeClass;
-    jobject js_locale = (jobject) env->GetStaticObjectField(timeClass, g_localeField);
-    if (js_locale_previous != js_locale) {
-        if (js_locale_previous != NULL) {
-            // Free the old one.
-            for (int i = 0; i < 12; i++) {
-                env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]);
-                env->ReleaseStringUTFChars(js_month[i], locale.month[i]);
-                env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]);
-                env->DeleteGlobalRef(js_mon[i]);
-                env->DeleteGlobalRef(js_month[i]);
-                env->DeleteGlobalRef(js_standalone_month[i]);
-            }
-
-            for (int i = 0; i < 7; i++) {
-                env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]);
-                env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]);
-                env->DeleteGlobalRef(js_wday[i]);
-                env->DeleteGlobalRef(js_weekday[i]);
-            }
-
-            env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt);
-            env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt);
-            env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt);
-            env->ReleaseStringUTFChars(js_am, locale.am);
-            env->ReleaseStringUTFChars(js_pm, locale.pm);
-            env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt);
-            env->DeleteGlobalRef(js_X_fmt);
-            env->DeleteGlobalRef(js_x_fmt);
-            env->DeleteGlobalRef(js_c_fmt);
-            env->DeleteGlobalRef(js_am);
-            env->DeleteGlobalRef(js_pm);
-            env->DeleteGlobalRef(js_date_fmt);
-        }
-        js_locale_previous = js_locale;
-
-        jobjectArray ja;
-        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField);
-        for (int i = 0; i < 12; i++) {
-            // Calendar.JANUARY == 0.
-            js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
-            locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL);
-        }
-
-        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField);
-        for (int i = 0; i < 12; i++) {
-            // Calendar.JANUARY == 0.
-            js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
-            locale.month[i] = env->GetStringUTFChars(js_month[i], NULL);
-        }
-
-        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField);
-        for (int i = 0; i < 12; i++) {
-            // Calendar.JANUARY == 0.
-            js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
-            locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL);
-        }
-
-        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField);
-        for (int i = 0; i < 7; i++) {
-            // Calendar.SUNDAY == 1, and there's an empty string in element 0.
-            js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1));
-            locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL);
-        }
-
-        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField);
-        for (int i = 0; i < 7; i++) {
-            // Calendar.SUNDAY == 1, and there's an empty string in element 0.
-            js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1));
-            locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL);
-        }
-
-        js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
-                                                       timeClass, g_timeOnlyFormatField));
-        locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL);
-
-        js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
-                                                       timeClass, g_dateOnlyFormatField));
-        locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL);
-
-        js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
-                                                       timeClass, g_dateTimeFormatField));
-        locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL);
-
-        js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
-                                                    timeClass, g_amField));
-        locale.am = env->GetStringUTFChars(js_am, NULL);
-
-        js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
-                                                    timeClass, g_pmField));
-        locale.pm = env->GetStringUTFChars(js_pm, NULL);
-
-        js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
-                                                          timeClass, g_dateCommandField));
-        locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL);
-    }
-
-    ACQUIRE_TIMEZONE(This, t)
-
-    const char* format = env->GetStringUTFChars(formatObject, NULL);
-
-    String8 r = t.format(format, &locale);
-
-    env->ReleaseStringUTFChars(formatObject, format);
-    RELEASE_TIMEZONE(This, t)
-
-    return env->NewStringUTF(r.string());
-}
-
-
-static jstring android_text_format_Time_toString(JNIEnv* env, jobject This)
-{
-    Time t;
-    if (!java2time(env, &t, This)) return env->NewStringUTF("");
-    ACQUIRE_TIMEZONE(This, t)
-
-    String8 r = t.toString();
-
-    RELEASE_TIMEZONE(This, t)
-
-    return env->NewStringUTF(r.string());
-}
-
-static void android_text_format_Time_setToNow(JNIEnv* env, jobject This)
-{
-    env->SetBooleanField(This, g_allDayField, JNI_FALSE);
-    Time t;
-    ACQUIRE_TIMEZONE(This, t)
-
-    t.setToNow();
-
-    time2java(env, This, t);
-    RELEASE_TIMEZONE(This, t)
-}
-
-static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This,
-                                        jboolean ignoreDst)
-{
-    Time t;
-    if (!java2time(env, &t, This)) return 0L;
-    ACQUIRE_TIMEZONE(This, t)
-
-    int64_t result = t.toMillis(ignoreDst != 0);
-
-    RELEASE_TIMEZONE(This, t)
-
-    return static_cast<jlong>(result);
-}
-
-static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis)
-{
-    env->SetBooleanField(This, g_allDayField, JNI_FALSE);
-    Time t;
-    ACQUIRE_TIMEZONE(This, t)
-
-    t.set(millis);
-
-    time2java(env, This, t);
-    RELEASE_TIMEZONE(This, t)
-}
-
-
-// ============================================================================
-// Just do this here because it's not worth recreating the strings
-
-static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul,
-                    bool* thrown)
-{
-    jchar c = s[spos];
-    if (c >= '0' && c <= '9') {
-        return (c - '0') * mul;
-    } else {
-        if (!*thrown) {
-            jniThrowExceptionFmt(env, "android/util/TimeFormatException",
-                                 "Parse error at pos=%d", spos);
-            *thrown = true;
-        }
-        return 0;
-    }
-}
-
-static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected)
-{
-    jchar c = s[spos];
-    if (c != expected) {
-        jniThrowExceptionFmt(env, "android/util/TimeFormatException",
-                             "Unexpected character 0x%02x at pos=%d.  Expected %c.",
-                             c, spos, expected);
-        return false;
-    }
-    return true;
-}
-
-
-static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj)
-{
-    jsize len = env->GetStringLength(strObj);
-    if (len < 8) {
-        jniThrowException(env, "android/util/TimeFormatException",
-                          "String too short -- expected at least 8 characters.");
-        return JNI_FALSE;
-    }
-
-    jboolean inUtc = JNI_FALSE;
-
-    ScopedStringChars s(env, strObj);
-
-    // year
-    int n;
-    bool thrown = false;
-    n = get_char(env, s, 0, 1000, &thrown);
-    n += get_char(env, s, 1, 100, &thrown);
-    n += get_char(env, s, 2, 10, &thrown);
-    n += get_char(env, s, 3, 1, &thrown);
-    if (thrown) return JNI_FALSE;
-    env->SetIntField(This, g_yearField, n);
-
-    // month
-    n = get_char(env, s, 4, 10, &thrown);
-    n += get_char(env, s, 5, 1, &thrown);
-    n--;
-    if (thrown) return JNI_FALSE;
-    env->SetIntField(This, g_monField, n);
-
-    // day of month
-    n = get_char(env, s, 6, 10, &thrown);
-    n += get_char(env, s, 7, 1, &thrown);
-    if (thrown) return JNI_FALSE;
-    env->SetIntField(This, g_mdayField, n);
-
-    if (len > 8) {
-        // T
-        if (!check_char(env, s, 8, 'T')) return JNI_FALSE;
-        env->SetBooleanField(This, g_allDayField, JNI_FALSE);
-
-        // hour
-        n = get_char(env, s, 9, 10, &thrown);
-        n += get_char(env, s, 10, 1, &thrown);
-        if (thrown) return JNI_FALSE;
-        env->SetIntField(This, g_hourField, n);
-
-        // min
-        n = get_char(env, s, 11, 10, &thrown);
-        n += get_char(env, s, 12, 1, &thrown);
-        if (thrown) return JNI_FALSE;
-        env->SetIntField(This, g_minField, n);
-
-        // sec
-        n = get_char(env, s, 13, 10, &thrown);
-        n += get_char(env, s, 14, 1, &thrown);
-        if (thrown) return JNI_FALSE;
-        env->SetIntField(This, g_secField, n);
-
-        if (len > 15) {
-            // Z
-            if (!check_char(env, s, 15, 'Z')) return JNI_FALSE;
-            inUtc = JNI_TRUE;
-        }
-    } else {
-        env->SetBooleanField(This, g_allDayField, JNI_TRUE);
-        env->SetIntField(This, g_hourField, 0);
-        env->SetIntField(This, g_minField, 0);
-        env->SetIntField(This, g_secField, 0);
-    }
-
-    env->SetIntField(This, g_wdayField, 0);
-    env->SetIntField(This, g_ydayField, 0);
-    env->SetIntField(This, g_isdstField, -1);
-    env->SetLongField(This, g_gmtoffField, 0);
-
-    return inUtc;
-}
-
-static jboolean android_text_format_Time_parse3339(JNIEnv* env, 
-                                           jobject This, 
-                                           jstring strObj)
-{
-    jsize len = env->GetStringLength(strObj);
-    if (len < 10) {
-        jniThrowException(env, "android/util/TimeFormatException",
-                          "String too short --- expected at least 10 characters.");
-        return JNI_FALSE;
-    }
-
-    jboolean inUtc = JNI_FALSE;
-
-    ScopedStringChars s(env, strObj);
-
-    // year
-    int n;
-    bool thrown = false;
-    n = get_char(env, s, 0, 1000, &thrown);    
-    n += get_char(env, s, 1, 100, &thrown);
-    n += get_char(env, s, 2, 10, &thrown);
-    n += get_char(env, s, 3, 1, &thrown);
-    if (thrown) return JNI_FALSE;
-    env->SetIntField(This, g_yearField, n);
-    
-    // -
-    if (!check_char(env, s, 4, '-')) return JNI_FALSE;
-    
-    // month
-    n = get_char(env, s, 5, 10, &thrown);
-    n += get_char(env, s, 6, 1, &thrown);
-    --n;
-    if (thrown) return JNI_FALSE;
-    env->SetIntField(This, g_monField, n);
-
-    // -
-    if (!check_char(env, s, 7, '-')) return JNI_FALSE;
-
-    // day
-    n = get_char(env, s, 8, 10, &thrown);
-    n += get_char(env, s, 9, 1, &thrown);
-    if (thrown) return JNI_FALSE;
-    env->SetIntField(This, g_mdayField, n);
-
-    if (len >= 19) {
-        // T
-        if (!check_char(env, s, 10, 'T')) return JNI_FALSE;
-
-        env->SetBooleanField(This, g_allDayField, JNI_FALSE);
-        // hour
-        n = get_char(env, s, 11, 10, &thrown);
-        n += get_char(env, s, 12, 1, &thrown);
-        if (thrown) return JNI_FALSE;
-        int hour = n;
-        // env->SetIntField(This, g_hourField, n);
-
-        // :
-        if (!check_char(env, s, 13, ':')) return JNI_FALSE;
-
-        // minute
-        n = get_char(env, s, 14, 10, &thrown);
-        n += get_char(env, s, 15, 1, &thrown);
-        if (thrown) return JNI_FALSE;
-        int minute = n;
-        // env->SetIntField(This, g_minField, n);
-
-        // :
-        if (!check_char(env, s, 16, ':')) return JNI_FALSE;
-
-        // second
-        n = get_char(env, s, 17, 10, &thrown);
-        n += get_char(env, s, 18, 1, &thrown);
-        if (thrown) return JNI_FALSE;
-        env->SetIntField(This, g_secField, n);
-
-        // skip the '.XYZ' -- we don't care about subsecond precision.
-        int tz_index = 19;
-        if (tz_index < len && s[tz_index] == '.') {
-            do {
-                tz_index++;
-            } while (tz_index < len
-                && s[tz_index] >= '0'
-                && s[tz_index] <= '9');
-        }
-
-        int offset = 0;
-        if (len > tz_index) {
-            char c = s[tz_index];
-
-            // NOTE: the offset is meant to be subtracted to get from local time
-            // to UTC.  we therefore use 1 for '-' and -1 for '+'.
-            switch (c) {
-            case 'Z':
-                // Zulu time -- UTC
-                offset = 0;
-                break;
-            case '-': 
-                offset = 1;
-                break;
-            case '+': 
-                offset = -1;
-                break;
-            default:
-                jniThrowExceptionFmt(env, "android/util/TimeFormatException",
-                                     "Unexpected character 0x%02x at position %d.  Expected + or -",
-                                     c, tz_index);
-                return JNI_FALSE;
-            }
-            inUtc = JNI_TRUE;
-
-            if (offset != 0) {
-                if (len < tz_index + 6) {
-                    jniThrowExceptionFmt(env, "android/util/TimeFormatException",
-                                         "Unexpected length; should be %d characters",
-                                         tz_index + 6);
-                    return JNI_FALSE;
-                }
-
-                // hour
-                n = get_char(env, s, tz_index + 1, 10, &thrown);
-                n += get_char(env, s, tz_index + 2, 1, &thrown);
-                if (thrown) return JNI_FALSE;
-                n *= offset;
-                hour += n;
-
-                // :
-                if (!check_char(env, s, tz_index + 3, ':')) return JNI_FALSE;
-            
-                // minute
-                n = get_char(env, s, tz_index + 4, 10, &thrown);
-                n += get_char(env, s, tz_index + 5, 1, &thrown);
-                if (thrown) return JNI_FALSE;
-                n *= offset;
-                minute += n;
-            }
-        }
-        env->SetIntField(This, g_hourField, hour);
-        env->SetIntField(This, g_minField, minute);
-
-        if (offset != 0) {
-            // we need to normalize after applying the hour and minute offsets
-            android_text_format_Time_normalize(env, This, false /* use isdst */);
-            // The timezone is set to UTC in the calling Java code.
-        }
-    } else {
-        env->SetBooleanField(This, g_allDayField, JNI_TRUE);
-        env->SetIntField(This, g_hourField, 0);
-        env->SetIntField(This, g_minField, 0);
-        env->SetIntField(This, g_secField, 0);
-    }
-
-    env->SetIntField(This, g_wdayField, 0);
-    env->SetIntField(This, g_ydayField, 0);
-    env->SetIntField(This, g_isdstField, -1);
-    env->SetLongField(This, g_gmtoffField, 0);
-
-    return inUtc;
-}
-
-// ============================================================================
-/*
- * JNI registration.
- */
-static JNINativeMethod gMethods[] = {
-    /* name, signature, funcPtr */
-    { "normalize",               "(Z)J",                                        (void*)android_text_format_Time_normalize },
-    { "switchTimezone",          "(Ljava/lang/String;)V",                       (void*)android_text_format_Time_switchTimezone },
-    { "nativeCompare",           "(Landroid/text/format/Time;Landroid/text/format/Time;)I",     (void*)android_text_format_Time_compare },
-    { "format1",                 "(Ljava/lang/String;)Ljava/lang/String;",      (void*)android_text_format_Time_format },
-    { "format2445",              "()Ljava/lang/String;",                        (void*)android_text_format_Time_format2445 },
-    { "toString",                "()Ljava/lang/String;",                        (void*)android_text_format_Time_toString },
-    { "nativeParse",             "(Ljava/lang/String;)Z",                       (void*)android_text_format_Time_parse },
-    { "nativeParse3339",         "(Ljava/lang/String;)Z",                       (void*)android_text_format_Time_parse3339 },
-    { "setToNow",                "()V",                                         (void*)android_text_format_Time_setToNow },
-    { "toMillis",                "(Z)J",                                        (void*)android_text_format_Time_toMillis },
-    { "set",                     "(J)V",                                        (void*)android_text_format_Time_set }
-};
-
-int register_android_text_format_Time(JNIEnv* env)
-{
-    jclass timeClass = env->FindClass("android/text/format/Time");
-
-    g_timeClass = (jclass) env->NewGlobalRef(timeClass);
-
-    g_allDayField = env->GetFieldID(timeClass, "allDay", "Z");
-    g_secField = env->GetFieldID(timeClass, "second", "I");
-    g_minField = env->GetFieldID(timeClass, "minute", "I");
-    g_hourField = env->GetFieldID(timeClass, "hour", "I");
-    g_mdayField = env->GetFieldID(timeClass, "monthDay", "I");
-    g_monField = env->GetFieldID(timeClass, "month", "I");
-    g_yearField = env->GetFieldID(timeClass, "year", "I");
-    g_wdayField = env->GetFieldID(timeClass, "weekDay", "I");
-    g_ydayField = env->GetFieldID(timeClass, "yearDay", "I");
-    g_isdstField = env->GetFieldID(timeClass, "isDst", "I");
-    g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J");
-    g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;");
-
-    g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;");
-    g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;");
-    g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;");
-    g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;");
-    g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;");
-    g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;");
-    g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;");
-    g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;");
-    g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;");
-    g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;");
-    g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;");
-    g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;");
-
-    return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods));
-}
-
-}; // namespace android