Joda-Time — μια βιβλιοθήκη για εργασία με ημερομηνίες και ώρα σε Java, που δημιουργήθηκε από τον Stephen Colebourne ως εναλλακτική για τις άβολες java.util.Date και java.util.Calendar. Πριν από την κυκλοφορία της Java 8, αυτή η βιβλιοθήκη ήταν το de facto πρότυπο για εργασία με ημερομηνίες στη βιομηχανική ανάπτυξη, συμπεριλαμβανομένων των εφαρμογών Android. Σύμφωνα με το Oracle Java Magazine (2023), περισσότερο από το 60% των έργων μετανάστευσαν από Joda-Time σε java.time εντός δύο ετών από την κυκλοφορία του Android API 26.
Κύρια σημεία
Joda-Time — είναι μια βιβλιοθήκη ανοιχτού κώδικα (Apache 2.0) που παρέχει μια ποιοτική αντικατάσταση για τις κλάσεις java.util.Date και java.util.Calendar στη γλώσσα Java. Το έργο ιδρύθηκε από τον Stephen Colebourne το 2004 ως απάντηση στα πολυάριθμα προβλήματα των τυπικών API για εργασία με ημερομηνίες, συμπεριλαμβανομένης της μεταβλητότητας, της πολυπλοκότητας μορφοποίησης και της περιορισμένης υποστήριξης ζωνών ώρας. Η βιβλιοθήκη απέκτησε ευρεία διάδοση στην κοινότητα Java και χρησιμοποιήθηκε από χιλιάδες έργα, συμπεριλαμβανομένων μεγάλων enterprise συστημάτων και εφαρμογών Android, όπου τα ενσωματωμένα εργαλεία εργασίας με ημερομηνίες ήταν ιδιαίτερα άβολα. Από την κυκλοφορία της Java 8 το 2014 και την εμφάνιση του πακέτου java.time (JSR-310), η βιβλιοθήκη έχει μεταφερθεί σε λειτουργία συντήρησης — νέες λειτουργίες δεν προστίθενται και η υπάρχουσα λειτουργικότητα καλύπτεται πλήρως από το νέο API.
Το Joda-Time είναι χτισμένο στην αρχή της αμεταβλητότητας: κάθε αντικείμενο ημερομηνίας ή ώρας μετά τη δημιουργία δεν μπορεί να αλλάξει. Οποιαδήποτε λειτουργία — προσθήκη ημέρας, αλλαγή μήνα, ρύθμιση ζώνης ώρας — επιστρέφει ένα νέο αντικείμενο, αφήνοντας το πρωτότυπο αμετάβλητο. Αυτή η προσέγγιση εξαλείφει πλήρως την κατηγορία σφαλμάτων που σχετίζονται με μη ηθελημένη τροποποίηση αντικειμένων, η οποία ήταν χαρακτηριστική για τα μεταβλητά java.util.Date και java.util.Calendar. Ο αμετάβλητος σχεδιασμός καθιστά επίσης τη βιβλιοθήκη ασφαλή για νήματα χωρίς πρόσθετο συγχρονισμό.
Η βιβλιοθήκη παρέχει πολλές κύριες κλάσεις για διαφορετικά σενάρια: DateTime — πλήρης ημερομηνία και ώρα με ζώνη ώρας, LocalDate — μόνο ημερομηνία χωρίς ώρα, LocalTime — μόνο ώρα χωρίς ημερομηνία, LocalDateTime — ημερομηνία και ώρα χωρίς σύνδεση με ζώνη. Επιπλέον, περιλαμβάνονται κλάσεις για εργασία με διαστήματα (Interval), περιόδους (Period) και διάρκειες (Duration), που επιτρέπουν τον υπολογισμό της διαφοράς μεταξύ δύο χρονικών στιγμών σε διαφορετικές μονάδες μέτρησης. Κάθε κλάση υλοποιεί τη διεπαφή ReadableInstant ή ReadablePartial για ομοιόμορφη επεξεργασία.
Το πρόβλημα των τυπικών API ημερομηνίας στη Java ήταν γνωστό από την αρχή της πλατφόρμας. Οι κλάσεις java.util.Date και java.util.Calendar είχαν θεμελιώδη ελαττώματα: οι μήνες αριθμούνταν από το 0, το Date αντιπροσώπευε ταυτόχρονα ημερομηνία και ώρα, το Calendar ήταν μεταβλητό και απαιτούσε πολύπλοκη διαμόρφωση. Η μορφοποίηση μέσω SimpleDateFormat δεν ήταν ασφαλής για νήματα — σε πολυνηματικές εφαρμογές οδηγούσε σε εσφαλμένη ανάλυση ημερομηνιών. Ο Stephen Colebourne, εργαζόμενος σε έργα με εντατική χρήση ημερομηνιών, αποφάσισε να δημιουργήσει μια εναλλακτική που θα διόρθωνε όλα αυτά τα προβλήματα.
Σύμφωνα με συνέντευξη στο InfoQ (2014), ο Colebourne δοκίμαζε το Joda-Time σε βιομηχανικά έργα για περισσότερο από ένα χρόνο πριν από την πρώτη δημόσια κυκλοφορία. Η πρώτη έκδοση κυκλοφόρησε το 2004 και αμέσως τράβηξε την προσοχή της κοινότητας. Μέχρι το 2010, η βιβλιοθήκη είχε γίνει τυπική εξάρτηση στα περισσότερα έργα Java, συμπεριλαμβανομένων πλαισίων όπως το Spring και το Hibernate. Η επιτυχία του Joda-Time επηρέασε άμεσα την προδιαγραφή JSR-310 (Date and Time API), η οποία συμπεριλήφθηκε στη Java 8 — και ο συγγραφέας της προδιαγραφής ήταν ο ίδιος Stephen Colebourne.
Το Joda-Time όχι μόνο έλυσε το πρόβλημα ημερομηνίας — άλλαξε την προσέγγιση σχεδιασμού API στη Java. Η ιδέα των αμετάβλητων αντικειμένων τιμής, που επιδείχθηκε από τη βιβλιοθήκη, υιοθετήθηκε ως πρότυπο στη Java 8 για όλα τα νέα API. Επιπλέον, η δημοτικότητα του Joda-Time έδειξε στην Oracle ότι η κοινότητα δεν είναι διατεθειμένη να ανέχεται χαμηλής ποιότητας τυπικές βιβλιοθήκες — μετά το Joda-Time στη Java ανασχεδιάστηκαν τα API για εργασία με αρχεία (NIO.2), χρόνο (java.time) και προαιρετικές τιμές (Optional).
Το Joda-Time παρέχει ένα σύνολο δυνατοτήτων που πριν από τη Java 8 δεν ήταν διαθέσιμο στην τυπική βιβλιοθήκη. Τα κύρια πλεονεκτήματα περιλαμβάνουν υποστήριξη για πάνω από 200 ζώνες ώρας με αυτόματη επεξεργασία θερινής ώρας, πλούσιο API μορφοποίησης με μοτίβα και τοπικές ρυθμίσεις, καθώς και υπολογισμό περιόδων μεταξύ ημερομηνιών σε διάφορες μονάδες — έτη, μήνες, ημέρες, ώρες. Ξεχωριστή προσοχή αξίζει το σύστημα διαστημάτων: Interval (διάστημα μεταξύ δύο στιγμών), Period (διαφορά σε ημερολογιακές μονάδες) και Duration (ακριβής διάρκεια σε χιλιοστά του δευτερολέπτου).
Η κλάση DateTimeFormatter στο Joda-Time παρέχει ασφαλή μορφοποίηση ημερομηνιών για νήματα — σε αντίθεση με το java.text.SimpleDateFormat. Οι μορφοποιητές μπορούν να δημιουργηθούν μέσω μοτίβων (π.χ. "yyyy-MM-dd HH:mm:ss") ή μέσω στυλ (SHORT, MEDIUM, LONG, FULL) για τοπικοποιημένη έξοδο. Η βιβλιοθήκη υποστηρίζει επίσης προσωρινή αποθήκευση μορφοποιητών, γεγονός που επιταχύνει την εργασία σε εφαρμογές υψηλού φόρτου. Η ανάλυση ημερομηνιών από συμβολοσειρές γίνεται με έλεγχο αυστηρότητας — μπορούν να επιτραπούν ή να απαγορευτούν ελλιπείς ή λανθασμένες ημερομηνίες.
Ένα από τα δυνατά σημεία του Joda-Time είναι η εργασία με χρονικά διαστήματα. Η κλάση Interval αντιπροσωπεύει ένα ακριβές χρονικό τμήμα μεταξύ δύο στιγμών (Instant) και υποστηρίζει λειτουργίες τομής, ένωσης και ελέγχου συμμετοχής. Το Period, σε αντίθεση με το Interval, λειτουργεί με ημερολογιακές μονάδες — για παράδειγμα, η διαφορά μεταξύ 1 Μαρτίου και 1 Απριλίου είναι ακριβώς 1 μήνας, αν και σε διάρκεια μπορεί να είναι 28–31 ημέρες. Το Duration αντιπροσωπεύει την ακριβή διάρκεια σε χιλιοστά του δευτερολέπτου χωρίς σύνδεση με ημερολόγιο και είναι κατάλληλο για μέτρηση χρόνου εκτέλεσης λειτουργιών.
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(); // ημέρες = 7
Interval interval = new Interval(now, nextWeek);
boolean contains = interval.contains(now); // αληθές
Το πακέτο java.time (JSR-310) αναπτύχθηκε από τον ίδιο συγγραφέα — Stephen Colebourne — και απορρόφησε τις καλύτερες ιδέες του Joda-Time, λαμβάνοντας υπόψη δέκα χρόνια λειτουργικής εμπειρίας. Και τα δύο API χρησιμοποιούν αμετάβλητες κλάσεις, υποστηρίζουν ζώνες ώρας και προσφέρουν πλούσιο σύνολο λειτουργιών. Ωστόσο, το java.time είναι πλήρως ενσωματωμένο στην τυπική βιβλιοθήκη Java, δεν απαιτεί εξωτερικές εξαρτήσεις και χρησιμοποιεί ένα πιο συνεπές σύστημα ονοματοδοσίας: Instant αντί για ReadableInstant, LocalDate αντί για LocalDate (το όνομα διατηρήθηκε), ZonedDateTime αντί για DateTime.
| Χαρακτηριστικό | Joda-Time | java.time |
|---|---|---|
| Έτος κυκλοφορίας | 2004 | 2014 (Java 8) |
| Κατάσταση | Συντήρηση (maintenance) | Ενεργή ανάπτυξη |
| Βασική ημερομηνία/ώρα | DateTime | ZonedDateTime |
| Μόνο ημερομηνία | LocalDate | LocalDate |
| Αμεταβλητότητα | Ναι | Ναι |
| Μορφοποίηση | DateTimeFormatter | DateTimeFormatter |
| Διαφορά ημερομηνιών | Period / Duration | Period / Duration |
Το java.time διορθώνει αρκετές αρχιτεκτονικές αποφάσεις του Joda-Time. Πρώτον, στο java.time εξαλείφθηκε η σύγχυση μεταξύ μεθόδων που επιστρέφουν null και τιμών που λείπουν — αντί αυτού χρησιμοποιείται Optional. Δεύτερον, προστέθηκε υποστήριξη για εναλλακτικά ημερολογιακά συστήματα (Ιαπωνικό, Ταϊλανδικό, Ισλαμικό) μέσω της διεπαφής Chronology. Τρίτον, το java.time περιλαμβάνει την κλάση Instant για εργασία με χρονικές στιγμές σε UTC χωρίς σύνδεση με ημερολόγιο — αυτό απλοποιεί την ενσωμάτωση με πρωτόκολλα δικτύου και βάσεις δεδομένων. Για Android, συνιστάται η χρήση java.time από API 26 (Android 8.0) ή μέσω desugaring για παλαιότερες εκδόσεις.
Η μετάβαση από Joda-Time σε java.time — μια διαδικασία που οι συγγραφείς της βιβλιοθήκης προσπάθησαν να κάνουν όσο το δυνατόν ασφαλέστερη και προβλέψιμη. Οι περισσότερες κλάσεις έχουν άμεσα αντίστοιχες: DateTime → ZonedDateTime, LocalDate → LocalDate (το όνομα συμπίπτει), LocalTime → LocalTime, DateTimeZone → ZoneId, Period → Period, Duration → Duration. Οι κύριες αλλαγές αφορούν τον τρόπο δημιουργίας αντικειμένων και τα ονόματα μεθόδων — για παράδειγμα, αντί για `now.toDate()` χρησιμοποιείται `Date.from(instant)`, και το `DateTime.now()` αντικαθίσταται από `ZonedDateTime.now()`.
Συνιστάται η μετάβαση να ξεκινήσει με την απομόνωση του Joda-Time σε ένα λεπτό στρώμα αφαίρεσης — αυτό θα επιτρέψει την αντικατάσταση της βιβλιοθήκης σταδιακά, χωρίς να επηρεαστεί ολόκληρη η βάση κώδικα. Το πρώτο βήμα είναι συνήθως η αντικατάσταση της εργασίας με LocalDate και LocalTime — αυτές οι κλάσεις έχουν τις λιγότερες διαφορές μεταξύ των βιβλιοθηκών. Στο δεύτερο στάδιο γίνεται μετάβαση του DateTime (σε ZonedDateTime) και των ζωνών ώρας. Στο τρίτο στάδιο αντικαθίστανται τα Interval, Period και Duration — εδώ πρέπει να ελεγχθεί προσεκτικά η συμπεριφορά λαμβάνοντας υπόψη τη θερινή ώρα. Μετά την ολοκλήρωση της μετάβασης, το Joda-Time αφαιρείται πλήρως από τις εξαρτήσεις του έργου.
// Joda-Time (πριν από τη μετάβαση)
val dateTime = DateTime.now(DateTimeZone.forID("Europe/Moscow"))
val formatted = dateTime.toString("dd.MM.yyyy")
val plusDay = dateTime.plusDays(1)
// java.time (μετά από τη μετάβαση)
val zonedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Moscow"))
val formatted2 = zonedDateTime.format(
DateTimeFormatter.ofPattern("dd.MM.yyyy")
)
val plusDay2 = zonedDateTime.plusDays(1)
Υπάρχουν αρκετά βοηθητικά προγράμματα που διευκολύνουν τη μετάβαση από Joda-Time σε java.time. Το OpenRewrite (by Moderne) υποστηρίζει αυτόματη μετάβαση μέσω της συνταγής org.openrewrite.java.migrate.JodaTimeToJavaTime — αντικαθιστά κλάσεις, μεθόδους και imports, διατηρώντας τη λογική του προγράμματος. Για έργα Gradle, το πρόσθετο de.fayard.refreshVersions περιλαμβάνει επίσης κανόνες μετάβασης. Χειροκίνητα, η μετάβαση είναι πιο εύκολο να πραγματοποιηθεί με IDEA: το πρόσθετο Java Time Migration Assistant επισημαίνει κλήσεις Joda-Time και προτείνει αντίστοιχες αντικαταστάσεις σε java.time, γεγονός που επιταχύνει τη διαδικασία 2–3 φορές σε σύγκριση με τη χειροκίνητη αντικατάσταση.
Ας εξετάσουμε πρακτικά παραδείγματα εργασίας με Joda-Time σε εφαρμογές Android. Η βιβλιοθήκη είναι ιδιαίτερα χρήσιμη για εργασίες μορφοποίησης ημερομηνιών σύμφωνα με τοπικά πρότυπα, υπολογισμού ηλικίας χρήστη, προσδιορισμού διάρκειας γεγονότων και υπολογισμού διαστημάτων μεταξύ δύο ημερομηνιών. Όλα τα παραδείγματα χρησιμοποιούν αμετάβλητα αντικείμενα — κάθε κλήση επιστρέφει ένα νέο στιγμιότυπο, ενώ το αρχικό αντικείμενο παραμένει αμετάβλητο.
// Υπολογισμός ηλικίας χρήστη
val birthDate = LocalDate.parse("1990-05-15")
val today = LocalDate.now()
val age = Years.yearsBetween(birthDate, today).getYears()
// ηλικία == 36
// Μορφοποίηση με τοπικές ρυθμίσεις χρήστη
val dateTime = DateTime.now()
val formatter = DateTimeFormat.forStyle("MM").withLocale(Locale.getDefault())
val localized = formatter.print(dateTime)
Τα διαστήματα στο Joda-Time επιτρέπουν την αποτελεσματική επίλυση εργασιών ελέγχου τομής χρονικών τμημάτων, υπολογισμού επικαλύψεων και εύρεσης του πλησιέστερου γεγονότος. Για παράδειγμα, κατά την ανάπτυξη μιας εφαρμογής ημερολογίου μπορεί να ελεγχθεί αν ένα νέο γεγονός επικαλύπτεται με υπάρχοντα γεγονότα. Η κλάση Interval υποστηρίζει λειτουργίες contains, overlaps, gap και abuts — η τελευταία δείχνει αν τα διαστήματα εφάπτονται στα όρια. Για τη μέτρηση διάρκειας μεταξύ δύο στιγμών χρησιμοποιείται Duration με ακρίβεια χιλιοστού του δευτερολέπτου.
// Έλεγχος επικάλυψης διαστήματος
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); // αληθές
Duration gap = meeting1.gap(meeting2); // null (χωρίς κενό)
Συχνές Ερωτήσεις
Joda-Time — μια βιβλιοθήκη για εργασία με ημερομηνίες και ώρα σε Java, που δημιουργήθηκε πριν από την κυκλοφορία της Java 8. Διόρθωνε τα ελαττώματα των java.util.Date και java.util.Calendar: μεταβλητότητα, αρίθμηση μηνών από το 0, έλλειψη πλήρους υποστήριξης ζωνών ώρας και μη ασφαλή μορφοποίηση για νήματα.
Το Java.time (JSR-310) — ο διάδοχος του Joda-Time, που αναπτύχθηκε από τον ίδιο συγγραφέα. Κύριες διαφορές: το java.time είναι ενσωματωμένο στην τυπική βιβλιοθήκη Java 8+, χρησιμοποιεί πιο συνεπές σύστημα ονοματοδοσίας, υποστηρίζει εναλλακτικά ημερολογιακά συστήματα και περιλαμβάνει την κλάση Instant για εργασία με ώρα UTC.
Όχι, για νέα έργα συνιστάται η χρήση java.time. Το Joda-Time είναι σε λειτουργία συντήρησης από το 2014 και δεν λαμβάνει πλέον νέες λειτουργίες. Για Android με API 26+, το java.time είναι διαθέσιμο εγγενώς, και για παλαιότερες εκδόσεις χρησιμοποιείται desugaring — ένας μηχανισμός που επιτρέπει τη χρήση java.time σε συσκευές με API 19+.
Η μετάβαση γίνεται σταδιακά: απομονώστε το Joda-Time σε αφαιρέσεις, αντικαταστήστε πρώτα LocalDate και LocalTime, στη συνέχεια DateTime → ZonedDateTime, και στο τελικό στάδιο — Period, Duration και Interval. Το εργαλείο OpenRewrite υποστηρίζει αυτόματη μετάβαση μέσω της συνταγής JodaTimeToJavaTime, επιταχύνοντας τη διαδικασία 2–3 φορές.
DateTime → ZonedDateTime, LocalDate → LocalDate (συμπίπτει), LocalTime → LocalTime, LocalDateTime → LocalDateTime, DateTimeZone → ZoneId, Interval → δεν έχει άμεσο αντίστοιχο (χρησιμοποιείται συνδυασμός Instant + Duration), Period → Period, Duration → Duration. Τα ονόματα μεθόδων είναι επίσης παρόμοια: plusDays, minusMonths, withZone είναι διαθέσιμα και στις δύο βιβλιοθήκες.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης