What is an ideal way to parse DateTime?

What is an ideal way to parse DateTime?

Let there be a standardized and proper DateTime

DateTime should always be processed and parsed in model layer,

In this sense, Developer can display whatever time format that is designed to looks neat on the UI when we have the DateTime instance.

1
2
3
4
5
6
7
8
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();

Retrofit retrofitAdapter = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();

However, in a real world, a lot of backend does not have a standard on date time returned,

there might be different date time format return in different API calls, so developer can implement their serializer in order to fit the needs :

sauce: https://gist.github.com/jromero/9312366

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Converter
public static Gson getGson() {
if (sGson == null) {
sGson = new GsonBuilder()

.registerTypeAdapter(Event.EventDateTime.class,
new DateDeserializer<Event.EventDateTime>(
Event.EventDateTime.DATE_FORMAT, Event.EventDateTime.class))

.registerTypeAdapter(Event.StartEndDateTime.class,
new DateDeserializer<Event.StartEndDateTime>(
Event.StartEndDateTime.DATE_FORMAT, Event.StartEndDateTime.class))

.registerTypeAdapter(Event.SimpleDate.class,
new DateDeserializer<Event.SimpleDate>(
Event.SimpleDate.DATE_FORMAT, Event.SimpleDate.class))
.create();
}

return sGson;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Serializer
public class DateDeserializer<T extends Date> implements JsonDeserializer<T> {

private static final String TAG = DateDeserializer.class.getSimpleName();

private final SimpleDateFormat mSimpleDateFormat;
private final Class<T> mClazz;

public DateDeserializer(SimpleDateFormat simpleDateFormat, Class<T> clazz) {
mSimpleDateFormat = simpleDateFormat;
mClazz = clazz;
}

@Override
public T deserialize(JsonElement element, Type arg1, JsonDeserializationContext context) throws JsonParseException {
String dateString = element.getAsString();
try {
T date = mClazz.newInstance();
date.setTime(mSimpleDateFormat.parse(dateString).getTime());
return date;
} catch (InstantiationException e) {
throw new JsonParseException(e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new JsonParseException(e.getMessage(), e);
} catch (ParseException e) {
throw new JsonParseException(e.getMessage(), e);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Model
public class Event {
@SerializedName("created")
private EventDateTime mCreated;

//@SerializedName("updated")
private EventDateTime mUpdated;

@SerializedName("start")
private ConditionalDateTime mStart;

@SerializedName("end")
private ConditionalDateTime mEnd;
}

Additional: Java 8 new Time API :)

What is the problem of the plain old Simple Date Format?

  • it is not thread-safe, i.e. an instance cannot be used concurrently by several Java threads. Its javadoc says: “It is recommended to create separate format instances for each thread.”
  • SimpleDateFormat can change its configured time zone if it is asked to parse a date with a different time zone [bug report]
  • creating new instances is not cheap. Its constructor compiles the passed date-time pattern and if you create a new instance of SimpleDateFormat for each usage then it usually appears in CPU profiling results.

Fret not! here comes Java8 DateTimeFormatter

https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

It is thread safe

1
2
3
4
5
6
7

try {
val ta = DateTimeFormatter.ofPattern(fromPattern, Locale.US).parse(date)
return DateTimeFormatter.ofPattern(toPattern, Locale.US).format(ta)
} catch (e: Exception) {

}

LocalDate , LocalDateTime, LocalTime differences

Based on the naming, we can know that they are serve for different purpose, date only, time only or date and time together,

  • It is using system default timezone

However there are some drawbacks for them, let’s say if u are parsing LocalDateTime without time, it will throw DateTimeParseException, same for the other class.

Exception Sample

Reason why it happen: LocalTime was expecting 11:30 but not a full format, source code:

Cheat Sheets: Common Date Time Format Pattern

1
2
const val DATE_TIME_OFFSET_ISO_8601 = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
const val DATE_TIME_OFFSET_FULL_ISO_8601 = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"

Comments