gson: DateTypeAdapter deserialization fails based on user's 24 hour preference

I’ve encountered a bug affecting (de)serialization of dates on Android devices using gson 2.7.

Description/Stacktrace

When trying to deserialize this date (Sep 26, 2016 18:13:24), after switching my device time preferences from 24 hour to 12 hour, using DateTypeAdapter.deserializeToDate, the following exception was thrown:

java.lang.NumberFormatException: Invalid number: Sep 
    at com.google.gson.internal.bind.util.ISO8601Utils.parseInt(ISO8601Utils.java:311)
    at com.google.gson.internal.bind.util.ISO8601Utils.parse(ISO8601Utils.java:129)

Steps to reproduce

Reproduced on a Nexus 5X running Android 7.0. I have also seen this crash reported from varying devices and Android versions.

  1. Create a java.util.Date object at the following time: Sep 26, 2016 18:13:24 (any date should work, but I will use this date for the example).
  2. Serialize the date object using DateTypeAdapter.write, observe that the output is either the string above or Sep 26, 2016 6:13:24 PM depending on your device’s 24 hour time settings.
  3. Go to device date & time settings.
  4. Switch to using 24-hour format (or vice versa)
  5. Try to deserialize the date string using DateTypeAdapter.deserializeToDate and observe the exception above being thrown.

If you are using the 24-hour format

  • DateTypeAdapter.deserializeToDate -> Sep 26, 2016 18:13:24 -> works as expected
  • DateTypeAdapter.deserializeToDate -> Sep 26, 2016 6:13:24 PM -> throws an exception

If you are using the 12-hour format

  • DateTypeAdapter.deserializeToDate -> Sep 26, 2016 18:13:24 -> throws an exception
  • DateTypeAdapter.deserializeToDate -> Sep 26, 2016 6:13:24 PM -> works as expected

That the ISO8601Utils cannot deserialize this string does not seem to be the issue. The problem seems to be that the DateFormat object enUsFormat is used by default to serialize the Date object. Then when it deserializes it, it tries to format it using this same DateFormat, if that fails it’s falling back to another DateFormat and finally the ISO8601Utils, which also will fail because it’s not meant to handle this format. When the 24-hour time preference is switched, the DateFormat object no longer can correctly format the serialized string, and therefore the adapter falls back to ISO8601Utils which throws an error. DefaultDateTypeAdapter also appears to have the same problem as DateTypeAdapter as it is contains the same (de)serialization logic for java.util.Date objects.

A workaround I am using is to create a TypeAdapter to handle dates and to use ISO8601Utils directly rather than default to using DateFormat so that the serialized string will be agnostic of the user’s time preferences.

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Comments: 16 (1 by maintainers)

Most upvoted comments

2021 and there is still no fix, this is ridiculous…

One more thing, I’ve needed to add Locale.US inside try 24 hour format, like this:

        // Try 24 hour
        try {
            SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss", Locale.US);
            Date result = formatter.parse(date);
            return result;
        } catch (ParseException e) {
            e.printStackTrace();
        }

Otherwise it seems that for some users it still won’t work (I guess depending on their locale)

@wman1980 I’ve tried your solution and it works. I’ve created GsonProvider and use it instead of doing new Gson() in the code:

public class GsonProvider {

    public static Gson provide() {
        return new GsonBuilder()
                .registerTypeAdapter(Date.class, new DateDeserializer())
                .create();
    }
}

Usage: Gson gson = GsonProvider.provide();

I had to use my own deserializer, because the UtcDateTypeAdapter added timezone differences to the date, e.g. +1 hour etc.

So I am using now my own:

public class DateDeserializer implements JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2) throws JsonParseException {
        String date = element.getAsString();

        // Try 12 hour
        try {
            SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy h:mm:ss a", Locale.ENGLISH);
            Date result = formatter.parse(date);
            return result;
        } catch (ParseException e) {
            e.printStackTrace();
        }

        // Try 24 hour
        try {
            SimpleDateFormat formatter = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss");
            Date result = formatter.parse(date);
            return result;
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return null;
    }
}

Maybe it helps.

@ssawchenko may i know where can i put that adapter in my application and how to specify our date object in my class . Please reply ASAP. Thanks in advance.