Joda-Time: ما هي، مكتبة التواريخ والاستبدال بـ Java 8

المؤلف: IT Sectr نُشر: 2026-07-14 وقت القراءة: 10 دق

Joda-Time هي مكتبة للعمل مع التواريخ والأوقات في Java، أنشأها Stephen Colebourne كبديل للفئات المرهقة java.util.Date و java.util.Calendar. قبل إصدار Java 8، كانت هذه المكتبة هي المعيار الفعلي للتعامل مع التواريخ في التطوير الصناعي، بما في ذلك تطبيقات Android. وفقاً لـ Oracle Java Magazine (2023)، هاجر أكثر من 60% من المشاريع من Joda-Time إلى java.time في غضون عامين بعد إصدار Android API 26.

الملامح الرئيسية

  • Joda-Time — مكتبة للعمل مع التواريخ والأوقات، سابقة لـ java.time في Java 8
  • الفئات غير القابلة للتغيير — DateTime، LocalDate، LocalTime، LocalDateTime تضمن أمان الخيوط
  • دعم المناطق الزمنية — قاعدة بيانات مناطق زمنية مدمجة مع تحديثات تلقائية
  • الهجرة — java.time (JSR-310) يحل محل Joda-Time بالكامل، يوصي مؤلفو المكتبة بالانتقال
  • Android — يعمل Joda-Time من API 1، ولكن لـ Android 8+ (API 26) يُفضل java.time

ما هو Joda-Time؟

Joda-Time هي مكتبة مفتوحة المصدر (Apache 2.0) توفر بديلاً عالي الجودة لفئتي java.util.Date و java.util.Calendar في لغة Java. أسس المشروع Stephen Colebourne في عام 2004 استجابةً للمشاكل العديدة في واجهات برمجة التطبيقات القياسية للتواريخ والأوقات، بما في ذلك قابلية التغيير، وتعقيد التنسيق، والدعم المحدود للمناطق الزمنية. حظيت المكتبة بتبني واسع في مجتمع Java واستخدمتها آلاف المشاريع، بما في ذلك أنظمة المؤسسات الكبيرة وتطبيقات Android، حيث كانت أدوات التعامل مع التواريخ المدمجة غير ملائمة بشكل خاص. منذ إصدار Java 8 في عام 2014 وظهور حزمة java.time (JSR-310)، تم وضع المكتبة في وضع الصيانة — لا تتم إضافة ميزات جديدة، والوظائف الحالية مغطاة بالكامل بواسطة API الجديدة.

هندسة المكتبة

تم بناء Joda-Time على مبدأ عدم القابلية للتغيير: لا يمكن تغيير أي كائن تاريخ أو وقت بعد إنشائه. أي عملية — إضافة يوم، تغيير شهر، تعيين منطقة زمنية — تُرجع كائناً جديداً، تاركة الأصل دون تغيير. هذا النهج يزيل تماماً فئة الأخطاء المرتبطة بالتعديل غير المقصود للكائنات التي كانت سمة مميزة للفئات القابلة للتغيير java.util.Date و java.util.Calendar. التصميم غير القابل للتغيير يجعل المكتبة أيضاً آمنة للخيوط دون مزامنة إضافية.

الفئات الأساسية لـ Joda-Time

توفر المكتبة عدة فئات رئيسية لسيناريوهات مختلفة: DateTime — التاريخ والوقت الكامل مع المنطقة الزمنية، LocalDate — التاريخ فقط بدون وقت، LocalTime — الوقت فقط بدون تاريخ، LocalDateTime — التاريخ والوقت بدون ربط بالمنطقة الزمنية. بالإضافة إلى ذلك، تم تضمين فئات للعمل مع الفترات (Interval) والفترات الزمنية (Period) والمدد (Duration)، التي تسمح بحساب الفرق بين نقطتين زمنيتين بوحدات قياس مختلفة. كل فئة تطبق واجهة ReadableInstant أو ReadablePartial للمعالجة الموحدة.

تاريخ ظهور Joda-Time

كانت مشكلة واجهات برمجة التطبيقات القياسية للتواريخ في 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 كقائد للمواصفات.

التأثير على نظام Java البيئي

