JodaOrg/joda-time
> The de facto Java date-time library of the pre-Java-8 era — now in maintenance-only mode, superseded by its own descendant java.time.
GitHub repo · Official website · License: Apache-2.0
Overview
Joda-Time is a date and time library for Java, created by Stephen Colebourne to replace the deeply flawed java.util.Date and java.util.Calendar classes that shipped with the JDK. Its core design ideas — immutable value types, a clean separation between instants, partials (like LocalDate), durations, and periods, and pluggable chronologies (ISO, Gregorian, Julian, Buddhist, Coptic, Islamic, and others) — became the reference model for how date-time handling should work in a typed language.
The defining fact about Joda-Time in 2026 is that it is explicitly finished. The README states it "is no longer in active development except to keep timezone data up to date"1. This is not abandonment but a rare, deliberate wind-down: the same author, Stephen Colebourne, went on to lead JSR-310, which brought java.time into Java SE 8 in 20142. java.time is essentially Joda-Time's successor, redesigned with the benefit of hindsight (nanosecond precision, a cleaner nullable story, Instant/ZonedDateTime/LocalDate split). The project's own maintainers ask Java 8+ users to migrate.
So the tension here is unusual: Joda-Time is high quality, battle-tested, and still pulled by an enormous swath of the Maven ecosystem, yet using it in new code is a documented mistake if you are on a modern JDK. It survives mostly as a transitive dependency and in legacy codebases that have not paid down the migration.
Getting Started
Maven:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.14.2</version>
</dependency>
Gradle:
implementation 'joda-time:joda-time:2.14.2'
Minimal example:
import org.joda.time.*;
LocalDate today = LocalDate.now();
LocalDate newYear = today.plusYears(1).withDayOfYear(1);
Days remaining = Days.daysBetween(today, newYear);
DateTime now = new DateTime(DateTimeZone.forID("Europe/London"));
Period rental = new Period().withDays(2).withHours(12);
boolean overdue = now.plus(rental).isBeforeNow();
Note the classic footgun preserved from the API: months are 1-based (getMonthOfYear() == 2 is February), which is more sane than java.util.Calendar's 0-based months but differs from some ports.
Architecture / How It Works
Joda-Time separates the domain into a small number of orthogonal concepts:
- Instants —
DateTime,Instant,DateMidnight(deprecated): a specific
point on the timeline, tied to a chronology and zone.
- Partials —
LocalDate,LocalTime,LocalDateTime,YearMonth,
MonthDay: a date/time with no zone, representing "some 3 PM" rather than a specific instant.
- Durations vs Periods — a
Durationis an exact millisecond span; a
Period is a human span ("2 months, 3 days") whose millisecond length depends on when it is applied. Conflating these is the most common date-math bug and Joda-Time was one of the first libraries to force the distinction into the type system.
- Chronology — the pluggable calendar-system engine.
ISOChronologyis the
default; every field operation (monthOfYear(), dayOfWeek()) routes through the active chronology, which is how alternate calendars are supported without branching in user code.
Almost all core types are immutable and thread-safe; mutation methods return new instances (withDayOfYear, plusDays). This immutability was a deliberate reaction to the mutable, footgun-heavy java.util.Calendar.
Time-zone data comes from the IANA TZDB, compiled into the jar. This is the one part of the library that still changes: point releases (2.x) exist almost entirely to ship updated zone rules as governments alter DST and offsets1.
Production Notes
The timezone-data coupling is the real operational concern. Because zone rules are baked into the jar, an old Joda-Time pin will silently compute wrong local times after a country changes its DST policy. Keep the dependency current even though the code is "finished" — the whole point of ongoing releases is the TZDB refresh. External zone data can be supplied via DateTimeZone.setProvider, but almost nobody does.
Migration to java.time is the dominant long-term cost. The APIs look similar but are not drop-in: Joda's DateTime maps loosely to java.time.ZonedDateTime, LocalDate/LocalTime/LocalDateTime names collide but have subtly different semantics, and Joda Period/Duration do not line up one-to-one with java.time.Period/Duration. Teams typically bridge at serialization boundaries (Jackson has both jackson-datatype-joda and jackson-datatype-jsr310) and migrate module by module.
Null and parsing behavior differ from the JDK. Joda-Time formatters (DateTimeFormat.forPattern) are lenient in places java.time's DateTimeFormatter is strict, so a mechanical port can change which inputs parse.
Android: for minSdk below 26, java.time is unavailable natively; the common replacement is not Joda-Time but ThreeTenABP, a backport of JSR-310, which the README itself recommends1. There was also a stripped-down joda-time-android fork built for method-count-sensitive Android apps, but ThreeTenABP is now the standard answer.
Security/support: vulnerability handling is routed through Tidelift rather than public GitHub issues1.
When to Use / When Not
Use when:
- You are on Java 6/7 and cannot use
java.timeat all. - You are maintaining an existing Joda-Time codebase and a full migration is not
yet justified — keep the dependency patched for TZDB updates.
- A dependency you rely on exposes Joda types in its public API.
Avoid when:
- You are writing new code on Java 8 or later — use
java.time(JSR-310), which
is the maintainer-endorsed successor.
- You need nanosecond precision (Joda-Time is millisecond-resolution).
- You are on modern Android (
minSdk26+) — usejava.timedirectly; below that,
use ThreeTenABP.
Alternatives
- java.time (JSR-310, part of the JDK) — the successor by the same author; use it for any new code on Java 8+.
- ThreeTen/threetenbp — Stephen Colebourne's standalone backport of
java.timefor Java 6/7; use when you want JSR-310 semantics on an old JDK. - JakeWharton/ThreeTenABP — Android packaging of threetenbp with lazy TZDB loading; use for Android
minSdk< 26. - apache/commons-lang (DateUtils) — use only for small utility helpers over legacy
java.util.Date, not as a full date model.
History
| Version | Date | Notes |
|---|---|---|
| 1.0 | 2005-01 | First stable release; immutable model, pluggable chronologies. |
| 2.0 | 2011-08 | Major release; java.util.Date-free internals, improved partials. |
| — | 2014-03 | Java SE 8 ships java.time (JSR-310), the maintainer-authored successor2. |
| 2.9.x | 2015–2017 | Steady TZDB refresh releases; API considered complete. |
| 2.10.x | 2018–2020 | Continued zone-data updates; project declared maintenance-only. |
| 2.14.2 | 2026 | Current release; JDK 1.5+, stable, TZDB updates only1. |
References
- ^ Joda-Time README and project home page — migration guidance, maintenance
status, TZDB update policy, Android/ThreeTenABP recommendation, Tidelift security contact. https://www.joda.org/joda-time/
- ^ JSR-310 "Date and Time API", delivered in Java SE 8 (2014), led by Stephen
Colebourne — the maintainer-endorsed successor to Joda-Time. https://jcp.org/en/jsr/detail?id=310
Tags
java, date-time, jvm, immutable, timezone, calendar-systems, jsr-310, legacy, maintenance-mode, library