Why should I use Joda-Time or not?

Asked

Viewed 1,140 times

12

The question comes mainly based on comment from one of our most active users today and who has already demonstrated extraordinary knowledge in time use, especially in Java.

the java.time API would be more "inspired" in Joda-Time than a simple copy

some ideas and concepts were used, but other things he considered "wrong" or bad, he took the opportunity to correct

I was wondering if Java, which I know improved its API, took advantage of something from Joda-Time.

So I wanted to better understand what’s wrong with her and if you still have a reason to use her if the Java API took advantage of her ideas.

I am interested as curious because I don’t use Java, but I use Nodatime (inspired by Joda-Time) which I consider good and I have seen some things that she is better than Joda-Time, who knows I could have a better view of Nodatime understanding more about Joda-Time. It would be an interesting addition if it is pertinent and known.

  • @Manieiro, I don’t know how to answer your question, but there’s a post on EN that questions the cons of joda, given the difficulty of the question, might help with something https://stackoverflow.com/questions/375544/are-there-any-cons-to-using-joda-time, I was curious about your question and am researching too.

  • @Scarabelo has already helped. I imagine hkotsubo will respond in the next few days :) My goal is to really provoke a retrospective about what this lib is like, what’s wrong with it and how Java improved when it was inspired by it. I admit that my biggest curiosity has to do with the Nodatime that was also inspired and I don’t know if she improved everything that could and the right way, but the focus is on the same Java, who else forward I can ask some more specific question about the Noda, even with subsidy of what is answered here.

1 answer

20


In general, whether or not to use a language/framework/library/technology depends on several factors (from technical aspects to personal taste). In the specific case of Joda-Time, I think it is worth "giving an overview" in the library, looking at its history and listing some similarities and differences with respect to java.time, and then everyone draws their own conclusions.

Brief - but not so much - summary of Joda-Time

For a long time the Joda-Time was the best alternative to the native Java date API, because at the time all we had were the classes java.util.Date and java.util.Calendar (besides his "friends": java.text.SimpleDateFormat, java.util.TimeZone and the classes Date, Time and Timestamp package java.sql). They all have one long list of problems and failures of design (I will not list one by one, but some will be cited below when they are pertinent to the discussion).

One of the innovations of Joda-Time (which today may seem "beaten" and trivial, but at the time was the reason for "wow!") was the fluid and simpler-to-use API. For example, to create a specific date and add a few days and months to it:

// antes do Joda-Time
Calendar cal = Calendar.getInstance();
cal.set(2019, Calendar.JANUARY, 20); // 20 de janeiro de 2019
cal.add(Calendar.DAY_OF_MONTH, 5); // somar 5 dias
cal.add(Calendar.MONTH, 3); // somar 3 meses
System.out.println(cal.getTime()); // Thu Apr 25 16:15:27 BRT 2019

// depois do Joda-Time
LocalDate data = new LocalDate(2019, 1, 20) // 20 de janeiro de 2019
    .plusDays(5) // somar 5 dias
    .plusMonths(3); // somar 3 meses
System.out.println(data); // 2019-04-25

In the above code we can notice other details. The native API only had two classes to represent dates and times:

Already Joda-Time created several different classes to represent concepts such as "only one date (day, month and year)", "only time", "date and time with (or without) time zone", etc. This was another major innovation, since with Date and Calendar, you are always dealing with timestamps directly or indirectly. Calendar still has a built-in Timezone, always acting "behind the scenes", so adjustments like daylight savings time can occur without you noticing. In Joda-Time you can choose which fields to use and have greater control over the dates and times.

Note the example above. When printing cal.getTime(), the return is a java.util.Date, that when printed calls the method toString(), which in turn uses Timezone default of the JVM to know what date and time to display - behavior that is explained here and here (in the "Dates and timezones" section). And as I only set the day, month and year, the time fields continued set to the current time (at the time when the Calendar was created with getInstance()). One already uses a org.joda.time.LocalDate, these problems do not occur. Since this class does not have the time fields, they are not even shown when printing the date. And since this class also has no time zone information, it does not suffer Timezone interference default jvm.