لم تحل Joda-Time مشكلة التواريخ فحسب — بل غيرت نهج تصميم واجهات برمجة التطبيقات في Java. مفهوم كائنات القيم غير القابلة للتغيير، الذي أظهرته المكتبة، تم اعتماده كمعيار في Java 8 لجميع واجهات برمجة التطبيقات الجديدة. علاوة على ذلك، أظهرت شعبية Joda-Time لـ Oracle أن المجتمع غير مستعد لتحمل المكتبات القياسية منخفضة الجودة — بعد Joda-Time، تم إعادة تصميم واجهات برمجة التطبيقات في Java للتعامل مع الملفات (NIO.2) والوقت (java.time) والقيم الاختيارية (Optional).

الميزات الرئيسية لمكتبة Joda-Time

توفر Joda-Time مجموعة من الميزات التي لم تكن متوفرة في المكتبة القياسية قبل Java 8. تشمل المزايا الرئيسية دعم أكثر من 200 منطقة زمنية مع معالجة تلقائية للتوقيت الصيفي، واجهة برمجة تطبيقات غنية للتنسيق مع الأنماط والإعدادات المحلية، بالإضافة إلى حسابات الفترات بين التواريخ بوحدات مختلفة — سنوات، أشهر، أيام، ساعات. يستحق نظام الفترات اهتماماً خاصاً: Interval (فاصل زمني بين لحظتين)، Period (الفرق بوحدات التقويم)، و Duration (المدة الدقيقة بالملي ثانية).

التنسيق والتحليل

توفر الفئة DateTimeFormatter في Joda-Time تنسيقاً آمناً للخيوط — على عكس java.text.SimpleDateFormat. يمكن إنشاء المنسقات من خلال الأنماط (مثل «yyyy-MM-dd HH:mm:ss») أو من خلال الأنماط (SHORT، MEDIUM، LONG، FULL) للإخراج المترجم. تدعم المكتبة أيضاً التخزين المؤقت للمنسقات، مما يسرع الأداء في التطبيقات عالية الحمل. يتم تحليل التواريخ من السلاسل النصية مع التحكم في الدقة — يمكن السماح أو منع التواريخ غير الكاملة أو غير الصحيحة.

الفترات والحسابات

من النقاط القوية لـ Joda-Time العمل مع الفترات الزمنية. تمثل الفئة Interval فاصل زمني دقيق بين لحظتين وتدعم عمليات التقاطع والاتحاد والتحقق من الاحتواء. Period، على عكس Interval، تعمل بوحدات التقويم — على سبيل المثال، الفرق بين 1 مارس و 1 أبريل هو شهر واحد بالضبط، على الرغم من أن المدة قد تكون 28–31 يوماً. Duration، من ناحية أخرى، تمثل مدة دقيقة بالملي ثانية دون ربط بالتقويم وهي مناسبة لقياس وقت تنفيذ العمليات.

java
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); // صحيح

مقارنة بين Joda-Time و java.time

تم تطوير حزمة java.time (JSR-310) بواسطة نفس المؤلف — Stephen Colebourne — ودمجت أفضل أفكار Joda-Time مع عشر سنوات من الخبرة التشغيلية. تستخدم كلتا الواجهتين فئات غير قابلة للتغيير، وتدعمان المناطق الزمنية، وتوفران مجموعة غنية من العمليات. ومع ذلك، فإن java.time مدمجة بالكامل في المكتبة القياسية لـ Java، ولا تتطلب تبعيات خارجية، وتستخدم نظام تسمية أكثر اتساقاً: Instant بدلاً من ReadableInstant، LocalDate بدلاً من LocalDate (الاسم محفوظ)، ZonedDateTime بدلاً من DateTime.

الخاصيةJoda-Timejava.time
سنة الإصدار20042014 (Java 8)
الحالةصيانةتطوير نشط
التاريخ/الوقت الأساسيDateTimeZonedDateTime
التاريخ فقطLocalDateLocalDate
عدم القابلية للتغييرنعمنعم
التنسيقDateTimeFormatterDateTimeFormatter
فرق التواريخPeriod / DurationPeriod / 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

الهجرة من 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 بالكامل من تبعيات المشروع.

kotlin
// 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 (من Moderne) الهجرة التلقائية من خلال وصفة org.openrewrite.java.migrate.JodaTimeToJavaTime — حيث يستبدل الفئات والطرق والواردات مع الحفاظ على منطق البرنامج. بالنسبة لمشاريع Gradle، يتضمن إضافة de.fayard.refreshVersions أيضاً قواعد الهجرة. يدوياً، أسهل طريقة لإجراء الهجرة هي باستخدام IDEA: إضافة Java Time Migration Assistant تضيء استدعاءات Joda-Time وتقترح البدائل المقابلة في java.time، مما يسرع العملية بمقدار 2–3 مرات مقارنة بالاستبدال اليدوي.

