Move Date-related classes to shared, so they can be used by
server-side i18n code.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10061 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/tools/api-checker/config/gwt22_23userApi.conf b/tools/api-checker/config/gwt22_23userApi.conf
index d97d277..2055de3 100644
--- a/tools/api-checker/config/gwt22_23userApi.conf
+++ b/tools/api-checker/config/gwt22_23userApi.conf
@@ -158,3 +158,6 @@
 # Adding protected Video(Element) that conflicts with existing (now deprecated) public Video(String)
 com.google.gwt.media.client.Video::Video(Ljava/lang/String;) OVERLOADED_METHOD_CALL
 
+# move i18n classes to shared
+com.google.gwt.i18n.client.DateTimeFormat::format(Ljava/util/Date;Lcom/google/gwt/i18n/client/TimeZone;) OVERRIDABLE_METHOD_ARGUMENT_TYPE_CHANGE
+com.google.gwt.i18n.client.impl.DateRecord MISSING
diff --git a/user/src/com/google/gwt/i18n/I18N.gwt.xml b/user/src/com/google/gwt/i18n/I18N.gwt.xml
index a22c58f..6473a34 100644
--- a/user/src/com/google/gwt/i18n/I18N.gwt.xml
+++ b/user/src/com/google/gwt/i18n/I18N.gwt.xml
@@ -75,7 +75,7 @@
       generator="com.google.gwt.i18n.linker.LocalePropertyProviderGenerator"/>
 
   <generate-with class="com.google.gwt.i18n.rebind.LocalizableGenerator">
-    <when-type-assignable class="com.google.gwt.i18n.client.Localizable" />
+    <when-type-assignable class="com.google.gwt.i18n.shared.Localizable" />
   </generate-with>
   <generate-with class="com.google.gwt.i18n.rebind.LocaleInfoGenerator">
     <when-type-is class="com.google.gwt.i18n.client.impl.LocaleInfoImpl" />
@@ -84,7 +84,7 @@
     <when-type-is class="com.google.gwt.i18n.client.CurrencyList" />
   </generate-with>
   <generate-with class="com.google.gwt.i18n.rebind.CustomDateTimeFormatGenerator">
-    <when-type-assignable class="com.google.gwt.i18n.client.CustomDateTimeFormat" />
+    <when-type-assignable class="com.google.gwt.i18n.shared.CustomDateTimeFormat" />
   </generate-with>
   
   <!--
diff --git a/user/src/com/google/gwt/i18n/client/CustomDateTimeFormat.java b/user/src/com/google/gwt/i18n/client/CustomDateTimeFormat.java
index e204561..d590fe6 100644
--- a/user/src/com/google/gwt/i18n/client/CustomDateTimeFormat.java
+++ b/user/src/com/google/gwt/i18n/client/CustomDateTimeFormat.java
@@ -19,15 +19,21 @@
  * Create a custom localized date/time format at compile time. All methods on
  * subtypes of this interface must take no parameters and return DateTimeFormat
  * (which will be an initialized instance).
+ * 
+ * @deprecated use {@link com.google.gwt.i18n.shared.CustomDateTimeFormat} instead
  */
