in Android

Jodatime vs ThreeTenABP (Android Date/Time tool comparison)

Reaching the infamous dex method limit kinda sucks. Which is why I started to trim libraries in my app instead of just going the Multidex route. With the help of this nifty APK method count tool , I’ve been able to identify some libraries I might reconsider using.

Jodatime has 4713 methods and ThreeTenABP has 2827 methods (debug apk)

I’ve been using the Jodatime port  for a while now. It makes dealing with date and time functionality easy. I came across ThreeTenABP which is an Android backport of  Java 8’s JSR310 implementation. The functionality between the 2 seems comparable for what I use Jodatime for in my app. What stood out for me though was the method count.

Comparing Jodatime  (2.9.2) and ThreeTenABP (1.0.3) , the latter has a significantly lower method count. Jodatime has 4713 methods and ThreeTenABP has 2827 methods (debug apk). That difference was big enough for me to consider replacing my implementation and it was not as painful as I anticipated. I went through Jodatime’s quick start guide and tried implementing the same examples using ThreeTenABP.

Jodatime:


DateTime dt = DateTime.now();
int jd = dt.getDayOfMonth();
int jm = dt.getMonthOfYear();
int jy = dt.getYear();
Log.d(TAG,"Joda : "+ dt.toString());
Log.d(TAG,"Joda : [day: "+jd+"] [month: "+jm+"] [year: "+jy+"]");
Log.d(TAG,"Joda : [day: "+dt.dayOfWeek().getAsText()+"] [month: "+dt.monthOfYear().getAsText()+"] [year: "+dt.year().getAsText()+"]");
dt.withYear(2000);
dt.plusHours(2);
Log.d(TAG,"Joda : "+dt.toString());
String frenchShortName = dt.monthOfYear().getAsShortText(Locale.FRENCH);
boolean isLeapYear = dt.year().isLeap();
DateTime rounded = dt.dayOfMonth().roundFloorCopy();
Log.d(TAG,"Joda : [french Short: "+frenchShortName+"] [leapyear: "+isLeapYear+"] [rounded: "+rounded+"]");
dt = new DateTime(2005, 3, 26, 12, 0, 0, 0);
Log.d(TAG,"Joda : "+ dt.toString());
DateTime plusPeriod = dt.plus(Period.days(1));
Log.d(TAG,"Joda : +1day "+ plusPeriod.toString());
DateTime plusDuration = dt.plus(new Duration(24L*60L*60L*1000L));
Log.d(TAG,"Joda : +24h "+ plusDuration.toString());
DateTime today = DateTime.now();
DateTime yesterday = today.minusDays(1);
Hours diff = Hours.hoursBetween(today,yesterday);
Log.d(TAG,"Joda : hours between "+ diff.getHours());

ThreeTenABP:


LocalDateTime ldt = LocalDateTime.now();
int ld = ldt.getDayOfMonth();
int lm = ldt.getMonthValue();
int ly = ldt.getYear();
Log.d(TAG,"3ten : "+ldt.toString());
Log.d(TAG,"3ten : [day: "+ld+"] [month: "+lm+"] [year: "+ly+"]");
Log.d(TAG,"3ten : [day: "+ldt.getDayOfWeek().name()+"] [month: "+ldt.getMonth().name()+"] [year: "+ldt.getYear()+"]");
ldt.withYear(2000);
ldt.plusHours(2);
Log.d(TAG,"3ten : " +ldt.toString());
String frenchShortName = ldt.getMonth().getDisplayName(TextStyle.SHORT,Locale.FRENCH);
boolean isLeapYear = false; // could not find a matching function
LocalDateTime rounded = ldt.truncatedTo(ChronoUnit.DAYS);
Log.d(TAG,"3ten : [french Short: "+frenchShortName+"] [leapyear: "+isLeapYear+"] [rounded: "+rounded+"]");
ldt = LocalDateTime.of(2005, 3, 26, 12, 0, 0, 0);
Log.d(TAG,"3ten : "+ ldt.toString());
LocalDateTime plusPeriod = ldt.plusDays(1);
Log.d(TAG,"3ten : +1day: "+ plusPeriod.toString());
LocalDateTime plusDuration = ldt.plus(24,ChronoUnit.HOURS);
Log.d(TAG,"3ten : +24h : "+ plusDuration.toString());
LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = today.minusDays(1);
org.threeten.bp.Duration diff = org.threeten.bp.Duration.between(today,yesterday);
Log.d(TAG,"3ten : hours between "+ diff.toHours());

You can get most of the functionality provided by Jodatime from ThreeTenABP. I also noticed that Jodatime now seems to have the JSR-310 implementations as well. The only reason I ended up changing was because of the method count. Your use case might vary to mine.

  • sakis

    I’m way past that 65k limit :/

    • kurt

      Still worth evaluating all the libraries in your project.

      • sakis

        Indeed it is. But it’s moving from being a high priority thing to waaaay down the list 😛

  • Edy Bolos

    Jodatime for Android takes more than 50ms to initialize. Do you know how ThreeTenABP compares to it in this matter?

    • kurt

      It uses a JAR resource for loading timezone information so I guess its more or less the same.

  • Meno Hochschild

    You can also evaluate my library Time4A (https://github.com/MenoData/Time4A ). The apk-method-count is not significantly higher than in Threeten-ABP (2881 vs. 2827) but there are many more features including excellent internationalization and various other calendars (feature comparison see http://time4j.net/tutorial/appendix.html ).

    • Meno Hochschild

      Meanwhile I fear that Threeten-ABP does not care much about timezone updates. The last one is from November (2015g), and we already have tzdb-version 2016c. I also do not see that recent updates/fixes to Java-8 are backtracked in threeten-bp for a while (just a vague impression). So count of apk-methods should not be the only criterion how to choose a library.

  • Meno Hochschild

    I have tested a simple application printing the current local time and applied Proguard (minifyEnabled=true). With this optimization, the difference between Joda-Time-Android and ThreetenABP is no longer big (~1350 versus ~1050). Informational note, my lib Time4A is the biggest (~6280, with Proguard ~2750) in terms of size AND features.

    So the apk-method-counts given above should rather be interpreted as UPPER BOUNDS and represent some kind of measurement for count of supported features. That said, it depends on your needs if you interprete the apk-method-count in a positive or negative way. I know that some people appreciate a library like Date4J on Android (as extreme counterpart to Time4A) because it is unbeatable small (far less than 100 methods), and if those people don’t need more then why not.

    Finally users have to make a trade-off between size requirements and needed features.