Java: String to Date Parsing with GSON (and UTC Time Zone Handling)
Recently I was parsing a REST API that returned data in JSON format, and noticed that this particular API formatted it's dates in a peculiar way:
"time": "4/29/13 3:39 PM"
What's peculiar about this is that there is no time zone information in the string (and that it's a string in the first place). I assumed that the time zone was UTC, and further research into the API proved this to be the case.
Using GSON, a problem arose with this date format. It was fine parsing the string into a Date object, but it was expecting the time zone to be the system timezone, EDT. Obviously this is ineffective since the above time should be 11:39 AM EDT, but it parsed it as 3:39 PM EDT.
A little digging into the GSON date formatting API revealed the problem. I had been using the following code to instruct GSON on how to parse the dates:
GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setDateFormat("M/d/yy hh:mm a"); ...
The problem is that this method doesn't instruct GSON which time zone to use, so it uses that system setting.
The solution is to implement your own date deserializer, which we can use to hardcode the time zone to use (in this case UTC). In addition, you can also parse the date any way you need, giving you more control over them (the process is very similar to mapping integers to enums).
First, we create our deserializer:
import java.lang.reflect.Type; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; public class DateDeserializer implements JsonDeserializer<Date> { @Override public Date deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2) throws JsonParseException { String date = element.getAsString(); SimpleDateFormat formatter = new SimpleDateFormat("M/d/yy hh:mm a"); formatter.setTimeZone(TimeZone.getTimeZone("UTC")); try { return formatter.parse(date); } catch (ParseException e) { System.err.println("Failed to parse Date due to:", e); return null; } } }
When called, this will read a JsonElement as a String, and attempt to parse it as a UTC timestamp with the format M/d/yy hh:mm a (seen above). If the parsing fails, it will log the error and return null.
With the deserializer ready to go, all that's left is to instruct GSON to make use of it:
GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Date.class, new DateDeserializer()); ...
Now any time one of your models with a Date property is being constructed by GSON, it will invoke the DateDeserializer#deserialize method and allow us to completely customize how the date should be handled.
This method of deserializing dates is also completely compatible with using GSON with Android.