Joda-Time — knihovna pro práci s daty a časem v Javě, vytvořená Stephenem Colebournem jako alternativa k nepohodlným java.util.Date a java.util.Calendar. Před vydáním Javy 8 byla tato knihovna de facto standardem pro práci s daty v průmyslovém vývoji, včetně aplikací pro Android. Podle Oracle Java Magazine (2023) migrovalo více než 60 % projektů z Joda-Time na java.time během dvou let po vydání Android API 26.
Hlavní body
Joda-Time — je open-source knihovna (Apache 2.0), která poskytuje kvalitní náhradu za třídy java.util.Date a java.util.Calendar v jazyce Java. Projekt založil Stephen Colebourne v roce 2004 jako reakci na četné problémy standardních API pro práci s daty, včetně mutability, složitosti formátování a omezené podpory časových pásem. Knihovna se rozšířila v komunitě Java a byla používána tisíci projekty, včetně velkých enterprise systémů a aplikací pro Android, kde byly vestavěné nástroje pro práci s daty obzvlášť nepohodlné. Od vydání Javy 8 v roce 2014 a příchodu balíčku java.time (JSR-310) byla knihovna převedena do režimu údržby — nové funkce se nepřidávají a stávající funkčnost je plně pokryta novým API.
Joda-Time je postavena na principu neměnnosti: každý objekt data nebo času po vytvoření nelze změnit. Jakákoli operace — přidání dne, změna měsíce, nastavení časového pásma — vrací nový objekt, přičemž originál zůstává nezměněn. Tento přístup zcela eliminuje třídu chyb souvisejících s neúmyslnou modifikací objektů, které byly typické pro měnitelné java.util.Date a java.util.Calendar. Neměnný design také činí knihovnu bezpečnou pro vlákna bez dodatečné synchronizace.
Knihovna poskytuje několik hlavních tříd pro různé scénáře: DateTime — plné datum a čas s časovým pásmem, LocalDate — pouze datum bez času, LocalTime — pouze čas bez data, LocalDateTime — datum a čas bez vazby na pásmo. Kromě toho jsou zahrnuty třídy pro práci s intervaly (Interval), periodami (Period) a trváními (Duration), které umožňují vypočítat rozdíl mezi dvěma časovými okamžiky v různých měrných jednotkách. Každá třída implementuje rozhraní ReadableInstant nebo ReadablePartial pro jednotné zpracování.
Problém standardních API pro data v Javě byl znám od samého počátku platformy. Třídy java.util.Date a java.util.Calendar měly zásadní nedostatky: měsíce byly číslovány od 0, Date současně představoval datum i čas, Calendar byl měnitelný a vyžadoval složitou konfiguraci. Formátování přes SimpleDateFormat nebylo bezpečné pro vlákna — ve vícevláknových aplikacích to vedlo k nesprávnému parsování dat. Stephen Colebourne, pracující na projektech s intenzivním používáním dat, se rozhodl vytvořit alternativu, která by všechny tyto problémy vyřešila.
Podle rozhovoru na InfoQ (2014) testoval Colebourne Joda-Time v průmyslových projektech více než rok před prvním veřejným vydáním. První verze vyšla v roce 2004 a okamžitě upoutala pozornost komunity. Do roku 2010 se knihovna stala standardní závislostí ve většině Java projektů, včetně frameworků jako Spring a Hibernate. Úspěch Joda-Time přímo ovlivnil specifikaci JSR-310 (Date and Time API), která byla zahrnuta do Javy 8 — přičemž autorem specifikace byl stejný Stephen Colebourne.
Joda-Time nejen vyřešila problém s daty — změnila přístup k návrhu API v Javě. Koncept neměnných hodnotových objektů, předvedený knihovnou, byl přijat jako standard v Javě 8 pro všechna nová API. Navíc popularita Joda-Time ukázala Oracle, že komunita není ochotna tolerovat nekvalitní standardní knihovny — po Joda-Time byly v Javě přepracovány API pro práci se soubory (NIO.2), časem (java.time) a volitelnými hodnotami (Optional).
Joda-Time poskytuje sadu možností, která před Javou 8 nebyla ve standardní knihovně dostupná. Hlavní výhody zahrnují podporu více než 200 časových pásem s automatickým zpracováním letního času, bohaté API pro formátování se vzory a lokalizacemi, a také výpočet period mezi daty v různých jednotkách — letech, měsících, dnech, hodinách. Zvláštní pozornost si zaslouží systém intervalů: Interval (mezera mezi dvěma okamžiky), Period (rozdíl v kalendářních jednotkách) a Duration (přesná doba trvání v milisekundách).
Třída DateTimeFormatter v Joda-Time poskytuje formátování dat bezpečné pro vlákna — na rozdíl od java.text.SimpleDateFormat. Formátovače lze vytvářet pomocí vzorů (např. "yyyy-MM-dd HH:mm:ss") nebo pomocí stylů (SHORT, MEDIUM, LONG, FULL) pro lokalizovaný výstup. Knihovna také podporuje cachování formátovačů, což urychluje práci ve vysoce zatížených aplikacích. Parsování dat z řetězců se provádí s kontrolou přísnosti — lze povolit nebo zakázat neúplná nebo nesprávná data.
Jednou z silných stránek Joda-Time je práce s časovými intervaly. Třída Interval představuje přesný časový úsek mezi dvěma okamžiky (Instant) a podporuje operace průniku, sjednocení a kontroly příslušnosti. Period, na rozdíl od Intervalu, pracuje s kalendářními jednotkami — například rozdíl mezi 1. březnem a 1. dubnem je přesně 1 měsíc, i když z hlediska trvání to může být 28–31 dní. Duration představuje přesnou dobu trvání v milisekundách bez vazby na kalendář a je vhodná pro měření doby provádění operací.
import org.joda.time.*;
import org.joda.time.format.*;
DateTime now = new DateTime();
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
String formatted = fmt.print(now);
DateTime nextWeek = now.plusWeeks(1);
Period period = new Period(now, nextWeek);
int days = period.getDays(); // dny = 7
Interval interval = new Interval(now, nextWeek);
boolean contains = interval.contains(now); // pravda
Balíček java.time (JSR-310) byl vyvinut stejným autorem — Stephenem Colebournem — a absorboval nejlepší myšlenky Joda-Time s ohledem na deset let provozních zkušeností. Obě API používají neměnné třídy, podporují časová pásma a nabízejí bohatou sadu operací. Nicméně java.time je plně integrován do standardní knihovny Javy, nevyžaduje externí závislosti a používá konzistentnější systém pojmenování: Instant místo ReadableInstant, LocalDate místo LocalDate (název zachován), ZonedDateTime místo DateTime.
| Vlastnost | Joda-Time | java.time |
|---|---|---|
| Rok vydání | 2004 | 2014 (Java 8) |
| Stav | Údržba (maintenance) | Aktivní vývoj |
| Základní datum/čas | DateTime | ZonedDateTime |
| Pouze datum | LocalDate | LocalDate |
| Neměnnost | Ano | Ano |
| Formátování | DateTimeFormatter | DateTimeFormatter |
| Rozdíl dat | Period / Duration | Period / Duration |
java.time opravuje několik architektonických rozhodnutí Joda-Time. Zaprvé, v java.time byla odstraněna nejasnost mezi metodami vracejícími null a chybějícími hodnotami — místo toho se používá Optional. Zadruhé, byla přidána podpora alternativních kalendářních systémů (japonský, thajský, islámský) prostřednictvím rozhraní Chronology. Zatřetí, java.time obsahuje třídu Instant pro práci s časovými okamžiky v UTC bez vazby na kalendář — to zjednodušuje integraci s síťovými protokoly a databázemi. Pro Android se doporučuje používat java.time od API 26 (Android 8.0) nebo prostřednictvím desugaring pro starší verze.
Migrace z Joda-Time na java.time — proces, který se autoři knihovny snažili učinit co nejbezpečnějším a předvídatelným. Většina tříd má přímé analogy: DateTime → ZonedDateTime, LocalDate → LocalDate (název se shoduje), LocalTime → LocalTime, DateTimeZone → ZoneId, Period → Period, Duration → Duration. Hlavní změny se týkají způsobu vytváření objektů a názvů metod — například místo `now.toDate()` se používá `Date.from(instant)` a `DateTime.now()` je nahrazeno `ZonedDateTime.now()`.
Migraci se doporučuje začít izolací Joda-Time v tenké vrstvě abstrakce — to umožní výměnu knihovny po etapách, aniž by to ovlivnilo celou kódovou základnu. Prvním krokem je obvykle nahrazení práce s LocalDate a LocalTime — tyto třídy mají mezi knihovnami nejméně rozdílů. Druhou etapou se migruje DateTime (na ZonedDateTime) a časová pásma. Třetí etapou se nahrazují Interval, Period a Duration — zde je třeba pečlivě zkontrolovat chování s ohledem na letní čas. Po dokončení migrace je Joda-Time zcela odstraněna ze závislostí projektu.
// Joda-Time (před migrací)
val dateTime = DateTime.now(DateTimeZone.forID("Europe/Moscow"))
val formatted = dateTime.toString("dd.MM.yyyy")
val plusDay = dateTime.plusDays(1)
// java.time (po migraci)
val zonedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Moscow"))
val formatted2 = zonedDateTime.format(
DateTimeFormatter.ofPattern("dd.MM.yyyy")
)
val plusDay2 = zonedDateTime.plusDays(1)
Existuje několik nástrojů, které usnadňují přechod z Joda-Time na java.time. OpenRewrite (by Moderne) podporuje automatickou migraci prostřednictvím receptu org.openrewrite.java.migrate.JodaTimeToJavaTime — nahrazuje třídy, metody a importy při zachování logiky programu. Pro projekty Gradle obsahuje plugin de.fayard.refreshVersions také pravidla migrace. Ručně se migrace nejsnáze provádí pomocí IDEA: plugin Java Time Migration Assistant zvýrazňuje volání Joda-Time a navrhuje odpovídající náhrady v java.time, což urychluje proces 2–3krát ve srovnání s ručním nahrazováním.
Podívejme se na praktické příklady práce s Joda-Time v aplikacích pro Android. Knihovna je zvláště užitečná pro úkoly formátování dat podle místních standardů, výpočtu věku uživatele, určování doby trvání událostí a výpočtu intervalů mezi dvěma daty. Všechny příklady používají neměnné objekty — každé volání vrací novou instanci, zatímco původní objekt zůstává nezměněn.
// Vypočítat věk uživatele
val birthDate = LocalDate.parse("1990-05-15")
val today = LocalDate.now()
val age = Years.yearsBetween(birthDate, today).getYears()
// věk == 36
// Formátovat s locale uživatele
val dateTime = DateTime.now()
val formatter = DateTimeFormat.forStyle("MM").withLocale(Locale.getDefault())
val localized = formatter.print(dateTime)
Intervaly v Joda-Time umožňují efektivně řešit úkoly kontroly průniku časových úseků, výpočtu překryvů a hledání nejbližší události. Například při vývoji kalendářové aplikace lze zkontrolovat, zda se nová událost překrývá s existujícími. Třída Interval podporuje operace contains, overlaps, gap a abuts — poslední ukazuje, zda se intervaly dotýkají na hranicích. K měření doby trvání mezi dvěma okamžiky se používá Duration s přesností na milisekundy.
// Zkontrolovat překrytí intervalu
Interval meeting1 = new Interval(
new DateTime(2026, 7, 21, 10, 0),
new DateTime(2026, 7, 21, 11, 0)
);
Interval meeting2 = new Interval(
new DateTime(2026, 7, 21, 10, 30),
new DateTime(2026, 7, 21, 11, 30)
);
boolean overlaps = meeting1.overlaps(meeting2); // pravda
Duration gap = meeting1.gap(meeting2); // null (bez mezery)
Často kladené otázky
Joda-Time — knihovna pro práci s daty a časem v Javě, vytvořená před vydáním Javy 8. Opravovala nedostatky java.util.Date a java.util.Calendar: mutabilitu, číslování měsíců od 0, chybějící plnou podporu časových pásem a nebezpečné formátování pro vlákna.
Java.time (JSR-310) — nástupce Joda-Time, vyvinutý stejným autorem. Hlavní rozdíly: java.time je zabudován do standardní knihovny Javy 8+, používá konzistentnější systém pojmenování, podporuje alternativní kalendářní systémy a obsahuje třídu Instant pro práci s časem UTC.
Ne, pro nové projekty se doporučuje používat java.time. Joda-Time je od roku 2014 v režimu údržby a již nedostává nové funkce. Pro Android s API 26+ je java.time k dispozici nativně a pro starší verze se používá desugaring — mechanismus umožňující používat java.time na zařízeních s API 19+.
Migrace se provádí po etapách: izolujte Joda-Time v abstrakcích, nahraďte nejprve LocalDate a LocalTime, poté DateTime → ZonedDateTime, a v poslední etapě — Period, Duration a Interval. Nástroj OpenRewrite podporuje automatickou migraci prostřednictvím receptu JodaTimeToJavaTime, což urychluje proces 2–3krát.
DateTime → ZonedDateTime, LocalDate → LocalDate (shoduje se), LocalTime → LocalTime, LocalDateTime → LocalDateTime, DateTimeZone → ZoneId, Interval → nemá přímý analog (používá se kombinace Instant + Duration), Period → Period, Duration → Duration. Názvy metod jsou také podobné: plusDays, minusMonths, withZone jsou dostupné v obou knihovnách.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také