Other great improvement was to use the correct values for the months. In the native API, the months were indexed to zero (January is zero, February is 1, etc.), and even today there are times when I forget and end up using the wrong value. So I chose to use the constant Calendar.JANUARY (zero, by the way), which is less prone to errors. It may seem like a silly detail, but the Javascript API has this same problem, and to this day causes confusion among developers.

Other characteristics are immutable classes and thread safe, one Formatting API and Parsing far superior to SimpleDateFormat (which corrects various bizarre problems of this, in addition to being more flexible and complete), more detailed support for timezones, support for other calendars, in addition to classes to represent durations (amount of time), something that did not exist in the native API. And also other facilities, such as comparison methods isBefore and isAfter (clearer and more semantic than compareTo), getters specific to obtain the day, month, year, etc (versus the "getter generic" of Calendar, that forces you to do things like calendar.get(Calendar.MONTH), conversions to/from the native API and many other "little things" that added up, made Joda-Time the API of preferred dates of the one who writes to you (today is no more).


In the comments was indicated this question on the disadvantages of Joda-Time. The answer from Jon Skeet says one of the problems was when he tried to write his own Timezone. Well, I’m yet to see an API that allows me to do this in an easy way (I’ve also tried it with Joda-Time and other Apis, and it’s not something trivial depending on what you want to do), but anyway it’s a rare use case (I just did it out of curiosity) and I wouldn’t consider it such a serious problem. Because it is not simple at all, and I understand that no API has provided an easy way to do it. For the vast majority of cases, using the timezones that already exist is enough.

Another point you may consider an advantage or disadvantage is that the timezone information are in the Joda-Time JAR itself, separate from the JVM. On the one hand it can be good, because you can update them separately, without having to touch the Java installation. On the other hand, it can be bad, because it is more a place to change (since usually updates - or should update - the JVM as well, as that’s where the native API reads this information). Anyway, this "disadvantage" I consider debatable: there are cases where the JVM update is in charge of another team and you can not wait for them, so using Joda-Time would ensure that at least your application will be with updated information (but if it does conversions from/to the legacy API, there is no way, have to update the JVM too).

Anyway, I won’t comment on all the answers one by one, because some may be outdated (the question is from 2008), but one of the disadvantages is precisely the fact that it is an old API, of a project that declared itself to be closed. Seeing the latest versions, most are timezones updates or minor fixes. Also, on your own site there’s a warning:

Note that Joda-Time is considered to be a largely "finished" project. No major Nhancements are Planned. If using Java SE 8, Please migrate to java.time (JSR-310).

In free translation:

Joda-Time is considered a "shutdown" project. There are no major improvements planned. If you are using Java 8, please migrate to java.time (JSR-310).

I mean, Joda-Time himself is saying, "don’t use me, go to the java.time". So let’s go...


The path to the API java.time

When was it announced that Stephen Colebourne (the creator of Joda-Time) would be one of the leaders of JSR-310 (which would give rise to a new Java Date API, the java.time), A lot of people - including me - thought that he would just copy or adapt Joda-Time, make some adjustments here and there, and that’s it. But that’s not exactly what happened.

In your blog, Stephen explains some of the things he considers flaws of design in Joda-Time, which he decided to correct in the new API. The first is the dichotomy between the ways humans and machines "see" the concept of "time".

Basically, humans have created several different concepts, such as calendar systems (we have the Gregorian, Buddhist, Hebrew, Chinese, Japanese, Hindu, etc.), which divide the timeline into arbitrary pieces: years, months, days, hours, minutes and seconds. Not to mention the huge mess that are the time zones, with its constant rule changes (always have someone wanting to change the time zone of their country/state/city/province/etc, or wanting to adopt/abolish daylight time, so that "people have more hours of sunshine" or any other reason).

Thanks to these concepts and definitions, the same instant can represent a completely different date and time, depending on the system you use (either a time zone or a different calendar). For example, when it’s October 1, 2019, at 6:00 pm on Brasilia time, in Tokyo, it’s already October 2, at 6 am. But if we consider the coptic calendar, this same date corresponds to day 20 of month 1 of year 1736 (to know this, I used this class, also made by Stephen Colebourne, so if you’re wrong complain with him :P).

Finally, the human vision of time is one side of the coin. The other is the "vision of machines", which would be the one already mentioned timestamp: a number representing the number of seconds (or milliseconds, or nanoseconds, varies according to implementation) since the Unix Epoch (1970-01-01T00:00Z - January 1, 1970 at midnight on UTC). This number is "absolute", in the sense of being the same for everyone, regardless of time zone, calendar, etc. Although it is possible to convert a timestamp to a date and time in a given calendar and Timezone (and vice versa), they are 2 separate concepts.

In Joda-Time there are classes org.joda.time.Instant (representing the "machine vision") and org.joda.time.DateTime (representing the "human vision"), but both implement the interface org.joda.time.ReadableInstant (which also represents the "machine view"). That is, we have a class that represents a concept (DateTime), but that implements an interface that represents another completely different one. This is one of the flaws of design that he wanted to fix in the new API.


The other flaw (according to the author) is that all Joda-Time date and time classes have a pluggable chronology (and "chronology" is a synonym for "calendar system", because there is a chronology class for each calendar). Therefore, there are some cases where this may affect some fields, such as the maximum value that the month may have (some calendars have 13-month years). In several classes it is possible to change the chronology, giving room for confusing and error-prone codes (since almost no one remembers to check if the chronology used is correct - most do not even conceive the possibility of using a different calendar).

As most usual applications will end up using the ISO 8601 calendar (which is what we wear on a day-to-day basis), he thought it best to leave this as the default of the new API, and dates that use other calendars would have their own classes (as we can see in bundle java.time.chrono).

The other faults mentioned are:

  • use of null at many points of the API, which depending on the context may mean the Unix Epoch, or a duration of zero, which according to the author was not a good decision, because it caused many bugs. In the new API, many classes and methods check for null values and throw an exception.
  • Because of the problems already mentioned (especially the first one), many internal details of implementation ended up being very complex. He mentions that he also took the opportunity to better consolidate some concepts and behaviors of the API, such as what to do in daylight savings (Joda-Time may give error in some cases, while the java.time makes some automatic adjustments) and in some corner cases in date arithmetic (Ex: how many years ago between 02/29/2020 and 02/28/2021? For Joda-Time, the answer is 1, to the java.time, is zero - see here) - the latter is not mentioned in the blog, but I do not believe that it has changed for nothing.

Finally, the author himself mentions that the "JSR-310 started from scratch, but with an API inspired by Joda-Time".


Similarities and differences between Joda-Time and java.time

Here and here has a great summary, and the tables of the first link can see that several concepts of Joda-Time are present in java.time:

  • specific classes for each situation: one for date (day, month and year), one for time, one for date and time (no time zone), one for date and time in a time zone, etc. It is interesting to note that some classes have been added, especially the enum's for the months and the weekdays.
  • Joda-Time has changeable versions of the classes, which were removed on java.time (in the new API, all classes are immutable and thread safe)
  • in the java.time the concepts of Timezone and offset have been separated, which is a very rare thing to see. The vast majority of API’s treat them as if they were the same thing, but there is a subtle difference: the offset is simply the difference with UTC (as -03:00 to indicate 3 hours behind UTC), a Timezone already contains the history of changes to the offset of a particular region of the planet (such as America/Sao_Paulo, which contains all changes to the Official Time of Brasilia, in which it is used -03:00 during part of the year, and changes to -02:00 during daylight saving). For more details, see wiki of the tag Timezone (in the section "Differences between Timezone and offset").
  • one thing I didn’t understand was the removal of the formatting of durations (although less common than date formatting, it is still a relatively common use case)

Although many classes (mainly the core of the API’s) have the same names and methods, some details in their operation are different. One that one notes is the use of Factory methods instead of builders:

// Java 8, uso dos factory methods "of()" e "now()"
java.time.LocalDate.of(2018, 2, 1);
java.time.LocalDate.now(); // data atual

// Joda-Time, uso de construtor
new org.joda.time.LocalDate(2018, 2, 1);
new org.joda.time.LocalDate(); // data atual

Note that I used the full class name to not get confused.

Another difference are those already mentioned enum's for the month and day of the week. In Joda-Time, the method getDayOfWeek(), for example, returns a int whose values are defined in the class org.joda.time.DateTimeConstants. This class lists the days of the week as defined by ISO 8601, which considers Monday as the first day of the week. So, Monday (DateTimeConstants.MONDAY) has the value 1, Tuesday is 2, and so on, until Sunday, whose value is 7.

The fact that there is a type java.time.DayOfWeek in Java 8 makes the code clearer and less prone to errors, since we do not run the risk of passing an invalid value if a method is waiting a day of the week, as only those defined in enum. The same goes for java.time.Month, which is the enum for the months (and January has the value/).


One of the main differences between Apis is how they are treated DST gaps (when summer time starts and clock is early in an hour). We will use the Timezone America/Sao_Paulo as an example. In this Timezone, on October 15, 2017, at midnight, the clocks were put forward an hour. That means that this time was "skipped", ie every minute between 00:00 and 00:59 do not exist on this day, for the Timezone in question.

In the java.time this is solved by setting the time to the next valid time. For example, when creating a ZonedDateTime for "15 October 2017 at midnight and a half in São Paulo", the time is automatically adjusted to 01:30 (the next valid hour):

// 15 de outubro de 2017, meia-noite e meia, timezone America/Sao_Paulo
ZonedDateTime z = ZonedDateTime.of(2017, 10, 15, 0, 30, 0, 0, ZoneId.of("America/Sao_Paulo"));
// Devido ao horário de verão, 00:30 é ajustado para 01:30
System.out.println(z); // 2017-10-15T01:30-02:00[America/Sao_Paulo]

This is an interesting approach to the API, as it simulates a person manually advancing the clock when noticing that the time was incorrect. In Joda-Time, this adjustment is not done and the attempt to create this date throws an exception:

// 15 de outubro de 2017, meia-noite e meia, timezone America/Sao_Paulo
DateTime d = new DateTime(2017, 10, 15, 0, 30, 0, 0, DateTimeZone.forID("America/Sao_Paulo"));

This code makes the following exception:

org.joda.time.Illegalinstantexception: Illegal Instant due to time zone offset Transition (Daylight Savings time 'gap'): 2017-10-15T00:30:00.000 (America/Sao_paulo)

Note that the exception description informs that there was a DST gap and so the reported time does not exist on that day, for the Timezone in question. To create a DateTime valid, you must first check if the date and time are in a DST gap, and if so, adjust the time to the next hour. For this, we will create a org.joda.time.DateTimeZone for the Timezone America/Sao_Paulo and a org.joda.time.LocalDateTime to check if the date and time are in a DST gap:

// timezone America/Sao_Paulo
DateTimeZone zone = DateTimeZone.forID("America/Sao_Paulo");
// 15 de outubro de 2017, meia-noite e meia (sem timezone)
LocalDateTime localDt = new LocalDateTime(2017, 10, 15, 0, 30, 0, 0);
// se o localDt está em um DST gap, ajustar para a hora seguinte
if (zone.isLocalDateTimeGap(localDt)) {
    localDt = localDt.plusHours(1);
}
// criar o DateTime: 15 de outubro de 2017, à 01:30, timezone America/Sao_Paulo
DateTime dt = localDt.toDateTime(zone);

The method isLocalDateTimeGap() checks whether the LocalDateTime is in a DST gap, and if so, we have adjusted it to the next valid time. In practice, we are manually making the adjustment that ZonedDateTime does automatically. There’s only one catch.

The above code may work for most cases, but not all DST gaps are 1 hour. There are timezones, for example Australia/Lord_Howe, that advance the clock by 30 minutes during daylight saving time. Or else Asia/Kuching, that In the 1930s, the clock was ticking in just 20 minutes.

Besides, not all the gaps are because of daylight savings. There are cases where a particular region simply changes its time zone "definitively" (in quotes because nothing guarantees that it will not change again). One example is North Korea, which at midnight on May 5, 2018 set the clock ahead by 30 minutes to align its schedule with South Korea. Is a gap 30 minutes, but not related to daylight saving time. But because it is a gap, can also cause a IllegalInstantException.

Another notable example is the case of Samoa: on December 30, 2011 at midnight they changed their offset from -10:00 for +14:00. Thereby, all day 30 was jumped, namely, a gap 24 hours! All times of December 30, 2011 do not exist in this Timezone. This was only possible because the country is located next to International Date Line.

Therefore, the previous solution (add 1 hour to the LocalDateTime) does not work well for these cases. In the case of Samoa, for example, we would have to add up at least 24 hours to make sure we’re no longer inside the gap. But by adding up to 24 hours, we run the risk of getting an "early" date. One way around this would be to add up to one minute, until you find a valid time:

// ajustar para o minuto seguinte, até encontrar uma hora que não está em um gap
while (zone.isLocalDateTimeGap(localDt)) {
    localDt = localDt.plusMinutes(1);
}

With that, the LocalDateTime will be incremented until we find a valid time that is not in a gap, no matter if it is related to daylight saving time or not, or if it is 1 hour, 20 minutes or a full day. Already the java.time makes these settings automatically, always advancing to a valid date and time.


The java.time also changed some things for formatting and Parsing of dates. Some Patterns do not work the same way: in Joda-Time, the lyrics e is the day of the week according to the values of the respective values in the class DateTimeConstants, which we have seen previously: Monday has value 1, Tuesday is 2 etc. However, no java.time, the letter e represents the day of the week as per Locale used (and if none Locale specified, the JVM default locale is used). According to Locale, the week can start on Saturday, Sunday or Monday, then a DateTimeFormatter with the letter e can return a different value for the same date, depending on the Locale used (something that does not occur in Joda-Time, which uses a week setting that does not depend on the Locale). Here has a very detailed explanation about it.

Another thing that was - in my opinion - improved are TemporalField and TemporalUnit, which correspond respectively to fields of a date (such as day, month, year, etc.) and units of a duration (days, months, hours, minutes, etc.). These concepts also exist in Joda-Time but I have always found it confusing to use them, and in java.time it seems to me that it has become easier to understand and use (but there is already my opinion).

There are still some news that Joda-Time does not have:

  • the interface TemporalQuery, that allows to obtain information of a date in a more customized way. Ex:

    TemporalQuery<MeuResponseCustomizado> info = (t) -> {
        DayOfWeek dow = DayOfWeek.from(t); // obtém o dia da semana
        MeuResponseCustomizado resp;
        if (condicaoComplexa(dow)) {
            resp = fazAlgoBemComplicado(t);
        } else {
            resp = respostaDefault(t);
        }
        return resp;
    };
    MeuResponseCustomizado resposta = LocalDate.now().query(info);
    

    The detail is that a TemporalQuery receives as parameter a TemporalAccessor (an interface that all API data classes implement), making it very flexible (it is possible to use the same TemporalQuery with several different classes). Of course in Joda-Time it would be enough to create a static method in a utility class, but the TemporalQuery facilitates some things (such as using the pattern Strategy, can pass the query as parameter, exchange it for mocks in tests, etc)

  • the interface TemporalAdjuster, for useful adjustments, how to obtain the last day of the month, the third Thursday of the month, among others. These I mentioned, including, are already ready in the API itself.

  • java.time.Clock, with which it is possible to change the current date/time (very useful in testing, for example). In Joda-Time this was done with a static method, that changed the current date/time for the entire application, already on java.time you can have several Clock'different s at the same time, they can be configured by container injection of dependency, etc.

Another difference is that the java.time has nanosecond accuracy (9 decimal places in the fraction of seconds), while Joda-Time accuracy is millisecond (3 decimal places). Depending on what you need, this may or may not be a great advantage.

Anyway, there are several other details that are different (I have a class on Github that details these and other points better), but to summarize:

  • some concepts and ideas of Joda-Time java.time
  • some things have been improved and do not work in exactly the same way
  • others have been completely modified, and some new concepts added
  • the new API is inspired by the old one, but is not a mere copy

But I must or must not?

In my opinion: in new projects using JDK >= 8, use the java.time. The API supplies most of the needs that Joda-Time provided, and the support of the "famous" Apis only increases: Jackson already has its own module, JDBC 4.2 already supports these classes (check if your database already has one driver compatible) and so on.

If using JDK 6 or 7, use Threeten Backport, one backport that has most of the functionalities of java.time. The API is pretty much the same (save some details, such as converting to/from Date and Calendar, that in Java 8 is made through new methods, and in the backport is made by a utility class). In addition to having the same features, this facilitates a future migration to JDK 8, as for most cases it would be enough to change the import of org.threeten.bp for java.time.

For Android, the java.time is also available (here has instructions to use it), but it is also possible to use the backport (here has instructions to use it on Android).

But if you are still stuck with JDK 5 (I don’t doubt it still exists), there is no prospect of changing the version, and want to use something better than Date and Calendar, then I recommend Joda-Time. Another case is when there is too much legacy code using Joda-Time and it is impossible to migrate it (being that "too much" and "unviable" can be subjective, but whether is considering continuing or not using, is an analysis that has to be made).


This question from Soen also compares the 2 API’s. As for Nodatime, I don’t know enough to say anything meaningful.

  • 2

    Already the +1 just for the effort (and I know it’s great), so I do not run the risk of being without votes today (already almost :D)

  • @Maniero Thanks for the confidence. Well I missed a vote so fast :-)

  • 1

    What a reply, thank you very much @hkotsubo, very detailed.

  • I’m thinking about bringing Joda Money to .NET. They’ve done it but it’s gotten worse. And even Joda I think it’s really bad. There are more important reasons, but one of the things I think you need is to be able to create your own currency, which is a common thing in Brazil :D So it has some similarity, which is why I’m having to gather more information about Jon Skeet’s port, As far as I know, it’s better than Joda Time. I don’t know if you know anything about Money. I just need to consolidate ideas and find time to start doing her port, although I don’t even know if you can talk about port so different it will be.

  • The answer helped me think about some things about Money. Is there an easy place to see the implementation of this? Specifically how big is the object that saves time? Something tells me it’s too big for something unnecessary. If I remember correctly you can not store all nanoseconds (you can get 100 of them) in up to 64bits, which is a shame, but I would expect this from Java and considering that it is a class does not even make so much difference.

  • @Maniero I never used Joda-Money. About Nodatime, here says that several things of Joda-Time have been changed because they are considered errors (but does not say which ones). Maybe in mailing list vc get more details. As I already said, I do not know in depth this lib, so I do not know which points have been changed/improved.

  • @Maniero About the implementation of Java, I suggest the openjdk repository. I know nanoseconds stay in a field int separate. A Instant, for example, has 2 fields for timestamp: a long with the amount of seconds and a int with nanoseconds (and methods such as toEpochMilli and ofEpochMilli, working with the value in milliseconds, make the necessary accounts).

  • 2

    I’ve read enough about Noda. The easiest place to help https://softwareengineering.stackexchange.com/questions/188110/noda-time-vs-joda-time. Many of the changes are that C# is a much better language :P

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.