أمثلة كود Joda-Time

دعنا نلقي نظرة على أمثلة عملية للعمل مع Joda-Time في تطبيقات Android. المكتبة مفيدة بشكل خاص لتنسيق التواريخ وفقاً للمعايير المحلية، وحساب عمر المستخدم، وتحديد مدة الأحداث، وحساب الفترات بين تاريخين. جميع الأمثلة تستخدم كائنات غير قابلة للتغيير — كل استدعاء يُرجع مثيلاً جديداً، بينما يبقى الكائن الأصلي دون تغيير.

kotlin
// حساب عمر المستخدم
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 لقياس المدة بين لحظتين بدقة ملي ثانية.

java
// التحقق من تداخل الفترات
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); // فارغ (لا توجد فجوة)

الأسئلة المتكررة

ما هو Joda-Time ولماذا هو مطلوب؟

Joda-Time هي مكتبة للعمل مع التواريخ والأوقات في Java، تم إنشاؤها قبل إصدار Java 8. لقد صححت عيوب java.util.Date و java.util.Calendar: قابلية التغيير، ترقيم الأشهر من 0، عدم وجود دعم كامل للمناطق الزمنية، والتنسيق غير الآمن للخيوط.

كيف يختلف Joda-Time عن java.time؟

Java.time (JSR-310) هو خليفة Joda-Time، الذي طوره نفس المؤلف. الاختلافات الرئيسية: java.time مدمج في المكتبة القياسية لـ Java 8+، ويستخدم نظام تسمية أكثر اتساقاً، ويدعم أنظمة التقويم البديلة، ويتضمن فئة Instant للعمل مع وقت UTC.

هل يجب استخدام Joda-Time في المشاريع الجديدة؟

لا، للمشاريع الجديدة يوصى باستخدام java.time. Joda-Time في وضع الصيانة منذ 2014 ولم يعد يتلقى ميزات جديدة. لـ Android مع API 26+، java.time متاح أصلاً، وللإصدارات الأقدم يُستخدم desugaring — آلية تسمح باستخدام java.time على الأجهزة ذات API 19+.

كيفية ترحيل الكود من Joda-Time إلى java.time؟

يتم الترحيل خطوة بخطوة: اعزل Joda-Time في تجريدات، واستبدل LocalDate و LocalTime أولاً، ثم DateTime → ZonedDateTime، وأخيراً Period و Duration و Interval. أداة OpenRewrite تدعم الترحيل التلقائي من خلال وصفة JodaTimeToJavaTime، مما يسرع العملية بمقدار 2–3 مرات.

ما هي فئات Joda-Time المقابلة لـ java.time؟

DateTime → ZonedDateTime، LocalDate → LocalDate (متطابق)، LocalTime → LocalTime، LocalDateTime → LocalDateTime، DateTimeZone → ZoneId، Interval → لا يوجد مقابل مباشر (يُستخدم مزيج من Instant + Duration)، Period → Period، Duration → Duration. أسماء الطرق متشابهة أيضاً: plusDays، minusMonths، withZone متوفرة في كلتا المكتبتين.

الخلاصة

  • Joda-Time هي مكتبة تواريخ وأوقات لـ Java أصبحت المعيار قبل Java 8 وأثرت بشكل مباشر على مواصفة JSR-310
  • تصميم غير قابل للتغيير — جميع فئات Joda-Time غير قابلة للتغيير، مما يضمن أمان الخيوط ويمنع التحولات العرضية للكائنات
  • الفئات الرئيسية — DateTime، LocalDate، LocalTime، LocalDateTime، Period، Duration و Interval تغطي جميع سيناريوهات التواريخ والأوقات
  • دعم المناطق الزمنية — قاعدة بيانات مدمجة لأكثر من 200 منطقة زمنية مع معالجة تلقائية للتوقيت الصيفي وتحديثات
  • الهجرة إلى java.time — موصى بها لجميع المشاريع؛ تتم بشكل تدريجي باستخدام أدوات OpenRewrite وميزات IDE المدمجة
  • التوافق مع Android — يعمل Joda-Time من API 1، ولكن لـ API 26+ يُفضل java.time، المتاح من خلال desugaring في الإصدارات الأقدم
  • الأهمية التاريخية — غير Joda-Time نهج تصميم واجهات برمجة التطبيقات في Java وأظهر للمجتمع فوائد كائنات القيم غير القابلة للتغيير

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا