Joda-Time — کتابخانهای برای کار با تاریخ و زمان در جاوا، ساخته شده توسط استیون کولبورن به عنوان جایگزینی برای java.util.Date و java.util.Calendar ناخوشایند. قبل از انتشار جاوا 8، این کتابخانه دوفاکتو استاندارد کار با تاریخها در توسعه صنعتی، از جمله برنامههای اندروید بود. طبق دادههای Oracle Java Magazine (2023)، بیش از ۶۰٪ پروژهها ظرف دو سال پس از انتشار Android API 26 از Joda-Time به java.time مهاجرت کردند.
نکات اصلی
Joda-Time — یک کتابخانه متنباز (Apache 2.0) است که جایگزینی باکیفیت برای کلاسهای java.util.Date و java.util.Calendar در زبان جاوا ارائه میدهد. این پروژه توسط استیون کولبورن در سال ۲۰۰۴ در پاسخ به مشکلات متعدد APIهای استاندارد کار با تاریخها، از جمله قابلیت تغییر، پیچیدگی قالببندی و پشتیبانی محدود از مناطق زمانی تأسیس شد. این کتابخانه در جامعه جاوا گسترش یافت و توسط هزاران پروژه، از جمله سیستمهای سازمانی بزرگ و برنامههای اندروید استفاده شد، جایی که ابزارهای داخلی کار با تاریخها به ویژه ناخوشایند بودند. از زمان انتشار جاوا ۸ در سال ۲۰۱۴ و ظهور بسته java.time (JSR-310)، کتابخانه به حالت نگهداری منتقل شده است — ویژگیهای جدید اضافه نمیشوند و عملکرد موجود به طور کامل توسط API جدید پوشش داده میشود.
Joda-Time بر اساس اصل تغییرناپذیری ساخته شده است: هر شیء تاریخ یا زمان پس از ایجاد قابل تغییر نیست. هر عملیات — افزودن روز، تغییر ماه، تنظیم منطقه زمانی — یک شیء جدید برمیگرداند و اصل را بدون تغییر باقی میگذارد. این رویکرد به طور کامل کلاس خطاهای مربوط به تغییر غیرعمدی اشیاء را که مشخصه java.util.Date و java.util.Calendar قابل تغییر بود، حذف میکند. طراحی تغییرناپذیر همچنین کتابخانه را بدون همگامسازی اضافی، نخایمن میکند.
کتابخانه چندین کلاس اصلی برای سناریوهای مختلف ارائه میدهد: DateTime — تاریخ و زمان کامل با منطقه زمانی، LocalDate — فقط تاریخ بدون زمان، LocalTime — فقط زمان بدون تاریخ، LocalDateTime — تاریخ و زمان بدون وابستگی به منطقه. علاوه بر این، کلاسهایی برای کار با بازهها (Interval)، دورهها (Period) و مدتها (Duration) گنجانده شده است که امکان محاسبه تفاوت بین دو لحظه زمانی را در واحدهای مختلف اندازهگیری فراهم میکند. هر کلاس رابط ReadableInstant یا ReadablePartial را برای پردازش یکپارچه پیادهسازی میکند.
مشکل APIهای استاندارد تاریخ در جاوا از همان ابتدای پلتفرم شناخته شده بود. کلاسهای java.util.Date و java.util.Calendar دارای نقصهای اساسی بودند: ماهها از ۰ شمارهگذاری میشدند، Date همزمان تاریخ و زمان را نشان میداد، Calendar قابل تغییر بود و نیاز به پیکربندی پیچیده داشت. قالببندی از طریق SimpleDateFormat نخایمن نبود — در برنامههای چندنخی این منجر به تجزیه نادرست تاریخها میشد. استیون کولبورن، در حین کار روی پروژههایی با استفاده فشرده از تاریخها، تصمیم گرفت جایگزینی ایجاد کند که همه این مشکلات را برطرف کند.
به گزارش مصاحبه در InfoQ (۲۰۱۴)، کولبورن Joda-Time را بیش از یک سال قبل از اولین انتشار عمومی در پروژههای صنعتی آزمایش میکرد. اولین نسخه در سال ۲۰۰۴ منتشر شد و بلافاصله توجه جامعه را جلب کرد. تا سال ۲۰۱۰، این کتابخانه به یک وابستگی استاندارد در اکثر پروژههای جاوا، از جمله فریمورکهایی مانند Spring و Hibernate تبدیل شد. موفقیت Joda-Time مستقیماً بر مشخصات JSR-310 (Date and Time API) تأثیر گذاشت که در جاوا ۸ گنجانده شد — و نویسنده مشخصات همان استیون کولبورن بود.
Joda-Time نه تنها مشکل تاریخ را حل کرد — بلکه رویکرد طراحی API در جاوا را تغییر داد. مفهوم اشیاء مقدار تغییرناپذیر که توسط کتابخانه به نمایش گذاشته شد، به عنوان استاندارد در جاوا ۸ برای همه APIهای جدید پذیرفته شد. علاوه بر این، محبوبیت Joda-Time به Oracle نشان داد که جامعه حاضر به تحمل کتابخانههای استاندارد بیکیفیت نیست — پس از Joda-Time در جاوا APIهای کار با فایلها (NIO.2)، زمان (java.time) و مقادیر اختیاری (Optional) بازطراحی شدند.
Joda-Time مجموعهای از امکانات را ارائه میدهد که قبل از جاوا ۸ در کتابخانه استاندارد در دسترس نبود. مزایای اصلی شامل پشتیبانی از بیش از ۲۰۰ منطقه زمانی با پردازش خودکار ساعت تابستانی، API غنی قالببندی با الگوها و localeها، و همچنین محاسبه دورههای بین تاریخها در واحدهای مختلف — سالها، ماهها، روزها، ساعتها است. سیستم بازهها شایسته توجه ویژه است: 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، با واحدهای تقویمی کار میکند — برای مثال، تفاوت بین ۱ مارس و ۱ آوریل دقیقاً ۱ ماه است، اگرچه از نظر مدت ممکن است ۲۸–۳۱ روز باشد. 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(); // روزها = ۷
Interval interval = new Interval(now, nextWeek);
boolean contains = interval.contains(now); // درست
بسته java.time (JSR-310) توسط همان نویسنده — استیون کولبورن — توسعه یافته و بهترین ایدههای Joda-Time را با در نظر گرفتن ده سال تجربه عملیاتی جذب کرده است. هر دو API از کلاسهای تغییرناپذیر استفاده میکنند، از مناطق زمانی پشتیبانی میکنند و مجموعه غنی از عملیات را ارائه میدهند. با این حال، java.time به طور کامل در کتابخانه استاندارد جاوا ادغام شده است، به وابستگیهای خارجی نیاز ندارد و از سیستم نامگذاری منسجمتری استفاده میکند: Instant به جای ReadableInstant، LocalDate به جای LocalDate (نام حفظ شده)، ZonedDateTime به جای DateTime.
| ویژگی | Joda-Time | java.time |
|---|---|---|
| سال انتشار | ۲۰۰۴ | ۲۰۱۴ (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 بدون وابستگی به تقویم است — این کار ادغام با پروتکلهای شبکه و پایگاههای داده را ساده میکند. برای اندروید توصیه میشود از java.time از API ۲۶ (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 پشتیبانی میکند — آن کلاسها، روشها و importها را جایگزین میکند و منطق برنامه را حفظ میکند. برای پروژههای Gradle، پلاگین de.fayard.refreshVersions نیز شامل قوانین مهاجرت است. مهاجرت دستی با IDEA سادهتر است: پلاگین Java Time Migration Assistant فراخوانیهای Joda-Time را برجسته میکند و جایگزینهای مربوطه را در java.time پیشنهاد میدهد که فرآیند را ۲–۳ برابر در مقایسه با جایگزینی دستی سرعت میبخشد.
بیایید نمونههای عملی کار با Joda-Time در برنامههای اندروید را بررسی کنیم. این کتابخانه به ویژه برای کارهای قالببندی تاریخ مطابق با استانداردهای محلی، محاسبه سن کاربر، تعیین مدت رویدادها و محاسبه بازههای بین دو تاریخ مفید است. همه نمونهها از اشیاء تغییرناپذیر استفاده میکنند — هر فراخوانی یک نمونه جدید برمیگرداند و شیء اصلی بدون تغییر باقی میماند.
// محاسبه سن کاربر
val birthDate = LocalDate.parse("1990-05-15")
val today = LocalDate.now()
val age = Years.yearsBetween(birthDate, today).getYears()
// سن == ۳۶
// قالببندی با locale کاربر
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.util.Date و java.util.Calendar را برطرف میکرد: قابلیت تغییر، شمارهگذاری ماهها از ۰، عدم پشتیبانی کامل از مناطق زمانی و قالببندی غیر نخایمن.
Java.time (JSR-310) — جانشین Joda-Time است که توسط همان نویسنده توسعه یافته است. تفاوتهای اصلی: java.time در کتابخانه استاندارد جاوا ۸+ تعبیه شده است، از سیستم نامگذاری منسجمتری استفاده میکند، از سیستمهای تقویم جایگزین پشتیبانی میکند و شامل کلاس Instant برای کار با زمان UTC است.
خیر، برای پروژههای جدید استفاده از java.time توصیه میشود. Joda-Time از سال ۲۰۱۴ در حالت نگهداری است و دیگر امکانات جدیدی دریافت نمیکند. برای اندروید با API ۲۶+، java.time به صورت بومی در دسترس است و برای نسخههای قدیمیتر از desugaring استفاده میشود — مکانیزمی که امکان استفاده از java.time را در دستگاههای با API ۱۹+ فراهم میکند.
مهاجرت به صورت مرحلهای انجام میشود: Joda-Time را در انتزاعات جداسازی کنید، ابتدا LocalDate و LocalTime را جایگزین کنید، سپس DateTime → ZonedDateTime، و در مرحله آخر — Period، Duration و Interval. ابزار OpenRewrite از مهاجرت خودکار از طریق دستور JodaTimeToJavaTime پشتیبانی میکند که فرآیند را ۲–۳ برابر سرعت میبخشد.
DateTime → ZonedDateTime، LocalDate → LocalDate (مطابقت دارد)، LocalTime → LocalTime، LocalDateTime → LocalDateTime، DateTimeZone → ZoneId، Interval → مشابه مستقیم ندارد (ترکیبی از Instant + Duration استفاده میشود)، Period → Period، Duration → Duration. نام روشها نیز نزدیک است: plusDays، minusMonths، withZone در هر دو کتابخانه در دسترس هستند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید