Repository: google/gson (Java)
Date: 2016-01-11 17:37:29+00:00
Task type: Bug Fix

Issue:
ISO8601 is not fully implemented
Hi guys,

I'm working on a project where I have to parse `2016-01-11T11:06:14.000-02` to java.util.Date which is a valid date according to [RFC3339](https://www.ietf.org/rfc/rfc3339.txt) on page 12.

But I got an Exception trying to archive it

```
Caused by: com.google.gson.JsonSyntaxException: 2016-01-11T11:06:14.000-02
        at com.google.gson.DefaultDateTypeAdapter.deserializeToDate(DefaultDateTypeAdapter.java:107)
        at com.google.gson.DefaultDateTypeAdapter.deserialize(DefaultDateTypeAdapter.java:84)
        at com.google.gson.DefaultDateTypeAdapter.deserialize(DefaultDateTypeAdapter.java:38)
        at com.google.gson.TreeTypeAdapter.read(TreeTypeAdapter.java:58)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:117)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:217)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:117)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:217)
        at com.google.gson.Gson.fromJson(Gson.java:861)
        at com.google.gson.Gson.fromJson(Gson.java:926)
        at com.google.gson.Gson.fromJson(Gson.java:899)
        at ...
Caused by: java.text.ParseException: Failed to parse date ["2016-01-11T11:06:14.000-02']: Mismatching time zone indicator: GMT-02 given, resolves to GMT-02:00
        at com.google.gson.internal.bind.util.ISO8601Utils.parse(ISO8601Utils.java:270)
        at com.google.gson.DefaultDateTypeAdapter.deserializeToDate(DefaultDateTypeAdapter.java:105)
        ... 31 more
Caused by: java.lang.IndexOutOfBoundsException: Mismatching time zone indicator: GMT-02 given, resolves to GMT-02:00
        at com.google.gson.internal.bind.util.ISO8601Utils.parse(ISO8601Utils.java:236)
        ... 32 more
```

I'm able to fix this if it sounds reasonable.



Issue discussion:
Does using the `UtcDateTypeAdapter` work? https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java


Solution patch:
diff --git a/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java b/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java
--- a/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java
@@ -113,7 +113,7 @@ public static String format(Date date, boolean millis, TimeZone tz) {
 
     /**
      * Parse a date from ISO-8601 formatted string. It expects a format
-     * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh:mm]]
+     * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:mm]]]
      * 
      * @param date ISO string to parse in the appropriate format.
      * @param pos The position to start parsing from, updated to where parsing stopped.
@@ -209,6 +209,10 @@ public static Date parse(String date, ParsePosition pos) throws ParseException {
                 offset += 1;
             } else if (timezoneIndicator == '+' || timezoneIndicator == '-') {
                 String timezoneOffset = date.substring(offset);
+
+                // When timezone has no minutes, we should append it, valid timezones are, for example: +00:00, +0000 and +00
+                timezoneOffset = timezoneOffset.length() >= 5 ? timezoneOffset : timezoneOffset + "00";
+
                 offset += timezoneOffset.length();
                 // 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00"
                 if ("+0000".equals(timezoneOffset) || "+00:00".equals(timezoneOffset)) {


Test patch:
diff --git a/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java b/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java
--- a/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java
+++ b/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java
@@ -130,6 +130,7 @@ public void testDateDeserializationISO8601() throws Exception {
     assertParsed("1970-01-01T00:00Z", adapter);
     assertParsed("1970-01-01T00:00:00+00:00", adapter);
     assertParsed("1970-01-01T01:00:00+01:00", adapter);
+    assertParsed("1970-01-01T01:00:00+01", adapter);
   }
   
   public void testDateSerialization() throws Exception {