-public interface CustomDateTimeFormat {
+@Deprecated
+public interface CustomDateTimeFormat extends com.google.gwt.i18n.shared.CustomDateTimeFormat {
 
   /**
    * Annotation containing the pattern skeleton.
    * 
    * <p>The order of pattern characters and any literals don't matter, just
    * which pattern characters are present and their counts.
+   * 
+   * @deprecated use {@link com.google.gwt.i18n.shared.CustomDateTimeFormat.Pattern} instead
    */
+  @Deprecated
   public @interface Pattern {
 
     /**
diff --git a/user/src/com/google/gwt/i18n/client/DateTimeFormat.java b/user/src/com/google/gwt/i18n/client/DateTimeFormat.java
index 35e9fb4..ef3ea0a 100644
--- a/user/src/com/google/gwt/i18n/client/DateTimeFormat.java
+++ b/user/src/com/google/gwt/i18n/client/DateTimeFormat.java
@@ -13,15 +13,11 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package com.google.gwt.i18n.client;
 
 import com.google.gwt.i18n.client.constants.DateTimeConstants;
-import com.google.gwt.i18n.client.impl.DateRecord;
 import com.google.gwt.i18n.client.impl.cldr.DateTimeFormatInfoImpl_en;
 
-import java.util.ArrayList;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -385,15 +381,19 @@
  * </p>
  * 
  * <h3>Example</h3> {@example com.google.gwt.examples.DateTimeFormatExample}
- * 
+ *
+ * @deprecated use {@link com.google.gwt.i18n.shared.DateTimeFormat} instead
  */
-@SuppressWarnings("deprecation")
-public class DateTimeFormat {
+@Deprecated
+public class DateTimeFormat extends com.google.gwt.i18n.shared.DateTimeFormat {
 
   /**
    * Predefined date/time formats -- see {@link CustomDateTimeFormat} if you
    * need some format that isn't supplied here.
+   *
+   * @deprecated use {@link com.google.gwt.i18n.shared.DateTimeFormat.PredefinedFormat} instead
    */
+  @Deprecated
   public enum PredefinedFormat {
     // TODO(jat): Javadoc to explain these formats
 
@@ -451,43 +451,8 @@
     YEAR_QUARTER_ABBR,
   }
 
-  /**
-   * Class PatternPart holds a "compiled" pattern part.
-   */
-  private static class PatternPart {
-    public String text;
-    public int count; // 0 has a special meaning, it stands for literal
-    public boolean abutStart;
-
-    public PatternPart(String txt, int cnt) {
-      text = txt;
-      count = cnt;
-      abutStart = false;
-    }
-  }
-
-  private static final int NUMBER_BASE = 10;
-  private static final int JS_START_YEAR = 1900;
-
   private static final Map<String, DateTimeFormat> cache;
 
-  private static final int NUM_MILLISECONDS_IN_DAY = 24 * 60 * 60000;
-
-  private static final String PATTERN_CHARS = "GyMLdkHmsSEcDahKzZv";
-
-  // Note: M & L must be the first two characters
-  private static final String NUMERIC_FORMAT_CHARS = "MLydhHmsSDkK";
-
-  private static final String WHITE_SPACE = " \t\r\n";
-
-  private static final String GMT = "GMT";
-  private static final String UTC = "UTC";
-
-  private static final int MINUTES_PER_HOUR = 60;
-
-  private static final String RFC2822_PATTERN = "EEE, d MMM yyyy HH:mm:ss Z";
-  private static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ";
-
   static {
     cache = new HashMap<String, DateTimeFormat>();
   }
@@ -506,10 +471,10 @@
       String pattern;
       switch (predef) {
         case RFC_2822:
-          pattern = RFC2822_PATTERN;
+          pattern = com.google.gwt.i18n.shared.DateTimeFormat.RFC2822_PATTERN;
           break;
         case ISO_8601:
-          pattern = ISO8601_PATTERN;
+          pattern = com.google.gwt.i18n.shared.DateTimeFormat.ISO8601_PATTERN;
           break;
         default:
           throw new IllegalStateException("Unexpected predef type " + predef);
@@ -858,12 +823,6 @@
     }
   }
 
-  private final ArrayList<PatternPart> patternParts = new ArrayList<PatternPart>();
-
-  private final DateTimeFormatInfo dateTimeFormatInfo;
-
-  private final String pattern;
-
   /**
    * Constructs a format object using the specified pattern and the date time
    * constants for the default locale.
@@ -895,1441 +854,6 @@
    * @param dtfi DateTimeFormatInfo instance to use
    */
   protected DateTimeFormat(String pattern, DateTimeFormatInfo dtfi) {
-    this.pattern = pattern;
-    this.dateTimeFormatInfo = dtfi;
-
-    /*
-     * Even though the pattern is only compiled for use in parsing and parsing
-     * is far less common than formatting, the pattern is still parsed eagerly
-     * here to fail fast in case the pattern itself is malformed.
-     */
-    parsePattern(pattern);
-  }
-
-  /**
-   * Format a date object.
-   * 
-   * @param date the date object being formatted
-   * 
-   * @return string representation for this date in desired format
-   */
-  public String format(Date date) {
-    TimeZone timeZone = TimeZone.createTimeZone(date.getTimezoneOffset());
-    return format(date, timeZone);
-  }
-
-  /**
-   * Format a date object using specified time zone.
-   * 
-   * @param date the date object being formatted
-   * @param timeZone a TimeZone object that holds time zone information
-   * 
-   * @return string representation for this date in the format defined by this
-   *         object
-   */
-  public String format(Date date, TimeZone timeZone) {
-    // We use the Date class to calculate each date/time field in order
-    // to maximize performance and minimize code size.
-    // JavaScript only provides an API for rendering local time (in the os time
-    // zone). Here we want to render time in any timezone. So suppose we try to
-    // render the date (20:00 GMT0000, or 16:00 GMT-0400, or 12:00 GMT-0800) for
-    // time zone GMT-0400, and OS has time zone GMT-0800. By adding the
-    // difference between OS time zone (GMT-0800) and target time zone
-    // (GMT-0400) to "date", we end up with 16:00 GMT-0800. This date object
-    // has the same date/time fields (year, month, date, hour, minutes, etc)
-    // in GMT-0800 as original date in our target time zone (GMT-0400). We
-    // just need to take care of time zone display, but that's needed anyway.
-
-    // Things get a little bit more tricky when a daylight time transition
-    // happens. For example, if the OS timezone is America/Los_Angeles,
-    // it is just impossible to have a Date represent 2006/4/2 02:30, because
-    // 2:00 to 3:00 on that day does not exist in US Pacific time zone because
-    // of the daylight time switch.
-
-    // But we can use 2 separate date objects, one to represent 2006/4/2, one
-    // to represent 02:30. Of course, for the 2nd date object its date can be
-    // any other day in that year, except 2006/4/2. So we end up have 3 Date
-    // objects: one for resolving "Year, month, day", one for time within that
-    // day, and the original date object, which is needed for figuring out
-    // actual time zone offset.
-
-    int diff = (date.getTimezoneOffset() - timeZone.getOffset(date)) * 60000;
-    Date keepDate = new Date(date.getTime() + diff);
-    Date keepTime = keepDate;
-    if (keepDate.getTimezoneOffset() != date.getTimezoneOffset()) {
-      if (diff > 0) {
-        diff -= NUM_MILLISECONDS_IN_DAY;
-      } else {
-        diff += NUM_MILLISECONDS_IN_DAY;
-      }
-      keepTime = new Date(date.getTime() + diff);
-    }
-
-    StringBuffer toAppendTo = new StringBuffer(64);
-    int j, n = pattern.length();
-    for (int i = 0; i < n;) {
-      char ch = pattern.charAt(i);
-      if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
-        // ch is a date-time pattern character to be interpreted by subFormat().
-        // Count the number of times it is repeated.
-        for (j = i + 1; j < n && pattern.charAt(j) == ch; ++j) {
-        }
-        subFormat(toAppendTo, ch, j - i, date, keepDate, keepTime, timeZone);
-        i = j;
-      } else if (ch == '\'') {
-        // Handle an entire quoted string, included embedded
-        // doubled apostrophes (as in 'o''clock').
-
-        // i points after '.
-        ++i;
-
-        // If start with '', just add ' and continue.
-        if (i < n && pattern.charAt(i) == '\'') {
-          toAppendTo.append('\'');
-          ++i;
-          continue;
-        }
-
-        // Otherwise add the quoted string.
-        boolean trailQuote = false;
-        while (!trailQuote) {
-          // j points to next ' or EOS.
-          j = i;
-          while (j < n && pattern.charAt(j) != '\'') {
-            ++j;
-          }
-
-          if (j >= n) {
-            // Trailing ' (pathological).
-            throw new IllegalArgumentException("Missing trailing \'");
-          }
-
-          // Look ahead to detect '' within quotes.
-          if (j + 1 < n && pattern.charAt(j + 1) == '\'') {
-            ++j;
-          } else {
-            trailQuote = true;
-          }
-          toAppendTo.append(pattern.substring(i, j));
-          i = j + 1;
-        }
-      } else {
-        // Append unquoted literal characters.
-        toAppendTo.append(ch);
-        ++i;
-      }
-    }
-
-    return toAppendTo.toString();
-  }
-
-  /**
-   * Retrieve the pattern used in this DateTimeFormat object.
-   * 
-   * @return pattern string
-   */
-  public String getPattern() {
-    return pattern;
-  }
-
-  /**
-   * Parses text to produce a {@link Date} value. An
-   * {@link IllegalArgumentException} is thrown if either the text is empty or
-   * if the parse does not consume all characters of the text.
-   * 
-   * Dates are parsed leniently, so invalid dates will be wrapped around as
-   * needed. For example, February 30 will wrap to March 2.
-   * 
-   * @param text the string being parsed
-   * @return a parsed date/time value
-   * @throws IllegalArgumentException if the entire text could not be converted
-   *           into a number
-   */
-  public Date parse(String text) throws IllegalArgumentException {
-    return parse(text, false);
-  }
-
-  /**
-   * This method modifies a {@link Date} object to reflect the date that is
-   * parsed from an input string.
-   * 
-   * Dates are parsed leniently, so invalid dates will be wrapped around as
-   * needed. For example, February 30 will wrap to March 2.
-   * 
-   * @param text the string that need to be parsed
-   * @param start the character position in "text" where parsing should start
-   * @param date the date object that will hold parsed value
-   * 
-   * @return 0 if parsing failed, otherwise the number of characters advanced
-   */
-  public int parse(String text, int start, Date date) {
-    return parse(text, start, date, false);
-  }
-
-  /**
-   * Parses text to produce a {@link Date} value. An
-   * {@link IllegalArgumentException} is thrown if either the text is empty or
-   * if the parse does not consume all characters of the text.
-   * 
-   * Dates are parsed strictly, so invalid dates will result in an
-   * {@link IllegalArgumentException}.
-   * 
-   * @param text the string being parsed
-   * @return a parsed date/time value
-   * @throws IllegalArgumentException if the entire text could not be converted
-   *           into a number
-   */
-  public Date parseStrict(String text) throws IllegalArgumentException {
-    return parse(text, true);
-  }
-
-  /**
-   * This method modifies a {@link Date} object to reflect the date that is
-   * parsed from an input string.
-   * 
-   * Dates are parsed strictly, so invalid dates will return 0. For example,
-   * February 30 will return 0 because February only has 28 days.
-   * 
-   * @param text the string that need to be parsed
-   * @param start the character position in "text" where parsing should start
-   * @param date the date object that will hold parsed value
-   * 
-   * @return 0 if parsing failed, otherwise the number of characters advanced
-   */
-  public int parseStrict(String text, int start, Date date) {
-    return parse(text, start, date, true);
-  }
-
-  /**
-   * Method append current content in buf as pattern part if there is any, and
-   * clear buf for next part.
-   * 
-   * @param buf pattern part text specification
-   * @param count pattern part repeat count
-   */
-  private void addPart(StringBuffer buf, int count) {
-    if (buf.length() > 0) {
-      patternParts.add((new PatternPart(buf.toString(), count)));
-      buf.setLength(0);
-    }
-  }
-
-  /**
-   * Formats (0..11) Hours field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void format0To11Hours(StringBuffer buf, int count, Date date) {
-    int value = date.getHours() % 12;
-    zeroPaddingNumber(buf, value, count);
-  }
-
-  /**
-   * Formats (0..23) Hours field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void format0To23Hours(StringBuffer buf, int count, Date date) {
-    int value = date.getHours();
-    zeroPaddingNumber(buf, value, count);
-  }
-
-  /**
-   * Formats (1..12) Hours field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void format1To12Hours(StringBuffer buf, int count, Date date) {
-    int value = date.getHours() % 12;
-    if (value == 0) {
-      zeroPaddingNumber(buf, 12, count);
-    } else {
-      zeroPaddingNumber(buf, value, count);
-    }
-  }
-
-  /**
-   * Formats (1..24) Hours field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void format24Hours(StringBuffer buf, int count, Date date) {
-    int value = date.getHours();
-    if (value == 0) {
-      zeroPaddingNumber(buf, 24, count);
-    } else {
-      zeroPaddingNumber(buf, value, count);
-    }
-  }
-
-  /**
-   * Formats AM/PM field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param date hold the date object to be formatted
-   */
-  private void formatAmPm(StringBuffer buf, Date date) {
-    if (date.getHours() >= 12 && date.getHours() < 24) {
-      buf.append(dateTimeFormatInfo.ampms()[1]);
-    } else {
-      buf.append(dateTimeFormatInfo.ampms()[0]);
-    }
-  }
-
-  /**
-   * Formats Date field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatDate(StringBuffer buf, int count, Date date) {
-    int value = date.getDate();
-    zeroPaddingNumber(buf, value, count);
-  }
-
-  /**
-   * Formats Day of week field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatDayOfWeek(StringBuffer buf, int count, Date date) {
-    int value = date.getDay();
-    if (count == 5) {
-      buf.append(dateTimeFormatInfo.weekdaysNarrow()[value]);
-    } else if (count == 4) {
-      buf.append(dateTimeFormatInfo.weekdaysFull()[value]);
-    } else {
-      buf.append(dateTimeFormatInfo.weekdaysShort()[value]);
-    }
-  }
-
-  /**
-   * Formats Era field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatEra(StringBuffer buf, int count, Date date) {
-    int value = date.getYear() >= -JS_START_YEAR ? 1 : 0;
-    if (count >= 4) {
-      buf.append(dateTimeFormatInfo.erasFull()[value]);
-    } else {
-      buf.append(dateTimeFormatInfo.erasShort()[value]);
-    }
-  }
-
-  /**
-   * Formats Fractional seconds field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatFractionalSeconds(StringBuffer buf, int count, Date date) {
-    /*
-     * Fractional seconds should be left-justified, ie. zero must be padded from
-     * left. For example, if the value in milliseconds is 5, and the count is 3,
-     * the output will be "005".
-     * 
-     * Values with less than three digits are rounded to the desired number of
-     * places, but the rounded values are truncated at 9 or 99 in order to avoid
-     * changing the values of seconds.
-     */
-    long time = date.getTime();
-    int value;
-    if (time < 0) {
-      value = 1000 - (int) (-time % 1000);
-      if (value == 1000) {
-        value = 0;
-      }
-    } else {
-      value = (int) (time % 1000);
-    }
-    if (count == 1) {
-      value = Math.min((value + 50) / 100, 9); // Round to 100ms, clamp to 9
-      buf.append((char) ('0' + value));
-    } else if (count == 2) {
-      value = Math.min((value + 5) / 10, 99); // Round to 10ms, clamp to 99
-      zeroPaddingNumber(buf, value, 2);
-    } else {
-      zeroPaddingNumber(buf, value, 3);
-
-      if (count > 3) {
-        zeroPaddingNumber(buf, 0, count - 3);
-      }
-    }
-  }
-
-  /**
-   * Formats Minutes field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatMinutes(StringBuffer buf, int count, Date date) {
-    int value = date.getMinutes();
-    zeroPaddingNumber(buf, value, count);
-  }
-
-  /**
-   * Formats Month field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatMonth(StringBuffer buf, int count, Date date) {
-    int value = date.getMonth();
-    switch (count) {
-      case 5:
-        buf.append(dateTimeFormatInfo.monthsNarrow()[value]);
-        break;
-      case 4:
-        buf.append(dateTimeFormatInfo.monthsFull()[value]);
-        break;
-      case 3:
-        buf.append(dateTimeFormatInfo.monthsShort()[value]);
-        break;
-      default:
-        zeroPaddingNumber(buf, value + 1, count);
-    }
-  }
-
-  /**
-   * Formats Quarter field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatQuarter(StringBuffer buf, int count, Date date) {
-    int value = date.getMonth() / 3;
-    if (count < 4) {
-      buf.append(dateTimeFormatInfo.quartersShort()[value]);
-    } else {
-      buf.append(dateTimeFormatInfo.quartersFull()[value]);
-    }
-  }
-
-  /**
-   * Formats Seconds field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatSeconds(StringBuffer buf, int count, Date date) {
-    int value = date.getSeconds();
-    zeroPaddingNumber(buf, value, count);
-  }
-
-  /**
-   * Formats Standalone weekday field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatStandaloneDay(StringBuffer buf, int count, Date date) {
-    int value = date.getDay();
-    if (count == 5) {
-      buf.append(dateTimeFormatInfo.weekdaysNarrowStandalone()[value]);
-    } else if (count == 4) {
-      buf.append(dateTimeFormatInfo.weekdaysFullStandalone()[value]);
-    } else if (count == 3) {
-      buf.append(dateTimeFormatInfo.weekdaysShortStandalone()[value]);
-    } else {
-      zeroPaddingNumber(buf, value, 1);
-    }
-  }
-
-  /**
-   * Formats Standalone Month field according to pattern specified.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatStandaloneMonth(StringBuffer buf, int count, Date date) {
-    int value = date.getMonth();
-    if (count == 5) {
-      buf.append(dateTimeFormatInfo.monthsNarrowStandalone()[value]);
-    } else if (count == 4) {
-      buf.append(dateTimeFormatInfo.monthsFullStandalone()[value]);
-    } else if (count == 3) {
-      buf.append(dateTimeFormatInfo.monthsShortStandalone()[value]);
-    } else {
-      zeroPaddingNumber(buf, value + 1, count);
-    }
-  }
-
-  /**
-   * Formats Timezone field.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatTimeZone(StringBuffer buf, int count, Date date,
-      TimeZone timeZone) {
-    if (count < 4) {
-      buf.append(timeZone.getShortName(date));
-    } else {
-      buf.append(timeZone.getLongName(date));
-    }
-  }
-
-  /**
-   * Formats Timezone field following RFC.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date hold the date object to be formatted
-   */
-  private void formatTimeZoneRFC(StringBuffer buf, int count, Date date,
-      TimeZone timeZone) {
-    if (count < 3) {
-      buf.append(timeZone.getRFCTimeZoneString(date));
-    } else if (count == 3) {
-      buf.append(timeZone.getISOTimeZoneString(date));
-    } else {
-      buf.append(timeZone.getGMTString(date));
-    }
-  }
-
-  /**
-   * Formats Year field according to pattern specified. Javascript Date object
-   * seems incapable handling 1BC and year before. It can show you year 0 which
-   * does not exists. following we just keep consistent with javascript's
-   * toString method. But keep in mind those things should be unsupported.
-   * 
-   * @param buf where formatted string will be appended to
-   * @param count number of time pattern char repeats; this controls how a field
-   *     should be formatted; 2 is treated specially with the last two digits of
-   *     the year, while more than 2 digits are zero-padded
-   * @param date hold the date object to be formatted
-   */
-  private void formatYear(StringBuffer buf, int count, Date date) {
-    int value = date.getYear() + JS_START_YEAR;
-    if (value < 0) {
-      value = -value;
-    }
-    switch (count) {
-      case 1: // no padding
-        buf.append(value);
-        break;
-      case 2: // last 2 digits of year, zero-padded
-        zeroPaddingNumber(buf, value % 100, 2);
-        break;
-      default: // anything else is zero-padded
-        zeroPaddingNumber(buf, value, count);
-        break;
-    }
-  }
-
-  /**
-   * Method getNextCharCountInPattern calculate character repeat count in
-   * pattern.
-   * 
-   * @param pattern describe the format of date string that need to be parsed
-   * @param start the position of pattern character
-   * @return repeat count
-   */
-  private int getNextCharCountInPattern(String pattern, int start) {
-    char ch = pattern.charAt(start);
-    int next = start + 1;
-    while (next < pattern.length() && pattern.charAt(next) == ch) {
-      ++next;
-    }
-    return next - start;
-  }
-
-  /**
-   * Method identifies the start of a run of abutting numeric fields. Take the
-   * pattern "HHmmss" as an example. We will try to parse 2/2/2 characters of
-   * the input text, then if that fails, 1/2/2. We only adjust the width of the
-   * leftmost field; the others remain fixed. This allows "123456" => 12:34:56,
-   * but "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we try 4/2/2,
-   * 3/2/2, 2/2/2, and finally 1/2/2. The first field of connected numeric
-   * fields will be marked as abutStart, its width can be reduced to accomodate
-   * others.
-   */
-  private void identifyAbutStart() {
-    // 'abut' parts are continuous numeric parts. abutStart is the switch
-    // point from non-abut to abut.
-    boolean abut = false;
-
-    int len = patternParts.size();
-    for (int i = 0; i < len; i++) {
-      if (isNumeric(patternParts.get(i))) {
-        // If next part is not following abut sequence, and isNumeric.
-        if (!abut && i + 1 < len && isNumeric(patternParts.get(i + 1))) {
-          abut = true;
-          patternParts.get(i).abutStart = true;
-        }
-      } else {
-        abut = false;
-      }
-    }
-  }
-
-  /**
-   * Method checks if the pattern part is a numeric field.
-   * 
-   * @param part pattern part to be examined
-   * @return <code>true</code> if the pattern part is numberic field
-   */
-  private boolean isNumeric(PatternPart part) {
-    if (part.count <= 0) {
-      return false;
-    }
-    int i = NUMERIC_FORMAT_CHARS.indexOf(part.text.charAt(0));
-    // M & L (index 0 and 1) are only numeric if there are less than 3 chars
-    return (i > 1 || (i >= 0 && part.count < 3));
-  }
-
-  /**
-   * Method attempts to match the text at a given position against an array of
-   * strings. Since multiple strings in the array may match (for example, if the
-   * array contains "a", "ab", and "abc", all will match the input string
-   * "abcd") the longest match is returned.
-   * 
-   * @param text the time text being parsed
-   * @param start where to start parsing
-   * @param data the string array to parsed
-   * @param pos to receive where the match stopped
-   * @return the new start position if matching succeeded; a negative number
-   *         indicating matching failure
-   */
-  private int matchString(String text, int start, String[] data, int[] pos) {
-    int count = data.length;
-
-    // There may be multiple strings in the data[] array which begin with
-    // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
-    // We keep track of the longest match, and return that. Note that this
-    // unfortunately requires us to test all array elements.
-    int bestMatchLength = 0, bestMatch = -1;
-    String textInLowerCase = text.substring(start).toLowerCase();
-    for (int i = 0; i < count; ++i) {
-      int length = data[i].length();
-      // Always compare if we have no match yet; otherwise only compare
-      // against potentially better matches (longer strings).
-      if (length > bestMatchLength
-          && textInLowerCase.startsWith(data[i].toLowerCase())) {
-        bestMatch = i;
-        bestMatchLength = length;
-      }
-    }
-    if (bestMatch >= 0) {
-      pos[0] = start + bestMatchLength;
-    }
-    return bestMatch;
-  }
-
-  /**
-   * Parses text to produce a {@link Date} value. An
-   * {@link IllegalArgumentException} is thrown if either the text is empty or
-   * if the parse does not consume all characters of the text.
-   * 
-   * If using lenient parsing, certain invalid dates and times will be parsed.
-   * For example, February 32nd would be parsed as March 4th in lenient mode,
-   * but would throw an exception in non-lenient mode.
-   * 
-   * @param text the string being parsed
-   * @param strict true to be strict when parsing, false to be lenient
-   * @return a parsed date/time value
-   * @throws IllegalArgumentException if the entire text could not be converted
-   *           into a number
-   */
-  private Date parse(String text, boolean strict) {
-    Date curDate = new Date();
-    Date date = new Date(curDate.getYear(), curDate.getMonth(),
-        curDate.getDate());
-    int charsConsumed = parse(text, 0, date, strict);
-    if (charsConsumed == 0 || charsConsumed < text.length()) {
-      throw new IllegalArgumentException(text);
-    }
-    return date;
-  }
-
-  /**
-   * This method parses the input string and fills its value into a {@link Date}
-   * .
-   * 
-   * If using lenient parsing, certain invalid dates and times will be parsed.
-   * For example, February 32nd would be parsed as March 4th in lenient mode,
-   * but would return 0 in non-lenient mode.
-   * 
-   * @param text the string that need to be parsed
-   * @param start the character position in "text" where parsing should start
-   * @param date the date object that will hold parsed value
-   * @param strict true to be strict when parsing, false to be lenient
-   * 
-   * @return 0 if parsing failed, otherwise the number of characters advanced
-   */
-  private int parse(String text, int start, Date date, boolean strict) {
-    DateRecord cal = new DateRecord();
-    int[] parsePos = {start};
-
-    // For parsing abutting numeric fields. 'abutPat' is the
-    // offset into 'pattern' of the first of 2 or more abutting
-    // numeric fields. 'abutStart' is the offset into 'text'
-    // where parsing the fields begins. 'abutPass' starts off as 0
-    // and increments each time we try to parse the fields.
-    int abutPat = -1; // If >=0, we are in a run of abutting numeric fields.
-    int abutStart = 0;
-    int abutPass = 0;
-
-    for (int i = 0; i < patternParts.size(); ++i) {
-      PatternPart part = patternParts.get(i);
-
-      if (part.count > 0) {
-        if (abutPat < 0 && part.abutStart) {
-          abutPat = i;
-          abutStart = start;
-          abutPass = 0;
-        }
-
-        // Handle fields within a run of abutting numeric fields. Take
-        // the pattern "HHmmss" as an example. We will try to parse
-        // 2/2/2 characters of the input text, then if that fails,
-        // 1/2/2. We only adjust the width of the leftmost field; the
-        // others remain fixed. This allows "123456" => 12:34:56, but
-        // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
-        // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
-        if (abutPat >= 0) {
-          // If we are at the start of a run of abutting fields, then
-          // shorten this field in each pass. If we can't shorten
-          // this field any more, then the parse of this set of
-          // abutting numeric fields has failed.
-          int count = part.count;
-          if (i == abutPat) {
-            count -= abutPass++;
-            if (count == 0) {
-              return 0;
-            }
-          }
-
-          if (!subParse(text, parsePos, part, count, cal)) {
-            // If the parse fails anywhere in the run, back up to the
-            // start of the run and retry.
-            i = abutPat - 1;
-            parsePos[0] = abutStart;
-            continue;
-          }
-        } else {
-          // Handle non-numeric fields and non-abutting numeric fields.
-          abutPat = -1;
-          if (!subParse(text, parsePos, part, 0, cal)) {
-            return 0;
-          }
-        }
-      } else {
-        // Handle literal pattern characters. These are any
-        // quoted characters and non-alphabetic unquoted characters.
-        abutPat = -1;
-        // A run of white space in the pattern matches a run
-        // of white space in the input text.
-        if (part.text.charAt(0) == ' ') {
-          // Advance over run in input text.
-          int s = parsePos[0];
-          skipSpace(text, parsePos);
-
-          // Must see at least one white space char in input.
-          if (parsePos[0] > s) {
-            continue;
-          }
-        } else if (text.startsWith(part.text, parsePos[0])) {
-          parsePos[0] += part.text.length();
-          continue;
-        }
-
-        // We fall through to this point if the match fails.
-        return 0;
-      }
-    }
-
-    // Calculate the date from the parts
-    if (!cal.calcDate(date, strict)) {
-      return 0;
-    }
-
-    // Return progress.
-    return parsePos[0] - start;
-  }
-
-  /**
-   * Method parses a integer string and return integer value.
-   * 
-   * @param text string being parsed
-   * @param pos parse position
-   * 
-   * @return integer value
-   */
-  private int parseInt(String text, int[] pos) {
-    int ret = 0;
-    int ind = pos[0];
-    if (ind >= text.length()) {
-      return -1;
-    }
-    char ch = text.charAt(ind);
-    while (ch >= '0' && ch <= '9') {
-      ret = ret * 10 + (ch - '0');
-      ind++;
-      if (ind >= text.length()) {
-        break;
-      }
-      ch = text.charAt(ind);
-    }
-    if (ind > pos[0]) {
-      pos[0] = ind;
-    } else {
-      ret = -1;
-    }
-    return ret;
-  }
-
-  /**
-   * Method parses the input pattern string a generate a vector of pattern
-   * parts.
-   * 
-   * @param pattern describe the format of date string that need to be parsed
-   */
-  private void parsePattern(String pattern) {
-    StringBuffer buf = new StringBuffer(32);
-    boolean inQuote = false;
-
-    for (int i = 0; i < pattern.length(); i++) {
-      char ch = pattern.charAt(i);
-
-      // Handle space, add literal part (if exist), and add space part.
-      if (ch == ' ') {
-        addPart(buf, 0);
-        buf.append(' ');
-        addPart(buf, 0);
-        while (i + 1 < pattern.length() && pattern.charAt(i + 1) == ' ') {
-          i++;
-        }
-        continue;
-      }
-
-      // If inside quote, except two quote connected, just copy or exit.
-      if (inQuote) {
-        if (ch == '\'') {
-          if (i + 1 < pattern.length() && pattern.charAt(i + 1) == '\'') {
-            // Quote appeared twice continuously, interpret as one quote.
-            buf.append(ch);
-            ++i;
-          } else {
-            inQuote = false;
-          }
-        } else {
-          // Literal.
-          buf.append(ch);
-        }
-        continue;
-      }
-
-      // Outside quote now.
-      if (PATTERN_CHARS.indexOf(ch) > 0) {
-        addPart(buf, 0);
-        buf.append(ch);
-        int count = getNextCharCountInPattern(pattern, i);
-        addPart(buf, count);
-        i += count - 1;
-        continue;
-      }
-
-      // Two consecutive quotes is a quote literal, inside or outside of quotes.
-      if (ch == '\'') {
-        if (i + 1 < pattern.length() && pattern.charAt(i + 1) == '\'') {
-          buf.append('\'');
-          i++;
-        } else {
-          inQuote = true;
-        }
-      } else {
-        buf.append(ch);
-      }
-    }
-
-    addPart(buf, 0);
-
-    identifyAbutStart();
-  }
-
-  /**
-   * Method parses time zone offset.
-   * 
-   * @param text the time text to be parsed
-   * @param pos Parse position
-   * @param cal DateRecord object that holds parsed value
-   * 
-   * @return <code>true</code> if parsing successful, otherwise
-   *         <code>false</code>
-   */
-  private boolean parseTimeZoneOffset(String text, int[] pos, DateRecord cal) {
-    if (pos[0] >= text.length()) {
-      cal.setTzOffset(0);
-      return true;
-    }
-
-    int sign;
-    switch (text.charAt(pos[0])) {
-      case '+':
-        sign = 1;
-        break;
-      case '-':
-        sign = -1;
-        break;
-      default:
-        cal.setTzOffset(0);
-        return true;
-    }
-    ++(pos[0]);
-
-    // Look for hours:minutes or hhmm.
-    int st = pos[0];
-    int value = parseInt(text, pos);
-    if (value == 0 && pos[0] == st) {
-      return false;
-    }
-
-    int offset;
-    if (pos[0] < text.length() && text.charAt(pos[0]) == ':') {
-      // This is the hours:minutes case.
-      offset = value * MINUTES_PER_HOUR;
-      ++(pos[0]);
-      st = pos[0];
-      value = parseInt(text, pos);
-      if (value == 0 && pos[0] == st) {
-        return false;
-      }
-      offset += value;
-    } else {
-      // This is the hhmm case.
-      offset = value;
-      // Assume "-23".."+23" refers to hours.
-      if (offset < 24 && (pos[0] - st) <= 2) {
-        offset *= MINUTES_PER_HOUR;
-      } else {
-        offset = offset % 100 + offset / 100 * MINUTES_PER_HOUR;
-      }
-    }
-
-    offset *= sign;
-    cal.setTzOffset(-offset);
-    return true;
-  }
-
-  /**
-   * Method skips space in the string as pointed by pos.
-   * 
-   * @param text input string
-   * @param pos where skip start, and return back where skip stop
-   */
-  private void skipSpace(String text, int[] pos) {
-    while (pos[0] < text.length()
-        && WHITE_SPACE.indexOf(text.charAt(pos[0])) >= 0) {
-      ++(pos[0]);
-    }
-  }
-
-  /**
-   * Formats a single field according to pattern specified.
-   * 
-   * @param ch pattern character for this field
-   * @param count number of time pattern char repeats; this controls how a field
-   *          should be formatted
-   * @param date the date object to be formatted
-   * @param adjustedDate holds the time zone adjusted date fields
-   * @param adjustedTime holds the time zone adjusted time fields
-   * 
-   * @return <code>true</code> if pattern valid, otherwise <code>false</code>
-   * 
-   */
-  private boolean subFormat(StringBuffer buf, char ch, int count, Date date,
-      Date adjustedDate, Date adjustedTime, TimeZone timezone) {
-    switch (ch) {
-      case 'G':
-        formatEra(buf, count, adjustedDate);
-        break;
-      case 'y':
-        formatYear(buf, count, adjustedDate);
-        break;
-      case 'M':
-        formatMonth(buf, count, adjustedDate);
-        break;
-      case 'k':
-        format24Hours(buf, count, adjustedTime);
-        break;
-      case 'S':
-        formatFractionalSeconds(buf, count, adjustedTime);
-        break;
-      case 'E':
-        formatDayOfWeek(buf, count, adjustedDate);
-        break;
-      case 'a':
-        formatAmPm(buf, adjustedTime);
-        break;
-      case 'h':
-        format1To12Hours(buf, count, adjustedTime);
-        break;
-      case 'K':
-        format0To11Hours(buf, count, adjustedTime);
-        break;
-      case 'H':
-        format0To23Hours(buf, count, adjustedTime);
-        break;
-      case 'c':
-        formatStandaloneDay(buf, count, adjustedDate);
-        break;
-      case 'L':
-        formatStandaloneMonth(buf, count, adjustedDate);
-        break;
-      case 'Q':
-        formatQuarter(buf, count, adjustedDate);
-        break;
-      case 'd':
-        formatDate(buf, count, adjustedDate);
-        break;
-      case 'm':
-        formatMinutes(buf, count, adjustedTime);
-        break;
-      case 's':
-        formatSeconds(buf, count, adjustedTime);
-        break;
-      case 'z':
-        formatTimeZone(buf, count, date, timezone);
-        break;
-      case 'v':
-        buf.append(timezone.getID());
-        break;
-      case 'Z':
-        formatTimeZoneRFC(buf, count, date, timezone);
-        break;
-      default:
-        return false;
-    }
-    return true;
-  }
-
-  /**
-   * Converts one field of the input string into a numeric field value. Returns
-   * <code>false</code> if failed.
-   * 
-   * @param text the time text to be parsed
-   * @param pos Parse position
-   * @param part the pattern part for this field
-   * @param digitCount when greater than 0, numeric parsing must obey the count
-   * @param cal DateRecord object that will hold parsed value
-   * 
-   * @return <code>true</code> if parsing successful
-   */
-  @SuppressWarnings("fallthrough")
-  private boolean subParse(String text, int[] pos, PatternPart part,
-      int digitCount, DateRecord cal) {
-
-    skipSpace(text, pos);
-
-    int start = pos[0];
-    char ch = part.text.charAt(0);
-
-    // Parse integer value if it is a numeric field.
-    int value = -1; // initialize value to be -1,
-    if (isNumeric(part)) {
-      if (digitCount > 0) {
-        if ((start + digitCount) > text.length()) {
-          return false;
-        }
-        value = parseInt(text.substring(0, start + digitCount), pos);
-      } else {
-        value = parseInt(text, pos);
-      }
-    }
-
-    switch (ch) {
-      case 'G': // era
-        value = matchString(text, start, dateTimeFormatInfo.erasFull(), pos);
-        cal.setEra(value);
-        return true;
-      case 'M': // month
-        return subParseMonth(text, pos, cal, value, start);
-      case 'L': // standalone month
-        return subParseStandaloneMonth(text, pos, cal, value, start);
-      case 'E': // day of week
-        return subParseDayOfWeek(text, pos, start, cal);
-      case 'c': // standalone day of week
-        return subParseStandaloneDay(text, pos, start, cal);
-      case 'a': // AM/PM
-        value = matchString(text, start, dateTimeFormatInfo.ampms(), pos);
-        cal.setAmpm(value);
-        return true;
-      case 'y': // year
-        return subParseYear(text, pos, start, value, part, cal);
-      case 'd': // day of month
-        if (value <= 0) {
-          return false;
-        }
-        cal.setDayOfMonth(value);
-        return true;
-      case 'S': // fractional seconds
-        if (value < 0) {
-          return false;
-        }
-        return subParseFractionalSeconds(value, start, pos[0], cal);
-      case 'h': // hour (1..12)
-        if (value == 12) {
-          value = 0;
-        }
-        // fall through
-      case 'K': // hour (0..11)
-      case 'H': // hour (0..23)
-        if (value < 0) {
-          return false;
-        }
-        cal.setHours(value);
-        return true;
-      case 'k': // hour (1..24)
-        if (value < 0) {
-          return false;
-        }
-        cal.setHours(value);
-        return true;
-      case 'm': // minute
-        if (value < 0) {
-          return false;
-        }
-        cal.setMinutes(value);
-        return true;
-      case 's': // second
-        if (value < 0) {
-          return false;
-        }
-        cal.setSeconds(value);
-        return true;
-
-      case 'z': // time zone offset
-      case 'Z': // time zone RFC
-      case 'v': // time zone generic
-        return subParseTimeZoneInGMT(text, start, pos, cal);
-      default:
-        return false;
-    }
-  }
-
-  /**
-   * Method subParseDayOfWeek parses day of the week field.
-   * 
-   * @param text the time text to be parsed
-   * @param pos Parse position
-   * @param start from where parse start
-   * @param cal DateRecord object that holds parsed value
-   * 
-   * @return <code>true</code> if parsing successful, otherwise
-   *         <code>false</code>
-   */
-  private boolean subParseDayOfWeek(String text, int[] pos, int start,
-      DateRecord cal) {
-    int value;
-    // 'E' - DAY_OF_WEEK
-    // Want to be able to parse both short and long forms.
-    // Try count == 4 (DDDD) first:
-    value = matchString(text, start, dateTimeFormatInfo.weekdaysFull(), pos);
-    if (value < 0) {
-      value = matchString(text, start, dateTimeFormatInfo.weekdaysShort(), pos);
-    }
-    if (value < 0) {
-      return false;
-    }
-    cal.setDayOfWeek(value);
-    return true;
-  }
-
-  /**
-   * Method subParseFractionalSeconds parses fractional seconds field.
-   * 
-   * @param value parsed numberic value
-   * @param start
-   * @param end parse position
-   * @param cal DateRecord object that holds parsed value
-   * @return <code>true</code> if parsing successful, otherwise
-   *         <code>false</code>
-   */
-  private boolean subParseFractionalSeconds(int value, int start, int end,
-      DateRecord cal) {
-    // Fractional seconds left-justify.
-    int i = end - start;
-    if (i < 3) {
-      while (i < 3) {
-        value *= 10;
-        i++;
-      }
-    } else {
-      int a = 1;
-      while (i > 3) {
-        a *= 10;
-        i--;
-      }
-      value = (value + (a >> 1)) / a;
-    }
-    cal.setMilliseconds(value);
-    return true;
-  }
-
-  /**
-   * Parses Month field.
-   * 
-   * @param text the time text to be parsed
-   * @param pos Parse position
-   * @param cal DateRecord object that will hold parsed value
-   * @param value numeric value if this field is expressed using numberic
-   *          pattern
-   * @param start from where parse start
-   * 
-   * @return <code>true</code> if parsing successful
-   */
-  private boolean subParseMonth(String text, int[] pos, DateRecord cal,
-      int value, int start) {
-    // When month is symbols, i.e., MMM or MMMM, value will be -1.
-    if (value < 0) {
-      // Want to be able to parse both short and long forms.
-      // Try count == 4 first:
-      value = matchString(text, start, dateTimeFormatInfo.monthsFull(), pos);
-      if (value < 0) { // count == 4 failed, now try count == 3.
-        value = matchString(text, start, dateTimeFormatInfo.monthsShort(), pos);
-      }
-      if (value < 0) {
-        return false;
-      }
-      cal.setMonth(value);
-      return true;
-    } else if (value > 0) {
-      cal.setMonth(value - 1);
-      return true;
-    }
-    return false;
-  }
-
-  /**
-   * Parses standalone day of the week field.
-   * 
-   * @param text the time text to be parsed
-   * @param pos Parse position
-   * @param start from where parse start
-   * @param cal DateRecord object that holds parsed value
-   * 
-   * @return <code>true</code> if parsing successful, otherwise
-   *         <code>false</code>
-   */
-  private boolean subParseStandaloneDay(String text, int[] pos, int start,
-      DateRecord cal) {
-    int value;
-    // 'c' - DAY_OF_WEEK
-    // Want to be able to parse both short and long forms.
-    // Try count == 4 (cccc) first:
-    value = matchString(text, start, dateTimeFormatInfo.weekdaysFullStandalone(),
-        pos);
-    if (value < 0) {
-      value = matchString(text, start,
-          dateTimeFormatInfo.weekdaysShortStandalone(), pos);
-    }
-    if (value < 0) {
-      return false;
-    }
-    cal.setDayOfWeek(value);
-    return true;
-  }
-
-  /**
-   * Parses a standalone month field.
-   * 
-   * @param text the time text to be parsed
-   * @param pos Parse position
-   * @param cal DateRecord object that will hold parsed value
-   * @param value numeric value if this field is expressed using numberic
-   *          pattern
-   * @param start from where parse start
-   * 
-   * @return <code>true</code> if parsing successful
-   */
-  private boolean subParseStandaloneMonth(String text, int[] pos,
-      DateRecord cal, int value, int start) {
-    // When month is symbols, i.e., LLL or LLLL, value will be -1.
-    if (value < 0) {
-      // Want to be able to parse both short and long forms.
-      // Try count == 4 first:
-      value = matchString(text, start,
-          dateTimeFormatInfo.monthsFullStandalone(), pos);
-      if (value < 0) { // count == 4 failed, now try count == 3.
-        value = matchString(text, start,
-            dateTimeFormatInfo.monthsShortStandalone(), pos);
-      }
-      if (value < 0) {
-        return false;
-      }
-      cal.setMonth(value);
-      return true;
-    } else if (value > 0) {
-      cal.setMonth(value - 1);
-      return true;
-    }
-    return false;
-  }
-
-  /**
-   * Method parses GMT type timezone.
-   * 
-   * @param text the time text to be parsed
-   * @param start from where parse start
-   * @param pos Parse position
-   * @param cal DateRecord object that holds parsed value
-   * 
-   * @return <code>true</code> if parsing successful, otherwise
-   *         <code>false</code>
-   */
-  private boolean subParseTimeZoneInGMT(String text, int start, int[] pos,
-      DateRecord cal) {
-    // First try to parse generic forms such as GMT-07:00. Do this first
-    // in case localized DateFormatZoneData contains the string "GMT"
-    // for a zone; in that case, we don't want to match the first three
-    // characters of GMT+/-HH:MM etc.
-
-    // For time zones that have no known names, look for strings
-    // of the form:
-    // GMT[+-]hours:minutes or
-    // GMT[+-]hhmm or
-    // GMT.
-    if (text.startsWith(GMT, start)) {
-      pos[0] = start + GMT.length();
-      return parseTimeZoneOffset(text, pos, cal);
-    }
-    // Likewise for UTC.
-    if (text.startsWith(UTC, start)) {
-      pos[0] = start + UTC.length();
-      return parseTimeZoneOffset(text, pos, cal);
-    }
-
-    // At this point, check for named time zones by looking through
-    // the locale data from the DateFormatZoneData strings.
-    // Want to be able to parse both short and long forms.
-    /*
-     * i = subParseZoneString(text, start, cal); if (i != 0) return i;
-     */
-
-    // As a last resort, look for numeric timezones of the form
-    // [+-]hhmm as specified by RFC 822. This code is actually
-    // a little more permissive than RFC 822. It will try to do
-    // its best with numbers that aren't strictly 4 digits long.
-    return parseTimeZoneOffset(text, pos, cal);
-  }
-
-  /**
-   * Method subParseYear parse year field. Year field is special because 1, two
-   * digit year need to be resolved. 2, we allow year to take a sign. 3, year
-   * field participate in abut processing. In my testing, negative year does not
-   * seem working due to JDK (or GWT implementation) limitation. It is not a
-   * big deal so we don't worry about it. But keep the logic here so that we
-   * might want to replace DateRecord with our a calendar class.
-   * 
-   * @param text the time text to be parsed
-   * @param pos parse position
-   * @param start where this field starts
-   * @param value integer value of year
-   * @param part the pattern part for this field
-   * @param cal DateRecord object that will hold parsed value
-   * 
-   * @return <code>true</code> if successful
-   */
-  private boolean subParseYear(String text, int[] pos, int start, int value,
-      PatternPart part, DateRecord cal) {
-    char ch = ' ';
-    if (value < 0) {
-      if (pos[0] >= text.length()) {
-        return false;
-      }
-      ch = text.charAt(pos[0]);
-      // Check if it is a sign.
-      if (ch != '+' && ch != '-') {
-        return false;
-      }
-      ++(pos[0]);
-      value = parseInt(text, pos);
-      if (value < 0) {
-        return false;
-      }
-      if (ch == '-') {
-        value = -value;
-      }
-    }
-
-    // no sign, only 2 digit was actually parsed, pattern say it has 2 digit.
-    if (ch == ' ' && (pos[0] - start) == 2 && part.count == 2) {
-      // Assume for example that the defaultCenturyStart is 6/18/1903.
-      // This means that two-digit years will be forced into the range
-      // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
-      // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
-      // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
-      // other fields specify a date before 6/18, or 1903 if they specify a
-      // date afterwards. As a result, 03 is an ambiguous year. All other
-      // two-digit years are unambiguous.
-      Date date = new Date();
-      int defaultCenturyStartYear = date.getYear() + 1900 - 80;
-      int ambiguousTwoDigitYear = defaultCenturyStartYear % 100;
-      cal.setAmbiguousYear(value == ambiguousTwoDigitYear);
-      value += (defaultCenturyStartYear / 100) * 100
-          + (value < ambiguousTwoDigitYear ? 100 : 0);
-    }
-    cal.setYear(value);
-    return true;
-  }
-
-  /**
-   * Formats a number with the specified minimum number of digits, using zero to
-   * fill the gap.
-   * 
-   * @param buf where zero padded string will be written to
-   * @param value the number value being formatted
-   * @param minWidth minimum width of the formatted string; zero will be padded
-   *          to reach this width
-   */
-  private void zeroPaddingNumber(StringBuffer buf, int value, int minWidth) {
-    int b = NUMBER_BASE;
-    for (int i = 0; i < minWidth - 1; i++) {
-      if (value < b) {
-        buf.append('0');
-      }
-      b *= NUMBER_BASE;
-    }
-    buf.append(value);
+    super(pattern, dtfi);
   }
 }
diff --git a/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java b/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java
index 52c6e00..6cdbdbf 100644
--- a/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java
+++ b/user/src/com/google/gwt/i18n/client/DateTimeFormatInfo.java
@@ -16,333 +16,10 @@
 package com.google.gwt.i18n.client;
 
 /**
- * Information required for formatting and parsing localized date/time values.
+ * For backwards compatibility only.
  *
- * <p>Implementors should subclass {@link DefaultDateTimeFormatInfo} so when
- * methods are added they will get reasonable defaults and not break.  See the
- * same class for example values returned by these methods.
+ * @deprecated use {@link com.google.gwt.i18n.shared.DateTimeFormatInfo} instead
  */
-public interface DateTimeFormatInfo {
-
-  /**
-   * Returns array of strings containing abbreviations for Ante Meridiem and
-   * Post Meridiem.
-   */
-  String[] ampms();
-
-  /**
-   * Returns a safe default date format.
-   */
-  String dateFormat();
-
-  /**
-   * Returns a "full" date format.
-   */
-  String dateFormatFull();
-
-  /**
-   * Returns a "long" date format.
-   */
-  String dateFormatLong();
-
-  /**
-   * Returns a "medium" date format.
-   */
-  String dateFormatMedium();
-
-  /**
-   * Returns a "short" date format.
-   */
-  String dateFormatShort();
-
-  /**
-   * Returns a date/time format from a date format pattern and a time format
-   * pattern, using the locale default joining.
-   *
-   * @param timePattern the time pattern String
-   * @param datePattern the data pattern String
-   */
-  String dateTime(String timePattern, String datePattern);
-
-  /**
-   * Returns a date/time format from a date format pattern and a time format
-   * pattern, using "full" joining.
-   *
-   * @param timePattern the time pattern String
-   * @param datePattern the data pattern String
-   */
-  String dateTimeFull(String timePattern, String datePattern);
-
-  /**
-   * Returns a date/time format from a date format pattern and a time format
-   * pattern, using "full" joining.
-   *
-   * @param timePattern the time pattern String
-   * @param datePattern the data pattern String
-   */
-  String dateTimeLong(String timePattern, String datePattern);
-
-  /**
-   * Returns a date/time format from a date format pattern and a time format
-   * pattern, using "full" joining.
-   *
-   * @param timePattern the time pattern String
-   * @param datePattern the data pattern String
-   */
-  String dateTimeMedium(String timePattern, String datePattern);
-
-  /**
-   * Returns a date/time format from a date format pattern and a time format
-   * pattern, using "full" joining.
-   *
-   * @param timePattern the time pattern String
-   * @param datePattern the data pattern String
-   */
-  String dateTimeShort(String timePattern, String datePattern);
-
-  /**
-   * Returns an array of the full era names.
-   */
-  String[] erasFull();
-
-  /**
-   * Returns abbreviations of the era names.
-   */
-  String[] erasShort();
-
-  /**
-   * Returns the day which generally comes first in a weekly calendar view, as
-   * an index into the return value of {@link #weekdaysFull()}.
-   */
-  int firstDayOfTheWeek();
-
-  /**
-   * Returns localized format equivalent to the "d" skeleton pattern.
-   */
-  String formatDay();
-
-  /**
-   * Returns localized format equivalent to the "hm" skeleton pattern.
-   */
-  String formatHour12Minute();
-
-  /**
-   * Returns localized format equivalent to the "hms" skeleton pattern.
-   */
-  String formatHour12MinuteSecond();
-
-  /**
-   * Returns localized format equivalent to the "Hm" skeleton pattern.
-   */
-  String formatHour24Minute();
-
-  /**
-   * Returns localized format equivalent to the "Hms" skeleton pattern.
-   */
-  String formatHour24MinuteSecond();
-
-  /**
-   * Returns localized format equivalent to the "ms" skeleton pattern.
-   */
-  String formatMinuteSecond();
-
-  /**
-   * Returns localized format equivalent to the "MMM" skeleton pattern.
-   */
-  String formatMonthAbbrev();
-
-  /**
-   * Returns localized format equivalent to the "MMMd" skeleton pattern.
-   */
-  String formatMonthAbbrevDay();
-
-  /**
-   * Returns localized format equivalent to the "MMMM" skeleton pattern.
-   */
-  String formatMonthFull();
-
-  /**
-   * Returns localized format equivalent to the "MMMMd" skeleton pattern.
-   */
-  String formatMonthFullDay();
-
-  /**
-   * Returns localized format equivalent to the "MMMMEEEEd" skeleton pattern.
-   */
-  String formatMonthFullWeekdayDay();
-
-  /**
-   * Returns localized format equivalent to the "Md" skeleton pattern.
-   */
-  String formatMonthNumDay();
-
-  /**
-   * Returns localized format equivalent to the "y" skeleton pattern.
-   */
-  String formatYear();
-
-  /**
-   * Returns localized format equivalent to the "yMMM" skeleton pattern.
-   */
-  String formatYearMonthAbbrev();
-
-  /**
-   * Returns localized format equivalent to the "yMMMd" skeleton pattern.
-   */
-  String formatYearMonthAbbrevDay();
-
-  /**
-   * Returns localized format equivalent to the "yMMMM" skeleton pattern.
-   */
-  String formatYearMonthFull();
-
-  /**
-   * Returns localized format equivalent to the "yMMMMd" skeleton pattern.
-   */
-  String formatYearMonthFullDay();
-
-  /**
-   * Returns localized format equivalent to the "yM" skeleton pattern.
-   */
-  String formatYearMonthNum();
-
-  /**
-   * Returns localized format equivalent to the "yMd" skeleton pattern.
-   */
-  String formatYearMonthNumDay();
-
-  /**
-   * Returns localized format equivalent to the "yMMMEEEd" skeleton pattern.
-   */
-  String formatYearMonthWeekdayDay();
-
-  /**
-   * Returns localized format equivalent to the "yQQQQ" skeleton pattern.
-   */
-  String formatYearQuarterFull();
-
-  /**
-   * Returns localized format equivalent to the "yQ" skeleton pattern.
-   */
-  String formatYearQuarterShort();
-
-  /**
-   * Returns an array of full month names.
-   */
-  String[] monthsFull();
-
-  /**
-   * Returns an array of month names for use in a stand-alone context.
-   */
-  String[] monthsFullStandalone();
-
-  /**
-   * Returns an array of the shortest abbreviations for months, typically a
-   * single character and not guaranteed to be unique.
-   */
-  String[] monthsNarrow();
-
-  /**
-   * Returns an array of the shortest abbreviations for months suitable for use
-   * in a stand-alone context, typically a single character and not guaranteed
-   * to be unique.
-   */
-  String[] monthsNarrowStandalone();
-
-  /**
-   * Returns an array of month abbreviations.
-   */
-  String[] monthsShort();
-
-  /**
-   * Returns an array of month abbreviations, suitable for use in a stand-alone
-   * context.
-   */
-  String[] monthsShortStandalone();
-
-  /**
-   * Returns an array of full quarter names.
-   */
-  String[] quartersFull();
-
-  /**
-   * Returns an array of abbreviations for quarters.
-   */
-  String[] quartersShort();
-
-  /**
-   * Returns a safe default time format.
-   */
-  String timeFormat();
-
-  /**
-   * Returns a "full" time format.
-   */
-  String timeFormatFull();
-
-  /**
-   * Returns a "long" time format.
-   */
-  String timeFormatLong();
-
-  /**
-   * Returns a "medium" time format.
-   */
-  String timeFormatMedium();
-
-  /**
-   * Returns a "short" time format.
-   */
-  String timeFormatShort();
-
-  /**
-   * Returns an array of the full names of weekdays.
-   */
-  String[] weekdaysFull();
-
-  /**
-   * Returns an array of the full names of weekdays, suitable for use in a
-   * stand-alone context.
-   */
-  String[] weekdaysFullStandalone();
-
-  /**
-   * Returns an array of the shortest abbreviations for weekdays, typically a
-   * single character and not guaranteed to be unique.
-   */
-  String[] weekdaysNarrow();
-
-  /**
-   * Returns an array of the shortest abbreviations for weekdays suitable for
-   * use in a stand-alone context, typically a single character and not
-   * guaranteed to be unique.
-   */
-  String[] weekdaysNarrowStandalone();
-
-  /**
-   * Returns an array of abbreviations for weekdays.
-   */
-  String[] weekdaysShort();
-
-  /**
-   * Returns an array of abbreviations for weekdays, suitable for use in a
-   * stand-alone context.
-   */
-  String[] weekdaysShortStandalone();
-
-  /**
-   * Returns the day which ends the weekend, as an index into the return value
-   * of {@link #weekdaysFull()}.
-   *
-   * <p>Note that this value may be numerically less than
-   * {@link #weekendStart()} - for example, {@link #weekendStart()} of 6 and
-   * {@link #weekendEnd()} of 0 means Saturday and Sunday are the weekend.
-   */
-  int weekendEnd();
-
-  /**
-   * Returns the day which starts the weekend, as an index into the return value
-   * of {@link #weekdaysFull()}.
-   */
-  int weekendStart();
+@Deprecated
+public interface DateTimeFormatInfo extends com.google.gwt.i18n.shared.DateTimeFormatInfo {
 }
diff --git a/user/src/com/google/gwt/i18n/client/DefaultDateTimeFormatInfo.java b/user/src/com/google/gwt/i18n/client/DefaultDateTimeFormatInfo.java
index 7d0fb80..658a140 100644
--- a/user/src/com/google/gwt/i18n/client/DefaultDateTimeFormatInfo.java
+++ b/user/src/com/google/gwt/i18n/client/DefaultDateTimeFormatInfo.java
@@ -15,325 +15,13 @@
  */
 package com.google.gwt.i18n.client;
 
-// DO NOT EDIT - GENERATED FROM CLDR AND ICU DATA
-
 /**
- * Default implementation of DateTimeFormatInfo interface, using values from
- * the CLDR root locale.
- * <p>
- * Users who need to create their own DateTimeFormatInfo implementation are
- * encouraged to extend this class so their implementation won't break when   
- * new methods are added.
+ * For backwards compatibility only.
+ * 
+ * @deprecated use {@link com.google.gwt.i18n.shared.DefaultDateTimeFormatInfo}
+ *     instead
  */
-public class DefaultDateTimeFormatInfo implements DateTimeFormatInfo {
-
-  public String[] ampms() {
-    return new String[] {
-        "AM",
-        "PM"
-    };
-  }
-
-  public String dateFormat() {
-    return dateFormatMedium();
-  }
-
-  public String dateFormatFull() {
-    return "EEEE, y MMMM dd";
-  }
-
-  public String dateFormatLong() {
-    return "y MMMM d";
-  }
-
-  public String dateFormatMedium() {
-    return "y MMM d";
-  }
-
-  public String dateFormatShort() {
-    return "yyyy-MM-dd";
-  }
-
-  public String dateTime(String timePattern, String datePattern) {
-    return dateTimeMedium(timePattern, datePattern);
-  }
-
-  public String dateTimeFull(String timePattern, String datePattern) {
-    return new java.lang.StringBuffer().append(datePattern).append(" ").append(timePattern).toString();
-  }
-
-  public String dateTimeLong(String timePattern, String datePattern) {
-    return new java.lang.StringBuffer().append(datePattern).append(" ").append(timePattern).toString();
-  }
-
-  public String dateTimeMedium(String timePattern, String datePattern) {
-    return new java.lang.StringBuffer().append(datePattern).append(" ").append(timePattern).toString();
-  }
-
-  public String dateTimeShort(String timePattern, String datePattern) {
-    return new java.lang.StringBuffer().append(datePattern).append(" ").append(timePattern).toString();
-  }
-
-  public String[] erasFull() {
-    return new String[] {
-        "Before Christ",
-        "Anno Domini"
-    };
-  }
-
-  public String[] erasShort() {
-    return new String[] {
-        "BC",
-        "AD"
-    };
-  }
-
-  public int firstDayOfTheWeek() {
-    return 1;
-  }
-
-  public String formatDay() {
-    return "d";
-  }
-
-  public String formatHour12Minute() {
-    return "h:mm a";
-  }
-
-  public String formatHour12MinuteSecond() {
-    return "h:mm:ss a";
-  }
-
-  public String formatHour24Minute() {
-    return "HH:mm";
-  }
-
-  public String formatHour24MinuteSecond() {
-    return "HH:mm:ss";
-  }
-
-  public String formatMinuteSecond() {
-    return "mm:ss";
-  }
-
-  public String formatMonthAbbrev() {
-    return "LLL";
-  }
-
-  public String formatMonthAbbrevDay() {
-    return "MMM d";
-  }
-
-  public String formatMonthFull() {
-    return "LLLL";
-  }
-
-  public String formatMonthFullDay() {
-    return "MMMM d";
-  }
-
-  public String formatMonthFullWeekdayDay() {
-    return "EEEE MMMM d";
-  }
-
-  public String formatMonthNumDay() {
-    return "M-d";
-  }
-
-  public String formatYear() {
-    return "y";
-  }
-
-  public String formatYearMonthAbbrev() {
-    return "y MMM";
-  }
-
-  public String formatYearMonthAbbrevDay() {
-    return "y MMM d";
-  }
-
-  public String formatYearMonthFull() {
-    return "y MMMM";
-  }
-
-  public String formatYearMonthFullDay() {
-    return "y MMMM d";
-  }
-
-  public String formatYearMonthNum() {
-    return "y-M";
-  }
-
-  public String formatYearMonthNumDay() {
-    return "y-M-d";
-  }
-
-  public String formatYearMonthWeekdayDay() {
-    return "EEE, y MMM d";
-  }
-
-  public String formatYearQuarterFull() {
-    return "y QQQQ";
-  }
-
-  public String formatYearQuarterShort() {
-    return "y Q";
-  }
-
-  public String[] monthsFull() {
-    return new String[] {
-        "January",
-        "February",
-        "March",
-        "April",
-        "May",
-        "June",
-        "July",
-        "August",
-        "September",
-        "October",
-        "November",
-        "December"
-    };
-  }
-
-  public String[] monthsFullStandalone() {
-    return monthsFull();
-  }
-
-  public String[] monthsNarrow() {
-    return new String[] {
-        "J",
-        "F",
-        "M",
-        "A",
-        "M",
-        "J",
-        "J",
-        "A",
-        "S",
-        "O",
-        "N",
-        "D"
-    };
-  }
-
-  public String[] monthsNarrowStandalone() {
-    return monthsNarrow();
-  }
-
-  public String[] monthsShort() {
-    return new String[] {
-        "Jan",
-        "Feb",
-        "Mar",
-        "Apr",
-        "May",
-        "Jun",
-        "Jul",
-        "Aug",
-        "Sep",
-        "Oct",
-        "Nov",
-        "Dec"
-    };
-  }
-
-  public String[] monthsShortStandalone() {
-    return monthsShort();
-  }
-
-  public String[] quartersFull() {
-    return new String[] {
-        "1st quarter",
-        "2nd quarter",
-        "3rd quarter",
-        "4th quarter"
-    };
-  }
-
-  public String[] quartersShort() {
-    return new String[] {
-        "Q1",
-        "Q2",
-        "Q3",
-        "Q4"
-    };
-  }
-
-  public String timeFormat() {
-    return timeFormatMedium();
-  }
-
-  public String timeFormatFull() {
-    return "HH:mm:ss zzzz";
-  }
-
-  public String timeFormatLong() {
-    return "HH:mm:ss z";
-  }
-
-  public String timeFormatMedium() {
-    return "HH:mm:ss";
-  }
-
-  public String timeFormatShort() {
-    return "HH:mm";
-  }
-
-  public String[] weekdaysFull() {
-    return new String[] {
-        "Sunday",
-        "Monday",
-        "Tuesday",
-        "Wednesday",
-        "Thursday",
-        "Friday",
-        "Saturday"
-    };
-  }
-
-  public String[] weekdaysFullStandalone() {
-    return weekdaysFull();
-  }
-
-  public String[] weekdaysNarrow() {
-    return new String[] {
-        "S",
-        "M",
-        "T",
-        "W",
-        "T",
-        "F",
-        "S"
-    };
-  }
-
-  public String[] weekdaysNarrowStandalone() {
-    return weekdaysNarrow();
-  }
-
-  public String[] weekdaysShort() {
-    return new String[] {
-        "Sun",
-        "Mon",
-        "Tue",
-        "Wed",
-        "Thu",
-        "Fri",
-        "Sat"
-    };
-  }
-
-  public String[] weekdaysShortStandalone() {
-    return weekdaysShort();
-  }
-
-  public int weekendEnd() {
-    return 0;
-  }
-
-  public int weekendStart() {
-    return 6;
-  }
+@Deprecated
+public class DefaultDateTimeFormatInfo extends com.google.gwt.i18n.shared.DefaultDateTimeFormatInfo
+    implements DateTimeFormatInfo {
 }
diff --git a/user/src/com/google/gwt/i18n/client/Localizable.java b/user/src/com/google/gwt/i18n/client/Localizable.java
index 5ef7c99..ed97239 100644
--- a/user/src/com/google/gwt/i18n/client/Localizable.java
+++ b/user/src/com/google/gwt/i18n/client/Localizable.java
@@ -13,94 +13,13 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package com.google.gwt.i18n.client;
 
 /**
- * A tag interface that serves as the root of a family of types used in static
- * internationalization. Using <code>GWT.create(<i>class</i>)</code> to
- * instantiate a type that directly extends or implements
- * <code>Localizable</code> invites locale-sensitive type substitution.
+ * For backwards compatibility only.
  * 
- * <h3>Locale-sensitive Type Substitution</h3>
- * If a type <code>Type</code> directly extends or implements
- * <code>Localizable</code> (as opposed to
- * {@link com.google.gwt.i18n.client.Constants} or
- * {@link com.google.gwt.i18n.client.Messages}) and the following code is used
- * to create an object from <code>Type</code> as follows:
- * 
- * <pre class="code">Type localized = (Type)GWT.create(Type.class);</pre>
- * 
- * then <code>localized</code> will be assigned an instance of a localized
- * subclass, selected based on the value of the <code>locale</code> client
- * property. The choice of subclass is determined by the following naming
- * pattern:
- * 
- * <table>
- * 
- * <tr>
- * <th align='left'>If <code>locale</code> is...&#160;&#160;&#160;&#160;</th>
- * <th align='left'>The substitute class for <code>Type</code> is...</th>
- * </tr>
- * 
- * <tr>
- * <td><i>unspecified</i></td>
- * <td><code>Type</code> itself, or <code>Type_</code> if <code>Type</code>
- * is an interface</td>
- * </tr>
- * 
- * <tr>
- * <td><code>x</code></td>
- * <td>Class <code>Type_x</code> if it exists, otherwise treated as if
- * <code>locale</code> were <i>unspecified</i></td>
- * </tr>
- * 
- * <tr>
- * <td><code>x_Y</code></td>
- * <td>Class <code>Type_x_Y</code> if it exists, otherwise treated as if
- * <code>locale</code> were <code>x</code></td>
- * </tr>
- * 
- * </table>
- * 
- * where in the table above <code>x</code> is a <a
- * href="http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt">ISO language
- * code</a> and <code>Y</code> is a two-letter <a
- * href="http://userpage.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">ISO
- * country code</a>.
- * 
- * <h3>Specifying Locale</h3>
- * The locale of a module is specified using the <code>locale</code> client
- * property, which can be specified using either a meta tag or as part of the
- * query string in the host page's URL. If both are specified, the query string
- * takes precedence.
- * 
- * <p>
- * To specify the <code>locale</code> client property using a meta tag in the
- * host HTML, use <code>gwt:property</code> as follows:
- * 
- * <pre>&lt;meta name="gwt:property" content="locale=x_Y"&gt;</pre>
- * 
- * For example, the following host HTML page sets the locale to "ja_JP":
- * 
- * {@gwt.include com/google/gwt/examples/i18n/ColorNameLookupExample_ja_JP.html}
- * </p>
- * 
- * <p>
- * To specify the <code>locale</code> client property using a query string,
- * specify a value for the name <code>locale</code>. For example,
- * 
- * <pre>http://www.example.org/myapp.html?locale=fr_CA</pre>
- * 
- * </p>
- * 
- * <h3>For More Information</h3>
- * See the GWT Developer Guide for an introduction to internationalization.
- * 
- * @see com.google.gwt.i18n.client.Constants
- * @see com.google.gwt.i18n.client.ConstantsWithLookup
- * @see com.google.gwt.i18n.client.Messages
- * @see com.google.gwt.i18n.client.Dictionary
+ * @deprecated use {@link com.google.gwt.i18n.shared.Localizable} instead
  */
-public interface Localizable {
+@Deprecated
+public interface Localizable extends com.google.gwt.i18n.shared.Localizable {
 }
diff --git a/user/src/com/google/gwt/i18n/client/TimeZone.java b/user/src/com/google/gwt/i18n/client/TimeZone.java
index 57ab166..1394fe3 100644
--- a/user/src/com/google/gwt/i18n/client/TimeZone.java
+++ b/user/src/com/google/gwt/i18n/client/TimeZone.java
@@ -13,7 +13,6 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package com.google.gwt.i18n.client;
 
 import com.google.gwt.core.client.JsArrayInteger;
@@ -35,7 +34,7 @@
  * there is a decent fallback that only uses the time zone offset to create a
  * TimeZone object.
  */
-public class TimeZone {
+public class TimeZone implements com.google.gwt.i18n.shared.TimeZone {
   // constants to reference time zone names in the time zone names array
   private static final int STD_SHORT_NAME = 0;
   private static final int STD_LONG_NAME = 1;
@@ -176,13 +175,8 @@
   private TimeZone() {
   }
 
-  /**
-   * Returns the daylight savings time adjustment, in minutes, for the given
-   * date. If daylight savings time is in effect on the given date, the number
-   * will be positive, otherwise 0.
-   *
-   * @param date the date to check
-   * @return offset amount
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getDaylightAdjustment(java.util.Date)
    */
   public int getDaylightAdjustment(Date date) {
     if (transitionPoints == null) {
@@ -197,32 +191,22 @@
     return (index == 0) ? 0 : adjustments[index - 1];
   }
 
-  /**
-   * Returns the GMT representation of this time zone object.
-   *
-   * @param date The date from which the time information should be extracted
-   * @return A GMT representation of the time given by the date
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getGMTString(java.util.Date)
    */
   public String getGMTString(Date date) {
     return composeGMTString(getOffset(date));
   }
 
-  /**
-   * Returns time zone id for this time zone. For time zone objects that have
-   * been instantiated from a time zone offset, the POSIX time zone id will be
-   * returned.
-   *
-   * @return time zone id
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getID()
    */
   public String getID() {
     return timezoneID;
   }
 
-  /**
-   * To get ISO-style (+00:00) representation of the time zone for given date.
-   *
-   * @param date The date for which time to retrieve RFC time zone string
-   * @return ISO-style time zone string
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getISOTimeZoneString(java.util.Date)
    */
   public String getISOTimeZoneString(Date date) {
     int offset = -getOffset(date);
@@ -238,34 +222,22 @@
     return new String(data);
   }
 
-  /**
-   * Returns the long version of the time zone name for the given date; the
-   * result of this method will be different if daylight savings time is in
-   * effect.
-   *
-   * @param date The date for which the long time zone name is returned
-   * @return long time zone name
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getLongName(java.util.Date)
    */
   public String getLongName(Date date) {
     return tzNames[isDaylightTime(date) ? DLT_LONG_NAME : STD_LONG_NAME];
   }
 
-  /**
-   * Returns the RFC representation of the time zone name for the given date.
-   * To be consistent with JDK/Javascript API, west of Greenwich will be
-   * positive.
-   *
-   *  @param date The date for which time to retrieve time zone offset
-   *  @return time zone offset in minutes
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getOffset(java.util.Date)
    */
   public int getOffset(Date date) {
     return standardOffset - getDaylightAdjustment(date);
   }
 
-  /**
-   * To get RFC representation of certain time zone name for given date.
-   * @param date The date for which time to retrieve RFC time zone string
-   * @return RFC time zone string
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getRFCTimeZoneString(java.util.Date)
    */
   public String getRFCTimeZoneString(Date date) {
     int offset = -getOffset(date);
@@ -281,29 +253,22 @@
     return new String(data);
   }
 
-  /**
-   * Returns the short time zone name for a given date.
-   *
-   * @param date The date for which time to retrieve short time zone
-   * @return short time zone name
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getShortName(java.util.Date)
    */
   public String getShortName(Date date) {
     return tzNames[isDaylightTime(date) ? DLT_SHORT_NAME : STD_SHORT_NAME];
   }
 
-  /**
-   * Returns the standard time zone offset, in minutes.
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#getStandardOffset()
    */
   public int getStandardOffset() {
     return standardOffset;
   }
 
-  /**
-   * Check whether the given date and time falls within a daylight savings time
-   * period.
-   *
-   * @param date and time to check
-   * @return true if daylight savings time is in effect
+  /* (non-Javadoc)
+   * @see com.google.gwt.i18n.client.TimeZoneIntf#isDaylightTime(java.util.Date)
    */
   public boolean isDaylightTime(Date date) {
     return getDaylightAdjustment(date) > 0;
diff --git a/user/src/com/google/gwt/i18n/rebind/CustomDateTimeFormatGenerator.java b/user/src/com/google/gwt/i18n/rebind/CustomDateTimeFormatGenerator.java
index 658f36a..d69f8b3 100644
--- a/user/src/com/google/gwt/i18n/rebind/CustomDateTimeFormatGenerator.java
+++ b/user/src/com/google/gwt/i18n/rebind/CustomDateTimeFormatGenerator.java
@@ -25,9 +25,9 @@
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
-import com.google.gwt.i18n.client.CustomDateTimeFormat;
-import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.i18n.client.CustomDateTimeFormat.Pattern;
+import com.google.gwt.i18n.shared.CustomDateTimeFormat;
+import com.google.gwt.i18n.shared.CustomDateTimeFormat.Pattern;
+import com.google.gwt.i18n.shared.DateTimeFormat;
 import com.google.gwt.i18n.shared.GwtLocale;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
@@ -50,6 +50,7 @@
    * @return generated class name
    * @throws UnableToCompleteException
    */
+  @SuppressWarnings("deprecation")
   @Override
   public final String generate(TreeLogger logger, final GeneratorContext context,
       String typeName) throws UnableToCompleteException {
@@ -79,6 +80,14 @@
           + CustomDateTimeFormat.class.getName());
       throw new UnableToCompleteException();
     }
+    JClassType oldDateTimeFormat;
+    try {
+      oldDateTimeFormat = typeOracle.getType(
+          com.google.gwt.i18n.client.DateTimeFormat.class.getName());
+    } catch (NotFoundException e) {
+      logger.log(TreeLogger.ERROR, "No client DateTimeFormat type?", e);
+      throw new UnableToCompleteException();
+    }
     JClassType dateTimeFormat;
     try {
       dateTimeFormat = typeOracle.getType(DateTimeFormat.class.getName());
@@ -102,24 +111,30 @@
       writer.indent();
       for (JMethod method : targetClass.getMethods()) {
         JType returnType = method.getReturnType();
-        if (returnType != dateTimeFormat) {
+        if (returnType != dateTimeFormat && returnType != oldDateTimeFormat) {
           logger.log(TreeLogger.ERROR, typeName + "." + method.getName()
               + " must return DateTimeFormat");
           throw new UnableToCompleteException();
         }
+        String pattern;
         Pattern annotation = method.getAnnotation(Pattern.class);
         if (annotation == null) {
-          logger.log(TreeLogger.ERROR, typeName + "." + method.getName()
-              + " must have an @Pattern annotation");
-          throw new UnableToCompleteException();
+          com.google.gwt.i18n.client.CustomDateTimeFormat.Pattern oldAnnotation
+              = method.getAnnotation(com.google.gwt.i18n.client.CustomDateTimeFormat.Pattern.class);
+          if (oldAnnotation == null) {
+            logger.log(TreeLogger.ERROR, typeName + "." + method.getName()
+                + " must have an @Pattern annotation");
+            throw new UnableToCompleteException();
+          }
+          pattern = oldAnnotation.value();
+        } else {
+          pattern = annotation.value();
         }
-        String pattern = annotation.value();
         pattern = dtpg.getBestPattern(pattern); 
         writer.println();
-        writer.println("public "
-            + method.getReturnType().getQualifiedSourceName() + " "
-            + method.getName() + "() {");
-        writer.println("  return DateTimeFormat.getFormat(\"" + pattern
+        String retTypeName = method.getReturnType().getQualifiedSourceName();
+        writer.println("public " + retTypeName + " " + method.getName() + "() {");
+        writer.println("  return " + retTypeName + ".getFormat(\"" + pattern
             + "\");");
         writer.println("}");
       }
diff --git a/user/src/com/google/gwt/i18n/shared/CustomDateTimeFormat.java b/user/src/com/google/gwt/i18n/shared/CustomDateTimeFormat.java
new file mode 100644
index 0000000..8ab2769
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/CustomDateTimeFormat.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * 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.
+ */
+package com.google.gwt.i18n.shared;
+
+/**
+ * Create a custom localized date/time format at compile time. All methods on
+ * subtypes of this interface must take no parameters and return DateTimeFormat
+ * (which will be an initialized instance).
+ */
+public interface CustomDateTimeFormat {
+
+  /**
+   * Annotation containing the pattern skeleton.
+   * 
+   * <p>The order of pattern characters and any literals don't matter, just
+   * which pattern characters are present and their counts.
+   */
+  public @interface Pattern {
+
+    /**
+     * The pattern skeleton for which to generate a localized pattern.  Note
+     * that the order of pattern characters don't matter, as the generated
+     * pattern will be derived from a localized pattern that conveys the same
+     * information.
+     * 
+     * @return the pattern skeleton
+     */
+    String value();
+  }
+}
diff --git a/user/src/com/google/gwt/i18n/shared/DateTimeFormat.java b/user/src/com/google/gwt/i18n/shared/DateTimeFormat.java
new file mode 100644
index 0000000..6cfb72f
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/DateTimeFormat.java
@@ -0,0 +1,2194 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * 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.
+ */
+package com.google.gwt.i18n.shared;
+
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.i18n.shared.impl.DateRecord;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Formats and parses dates and times using locale-sensitive patterns.
+ * 
+ * <h3>Patterns</h3>
+ * 
+ * <table>
+ * <tr>
+ * <th>Symbol</th>
+ * <th>Meaning</th>
+ * <th>Presentation</th>
+ * <th>Example</th>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>G</code></td>
+ * <td>era designator</td>
+ * <td>Text</td>
+ * <td><code>AD</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>y</code></td>
+ * <td>year</td>
+ * <td>Number</td>
+ * <td><code>1996</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>L</code></td>
+ * <td>standalone month in year</td>
+ * <td>Text or Number</td>
+ * <td><code>July (or) 07</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>M</code></td>
+ * <td>month in year</td>
+ * <td>Text or Number</td>
+ * <td><code>July (or) 07</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>d</code></td>
+ * <td>day in month</td>
+ * <td>Number</td>
+ * <td><code>10</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>h</code></td>
+ * <td>hour in am/pm (1-12)</td>
+ * <td>Number</td>
+ * <td><code>12</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>H</code></td>
+ * <td>hour in day (0-23)</td>
+ * <td>Number</td>
+ * <td><code>0</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>m</code></td>
+ * <td>minute in hour</td>
+ * <td>Number</td>
+ * <td><code>30</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>s</code></td>
+ * <td>second in minute</td>
+ * <td>Number</td>
+ * <td><code>55</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>S</code></td>
+ * <td>fractional second</td>
+ * <td>Number</td>
+ * <td><code>978</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>E</code></td>
+ * <td>day of week</td>
+ * <td>Text</td>
+ * <td><code>Tuesday</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>c</code></td>
+ * <td>standalone day of week</td>
+ * <td>Text</td>
+ * <td><code>Tuesday</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>a</code></td>
+ * <td>am/pm marker</td>
+ * <td>Text</td>
+ * <td><code>PM</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>k</code></td>
+ * <td>hour in day (1-24)</td>
+ * <td>Number</td>
+ * <td><code>24</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>K</code></td>
+ * <td>hour in am/pm (0-11)</td>
+ * <td>Number</td>
+ * <td><code>0</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>z</code></td>
+ * <td>time zone</td>
+ * <td>Text</td>
+ * <td><code>Pacific Standard Time(see comment)</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>Z</code></td>
+ * <td>time zone (RFC 822)</td>
+ * <td>Text</td>
+ * <td><code>-0800(See comment)</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>v</code></td>
+ * <td>time zone id</td>
+ * <td>Text</td>
+ * <td><code>America/Los_Angeles(See comment)</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>'</code></td>
+ * <td>escape for text</td>
+ * <td>Delimiter</td>
+ * <td><code>'Date='</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>''</code></td>
+ * <td>single quote</td>
+ * <td>Literal</td>
+ * <td><code>'o''clock'</code></td>
+ * </tr>
+ * </table>
+ * 
+ * <p>
+ * The number of pattern letters influences the format, as follows:
+ * </p>
+ * 
+ * <dl>
+ * <dt>Text</dt>
+ * <dd>if 4 or more, then use the full form; if less than 4, use short or
+ * abbreviated form if it exists (e.g., <code>"EEEE"</code> produces
+ * <code>"Monday"</code>, <code>"EEE"</code> produces <code>"Mon"</code>)</dd>
+ * 
+ * <dt>Number</dt>
+ * <dd>the minimum number of digits. Shorter numbers are zero-padded to this
+ * amount (e.g. if <code>"m"</code> produces <code>"6"</code>, <code>"mm"</code>
+ * produces <code>"06"</code>). Year is handled specially; that is, if the count
+ * of 'y' is 2, the Year will be truncated to 2 digits. (e.g., if
+ * <code>"yyyy"</code> produces <code>"1997"</code>, <code>"yy"</code> produces
+ * <code>"97"</code>.) Unlike other fields, fractional seconds are padded on the
+ * right with zero.</dd>
+ * 
+ * <dt>Text or Number</dt>
+ * <dd>3 or more, use text, otherwise use number. (e.g. <code>"M"</code>
+ * produces <code>"1"</code>, <code>"MM"</code> produces <code>"01"</code>,
+ * <code>"MMM"</code> produces <code>"Jan"</code>, and <code>"MMMM"</code>
+ * produces <code>"January"</code>.  Some pattern letters also treat a count
+ * of 5 specially, meaning a single-letter abbreviation: <code>L</code>,
+ * <code>M</code>, <code>E</code>, and <code>c</code>.</dd>
+ * </dl>
+ * 
+ * <p>
+ * Any characters in the pattern that are not in the ranges of ['<code>a</code>
+ * '..'<code>z</code>'] and ['<code>A</code>'..'<code>Z</code>'] will be treated
+ * as quoted text. For instance, characters like '<code>:</code>', '
+ * <code>.</code>', '<code> </code>' (space), '<code>#</code>' and '
+ * <code>@</code>' will appear in the resulting time text even they are not
+ * embraced within single quotes.
+ * </p>
+ * 
+ * <p>
+ * [Time Zone Handling] Web browsers don't provide all the information we need
+ * for proper time zone formating -- so GWT has a copy of the required data, for
+ * your convenience. For simpler cases, one can also use a fallback
+ * implementation that only keeps track of the current timezone offset. These
+ * two approaches are called, respectively, Common TimeZones and Simple
+ * TimeZones, although both are implemented with the same TimeZone class.
+ * 
+ * "TimeZone createTimeZone(String timezoneData)" returns a Common TimeZone
+ * object, and "TimeZone createTimeZone(int timeZoneOffsetInMinutes)" returns a
+ * Simple TimeZone object. The one provided by OS fall into to Simple TimeZone
+ * category. For formatting purpose, following table shows the behavior of GWT
+ * DateTimeFormat.
+ * </p>
+ * <table>
+ * <tr>
+ * <th>Pattern</th>
+ * <th>Common TimeZone</th>
+ * <th>Simple TimeZone</th>
+ * </tr>
+ * <tr>
+ * <td>z, zz, zzz</td>
+ * <td>PDT</td>
+ * <td>UTC-7</td>
+ * </tr>
+ * <tr>
+ * <td>zzzz</td>
+ * <td>Pacific Daylight Time</td>
+ * <td>UTC-7</td>
+ * </tr>
+ * <tr>
+ * <td>Z, ZZ</td>
+ * <td>-0700</td>
+ * <td>-0700</td>
+ * </tr>
+ * <tr>
+ * <td>ZZZ</td>
+ * <td>-07:00</td>
+ * <td>-07:00</td>
+ * </tr>
+ * <tr>
+ * <td>ZZZZ</td>
+ * <td>GMT-07:00</td>
+ * <td>GMT-07:00</td>
+ * </tr>
+ * <tr>
+ * <td>v, vv, vvv, vvvv</td>
+ * <td>America/Los_Angeles</td>
+ * <td>Etc/GMT+7</td>
+ * </tr>
+ * </table>
+ * 
+ * <h3>Parsing Dates and Times</h3>
+ * <p>
+ * The pattern does not need to specify every field.  If the year, month, or
+ * day is missing from the pattern, the corresponding value will be taken from
+ * the current date.  If the month is specified but the day is not, the day will
+ * be constrained to the last day within the specified month.  If the hour,
+ * minute, or second is missing, the value defaults to zero.
+ * </p>
+ * 
+ * <p>
+ * As with formatting (described above), the count of pattern letters determines
+ * the parsing behavior.
+ * </p>
+ * 
+ * <dl>
+ * <dt>Text</dt>
+ * <dd>4 or more pattern letters--use full form, less than 4--use short or
+ * abbreviated form if one exists. In parsing, we will always try long format,
+ * then short.</dd>
+ * 
+ * <dt>Number</dt>
+ * <dd>the minimum number of digits.</dd>
+ * 
+ * <dt>Text or Number</dt>
+ * <dd>3 or more characters means use text, otherwise use number</dd>
+ * </dl>
+ * 
+ * <p>
+ * Although the current pattern specification doesn't not specify behavior for
+ * all letters, it may in the future. It is strongly discouraged to use
+ * unspecified letters as literal text without quoting them.
+ * </p>
+ * <p>
+ * [Note on TimeZone] The time zone support for parsing is limited. Only
+ * standard GMT and RFC format are supported. Time zone specification using time
+ * zone id (like America/Los_Angeles), time zone names (like PST, Pacific
+ * Standard Time) are not supported. Normally, it is too much a burden for a
+ * client application to load all the time zone symbols. And in almost all those
+ * cases, it is a better choice to do such parsing on server side through
+ * certain RPC mechanism. This decision is based on particular use cases we have
+ * studied; in principle, it could be changed in future versions.
+ * </p>
+ * 
+ * <h3>Examples</h3>
+ * <table>
+ * <tr>
+ * <th>Pattern</th>
+ * <th>Formatted Text</th>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>"yyyy.MM.dd G 'at' HH:mm:ss vvvv"</code></td>
+ * <td><code>1996.07.10 AD at 15:08:56 America/Los_Angeles</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>"EEE, MMM d, ''yy"</code></td>
+ * <td><code>Wed, July 10, '96</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>"h:mm a"</code></td>
+ * <td><code>12:08 PM</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>"hh 'o''clock' a, zzzz"</code></td>
+ * <td><code> 12 o'clock PM, Pacific Daylight Time</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>"K:mm a, vvvv"</code></td>
+ * <td><code> 0:00 PM, America/Los_Angeles</code></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>"yyyyy.MMMMM.dd GGG hh:mm aaa"</code></td>
+ * <td><code>01996.July.10 AD 12:08 PM</code></td>
+ * </tr>
+ * </table>
+ * 
+ * <h3>Additional Parsing Considerations</h3>
+ * <p>
+ * When parsing a date string using the abbreviated year pattern (
+ * <code>"yy"</code>), the parser must interpret the abbreviated year relative
+ * to some century. It does this by adjusting dates to be within 80 years before
+ * and 20 years after the time the parser instance is created. For example,
+ * using a pattern of <code>"MM/dd/yy"</code> and a <code>DateTimeFormat</code>
+ * object created on Jan 1, 1997, the string <code>"01/11/12"</code> would be
+ * interpreted as Jan 11, 2012 while the string <code>"05/04/64"</code> would be
+ * interpreted as May 4, 1964. During parsing, only strings consisting of
+ * exactly two digits, as defined by {@link java.lang.Character#isDigit(char)},
+ * will be parsed into the default century. If the year pattern does not have
+ * exactly two 'y' characters, the year is interpreted literally, regardless of
+ * the number of digits. For example, using the pattern
+ * <code>"MM/dd/yyyy"</code>, "01/11/12" parses to Jan 11, 12 A.D.
+ * </p>
+ * 
+ * <p>
+ * When numeric fields abut one another directly, with no intervening delimiter
+ * characters, they constitute a run of abutting numeric fields. Such runs are
+ * parsed specially. For example, the format "HHmmss" parses the input text
+ * "123456" to 12:34:56, parses the input text "12345" to 1:23:45, and fails to
+ * parse "1234". In other words, the leftmost field of the run is flexible,
+ * while the others keep a fixed width. If the parse fails anywhere in the run,
+ * then the leftmost field is shortened by one character, and the entire run is
+ * parsed again. This is repeated until either the parse succeeds or the
+ * leftmost field is one character in length. If the parse still fails at that
+ * point, the parse of the run fails.
+ * </p>
+ * 
+ * <p>
+ * In the current implementation, timezone parsing only supports
+ * <code>GMT:hhmm</code>, <code>GMT:+hhmm</code>, and <code>GMT:-hhmm</code>.
+ * </p>
+ * 
+ * <h3>Example</h3> {@example com.google.gwt.examples.DateTimeFormatExample}
+ * 
+ */
+public class DateTimeFormat {
+
+  /**
+   * Predefined date/time formats -- see {@link CustomDateTimeFormat} if you
+   * need some format that isn't supplied here.
+   */
+  public enum PredefinedFormat {
+    // TODO(jat): Javadoc to explain these formats
+
+    /**
+     * ISO 8601 date format, fixed across all locales.
+     * <p>Example: {@code 2008-10-03T10:29:40.046-04:00}
+     * <p>http://code.google.com/p/google-web-toolkit/issues/detail?id=3068
+     * <p>http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/date_and_time_format.htm
+     */
+    ISO_8601,
+
+    /**
+     * RFC 2822 date format, fixed across all locales.
+     * <p>Example: {@code Thu, 20 May 2010 17:54:50 -0700}
+     * <p>http://tools.ietf.org/html/rfc2822#section-3.3
+     */
+    RFC_2822,
+
+    DATE_FULL,
+    DATE_LONG,
+    DATE_MEDIUM,
+    DATE_SHORT,
+
+    TIME_FULL,
+    TIME_LONG,
+    TIME_MEDIUM,
+    TIME_SHORT,
+
+    DATE_TIME_FULL,
+    DATE_TIME_LONG,
+    DATE_TIME_MEDIUM,
+    DATE_TIME_SHORT,
+
+    DAY,
+    HOUR_MINUTE,
+    HOUR_MINUTE_SECOND,
+    HOUR24_MINUTE,
+    HOUR24_MINUTE_SECOND,
+    MINUTE_SECOND,
+    MONTH,
+    MONTH_ABBR,
+    MONTH_ABBR_DAY,
+    MONTH_DAY,
+    MONTH_NUM_DAY,
+    MONTH_WEEKDAY_DAY,
+    YEAR,
+    YEAR_MONTH,
+    YEAR_MONTH_ABBR,
+    YEAR_MONTH_ABBR_DAY,
+    YEAR_MONTH_DAY,
+    YEAR_MONTH_NUM,
+    YEAR_MONTH_NUM_DAY,
+    YEAR_MONTH_WEEKDAY_DAY,
+    YEAR_QUARTER,
+    YEAR_QUARTER_ABBR,
+  }
+
+  /**
+   * Class PatternPart holds a "compiled" pattern part.
+   */
+  private static class PatternPart {
+    public String text;
+    public int count; // 0 has a special meaning, it stands for literal
+    public boolean abutStart;
+
+    public PatternPart(String txt, int cnt) {
+      text = txt;
+      count = cnt;
+      abutStart = false;
+    }
+  }
+
+  protected static final String RFC2822_PATTERN = "EEE, d MMM yyyy HH:mm:ss Z";
+  protected static final String ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZ";
+
+  private static final int NUMBER_BASE = 10;
+  private static final int JS_START_YEAR = 1900;
+
+  private static final Map<String, DateTimeFormat> cache;
+
+  private static final int NUM_MILLISECONDS_IN_DAY = 24 * 60 * 60000;
+
+  private static final String PATTERN_CHARS = "GyMLdkHmsSEcDahKzZv";
+
+  // Note: M & L must be the first two characters
+  private static final String NUMERIC_FORMAT_CHARS = "MLydhHmsSDkK";
+
+  private static final String WHITE_SPACE = " \t\r\n";
+
+  private static final String GMT = "GMT";
+  private static final String UTC = "UTC";
+
+  private static final int MINUTES_PER_HOUR = 60;
+
+  static {
+    cache = new HashMap<String, DateTimeFormat>();
+  }
+
+  /**
+   * Get a DateTimeFormat instance for a predefined format.
+   * 
+   * <p>See {@link CustomDateTimeFormat} if you need a localized format that is
+   * not supported here.
+   * 
+   * @param predef {@link PredefinedFormat} describing desired format
+   * @return a DateTimeFormat instance for the specified format
+   */
+  public static DateTimeFormat getFormat(PredefinedFormat predef) {
+    if (usesFixedEnglishStrings(predef)) {
+      String pattern;
+      switch (predef) {
+        case RFC_2822:
+          pattern = RFC2822_PATTERN;
+          break;
+        case ISO_8601:
+          pattern = ISO8601_PATTERN;
+          break;
+        default:
+          throw new IllegalStateException("Unexpected predef type " + predef);
+      }
+      return getFormat(pattern, new DefaultDateTimeFormatInfo());
+    }
+    DateTimeFormatInfo dtfi = getDefaultDateTimeFormatInfo();
+    String pattern;
+    switch (predef) {
+      case DATE_FULL:
+        pattern = dtfi.dateFormatFull();
+        break;
+      case DATE_LONG:
+        pattern = dtfi.dateFormatLong();
+        break;
+      case DATE_MEDIUM:
+        pattern = dtfi.dateFormatMedium();
+        break;
+      case DATE_SHORT:
+        pattern = dtfi.dateFormatShort();
+        break;
+      case DATE_TIME_FULL:
+        pattern = dtfi.dateTimeFull(dtfi.timeFormatFull(),
+            dtfi.dateFormatFull());
+        break;
+      case DATE_TIME_LONG:
+        pattern = dtfi.dateTimeLong(dtfi.timeFormatLong(),
+            dtfi.dateFormatLong());
+        break;
+      case DATE_TIME_MEDIUM:
+        pattern = dtfi.dateTimeMedium(dtfi.timeFormatMedium(),
+            dtfi.dateFormatMedium());
+        break;
+      case DATE_TIME_SHORT:
+        pattern = dtfi.dateTimeShort(dtfi.timeFormatShort(),
+            dtfi.dateFormatShort());
+        break;
+      case DAY:
+        pattern = dtfi.formatDay();
+        break;
+      case HOUR24_MINUTE:
+        pattern = dtfi.formatHour24Minute();
+        break;
+      case HOUR24_MINUTE_SECOND:
+        pattern = dtfi.formatHour24MinuteSecond();
+        break;
+      case HOUR_MINUTE:
+        pattern = dtfi.formatHour12Minute();
+        break;
+      case HOUR_MINUTE_SECOND:
+        pattern = dtfi.formatHour12MinuteSecond();
+        break;
+      case MINUTE_SECOND:
+        pattern = dtfi.formatMinuteSecond();
+        break;
+      case MONTH:
+        pattern = dtfi.formatMonthFull();
+        break;
+      case MONTH_ABBR:
+        pattern = dtfi.formatMonthAbbrev();
+        break;
+      case MONTH_ABBR_DAY:
+        pattern = dtfi.formatMonthAbbrevDay();
+        break;
+      case MONTH_DAY:
+        pattern = dtfi.formatMonthFullDay();
+        break;
+      case MONTH_NUM_DAY:
+        pattern = dtfi.formatMonthNumDay();
+        break;
+      case MONTH_WEEKDAY_DAY:
+        pattern = dtfi.formatMonthFullWeekdayDay();
+        break;
+      case TIME_FULL:
+        pattern = dtfi.timeFormatFull();
+        break;
+      case TIME_LONG:
+        pattern = dtfi.timeFormatLong();
+        break;
+      case TIME_MEDIUM:
+        pattern = dtfi.timeFormatMedium();
+        break;
+      case TIME_SHORT:
+        pattern = dtfi.timeFormatShort();
+        break;
+      case YEAR:
+        pattern = dtfi.formatYear();
+        break;
+      case YEAR_MONTH:
+        pattern = dtfi.formatYearMonthFull();
+        break;
+      case YEAR_MONTH_ABBR:
+        pattern = dtfi.formatYearMonthAbbrev();
+        break;
+      case YEAR_MONTH_ABBR_DAY:
+        pattern = dtfi.formatYearMonthAbbrevDay();
+        break;
+      case YEAR_MONTH_DAY:
+        pattern = dtfi.formatYearMonthFullDay();
+        break;
+      case YEAR_MONTH_NUM:
+        pattern = dtfi.formatYearMonthNum();
+        break;
+      case YEAR_MONTH_NUM_DAY:
+        pattern = dtfi.formatYearMonthNumDay();
+        break;
+      case YEAR_MONTH_WEEKDAY_DAY:
+        pattern = dtfi.formatYearMonthWeekdayDay();
+        break;
+      case YEAR_QUARTER:
+        pattern = dtfi.formatYearQuarterFull();
+        break;
+      case YEAR_QUARTER_ABBR:
+        pattern = dtfi.formatYearQuarterShort();
+        break;
+      default:
+        throw new IllegalArgumentException("Unexpected predefined format "
+            + predef);
+    }
+    return getFormat(pattern, dtfi);
+  }
+
+  /**
+   * Returns a DateTimeFormat object using the specified pattern. If you need to
+   * format or parse repeatedly using the same pattern, it is highly recommended
+   * that you cache the returned <code>DateTimeFormat</code> object and reuse it
+   * rather than calling this method repeatedly.
+   * 
+   * <p>Note that the pattern supplied is used as-is -- for example, if you
+   * supply "MM/dd/yyyy" as the pattern, that is the order you will get the
+   * fields, even in locales where the order is different.  It is recommended to
+   * use {@link #getFormat(PredefinedFormat)} instead -- if you use this method,
+   * you are taking responsibility for localizing the patterns yourself.
+   * 
+   * @param pattern string to specify how the date should be formatted
+   * 
+   * @return a <code>DateTimeFormat</code> object that can be used for format or
+   *         parse date/time values matching the specified pattern
+   * 
+   * @throws IllegalArgumentException if the specified pattern could not be
+   *           parsed
+   */
+  public static DateTimeFormat getFormat(String pattern) {
+    return getFormat(pattern, getDefaultDateTimeFormatInfo());
+  }
+
+  /**
+   * Internal factory method that provides caching.
+   * 
+   * @param pattern
+   * @param dtfi
+   * @return DateTimeFormat instance
+   */
+  protected static DateTimeFormat getFormat(String pattern,
+      DateTimeFormatInfo dtfi) {
+    DateTimeFormatInfo defaultDtfi = getDefaultDateTimeFormatInfo();
+    DateTimeFormat dtf = null;
+    if (dtfi == defaultDtfi) {
+      dtf = cache.get(pattern);
+    }
+    if (dtf == null) {
+      dtf = new DateTimeFormat(pattern, dtfi);
+      if (dtfi == defaultDtfi) {
+        cache.put(pattern, dtf);
+      }
+    }
+    return dtf;
+  }
+
+  private static DateTimeFormatInfo getDefaultDateTimeFormatInfo() {
+    // MUSTFIX(jat): implement
+    return LocaleInfo.getCurrentLocale().getDateTimeFormatInfo();
+  }
+
+  /**
+   * Returns true if the predefined format is one that specifies always using
+   * English names/separators.
+   * <p>This should be a method on PredefinedFormat, but that would defeat the
+   * enum optimizations GWT is currently capable of.
+   * @param predef
+   * @return true if the specified format requires English names/separators
+   */
+  private static boolean usesFixedEnglishStrings(PredefinedFormat predef) {
+    switch (predef) {
+      case RFC_2822:
+        return true;
+      case ISO_8601:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  private final ArrayList<PatternPart> patternParts = new ArrayList<PatternPart>();
+
+  private final DateTimeFormatInfo dateTimeFormatInfo;
+
+  private final String pattern;
+
+  /**
+   * Constructs a format object using the specified pattern and the date time
+   * constants for the default locale.
+   * 
+   * @param pattern string pattern specification
+   */
+  protected DateTimeFormat(String pattern) {
+    this(pattern, getDefaultDateTimeFormatInfo());
+  }
+
+  /**
+   * Constructs a format object using the specified pattern and user-supplied
+   * date time constants.
+   * 
+   * @param pattern string pattern specification
+   * @param dtfi DateTimeFormatInfo instance to use
+   */
+  protected DateTimeFormat(String pattern, DateTimeFormatInfo dtfi) {
+    this.pattern = pattern;
+    this.dateTimeFormatInfo = dtfi;
+
+    /*
+     * Even though the pattern is only compiled for use in parsing and parsing
+     * is far less common than formatting, the pattern is still parsed eagerly
+     * here to fail fast in case the pattern itself is malformed.
+     */
+    parsePattern(pattern);
+  }
+
+  /**
+   * Format a date object.
+   * 
+   * @param date the date object being formatted
+   * 
+   * @return string representation for this date in desired format
+   */
+  public String format(Date date) {
+    return format(date, null);
+  }
+
+  /**
+   * Format a date object using specified time zone.
+   * 
+   * @param date the date object being formatted
+   * @param timeZone a TimeZone object that holds time zone information, or
+   *     {@code null} to use the default
+   * 
+   * @return string representation for this date in the format defined by this
+   *         object
+   */
+  @SuppressWarnings("deprecation")
+  public String format(Date date, TimeZone timeZone) {
+    // We use the Date class to calculate each date/time field in order
+    // to maximize performance and minimize code size.
+    // JavaScript only provides an API for rendering local time (in the os time
+    // zone). Here we want to render time in any timezone. So suppose we try to
+    // render the date (20:00 GMT0000, or 16:00 GMT-0400, or 12:00 GMT-0800) for
+    // time zone GMT-0400, and OS has time zone GMT-0800. By adding the
+    // difference between OS time zone (GMT-0800) and target time zone
+    // (GMT-0400) to "date", we end up with 16:00 GMT-0800. This date object
+    // has the same date/time fields (year, month, date, hour, minutes, etc)
+    // in GMT-0800 as original date in our target time zone (GMT-0400). We
+    // just need to take care of time zone display, but that's needed anyway.
+
+    // Things get a little bit more tricky when a daylight time transition
+    // happens. For example, if the OS timezone is America/Los_Angeles,
+    // it is just impossible to have a Date represent 2006/4/2 02:30, because
+    // 2:00 to 3:00 on that day does not exist in US Pacific time zone because
+    // of the daylight time switch.
+
+    // But we can use 2 separate date objects, one to represent 2006/4/2, one
+    // to represent 02:30. Of course, for the 2nd date object its date can be
+    // any other day in that year, except 2006/4/2. So we end up have 3 Date
+    // objects: one for resolving "Year, month, day", one for time within that
+    // day, and the original date object, which is needed for figuring out
+    // actual time zone offset.
+
+    if (timeZone == null) {
+      timeZone = createTimeZone(date.getTimezoneOffset());
+    }
+    int diff = (date.getTimezoneOffset() - timeZone.getOffset(date)) * 60000;
+    Date keepDate = new Date(date.getTime() + diff);
+    Date keepTime = keepDate;
+    if (keepDate.getTimezoneOffset() != date.getTimezoneOffset()) {
+      if (diff > 0) {
+        diff -= NUM_MILLISECONDS_IN_DAY;
+      } else {
+        diff += NUM_MILLISECONDS_IN_DAY;
+      }
+      keepTime = new Date(date.getTime() + diff);
+    }
+
+    StringBuffer toAppendTo = new StringBuffer(64);
+    int j, n = pattern.length();
+    for (int i = 0; i < n;) {
+      char ch = pattern.charAt(i);
+      if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
+        // ch is a date-time pattern character to be interpreted by subFormat().
+        // Count the number of times it is repeated.
+        for (j = i + 1; j < n && pattern.charAt(j) == ch; ++j) {
+        }
+        subFormat(toAppendTo, ch, j - i, date, keepDate, keepTime, timeZone);
+        i = j;
+      } else if (ch == '\'') {
+        // Handle an entire quoted string, included embedded
+        // doubled apostrophes (as in 'o''clock').
+
+        // i points after '.
+        ++i;
+
+        // If start with '', just add ' and continue.
+        if (i < n && pattern.charAt(i) == '\'') {
+          toAppendTo.append('\'');
+          ++i;
+          continue;
+        }
+
+        // Otherwise add the quoted string.
+        boolean trailQuote = false;
+        while (!trailQuote) {
+          // j points to next ' or EOS.
+          j = i;
+          while (j < n && pattern.charAt(j) != '\'') {
+            ++j;
+          }
+
+          if (j >= n) {
+            // Trailing ' (pathological).
+            throw new IllegalArgumentException("Missing trailing \'");
+          }
+
+          // Look ahead to detect '' within quotes.
+          if (j + 1 < n && pattern.charAt(j + 1) == '\'') {
+            ++j;
+          } else {
+            trailQuote = true;
+          }
+          toAppendTo.append(pattern.substring(i, j));
+          i = j + 1;
+        }
+      } else {
+        // Append unquoted literal characters.
+        toAppendTo.append(ch);
+        ++i;
+      }
+    }
+
+    return toAppendTo.toString();
+  }
+
+  /**
+   * Retrieve the pattern used in this DateTimeFormat object.
+   * 
+   * @return pattern string
+   */
+  public String getPattern() {
+    return pattern;
+  }
+
+  /**
+   * Parses text to produce a {@link Date} value. An
+   * {@link IllegalArgumentException} is thrown if either the text is empty or
+   * if the parse does not consume all characters of the text.
+   * 
+   * Dates are parsed leniently, so invalid dates will be wrapped around as
+   * needed. For example, February 30 will wrap to March 2.
+   * 
+   * @param text the string being parsed
+   * @return a parsed date/time value
+   * @throws IllegalArgumentException if the entire text could not be converted
+   *           into a number
+   */
+  public Date parse(String text) throws IllegalArgumentException {
+    return parse(text, false);
+  }
+
+  /**
+   * This method modifies a {@link Date} object to reflect the date that is
+   * parsed from an input string.
+   * 
+   * Dates are parsed leniently, so invalid dates will be wrapped around as
+   * needed. For example, February 30 will wrap to March 2.
+   * 
+   * @param text the string that need to be parsed
+   * @param start the character position in "text" where parsing should start
+   * @param date the date object that will hold parsed value
+   * 
+   * @return 0 if parsing failed, otherwise the number of characters advanced
+   */
+  public int parse(String text, int start, Date date) {
+    return parse(text, start, date, false);
+  }
+
+  /**
+   * Parses text to produce a {@link Date} value. An
+   * {@link IllegalArgumentException} is thrown if either the text is empty or
+   * if the parse does not consume all characters of the text.
+   * 
+   * Dates are parsed strictly, so invalid dates will result in an
+   * {@link IllegalArgumentException}.
+   * 
+   * @param text the string being parsed
+   * @return a parsed date/time value
+   * @throws IllegalArgumentException if the entire text could not be converted
+   *           into a number
+   */
+  public Date parseStrict(String text) throws IllegalArgumentException {
+    return parse(text, true);
+  }
+
+  /**
+   * This method modifies a {@link Date} object to reflect the date that is
+   * parsed from an input string.
+   * 
+   * Dates are parsed strictly, so invalid dates will return 0. For example,
+   * February 30 will return 0 because February only has 28 days.
+   * 
+   * @param text the string that need to be parsed
+   * @param start the character position in "text" where parsing should start
+   * @param date the date object that will hold parsed value
+   * 
+   * @return 0 if parsing failed, otherwise the number of characters advanced
+   */
+  public int parseStrict(String text, int start, Date date) {
+    return parse(text, start, date, true);
+  }
+
+  /**
+   * @param timezoneOffset
+   * @return {@link TimeZone} instance
+   */
+  protected TimeZone createTimeZone(int timezoneOffset) {
+    // MUSTFIX(jat): implement
+    return com.google.gwt.i18n.client.TimeZone.createTimeZone(timezoneOffset);
+  }
+
+  /**
+   * Method append current content in buf as pattern part if there is any, and
+   * clear buf for next part.
+   * 
+   * @param buf pattern part text specification
+   * @param count pattern part repeat count
+   */
+  private void addPart(StringBuffer buf, int count) {
+    if (buf.length() > 0) {
+      patternParts.add((new PatternPart(buf.toString(), count)));
+      buf.setLength(0);
+    }
+  }
+
+  /**
+   * Formats (0..11) Hours field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  @SuppressWarnings("deprecation")
+  private void format0To11Hours(StringBuffer buf, int count, Date date) {
+    int value = date.getHours() % 12;
+    zeroPaddingNumber(buf, value, count);
+  }
+
+  /**
+   * Formats (0..23) Hours field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  @SuppressWarnings("deprecation")
+  private void format0To23Hours(StringBuffer buf, int count, Date date) {
+    int value = date.getHours();
+    zeroPaddingNumber(buf, value, count);
+  }
+
+  /**
+   * Formats (1..12) Hours field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  @SuppressWarnings("deprecation")
+  private void format1To12Hours(StringBuffer buf, int count, Date date) {
+    int value = date.getHours() % 12;
+    if (value == 0) {
+      zeroPaddingNumber(buf, 12, count);
+    } else {
+      zeroPaddingNumber(buf, value, count);
+    }
+  }
+
+  /**
+   * Formats (1..24) Hours field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  @SuppressWarnings("deprecation")
+  private void format24Hours(StringBuffer buf, int count, Date date) {
+    int value = date.getHours();
+    if (value == 0) {
+      zeroPaddingNumber(buf, 24, count);
+    } else {
+      zeroPaddingNumber(buf, value, count);
+    }
+  }
+
+  /**
+   * Formats AM/PM field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param date hold the date object to be formatted
+   */
+  @SuppressWarnings("deprecation")
+  private void formatAmPm(StringBuffer buf, Date date) {
+    if (date.getHours() >= 12 && date.getHours() < 24) {
+      buf.append(dateTimeFormatInfo.ampms()[1]);
+    } else {
+      buf.append(dateTimeFormatInfo.ampms()[0]);
+    }
+  }
+
+  /**
+   * Formats Date field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatDate(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getDate();
+    zeroPaddingNumber(buf, value, count);
+  }
+
+  /**
+   * Formats Day of week field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatDayOfWeek(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getDay();
+    if (count == 5) {
+      buf.append(dateTimeFormatInfo.weekdaysNarrow()[value]);
+    } else if (count == 4) {
+      buf.append(dateTimeFormatInfo.weekdaysFull()[value]);
+    } else {
+      buf.append(dateTimeFormatInfo.weekdaysShort()[value]);
+    }
+  }
+
+  /**
+   * Formats Era field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatEra(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getYear() >= -JS_START_YEAR ? 1 : 0;
+    if (count >= 4) {
+      buf.append(dateTimeFormatInfo.erasFull()[value]);
+    } else {
+      buf.append(dateTimeFormatInfo.erasShort()[value]);
+    }
+  }
+
+  /**
+   * Formats Fractional seconds field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatFractionalSeconds(StringBuffer buf, int count, Date date) {
+    /*
+     * Fractional seconds should be left-justified, ie. zero must be padded from
+     * left. For example, if the value in milliseconds is 5, and the count is 3,
+     * the output will be "005".
+     * 
+     * Values with less than three digits are rounded to the desired number of
+     * places, but the rounded values are truncated at 9 or 99 in order to avoid
+     * changing the values of seconds.
+     */
+    long time = date.getTime();
+    int value;
+    if (time < 0) {
+      value = 1000 - (int) (-time % 1000);
+      if (value == 1000) {
+        value = 0;
+      }
+    } else {
+      value = (int) (time % 1000);
+    }
+    if (count == 1) {
+      value = Math.min((value + 50) / 100, 9); // Round to 100ms, clamp to 9
+      buf.append((char) ('0' + value));
+    } else if (count == 2) {
+      value = Math.min((value + 5) / 10, 99); // Round to 10ms, clamp to 99
+      zeroPaddingNumber(buf, value, 2);
+    } else {
+      zeroPaddingNumber(buf, value, 3);
+
+      if (count > 3) {
+        zeroPaddingNumber(buf, 0, count - 3);
+      }
+    }
+  }
+
+  /**
+   * Formats Minutes field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatMinutes(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getMinutes();
+    zeroPaddingNumber(buf, value, count);
+  }
+
+  /**
+   * Formats Month field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatMonth(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getMonth();
+    switch (count) {
+      case 5:
+        buf.append(dateTimeFormatInfo.monthsNarrow()[value]);
+        break;
+      case 4:
+        buf.append(dateTimeFormatInfo.monthsFull()[value]);
+        break;
+      case 3:
+        buf.append(dateTimeFormatInfo.monthsShort()[value]);
+        break;
+      default:
+        zeroPaddingNumber(buf, value + 1, count);
+    }
+  }
+
+  /**
+   * Formats Quarter field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatQuarter(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getMonth() / 3;
+    if (count < 4) {
+      buf.append(dateTimeFormatInfo.quartersShort()[value]);
+    } else {
+      buf.append(dateTimeFormatInfo.quartersFull()[value]);
+    }
+  }
+
+  /**
+   * Formats Seconds field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatSeconds(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getSeconds();
+    zeroPaddingNumber(buf, value, count);
+  }
+
+  /**
+   * Formats Standalone weekday field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatStandaloneDay(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getDay();
+    if (count == 5) {
+      buf.append(dateTimeFormatInfo.weekdaysNarrowStandalone()[value]);
+    } else if (count == 4) {
+      buf.append(dateTimeFormatInfo.weekdaysFullStandalone()[value]);
+    } else if (count == 3) {
+      buf.append(dateTimeFormatInfo.weekdaysShortStandalone()[value]);
+    } else {
+      zeroPaddingNumber(buf, value, 1);
+    }
+  }
+
+  /**
+   * Formats Standalone Month field according to pattern specified.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatStandaloneMonth(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getMonth();
+    if (count == 5) {
+      buf.append(dateTimeFormatInfo.monthsNarrowStandalone()[value]);
+    } else if (count == 4) {
+      buf.append(dateTimeFormatInfo.monthsFullStandalone()[value]);
+    } else if (count == 3) {
+      buf.append(dateTimeFormatInfo.monthsShortStandalone()[value]);
+    } else {
+      zeroPaddingNumber(buf, value + 1, count);
+    }
+  }
+
+  /**
+   * Formats Timezone field.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatTimeZone(StringBuffer buf, int count, Date date,
+      TimeZone timeZone) {
+    if (count < 4) {
+      buf.append(timeZone.getShortName(date));
+    } else {
+      buf.append(timeZone.getLongName(date));
+    }
+  }
+
+  /**
+   * Formats Timezone field following RFC.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date hold the date object to be formatted
+   */
+  private void formatTimeZoneRFC(StringBuffer buf, int count, Date date,
+      TimeZone timeZone) {
+    if (count < 3) {
+      buf.append(timeZone.getRFCTimeZoneString(date));
+    } else if (count == 3) {
+      buf.append(timeZone.getISOTimeZoneString(date));
+    } else {
+      buf.append(timeZone.getGMTString(date));
+    }
+  }
+
+  /**
+   * Formats Year field according to pattern specified. Javascript Date object
+   * seems incapable handling 1BC and year before. It can show you year 0 which
+   * does not exists. following we just keep consistent with javascript's
+   * toString method. But keep in mind those things should be unsupported.
+   * 
+   * @param buf where formatted string will be appended to
+   * @param count number of time pattern char repeats; this controls how a field
+   *     should be formatted; 2 is treated specially with the last two digits of
+   *     the year, while more than 2 digits are zero-padded
+   * @param date hold the date object to be formatted
+   */
+  private void formatYear(StringBuffer buf, int count, Date date) {
+    @SuppressWarnings("deprecation")
+    int value = date.getYear() + JS_START_YEAR;
+    if (value < 0) {
+      value = -value;
+    }
+    switch (count) {
+      case 1: // no padding
+        buf.append(value);
+        break;
+      case 2: // last 2 digits of year, zero-padded
+        zeroPaddingNumber(buf, value % 100, 2);
+        break;
+      default: // anything else is zero-padded
+        zeroPaddingNumber(buf, value, count);
+        break;
+    }
+  }
+
+  /**
+   * Method getNextCharCountInPattern calculate character repeat count in
+   * pattern.
+   * 
+   * @param pattern describe the format of date string that need to be parsed
+   * @param start the position of pattern character
+   * @return repeat count
+   */
+  private int getNextCharCountInPattern(String pattern, int start) {
+    char ch = pattern.charAt(start);
+    int next = start + 1;
+    while (next < pattern.length() && pattern.charAt(next) == ch) {
+      ++next;
+    }
+    return next - start;
+  }
+
+  /**
+   * Method identifies the start of a run of abutting numeric fields. Take the
+   * pattern "HHmmss" as an example. We will try to parse 2/2/2 characters of
+   * the input text, then if that fails, 1/2/2. We only adjust the width of the
+   * leftmost field; the others remain fixed. This allows "123456" => 12:34:56,
+   * but "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we try 4/2/2,
+   * 3/2/2, 2/2/2, and finally 1/2/2. The first field of connected numeric
+   * fields will be marked as abutStart, its width can be reduced to accomodate
+   * others.
+   */
+  private void identifyAbutStart() {
+    // 'abut' parts are continuous numeric parts. abutStart is the switch
+    // point from non-abut to abut.
+    boolean abut = false;
+
+    int len = patternParts.size();
+    for (int i = 0; i < len; i++) {
+      if (isNumeric(patternParts.get(i))) {
+        // If next part is not following abut sequence, and isNumeric.
+        if (!abut && i + 1 < len && isNumeric(patternParts.get(i + 1))) {
+          abut = true;
+          patternParts.get(i).abutStart = true;
+        }
+      } else {
+        abut = false;
+      }
+    }
+  }
+
+  /**
+   * Method checks if the pattern part is a numeric field.
+   * 
+   * @param part pattern part to be examined
+   * @return <code>true</code> if the pattern part is numberic field
+   */
+  private boolean isNumeric(PatternPart part) {
+    if (part.count <= 0) {
+      return false;
+    }
+    int i = NUMERIC_FORMAT_CHARS.indexOf(part.text.charAt(0));
+    // M & L (index 0 and 1) are only numeric if there are less than 3 chars
+    return (i > 1 || (i >= 0 && part.count < 3));
+  }
+
+  /**
+   * Method attempts to match the text at a given position against an array of
+   * strings. Since multiple strings in the array may match (for example, if the
+   * array contains "a", "ab", and "abc", all will match the input string
+   * "abcd") the longest match is returned.
+   * 
+   * @param text the time text being parsed
+   * @param start where to start parsing
+   * @param data the string array to parsed
+   * @param pos to receive where the match stopped
+   * @return the new start position if matching succeeded; a negative number
+   *         indicating matching failure
+   */
+  private int matchString(String text, int start, String[] data, int[] pos) {
+    int count = data.length;
+
+    // There may be multiple strings in the data[] array which begin with
+    // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
+    // We keep track of the longest match, and return that. Note that this
+    // unfortunately requires us to test all array elements.
+    int bestMatchLength = 0, bestMatch = -1;
+    String textInLowerCase = text.substring(start).toLowerCase();
+    for (int i = 0; i < count; ++i) {
+      int length = data[i].length();
+      // Always compare if we have no match yet; otherwise only compare
+      // against potentially better matches (longer strings).
+      if (length > bestMatchLength
+          && textInLowerCase.startsWith(data[i].toLowerCase())) {
+        bestMatch = i;
+        bestMatchLength = length;
+      }
+    }
+    if (bestMatch >= 0) {
+      pos[0] = start + bestMatchLength;
+    }
+    return bestMatch;
+  }
+
+  /**
+   * Parses text to produce a {@link Date} value. An
+   * {@link IllegalArgumentException} is thrown if either the text is empty or
+   * if the parse does not consume all characters of the text.
+   * 
+   * If using lenient parsing, certain invalid dates and times will be parsed.
+   * For example, February 32nd would be parsed as March 4th in lenient mode,
+   * but would throw an exception in non-lenient mode.
+   * 
+   * @param text the string being parsed
+   * @param strict true to be strict when parsing, false to be lenient
+   * @return a parsed date/time value
+   * @throws IllegalArgumentException if the entire text could not be converted
+   *           into a number
+   */
+  private Date parse(String text, boolean strict) {
+    Date curDate = new Date();
+    @SuppressWarnings("deprecation")
+    Date date = new Date(curDate.getYear(), curDate.getMonth(),
+        curDate.getDate());
+    int charsConsumed = parse(text, 0, date, strict);
+    if (charsConsumed == 0 || charsConsumed < text.length()) {
+      throw new IllegalArgumentException(text);
+    }
+    return date;
+  }
+
+  /**
+   * This method parses the input string and fills its value into a {@link Date}
+   * .
+   * 
+   * If using lenient parsing, certain invalid dates and times will be parsed.
+   * For example, February 32nd would be parsed as March 4th in lenient mode,
+   * but would return 0 in non-lenient mode.
+   * 
+   * @param text the string that need to be parsed
+   * @param start the character position in "text" where parsing should start
+   * @param date the date object that will hold parsed value
+   * @param strict true to be strict when parsing, false to be lenient
+   * 
+   * @return 0 if parsing failed, otherwise the number of characters advanced
+   */
+  private int parse(String text, int start, Date date, boolean strict) {
+    DateRecord cal = new DateRecord();
+    int[] parsePos = {start};
+
+    // For parsing abutting numeric fields. 'abutPat' is the
+    // offset into 'pattern' of the first of 2 or more abutting
+    // numeric fields. 'abutStart' is the offset into 'text'
+    // where parsing the fields begins. 'abutPass' starts off as 0
+    // and increments each time we try to parse the fields.
+    int abutPat = -1; // If >=0, we are in a run of abutting numeric fields.
+    int abutStart = 0;
+    int abutPass = 0;
+
+    for (int i = 0; i < patternParts.size(); ++i) {
+      PatternPart part = patternParts.get(i);
+
+      if (part.count > 0) {
+        if (abutPat < 0 && part.abutStart) {
+          abutPat = i;
+          abutStart = start;
+          abutPass = 0;
+        }
+
+        // Handle fields within a run of abutting numeric fields. Take
+        // the pattern "HHmmss" as an example. We will try to parse
+        // 2/2/2 characters of the input text, then if that fails,
+        // 1/2/2. We only adjust the width of the leftmost field; the
+        // others remain fixed. This allows "123456" => 12:34:56, but
+        // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
+        // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
+        if (abutPat >= 0) {
+          // If we are at the start of a run of abutting fields, then
+          // shorten this field in each pass. If we can't shorten
+          // this field any more, then the parse of this set of
+          // abutting numeric fields has failed.
+          int count = part.count;
+          if (i == abutPat) {
+            count -= abutPass++;
+            if (count == 0) {
+              return 0;
+            }
+          }
+
+          if (!subParse(text, parsePos, part, count, cal)) {
+            // If the parse fails anywhere in the run, back up to the
+            // start of the run and retry.
+            i = abutPat - 1;
+            parsePos[0] = abutStart;
+            continue;
+          }
+        } else {
+          // Handle non-numeric fields and non-abutting numeric fields.
+          abutPat = -1;
+          if (!subParse(text, parsePos, part, 0, cal)) {
+            return 0;
+          }
+        }
+      } else {
+        // Handle literal pattern characters. These are any
+        // quoted characters and non-alphabetic unquoted characters.
+        abutPat = -1;
+        // A run of white space in the pattern matches a run
+        // of white space in the input text.
+        if (part.text.charAt(0) == ' ') {
+          // Advance over run in input text.
+          int s = parsePos[0];
+          skipSpace(text, parsePos);
+
+          // Must see at least one white space char in input.
+          if (parsePos[0] > s) {
+            continue;
+          }
+        } else if (text.startsWith(part.text, parsePos[0])) {
+          parsePos[0] += part.text.length();
+          continue;
+        }
+
+        // We fall through to this point if the match fails.
+        return 0;
+      }
+    }
+
+    // Calculate the date from the parts
+    if (!cal.calcDate(date, strict)) {
+      return 0;
+    }
+
+    // Return progress.
+    return parsePos[0] - start;
+  }
+
+  /**
+   * Method parses a integer string and return integer value.
+   * 
+   * @param text string being parsed
+   * @param pos parse position
+   * 
+   * @return integer value
+   */
+  private int parseInt(String text, int[] pos) {
+    int ret = 0;
+    int ind = pos[0];
+    if (ind >= text.length()) {
+      return -1;
+    }
+    char ch = text.charAt(ind);
+    while (ch >= '0' && ch <= '9') {
+      ret = ret * 10 + (ch - '0');
+      ind++;
+      if (ind >= text.length()) {
+        break;
+      }
+      ch = text.charAt(ind);
+    }
+    if (ind > pos[0]) {
+      pos[0] = ind;
+    } else {
+      ret = -1;
+    }
+    return ret;
+  }
+
+  /**
+   * Method parses the input pattern string a generate a vector of pattern
+   * parts.
+   * 
+   * @param pattern describe the format of date string that need to be parsed
+   */
+  private void parsePattern(String pattern) {
+    StringBuffer buf = new StringBuffer(32);
+    boolean inQuote = false;
+
+    for (int i = 0; i < pattern.length(); i++) {
+      char ch = pattern.charAt(i);
+
+      // Handle space, add literal part (if exist), and add space part.
+      if (ch == ' ') {
+        addPart(buf, 0);
+        buf.append(' ');
+        addPart(buf, 0);
+        while (i + 1 < pattern.length() && pattern.charAt(i + 1) == ' ') {
+          i++;
+        }
+        continue;
+      }
+
+      // If inside quote, except two quote connected, just copy or exit.
+      if (inQuote) {
+        if (ch == '\'') {
+          if (i + 1 < pattern.length() && pattern.charAt(i + 1) == '\'') {
+            // Quote appeared twice continuously, interpret as one quote.
+            buf.append(ch);
+            ++i;
+          } else {
+            inQuote = false;
+          }
+        } else {
+          // Literal.
+          buf.append(ch);
+        }
+        continue;
+      }
+
+      // Outside quote now.
+      if (PATTERN_CHARS.indexOf(ch) > 0) {
+        addPart(buf, 0);
+        buf.append(ch);
+        int count = getNextCharCountInPattern(pattern, i);
+        addPart(buf, count);
+        i += count - 1;
+        continue;
+      }
+
+      // Two consecutive quotes is a quote literal, inside or outside of quotes.
+      if (ch == '\'') {
+        if (i + 1 < pattern.length() && pattern.charAt(i + 1) == '\'') {
+          buf.append('\'');
+          i++;
+        } else {
+          inQuote = true;
+        }
+      } else {
+        buf.append(ch);
+      }
+    }
+
+    addPart(buf, 0);
+
+    identifyAbutStart();
+  }
+
+  /**
+   * Method parses time zone offset.
+   * 
+   * @param text the time text to be parsed
+   * @param pos Parse position
+   * @param cal DateRecord object that holds parsed value
+   * 
+   * @return <code>true</code> if parsing successful, otherwise
+   *         <code>false</code>
+   */
+  private boolean parseTimeZoneOffset(String text, int[] pos, DateRecord cal) {
+    if (pos[0] >= text.length()) {
+      cal.setTzOffset(0);
+      return true;
+    }
+
+    int sign;
+    switch (text.charAt(pos[0])) {
+      case '+':
+        sign = 1;
+        break;
+      case '-':
+        sign = -1;
+        break;
+      default:
+        cal.setTzOffset(0);
+        return true;
+    }
+    ++(pos[0]);
+
+    // Look for hours:minutes or hhmm.
+    int st = pos[0];
+    int value = parseInt(text, pos);
+    if (value == 0 && pos[0] == st) {
+      return false;
+    }
+
+    int offset;
+    if (pos[0] < text.length() && text.charAt(pos[0]) == ':') {
+      // This is the hours:minutes case.
+      offset = value * MINUTES_PER_HOUR;
+      ++(pos[0]);
+      st = pos[0];
+      value = parseInt(text, pos);
+      if (value == 0 && pos[0] == st) {
+        return false;
+      }
+      offset += value;
+    } else {
+      // This is the hhmm case.
+      offset = value;
+      // Assume "-23".."+23" refers to hours.
+      if (offset < 24 && (pos[0] - st) <= 2) {
+        offset *= MINUTES_PER_HOUR;
+      } else {
+        offset = offset % 100 + offset / 100 * MINUTES_PER_HOUR;
+      }
+    }
+
+    offset *= sign;
+    cal.setTzOffset(-offset);
+    return true;
+  }
+
+  /**
+   * Method skips space in the string as pointed by pos.
+   * 
+   * @param text input string
+   * @param pos where skip start, and return back where skip stop
+   */
+  private void skipSpace(String text, int[] pos) {
+    while (pos[0] < text.length()
+        && WHITE_SPACE.indexOf(text.charAt(pos[0])) >= 0) {
+      ++(pos[0]);
+    }
+  }
+
+  /**
+   * Formats a single field according to pattern specified.
+   * 
+   * @param ch pattern character for this field
+   * @param count number of time pattern char repeats; this controls how a field
+   *          should be formatted
+   * @param date the date object to be formatted
+   * @param adjustedDate holds the time zone adjusted date fields
+   * @param adjustedTime holds the time zone adjusted time fields
+   * 
+   * @return <code>true</code> if pattern valid, otherwise <code>false</code>
+   * 
+   */
+  private boolean subFormat(StringBuffer buf, char ch, int count, Date date,
+      Date adjustedDate, Date adjustedTime, TimeZone timezone) {
+    switch (ch) {
+      case 'G':
+        formatEra(buf, count, adjustedDate);
+        break;
+      case 'y':
+        formatYear(buf, count, adjustedDate);
+        break;
+      case 'M':
+        formatMonth(buf, count, adjustedDate);
+        break;
+      case 'k':
+        format24Hours(buf, count, adjustedTime);
+        break;
+      case 'S':
+        formatFractionalSeconds(buf, count, adjustedTime);
+        break;
+      case 'E':
+        formatDayOfWeek(buf, count, adjustedDate);
+        break;
+      case 'a':
+        formatAmPm(buf, adjustedTime);
+        break;
+      case 'h':
+        format1To12Hours(buf, count, adjustedTime);
+        break;
+      case 'K':
+        format0To11Hours(buf, count, adjustedTime);
+        break;
+      case 'H':
+        format0To23Hours(buf, count, adjustedTime);
+        break;
+      case 'c':
+        formatStandaloneDay(buf, count, adjustedDate);
+        break;
+      case 'L':
+        formatStandaloneMonth(buf, count, adjustedDate);
+        break;
+      case 'Q':
+        formatQuarter(buf, count, adjustedDate);
+        break;
+      case 'd':
+        formatDate(buf, count, adjustedDate);
+        break;
+      case 'm':
+        formatMinutes(buf, count, adjustedTime);
+        break;
+      case 's':
+        formatSeconds(buf, count, adjustedTime);
+        break;
+      case 'z':
+        formatTimeZone(buf, count, date, timezone);
+        break;
+      case 'v':
+        buf.append(timezone.getID());
+        break;
+      case 'Z':
+        formatTimeZoneRFC(buf, count, date, timezone);
+        break;
+      default:
+        return false;
+    }
+    return true;
+  }
+
+  /**
+   * Converts one field of the input string into a numeric field value. Returns
+   * <code>false</code> if failed.
+   * 
+   * @param text the time text to be parsed
+   * @param pos Parse position
+   * @param part the pattern part for this field
+   * @param digitCount when greater than 0, numeric parsing must obey the count
+   * @param cal DateRecord object that will hold parsed value
+   * 
+   * @return <code>true</code> if parsing successful
+   */
+  @SuppressWarnings("fallthrough")
+  private boolean subParse(String text, int[] pos, PatternPart part,
+      int digitCount, DateRecord cal) {
+
+    skipSpace(text, pos);
+
+    int start = pos[0];
+    char ch = part.text.charAt(0);
+
+    // Parse integer value if it is a numeric field.
+    int value = -1; // initialize value to be -1,
+    if (isNumeric(part)) {
+      if (digitCount > 0) {
+        if ((start + digitCount) > text.length()) {
+          return false;
+        }
+        value = parseInt(text.substring(0, start + digitCount), pos);
+      } else {
+        value = parseInt(text, pos);
+      }
+    }
+
+    switch (ch) {
+      case 'G': // era
+        value = matchString(text, start, dateTimeFormatInfo.erasFull(), pos);
+        cal.setEra(value);
+        return true;
+      case 'M': // month
+        return subParseMonth(text, pos, cal, value, start);
+      case 'L': // standalone month
+        return subParseStandaloneMonth(text, pos, cal, value, start);
+      case 'E': // day of week
+        return subParseDayOfWeek(text, pos, start, cal);
+      case 'c': // standalone day of week
+        return subParseStandaloneDay(text, pos, start, cal);
+      case 'a': // AM/PM
+        value = matchString(text, start, dateTimeFormatInfo.ampms(), pos);
+        cal.setAmpm(value);
+        return true;
+      case 'y': // year
+        return subParseYear(text, pos, start, value, part, cal);
+      case 'd': // day of month
+        if (value <= 0) {
+          return false;
+        }
+        cal.setDayOfMonth(value);
+        return true;
+      case 'S': // fractional seconds
+        if (value < 0) {
+          return false;
+        }
+        return subParseFractionalSeconds(value, start, pos[0], cal);
+      case 'h': // hour (1..12)
+        if (value == 12) {
+          value = 0;
+        }
+        // fall through
+      case 'K': // hour (0..11)
+      case 'H': // hour (0..23)
+        if (value < 0) {
+          return false;
+        }
+        cal.setHours(value);
+        return true;
+      case 'k': // hour (1..24)
+        if (value < 0) {
+          return false;
+        }
+        cal.setHours(value);
+        return true;
+      case 'm': // minute
+        if (value < 0) {
+          return false;
+        }
+        cal.setMinutes(value);
+        return true;
+      case 's': // second
+        if (value < 0) {
+          return false;
+        }
+        cal.setSeconds(value);
+        return true;
+
+      case 'z': // time zone offset
+      case 'Z': // time zone RFC
+      case 'v': // time zone generic
+        return subParseTimeZoneInGMT(text, start, pos, cal);
+      default:
+        return false;
+    }
+  }
+
+  /**
+   * Method subParseDayOfWeek parses day of the week field.
+   * 
+   * @param text the time text to be parsed
+   * @param pos Parse position
+   * @param start from where parse start
+   * @param cal DateRecord object that holds parsed value
+   * 
+   * @return <code>true</code> if parsing successful, otherwise
+   *         <code>false</code>
+   */
+  private boolean subParseDayOfWeek(String text, int[] pos, int start,
+      DateRecord cal) {
+    int value;
+    // 'E' - DAY_OF_WEEK
+    // Want to be able to parse both short and long forms.
+    // Try count == 4 (DDDD) first:
+    value = matchString(text, start, dateTimeFormatInfo.weekdaysFull(), pos);
+    if (value < 0) {
+      value = matchString(text, start, dateTimeFormatInfo.weekdaysShort(), pos);
+    }
+    if (value < 0) {
+      return false;
+    }
+    cal.setDayOfWeek(value);
+    return true;
+  }
+
+  /**
+   * Method subParseFractionalSeconds parses fractional seconds field.
+   * 
+   * @param value parsed numberic value
+   * @param start
+   * @param end parse position
+   * @param cal DateRecord object that holds parsed value
+   * @return <code>true</code> if parsing successful, otherwise
+   *         <code>false</code>
+   */
+  private boolean subParseFractionalSeconds(int value, int start, int end,
+      DateRecord cal) {
+    // Fractional seconds left-justify.
+    int i = end - start;
+    if (i < 3) {
+      while (i < 3) {
+        value *= 10;
+        i++;
+      }
+    } else {
+      int a = 1;
+      while (i > 3) {
+        a *= 10;
+        i--;
+      }
+      value = (value + (a >> 1)) / a;
+    }
+    cal.setMilliseconds(value);
+    return true;
+  }
+
+  /**
+   * Parses Month field.
+   * 
+   * @param text the time text to be parsed
+   * @param pos Parse position
+   * @param cal DateRecord object that will hold parsed value
+   * @param value numeric value if this field is expressed using numberic
+   *          pattern
+   * @param start from where parse start
+   * 
+   * @return <code>true</code> if parsing successful
+   */
+  private boolean subParseMonth(String text, int[] pos, DateRecord cal,
+      int value, int start) {
+    // When month is symbols, i.e., MMM or MMMM, value will be -1.
+    if (value < 0) {
+      // Want to be able to parse both short and long forms.
+      // Try count == 4 first:
+      value = matchString(text, start, dateTimeFormatInfo.monthsFull(), pos);
+      if (value < 0) { // count == 4 failed, now try count == 3.
+        value = matchString(text, start, dateTimeFormatInfo.monthsShort(), pos);
+      }
+      if (value < 0) {
+        return false;
+      }
+      cal.setMonth(value);
+      return true;
+    } else if (value > 0) {
+      cal.setMonth(value - 1);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Parses standalone day of the week field.
+   * 
+   * @param text the time text to be parsed
+   * @param pos Parse position
+   * @param start from where parse start
+   * @param cal DateRecord object that holds parsed value
+   * 
+   * @return <code>true</code> if parsing successful, otherwise
+   *         <code>false</code>
+   */
+  private boolean subParseStandaloneDay(String text, int[] pos, int start,
+      DateRecord cal) {
+    int value;
+    // 'c' - DAY_OF_WEEK
+    // Want to be able to parse both short and long forms.
+    // Try count == 4 (cccc) first:
+    value = matchString(text, start, dateTimeFormatInfo.weekdaysFullStandalone(),
+        pos);
+    if (value < 0) {
+      value = matchString(text, start,
+          dateTimeFormatInfo.weekdaysShortStandalone(), pos);
+    }
+    if (value < 0) {
+      return false;
+    }
+    cal.setDayOfWeek(value);
+    return true;
+  }
+
+  /**
+   * Parses a standalone month field.
+   * 
+   * @param text the time text to be parsed
+   * @param pos Parse position
+   * @param cal DateRecord object that will hold parsed value
+   * @param value numeric value if this field is expressed using numberic
+   *          pattern
+   * @param start from where parse start
+   * 
+   * @return <code>true</code> if parsing successful
+   */
+  private boolean subParseStandaloneMonth(String text, int[] pos,
+      DateRecord cal, int value, int start) {
+    // When month is symbols, i.e., LLL or LLLL, value will be -1.
+    if (value < 0) {
+      // Want to be able to parse both short and long forms.
+      // Try count == 4 first:
+      value = matchString(text, start,
+          dateTimeFormatInfo.monthsFullStandalone(), pos);
+      if (value < 0) { // count == 4 failed, now try count == 3.
+        value = matchString(text, start,
+            dateTimeFormatInfo.monthsShortStandalone(), pos);
+      }
+      if (value < 0) {
+        return false;
+      }
+      cal.setMonth(value);
+      return true;
+    } else if (value > 0) {
+      cal.setMonth(value - 1);
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Method parses GMT type timezone.
+   * 
+   * @param text the time text to be parsed
+   * @param start from where parse start
+   * @param pos Parse position
+   * @param cal DateRecord object that holds parsed value
+   * 
+   * @return <code>true</code> if parsing successful, otherwise
+   *         <code>false</code>
+   */
+  private boolean subParseTimeZoneInGMT(String text, int start, int[] pos,
+      DateRecord cal) {
+    // First try to parse generic forms such as GMT-07:00. Do this first
+    // in case localized DateFormatZoneData contains the string "GMT"
+    // for a zone; in that case, we don't want to match the first three
+    // characters of GMT+/-HH:MM etc.
+
+    // For time zones that have no known names, look for strings
+    // of the form:
+    // GMT[+-]hours:minutes or
+    // GMT[+-]hhmm or
+    // GMT.
+    if (text.startsWith(GMT, start)) {
+      pos[0] = start + GMT.length();
+      return parseTimeZoneOffset(text, pos, cal);
+    }
+    // Likewise for UTC.
+    if (text.startsWith(UTC, start)) {
+      pos[0] = start + UTC.length();
+      return parseTimeZoneOffset(text, pos, cal);
+    }
+
+    // At this point, check for named time zones by looking through
+    // the locale data from the DateFormatZoneData strings.
+    // Want to be able to parse both short and long forms.
+    /*
+     * i = subParseZoneString(text, start, cal); if (i != 0) return i;
+     */
+
+    // As a last resort, look for numeric timezones of the form
+    // [+-]hhmm as specified by RFC 822. This code is actually
+    // a little more permissive than RFC 822. It will try to do
+    // its best with numbers that aren't strictly 4 digits long.
+    return parseTimeZoneOffset(text, pos, cal);
+  }
+
+  /**
+   * Method subParseYear parse year field. Year field is special because 1, two
+   * digit year need to be resolved. 2, we allow year to take a sign. 3, year
+   * field participate in abut processing. In my testing, negative year does not
+   * seem working due to JDK (or GWT implementation) limitation. It is not a
+   * big deal so we don't worry about it. But keep the logic here so that we
+   * might want to replace DateRecord with our a calendar class.
+   * 
+   * @param text the time text to be parsed
+   * @param pos parse position
+   * @param start where this field starts
+   * @param value integer value of year
+   * @param part the pattern part for this field
+   * @param cal DateRecord object that will hold parsed value
+   * 
+   * @return <code>true</code> if successful
+   */
+  private boolean subParseYear(String text, int[] pos, int start, int value,
+      PatternPart part, DateRecord cal) {
+    char ch = ' ';
+    if (value < 0) {
+      if (pos[0] >= text.length()) {
+        return false;
+      }
+      ch = text.charAt(pos[0]);
+      // Check if it is a sign.
+      if (ch != '+' && ch != '-') {
+        return false;
+      }
+      ++(pos[0]);
+      value = parseInt(text, pos);
+      if (value < 0) {
+        return false;
+      }
+      if (ch == '-') {
+        value = -value;
+      }
+    }
+
+    // no sign, only 2 digit was actually parsed, pattern say it has 2 digit.
+    if (ch == ' ' && (pos[0] - start) == 2 && part.count == 2) {
+      // Assume for example that the defaultCenturyStart is 6/18/1903.
+      // This means that two-digit years will be forced into the range
+      // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
+      // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
+      // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
+      // other fields specify a date before 6/18, or 1903 if they specify a
+      // date afterwards. As a result, 03 is an ambiguous year. All other
+      // two-digit years are unambiguous.
+      Date date = new Date();
+      @SuppressWarnings("deprecation")
+      int defaultCenturyStartYear = date.getYear() + 1900 - 80;
+      int ambiguousTwoDigitYear = defaultCenturyStartYear % 100;
+      cal.setAmbiguousYear(value == ambiguousTwoDigitYear);
+      value += (defaultCenturyStartYear / 100) * 100
+          + (value < ambiguousTwoDigitYear ? 100 : 0);
+    }
+    cal.setYear(value);
+    return true;
+  }
+
+  /**
+   * Formats a number with the specified minimum number of digits, using zero to
+   * fill the gap.
+   * 
+   * @param buf where zero padded string will be written to
+   * @param value the number value being formatted
+   * @param minWidth minimum width of the formatted string; zero will be padded
+   *          to reach this width
+   */
+  private void zeroPaddingNumber(StringBuffer buf, int value, int minWidth) {
+    int b = NUMBER_BASE;
+    for (int i = 0; i < minWidth - 1; i++) {
+      if (value < b) {
+        buf.append('0');
+      }
+      b *= NUMBER_BASE;
+    }
+    buf.append(value);
+  }
+}
diff --git a/user/src/com/google/gwt/i18n/shared/DateTimeFormatInfo.java b/user/src/com/google/gwt/i18n/shared/DateTimeFormatInfo.java
new file mode 100644
index 0000000..ba1f772
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/DateTimeFormatInfo.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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.
+ */
+package com.google.gwt.i18n.shared;
+
+/**
+ * Information required for formatting and parsing localized date/time values.
+ *
+ * <p>Implementors should subclass {@link DefaultDateTimeFormatInfo} so when
+ * methods are added they will get reasonable defaults and not break.  See the
+ * same class for example values returned by these methods.
+ */
+public interface DateTimeFormatInfo {
+
+  /**
+   * Returns array of strings containing abbreviations for Ante Meridiem and
+   * Post Meridiem.
+   */
+  String[] ampms();
+
+  /**
+   * Returns a safe default date format.
+   */
+  String dateFormat();
+
+  /**
+   * Returns a "full" date format.
+   */
+  String dateFormatFull();
+
+  /**
+   * Returns a "long" date format.
+   */
+  String dateFormatLong();
+
+  /**
+   * Returns a "medium" date format.
+   */
+  String dateFormatMedium();
+
+  /**
+   * Returns a "short" date format.
+   */
+  String dateFormatShort();
+
+  /**
+   * Returns a date/time format from a date format pattern and a time format
+   * pattern, using the locale default joining.
+   *
+   * @param timePattern the time pattern String
+   * @param datePattern the data pattern String
+   */
+  String dateTime(String timePattern, String datePattern);
+
+  /**
+   * Returns a date/time format from a date format pattern and a time format
+   * pattern, using "full" joining.
+   *
+   * @param timePattern the time pattern String
+   * @param datePattern the data pattern String
+   */
+  String dateTimeFull(String timePattern, String datePattern);
+
+  /**
+   * Returns a date/time format from a date format pattern and a time format
+   * pattern, using "full" joining.
+   *
+   * @param timePattern the time pattern String
+   * @param datePattern the data pattern String
+   */
+  String dateTimeLong(String timePattern, String datePattern);
+
+  /**
+   * Returns a date/time format from a date format pattern and a time format
+   * pattern, using "full" joining.
+   *
+   * @param timePattern the time pattern String
+   * @param datePattern the data pattern String
+   */
+  String dateTimeMedium(String timePattern, String datePattern);
+
+  /**
+   * Returns a date/time format from a date format pattern and a time format
+   * pattern, using "full" joining.
+   *
+   * @param timePattern the time pattern String
+   * @param datePattern the data pattern String
+   */
+  String dateTimeShort(String timePattern, String datePattern);
+
+  /**
+   * Returns an array of the full era names.
+   */
+  String[] erasFull();
+
+  /**
+   * Returns abbreviations of the era names.
+   */
+  String[] erasShort();
+
+  /**
+   * Returns the day which generally comes first in a weekly calendar view, as
+   * an index into the return value of {@link #weekdaysFull()}.
+   */
+  int firstDayOfTheWeek();
+
+  /**
+   * Returns localized format equivalent to the "d" skeleton pattern.
+   */
+  String formatDay();
+
+  /**
+   * Returns localized format equivalent to the "hm" skeleton pattern.
+   */
+  String formatHour12Minute();
+
+  /**
+   * Returns localized format equivalent to the "hms" skeleton pattern.
+   */
+  String formatHour12MinuteSecond();
+
+  /**
+   * Returns localized format equivalent to the "Hm" skeleton pattern.
+   */
+  String formatHour24Minute();
+
+  /**
+   * Returns localized format equivalent to the "Hms" skeleton pattern.
+   */
+  String formatHour24MinuteSecond();
+
+  /**
+   * Returns localized format equivalent to the "ms" skeleton pattern.
+   */
+  String formatMinuteSecond();
+
+  /**
+   * Returns localized format equivalent to the "MMM" skeleton pattern.
+   */
+  String formatMonthAbbrev();
+
+  /**
+   * Returns localized format equivalent to the "MMMd" skeleton pattern.
+   */
+  String formatMonthAbbrevDay();
+
+  /**
+   * Returns localized format equivalent to the "MMMM" skeleton pattern.
+   */
+  String formatMonthFull();
+
+  /**
+   * Returns localized format equivalent to the "MMMMd" skeleton pattern.
+   */
+  String formatMonthFullDay();
+
+  /**
+   * Returns localized format equivalent to the "MMMMEEEEd" skeleton pattern.
+   */
+  String formatMonthFullWeekdayDay();
+
+  /**
+   * Returns localized format equivalent to the "Md" skeleton pattern.
+   */
+  String formatMonthNumDay();
+
+  /**
+   * Returns localized format equivalent to the "y" skeleton pattern.
+   */
+  String formatYear();
+
+  /**
+   * Returns localized format equivalent to the "yMMM" skeleton pattern.
+   */
+  String formatYearMonthAbbrev();
+
+  /**
+   * Returns localized format equivalent to the "yMMMd" skeleton pattern.
+   */
+  String formatYearMonthAbbrevDay();
+
+  /**
+   * Returns localized format equivalent to the "yMMMM" skeleton pattern.
+   */
+  String formatYearMonthFull();
+
+  /**
+   * Returns localized format equivalent to the "yMMMMd" skeleton pattern.
+   */
+  String formatYearMonthFullDay();
+
+  /**
+   * Returns localized format equivalent to the "yM" skeleton pattern.
+   */
+  String formatYearMonthNum();
+
+  /**
+   * Returns localized format equivalent to the "yMd" skeleton pattern.
+   */
+  String formatYearMonthNumDay();
+
+  /**
+   * Returns localized format equivalent to the "yMMMEEEd" skeleton pattern.
+   */
+  String formatYearMonthWeekdayDay();
+
+  /**
+   * Returns localized format equivalent to the "yQQQQ" skeleton pattern.
+   */
+  String formatYearQuarterFull();
+
+  /**
+   * Returns localized format equivalent to the "yQ" skeleton pattern.
+   */
+  String formatYearQuarterShort();
+
+  /**
+   * Returns an array of full month names.
+   */
+  String[] monthsFull();
+
+  /**
+   * Returns an array of month names for use in a stand-alone context.
+   */
+  String[] monthsFullStandalone();
+
+  /**
+   * Returns an array of the shortest abbreviations for months, typically a
+   * single character and not guaranteed to be unique.
+   */
+  String[] monthsNarrow();
+
+  /**
+   * Returns an array of the shortest abbreviations for months suitable for use
+   * in a stand-alone context, typically a single character and not guaranteed
+   * to be unique.
+   */
+  String[] monthsNarrowStandalone();
+
+  /**
+   * Returns an array of month abbreviations.
+   */
+  String[] monthsShort();
+
+  /**
+   * Returns an array of month abbreviations, suitable for use in a stand-alone
+   * context.
+   */
+  String[] monthsShortStandalone();
+
+  /**
+   * Returns an array of full quarter names.
+   */
+  String[] quartersFull();
+
+  /**
+   * Returns an array of abbreviations for quarters.
+   */
+  String[] quartersShort();
+
+  /**
+   * Returns a safe default time format.
+   */
+  String timeFormat();
+
+  /**
+   * Returns a "full" time format.
+   */
+  String timeFormatFull();
+
+  /**
+   * Returns a "long" time format.
+   */
+  String timeFormatLong();
+
+  /**
+   * Returns a "medium" time format.
+   */
+  String timeFormatMedium();
+
+  /**
+   * Returns a "short" time format.
+   */
+  String timeFormatShort();
+
+  /**
+   * Returns an array of the full names of weekdays.
+   */
+  String[] weekdaysFull();
+
+  /**
+   * Returns an array of the full names of weekdays, suitable for use in a
+   * stand-alone context.
+   */
+  String[] weekdaysFullStandalone();
+
+  /**
+   * Returns an array of the shortest abbreviations for weekdays, typically a
+   * single character and not guaranteed to be unique.
+   */
+  String[] weekdaysNarrow();
+
+  /**
+   * Returns an array of the shortest abbreviations for weekdays suitable for
+   * use in a stand-alone context, typically a single character and not
+   * guaranteed to be unique.
+   */
+  String[] weekdaysNarrowStandalone();
+
+  /**
+   * Returns an array of abbreviations for weekdays.
+   */
+  String[] weekdaysShort();
+
+  /**
+   * Returns an array of abbreviations for weekdays, suitable for use in a
+   * stand-alone context.
+   */
+  String[] weekdaysShortStandalone();
+
+  /**
+   * Returns the day which ends the weekend, as an index into the return value
+   * of {@link #weekdaysFull()}.
+   *
+   * <p>Note that this value may be numerically less than
+   * {@link #weekendStart()} - for example, {@link #weekendStart()} of 6 and
+   * {@link #weekendEnd()} of 0 means Saturday and Sunday are the weekend.
+   */
+  int weekendEnd();
+
+  /**
+   * Returns the day which starts the weekend, as an index into the return value
+   * of {@link #weekdaysFull()}.
+   */
+  int weekendStart();
+}
diff --git a/user/src/com/google/gwt/i18n/shared/DefaultDateTimeFormatInfo.java b/user/src/com/google/gwt/i18n/shared/DefaultDateTimeFormatInfo.java
new file mode 100644
index 0000000..390590c
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/DefaultDateTimeFormatInfo.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * 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.
+ */
+package com.google.gwt.i18n.shared;
+
+// DO NOT EDIT - GENERATED FROM CLDR AND ICU DATA
+
+/**
+ * Default implementation of DateTimeFormatInfo interface, using values from
+ * the CLDR root locale.
+ * <p>
+ * Users who need to create their own DateTimeFormatInfo implementation are
+ * encouraged to extend this class so their implementation won't break when   
+ * new methods are added.
+ */
+public class DefaultDateTimeFormatInfo implements DateTimeFormatInfo {
+
+  public String[] ampms() {
+    return new String[] {
+        "AM",
+        "PM"
+    };
+  }
+
+  public String dateFormat() {
+    return dateFormatMedium();
+  }
+
+  public String dateFormatFull() {
+    return "EEEE, y MMMM dd";
+  }
+
+  public String dateFormatLong() {
+    return "y MMMM d";
+  }
+
+  public String dateFormatMedium() {
+    return "y MMM d";
+  }
+
+  public String dateFormatShort() {
+    return "yyyy-MM-dd";
+  }
+
+  public String dateTime(String timePattern, String datePattern) {
+    return dateTimeMedium(timePattern, datePattern);
+  }
+
+  public String dateTimeFull(String timePattern, String datePattern) {
+    return new java.lang.StringBuffer().append(datePattern).append(" ").append(timePattern).toString();
+  }
+
+  public String dateTimeLong(String timePattern, String datePattern) {
+    return new java.lang.StringBuffer().append(datePattern).append(" ").append(timePattern).toString();
+  }
+
+  public String dateTimeMedium(String timePattern, String datePattern) {
+    return new java.lang.StringBuffer().append(datePattern).append(" ").append(timePattern).toString();
+  }
+
+  public String dateTimeShort(String timePattern, String datePattern) {
+    return new java.lang.StringBuffer().append(datePattern).append(" ").append(timePattern).toString();
+  }
+
+  public String[] erasFull() {
+    return new String[] {
+        "Before Christ",
+        "Anno Domini"
+    };
+  }
+
+  public String[] erasShort() {
+    return new String[] {
+        "BC",
+        "AD"
+    };
+  }
+
+  public int firstDayOfTheWeek() {
+    return 1;
+  }
+
+  public String formatDay() {
+    return "d";
+  }
+
+  public String formatHour12Minute() {
+    return "h:mm a";
+  }
+
+  public String formatHour12MinuteSecond() {
+    return "h:mm:ss a";
+  }
+
+  public String formatHour24Minute() {
+    return "HH:mm";
+  }
+
+  public String formatHour24MinuteSecond() {
+    return "HH:mm:ss";
+  }
+
+  public String formatMinuteSecond() {
+    return "mm:ss";
+  }
+
+  public String formatMonthAbbrev() {
+    return "LLL";
+  }
+
+  public String formatMonthAbbrevDay() {
+    return "MMM d";
+  }
+
+  public String formatMonthFull() {
+    return "LLLL";
+  }
+
+  public String formatMonthFullDay() {
+    return "MMMM d";
+  }
+
+  public String formatMonthFullWeekdayDay() {
+    return "EEEE MMMM d";
+  }
+
+  public String formatMonthNumDay() {
+    return "M-d";
+  }
+
+  public String formatYear() {
+    return "y";
+  }
+
+  public String formatYearMonthAbbrev() {
+    return "y MMM";
+  }
+
+  public String formatYearMonthAbbrevDay() {
+    return "y MMM d";
+  }
+
+  public String formatYearMonthFull() {
+    return "y MMMM";
+  }
+
+  public String formatYearMonthFullDay() {
+    return "y MMMM d";
+  }
+
+  public String formatYearMonthNum() {
+    return "y-M";
+  }
+
+  public String formatYearMonthNumDay() {
+    return "y-M-d";
+  }
+
+  public String formatYearMonthWeekdayDay() {
+    return "EEE, y MMM d";
+  }
+
+  public String formatYearQuarterFull() {
+    return "y QQQQ";
+  }
+
+  public String formatYearQuarterShort() {
+    return "y Q";
+  }
+
+  public String[] monthsFull() {
+    return new String[] {
+        "January",
+        "February",
+        "March",
+        "April",
+        "May",
+        "June",
+        "July",
+        "August",
+        "September",
+        "October",
+        "November",
+        "December"
+    };
+  }
+
+  public String[] monthsFullStandalone() {
+    return monthsFull();
+  }
+
+  public String[] monthsNarrow() {
+    return new String[] {
+        "J",
+        "F",
+        "M",
+        "A",
+        "M",
+        "J",
+        "J",
+        "A",
+        "S",
+        "O",
+        "N",
+        "D"
+    };
+  }
+
+  public String[] monthsNarrowStandalone() {
+    return monthsNarrow();
+  }
+
+  public String[] monthsShort() {
+    return new String[] {
+        "Jan",
+        "Feb",
+        "Mar",
+        "Apr",
+        "May",
+        "Jun",
+        "Jul",
+        "Aug",
+        "Sep",
+        "Oct",
+        "Nov",
+        "Dec"
+    };
+  }
+
+  public String[] monthsShortStandalone() {
+    return monthsShort();
+  }
+
+  public String[] quartersFull() {
+    return new String[] {
+        "1st quarter",
+        "2nd quarter",
+        "3rd quarter",
+        "4th quarter"
+    };
+  }
+
+  public String[] quartersShort() {
+    return new String[] {
+        "Q1",
+        "Q2",
+        "Q3",
+        "Q4"
+    };
+  }
+
+  public String timeFormat() {
+    return timeFormatMedium();
+  }
+
+  public String timeFormatFull() {
+    return "HH:mm:ss zzzz";
+  }
+
+  public String timeFormatLong() {
+    return "HH:mm:ss z";
+  }
+
+  public String timeFormatMedium() {
+    return "HH:mm:ss";
+  }
+
+  public String timeFormatShort() {
+    return "HH:mm";
+  }
+
+  public String[] weekdaysFull() {
+    return new String[] {
+        "Sunday",
+        "Monday",
+        "Tuesday",
+        "Wednesday",
+        "Thursday",
+        "Friday",
+        "Saturday"
+    };
+  }
+
+  public String[] weekdaysFullStandalone() {
+    return weekdaysFull();
+  }
+
+  public String[] weekdaysNarrow() {
+    return new String[] {
+        "S",
+        "M",
+        "T",
+        "W",
+        "T",
+        "F",
+        "S"
+    };
+  }
+
+  public String[] weekdaysNarrowStandalone() {
+    return weekdaysNarrow();
+  }
+
+  public String[] weekdaysShort() {
+    return new String[] {
+        "Sun",
+        "Mon",
+        "Tue",
+        "Wed",
+        "Thu",
+        "Fri",
+        "Sat"
+    };
+  }
+
+  public String[] weekdaysShortStandalone() {
+    return weekdaysShort();
+  }
+
+  public int weekendEnd() {
+    return 0;
+  }
+
+  public int weekendStart() {
+    return 6;
+  }
+}
diff --git a/user/src/com/google/gwt/i18n/shared/Localizable.java b/user/src/com/google/gwt/i18n/shared/Localizable.java
new file mode 100644
index 0000000..84ae693
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/Localizable.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * 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.
+ */
+package com.google.gwt.i18n.shared;
+
+/**
+ * A tag interface that serves as the root of a family of types used in static
+ * internationalization. Using <code>GWT.create(<i>class</i>)</code> to
+ * instantiate a type that directly extends or implements
+ * <code>Localizable</code> invites locale-sensitive type substitution.
+ * 
+ * <h3>Locale-sensitive Type Substitution</h3>
+ * If a type <code>Type</code> directly extends or implements
+ * <code>Localizable</code> (as opposed to
+ * {@link com.google.gwt.i18n.client.Constants} or
+ * {@link com.google.gwt.i18n.client.Messages}) and the following code is used
+ * to create an object from <code>Type</code> as follows:
+ * 
+ * <pre class="code">Type localized = (Type)GWT.create(Type.class);</pre>
+ * 
+ * then <code>localized</code> will be assigned an instance of a localized
+ * subclass, selected based on the value of the <code>locale</code> client
+ * property. The choice of subclass is determined by the following naming
+ * pattern:
+ * 
+ * <table>
+ * 
+ * <tr>
+ * <th align='left'>If <code>locale</code> is...&#160;&#160;&#160;&#160;</th>
+ * <th align='left'>The substitute class for <code>Type</code> is...</th>
+ * </tr>
+ * 
+ * <tr>
+ * <td><i>unspecified</i></td>
+ * <td><code>Type</code> itself, or <code>Type_</code> if <code>Type</code>
+ * is an interface</td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>x</code></td>
+ * <td>Class <code>Type_x</code> if it exists, otherwise treated as if
+ * <code>locale</code> were <i>unspecified</i></td>
+ * </tr>
+ * 
+ * <tr>
+ * <td><code>x_Y</code></td>
+ * <td>Class <code>Type_x_Y</code> if it exists, otherwise treated as if
+ * <code>locale</code> were <code>x</code></td>
+ * </tr>
+ * 
+ * </table>
+ * 
+ * where in the table above <code>x</code> is a <a
+ * href="http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt">ISO language
+ * code</a> and <code>Y</code> is a two-letter <a
+ * href="http://userpage.chemie.fu-berlin.de/diverse/doc/ISO_3166.html">ISO
+ * country code</a>.
+ * 
+ * <h3>Specifying Locale</h3>
+ * The locale of a module is specified using the <code>locale</code> client
+ * property, which can be specified using either a meta tag or as part of the
+ * query string in the host page's URL. If both are specified, the query string
+ * takes precedence.
+ * 
+ * <p>
+ * To specify the <code>locale</code> client property using a meta tag in the
+ * host HTML, use <code>gwt:property</code> as follows:
+ * 
+ * <pre>&lt;meta name="gwt:property" content="locale=x_Y"&gt;</pre>
+ * 
+ * For example, the following host HTML page sets the locale to "ja_JP":
+ * 
+ * {@gwt.include com/google/gwt/examples/i18n/ColorNameLookupExample_ja_JP.html}
+ * </p>
+ * 
+ * <p>
+ * To specify the <code>locale</code> client property using a query string,
+ * specify a value for the name <code>locale</code>. For example,
+ * 
+ * <pre>http://www.example.org/myapp.html?locale=fr_CA</pre>
+ * 
+ * </p>
+ * 
+ * <h3>For More Information</h3>
+ * See the GWT Developer Guide for an introduction to internationalization.
+ * 
+ * @see com.google.gwt.i18n.client.Constants
+ * @see com.google.gwt.i18n.client.ConstantsWithLookup
+ * @see com.google.gwt.i18n.client.Messages
+ * @see com.google.gwt.i18n.client.Dictionary
+ */
+public interface Localizable {
+}
diff --git a/user/src/com/google/gwt/i18n/shared/TimeZone.java b/user/src/com/google/gwt/i18n/shared/TimeZone.java
new file mode 100644
index 0000000..a188e09
--- /dev/null
+++ b/user/src/com/google/gwt/i18n/shared/TimeZone.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * 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.
+ */
+package com.google.gwt.i18n.shared;
+
+import java.util.Date;
+
+/**
+ * Abstracts a GWT timezone.
+ */
+public interface TimeZone {
+
+  /**
+   * Returns the daylight savings time adjustment, in minutes, for the given
+   * date. If daylight savings time is in effect on the given date, the number
+   * will be positive, otherwise 0.
+   *
+   * @param date the date to check
+   * @return offset amount
+   */
+  int getDaylightAdjustment(Date date);
+
+  /**
+   * Returns the GMT representation of this time zone object.
+   *
+   * @param date The date from which the time information should be extracted
+   * @return A GMT representation of the time given by the date
+   */
+  String getGMTString(Date date);
+
+  /**
+   * Returns time zone id for this time zone. For time zone objects that have
+   * been instantiated from a time zone offset, the POSIX time zone id will be
+   * returned.
+   *
+   * @return time zone id
+   */
+  String getID();
+
+  /**
+   * To get ISO-style (+00:00) representation of the time zone for given date.
+   *
+   * @param date The date for which time to retrieve RFC time zone string
+   * @return ISO-style time zone string
+   */
+  String getISOTimeZoneString(Date date);
+
+  /**
+   * Returns the long version of the time zone name for the given date; the
+   * result of this method will be different if daylight savings time is in
+   * effect.
+   *
+   * @param date The date for which the long time zone name is returned
+   * @return long time zone name
+   */
+  String getLongName(Date date);
+
+  /**
+   * Returns the RFC representation of the time zone name for the given date.
+   * To be consistent with JDK/Javascript API, west of Greenwich will be
+   * positive.
+   *
+   *  @param date The date for which time to retrieve time zone offset
+   *  @return time zone offset in minutes
+   */
+  int getOffset(Date date);
+
+  /**
+   * To get RFC representation of certain time zone name for given date.
+   * @param date The date for which time to retrieve RFC time zone string
+   * @return RFC time zone string
+   */
+  String getRFCTimeZoneString(Date date);
+
+  /**
+   * Returns the short time zone name for a given date.
+   *
+   * @param date The date for which time to retrieve short time zone
+   * @return short time zone name
+   */
+  String getShortName(Date date);
+
+  /**
+   * Returns the standard time zone offset, in minutes.
+   */
+  int getStandardOffset();
+
+  /**
+   * Check whether the given date and time falls within a daylight savings time
+   * period.
+   *
+   * @param date and time to check
+   * @return true if daylight savings time is in effect
+   */
+  boolean isDaylightTime(Date date);
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/i18n/client/impl/DateRecord.java b/user/src/com/google/gwt/i18n/shared/impl/DateRecord.java
similarity index 99%
rename from user/src/com/google/gwt/i18n/client/impl/DateRecord.java
rename to user/src/com/google/gwt/i18n/shared/impl/DateRecord.java
index ecea2ca..a403cd7 100644
--- a/user/src/com/google/gwt/i18n/client/impl/DateRecord.java
+++ b/user/src/com/google/gwt/i18n/shared/impl/DateRecord.java
@@ -13,8 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-
-package com.google.gwt.i18n.client.impl;
+package com.google.gwt.i18n.shared.impl;
 
 import java.util.Date;
 
diff --git a/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java b/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java
index 78af84d..dcb7c13 100644
--- a/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java
+++ b/user/src/com/google/gwt/text/client/DateTimeFormatRenderer.java
@@ -15,9 +15,9 @@
  */
 package com.google.gwt.text.client;
 
-import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.i18n.client.TimeZone;
-import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
+import com.google.gwt.i18n.shared.DateTimeFormat;
+import com.google.gwt.i18n.shared.DateTimeFormat.PredefinedFormat;
+import com.google.gwt.i18n.shared.TimeZone;
 import com.google.gwt.text.shared.AbstractRenderer;
 
 import java.util.Date;
@@ -56,7 +56,6 @@
     if (object == null) {
       return "";
     }
-    return timeZone == null ? format.format(object) : format.format(object,
-        timeZone);
+    return format.format(object, timeZone);
   }
 }
diff --git a/user/src/com/google/gwt/user/client/ui/DateLabel.java b/user/src/com/google/gwt/user/client/ui/DateLabel.java
index 1af0b05..c367d0a 100644
--- a/user/src/com/google/gwt/user/client/ui/DateLabel.java
+++ b/user/src/com/google/gwt/user/client/ui/DateLabel.java
@@ -15,8 +15,8 @@
  */
 package com.google.gwt.user.client.ui;
 
-import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.i18n.client.TimeZone;
+import com.google.gwt.i18n.shared.DateTimeFormat;
+import com.google.gwt.i18n.shared.TimeZone;
 import com.google.gwt.text.client.DateTimeFormatRenderer;
 
 import java.util.Date;
@@ -34,7 +34,7 @@
  * The format can be given with one of these attributes:
  * <dl>
  * <dt>format</dt><dd>a reference to a {@link DateTimeFormat} instance.</dd>
- * <dt>predefinedFormat</dt><dd>a {@link com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat DateTimeFormat.PredefinedFormat}.</dd>
+ * <dt>predefinedFormat</dt><dd>a {@link com.google.gwt.i18n.shared.DateTimeFormat.PredefinedFormat DateTimeFormat.PredefinedFormat}.</dd>
  * <dt>customFormat</dt><dd>a date time pattern that can be passed to {@link DateTimeFormat#getFormat(String)}.</dd>
  * </dl>
  * <p>
diff --git a/user/test/com/google/gwt/i18n/client/DateTimeFormat_de_Test.java b/user/test/com/google/gwt/i18n/client/DateTimeFormat_de_Test.java
index abe3c3e..acafbe1 100644
--- a/user/test/com/google/gwt/i18n/client/DateTimeFormat_de_Test.java
+++ b/user/test/com/google/gwt/i18n/client/DateTimeFormat_de_Test.java
@@ -16,6 +16,7 @@
 package com.google.gwt.i18n.client;
 
 import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.shared.DateTimeFormatTestBase;
 
 import java.util.Date;
 
diff --git a/user/test/com/google/gwt/i18n/client/DateTimeFormat_en_Test.java b/user/test/com/google/gwt/i18n/client/DateTimeFormat_en_Test.java
index 7e92b56..7944a63 100644
--- a/user/test/com/google/gwt/i18n/client/DateTimeFormat_en_Test.java
+++ b/user/test/com/google/gwt/i18n/client/DateTimeFormat_en_Test.java
@@ -18,12 +18,13 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.i18n.client.constants.TimeZoneConstants;
 import com.google.gwt.i18n.client.impl.cldr.DateTimeFormatInfoImpl_de;
+import com.google.gwt.i18n.shared.DateTimeFormatTestBase;
 
 import java.util.Date;
 
 /**
- * Tests formatting functionality in {@link DateTimeFormat} for the English
- * language.
+ * Tests formatting functionality in {@link com.google.gwt.i18n.shared.DateTimeFormat} for the
+ * English language.
  */
 @SuppressWarnings("deprecation")
 public class DateTimeFormat_en_Test extends DateTimeFormatTestBase {
diff --git a/user/test/com/google/gwt/i18n/server/MessageInterfaceTestBase.java b/user/test/com/google/gwt/i18n/server/MessageInterfaceTestBase.java
index 3a6d989..23e9d75 100644
--- a/user/test/com/google/gwt/i18n/server/MessageInterfaceTestBase.java
+++ b/user/test/com/google/gwt/i18n/server/MessageInterfaceTestBase.java
@@ -30,6 +30,7 @@
 
 import junit.framework.TestCase;
 
+import java.lang.annotation.Documented;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
@@ -822,7 +823,7 @@
     GenerateKeys generate = msgIntf.getAnnotation(GenerateKeys.class);
     assertNotNull(generate);
     assertEquals(MD5KeyGenerator.class.getName(), generate.value());
-    assertNull(msgIntf.getAnnotation(Deprecated.class));
+    assertNull(msgIntf.getAnnotation(Documented.class));
   }
 
   public void testGetClassName() {
@@ -870,7 +871,7 @@
   public void testIsAnnotationPresent() {
     assertTrue(msgIntf.isAnnotationPresent(DefaultLocale.class));
     assertTrue(msgIntf.isAnnotationPresent(GenerateKeys.class));
-    assertFalse(msgIntf.isAnnotationPresent(Deprecated.class));
+    assertFalse(msgIntf.isAnnotationPresent(Documented.class));
   }
 
   @Override
diff --git a/user/test/com/google/gwt/i18n/client/DateTimeFormatTestBase.java b/user/test/com/google/gwt/i18n/shared/DateTimeFormatTestBase.java
similarity index 100%
rename from user/test/com/google/gwt/i18n/client/DateTimeFormatTestBase.java
rename to user/test/com/google/gwt/i18n/shared/DateTimeFormatTestBase.java
diff --git a/user/test/com/google/gwt/text/client/DateTimeFormatRendererTest.java b/user/test/com/google/gwt/text/client/DateTimeFormatRendererTest.java
index d9844fe..2cdb2fe 100644
--- a/user/test/com/google/gwt/text/client/DateTimeFormatRendererTest.java
+++ b/user/test/com/google/gwt/text/client/DateTimeFormatRendererTest.java
@@ -15,8 +15,8 @@
  */
 package com.google.gwt.text.client;
 
-import com.google.gwt.i18n.client.DateTimeFormat;
-import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
+import com.google.gwt.i18n.shared.DateTimeFormat;
+import com.google.gwt.i18n.shared.DateTimeFormat.PredefinedFormat;
 import com.google.gwt.i18n.client.TimeZone;
 import com.google.gwt.junit.client.GWTTestCase;
 
diff --git a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
index c31ac24..9ec9d8c 100644
--- a/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
+++ b/user/test/com/google/gwt/uibinder/test/UiJavaResources.java
@@ -113,8 +113,8 @@
     public CharSequence getContent() {
       StringBuffer code = new StringBuffer();
       code.append("package com.google.gwt.user.client.ui;\n");
-      code.append("import com.google.gwt.i18n.client.DateTimeFormat;\n");
-      code.append("import com.google.gwt.i18n.client.TimeZone;\n");
+      code.append("import com.google.gwt.i18n.shared.DateTimeFormat;\n");
+      code.append("import com.google.gwt.i18n.shared.TimeZone;\n");
       code.append("public class DateLabel extends ValueLabel {\n");
       code.append("  public DateLabel() { super(null); } ");
       code.append("  public DateLabel(DateTimeFormat format) { super(null); } ");
@@ -124,12 +124,32 @@
     }
   };
   public static final MockJavaResource DATE_TIME_FORMAT = new MockJavaResource(
+      "com.google.gwt.i18n.shared.DateTimeFormat") {
+    @Override
+    public CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.i18n.shared;\n");
+      code.append("public class DateTimeFormat {\n");
+      code.append("  public static enum PredefinedFormat {\n");
+      PredefinedFormat[] values = PredefinedFormat.values();
+      for (int i = 0; i < values.length; i++) {
+        code.append("    ").append(values[i].name());
+        if (i < values.length - 1) {
+          code.append(",\n");
+        }
+      }
+      code.append("  }\n");
+      code.append("}\n");
+      return code;
+    }
+  };
+  public static final MockJavaResource DATE_TIME_FORMAT_OLD = new MockJavaResource(
       "com.google.gwt.i18n.client.DateTimeFormat") {
     @Override
     public CharSequence getContent() {
       StringBuffer code = new StringBuffer();
       code.append("package com.google.gwt.i18n.client;\n");
-      code.append("public class DateTimeFormat {\n");
+      code.append("public class DateTimeFormat extends com.google.gwt.i18n.shared.DateTimeFormat {\n");
       code.append("  public static enum PredefinedFormat {\n");
       PredefinedFormat[] values = PredefinedFormat.values();
       for (int i = 0; i < values.length; i++) {
@@ -553,12 +573,23 @@
     }
   };
   public static final MockJavaResource TIME_ZONE = new MockJavaResource(
+      "com.google.gwt.i18n.shared.TimeZone") {
+    @Override
+    public CharSequence getContent() {
+      StringBuffer code = new StringBuffer();
+      code.append("package com.google.gwt.i18n.shared;\n");
+      code.append("public interface TimeZone {\n");
+      code.append("}\n");
+      return code;
+    }
+  };
+  public static final MockJavaResource TIME_ZONE_OLD = new MockJavaResource(
       "com.google.gwt.i18n.client.TimeZone") {
     @Override
     public CharSequence getContent() {
       StringBuffer code = new StringBuffer();
       code.append("package com.google.gwt.i18n.client;\n");
-      code.append("public class TimeZone {\n");
+      code.append("public class TimeZone implements com.google.gwt.i18n.shared.TimeZone {\n");
       code.append("}\n");
       return code;
     }
@@ -638,6 +669,7 @@
     rtn.add(CURRENCY_DATA);
     rtn.add(DATE_LABEL);
     rtn.add(DATE_TIME_FORMAT);
+    rtn.add(DATE_TIME_FORMAT_OLD);
     rtn.add(DIALOG_BOX);
     rtn.add(DISCLOSURE_PANEL);
     rtn.add(DOCK_LAYOUT_PANEL);
@@ -671,6 +703,7 @@
     rtn.add(TAB_PANEL);
     rtn.add(TEXT_BOX_BASE);
     rtn.add(TIME_ZONE);
+    rtn.add(TIME_ZONE_OLD);
     rtn.add(TREE);
     rtn.add(TREE_ITEM);
     rtn.add(UI_OBJECT);
diff --git a/user/test_i18n_bar/com/google/gwt/i18n/client/DateTimeFormatTestBase_MyMessages_de.properties b/user/test_i18n_bar/com/google/gwt/i18n/shared/DateTimeFormatTestBase_MyMessages_de.properties
similarity index 100%
rename from user/test_i18n_bar/com/google/gwt/i18n/client/DateTimeFormatTestBase_MyMessages_de.properties
rename to user/test_i18n_bar/com/google/gwt/i18n/shared/DateTimeFormatTestBase_MyMessages_de.properties
diff --git a/user/test_i18n_bar/com/google/gwt/i18n/client/DateTimeFormatTestBase_MyMessages_en.properties b/user/test_i18n_bar/com/google/gwt/i18n/shared/DateTimeFormatTestBase_MyMessages_en.properties
similarity index 100%
rename from user/test_i18n_bar/com/google/gwt/i18n/client/DateTimeFormatTestBase_MyMessages_en.properties
rename to user/test_i18n_bar/com/google/gwt/i18n/shared/DateTimeFormatTestBase_MyMessages_en.properties