تبدیل تاریخ در جاوا با کتابخانه icu4j شرکت IBM
دوستان سلام. من میخاستم تاریخ شمسی رو به میلادی و قمری در زبان جاوا تبدیل کنم. بعد از چندین روز جستجوی های فراوان و صرف وقت بسیار، در نهایت تونستم این کار رو بکنم. در این پست می خوام به شما هم آموزش بدم تا وقت شما مثل من تلف نشه
در این پست می خوام اموزش تبدیل انواع تاریخ (شمسی به میلادی، شمسی به قمری، میلادی به شمسی، میلادی به قمری، قمری به شمسی و قمری به میلادی) رو آموزش بدم.
اول از همه بگم که با کلاس های خود جاوا نمیتونید این کار رو انجام بدید و بنابراین مجبورید از کد ها و لایبرری هایی که دیگران نوشته اند استفاده کنید.
اگه در گوگل سرچ کنید، کد های زیادی برای تبدیل تاریخ در جاوا پیدا میکنید، ولی اکثرا ایراد دارن و در یک محدوده ی زمانی مشخصی خروجی درست میدن و برای بقیه زمان ها خروجی غلط میدن.
من بعد از کلی جستجو، یک کتابخانه ی بسیار خوب به نام icu4j پیدا کردم که توسط شرکت معروف IBM برای جاوا توسعه داده شده. این کتابخانه قابلیت های فراوانی داره و یکی از قابلیت هاش تبدیل انواع تاریخ به یکدیگر هست.
دوستان عزیز، متاسفانه متاسفانه و باز هم متاسفانه، در اینترنت داکیومنت و مستند سازی خوبی برای این لایبرری و نحوه ی تبدیل تاریخ با اون انجام نشده و شما کل اینترنت رو جستجو کنید شاید یک یا نهایتا دو نمونه کد ازش پیدا کنید. من با کلی بدبختی و آزمون و خطا و صرف کلی وقت تونستم در نهایت باهاش تبدیل تاریخ انجام بدم و در مقاله میخوام به شما هم آموزش بدم.
دوستان یک نکته رو همینجا بگم، اصولا مبحث تبدیل تاریخ کار راحتی نیست، مفاهیم بسیار زیادی داره، از جمله سال کبیسه، مبحث timezone و موارد دیگه که مجالش نیست براتون بگم. خب بریم سراغ اصل کار و من براتون چندتا کد مینویسم و تبدیل تاریخ انجام میدیم.
دانلود فایل لایبرری icu4j و اضافه کردن ان به اکلیپس:
فایل icu4j.jar رو میتونید از اینجا دانلود کنید و به پروژتون در اکلیپس اضافه کنید (اسکرین شات های زیر رو تماشا کنید).
دوستان زمانی که من دارم این پست رو مینویسم اخرین ورژن این لایبرری 65.1 هست. ورژن های جدید تر ممکنه در آینده بیاد. برای دانلود آخرین ورژن لایبرری روی عدد 65.1 کلیک کنید (مطابق عکس زیر)
و اما بریم سراغ مثال ها و چندتا نمونه کد براتون بنویسم. اولین مثال رو به هر دو روش مینویسم. اما مثال های بعدی رو فقط با روش دوم.
تبدیل تاریخ میلادی به هجری شمسی در جاوا با icu4j: (روش اول)
package finalCal;
import java.time.ZoneId;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.ULocale;
public class From_Gregorian_to_Persian {
public From_Gregorian_to_Persian () {
ULocale PERSIAN_LOCALE = new ULocale("fa_IR@calendar=persian");
ULocale GREGORIAN_LOCALE = new ULocale("en@calendar=gregorian");
ZoneId IRAN_ZONE_ID = ZoneId.of("Asia/Tehran");
Calendar gregoriancalendar = new GregorianCalendar(GREGORIAN_LOCALE);
gregorianCalendar.setLenient(false);
gregoriancal.set(2025, Calendar.AUGUST, 1);
System.out.println("Gregorian Calendar:\t" + (gregoriancal.get(Calendar.YEAR)) + "/" + ( gregoriancal.get(Calendar.MONTH ) +1) + "/" + gregoriancal.get(Calendar.DATE));
Calendar persiancal = Calendar.getInstance(PERSIAN_LOCALE);
persiancal.clear();
persiancal.setTimeZone(TimeZone.getTimeZone("Asia/Tehran"));
persiancal.setTime(gregoriancal.getTime());
// Display the date.
System.out.println("Persian Calendar:\t" + (persiancal.get(Calendar.YEAR) ) + "/" + ( persiancal.get (Calendar.MONTH) + 1 ) + "/" + persiancal.get(Calendar.DATE));
}
}
Output:
Gregorian Calendar: 2025/8/1
Persian Calendar: 1404/5/10
در واقع در کد بالا، لازم نبود که آبجکت persiancal رو بسازیم. بدون اون هم میتونستیم تاریخ فارسی رو بدست بیارم. کد پایین رو از روش دوم نوشتم، ببینید
تبدیل تاریخ میلادی به هجری شمسی در جاوا با icu4j: (روش دوم)
package finalCal;
import java.time.ZoneId;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.ULocale;
public class From_Gregorian_to_Islamic {
public From_Gregorian_to_Islamic () {
ULocale PERSIAN_LOCALE = new ULocale("fa@calendar=persian");
ULocale GREGORIAN_LOCALE = new ULocale("en@calendar=gregorian");
ZoneId IRAN_ZONE_ID = ZoneId.of("Asia/Tehran");
Calendar gregorianCalendar = Calendar.getInstance(GREGORIAN_LOCALE);
gregorianCalendar.setLenient(false);
gregorianCalendar.clear();
gregorianCalendar.setTimeZone(TimeZone.getTimeZone("Asia/Tehran"));
// 29 september 2019
gregorianCalendar.set(2019, 8, 29);
// full date
DateFormat df = DateFormat.getDateInstance(DateFormat.FULL, PERSIAN_LOCALE);
System.out.println(df.format(gregorianCalendar.getTime()));
// date in "yyyy MMM dd" format
SimpleDateFormat df1 = new SimpleDateFormat ("yyyy MMM dd", PERSIAN_LOCALE );
System.out.println(df1.format(gregorianCalendar.getTime()));
// name of month
SimpleDateFormat df2 = new SimpleDateFormat (SimpleDateFormat.MONTH, PERSIAN_LOCALE );
System.out.println(df2.format(gregorianCalendar.getTime()));
// name of weekday
SimpleDateFormat df3 = new SimpleDateFormat (SimpleDateFormat.WEEKDAY, PERSIAN_LOCALE );
System.out.println(df3.format(gregorianCalendar.getTime()));
}
}
Output:
۱۳۹۸ مهر ۷, یکشنبه
۱۳۹۸ مهر ۰۷
مهر
یکشنبه
در مثال بالا باید چند تا نکته رو به شما بگم.
نکته ی اول: اول اینکه اگه در این کد ULocale PERSIAN_LOCALE = new ULocale("fa_IR@calendar=persian") اون fa_IR رو حذف کنید، نتیجه جالبی میگیرید (نتیجه این میشه که خروجی با حروف فارسی نیست و با حروف انگلیسی هست. اینجوری :
Output:
AP 1398 Mehr 7, Sun
1398 Mehr 07
Mehr
Sun
نکته ی دوم: اگه در کد SimpleDateFormat df1 = new SimpleDateFormat ("yyyy MMM dd", PERSIAN_LOCALE ) بجای سه تا MMM دوتا MM بزارید، ماه رو دیگه فارسی نمینویسه، فقط عددشو میگه. اینجوری:
Output:
۱۳۹۸ مهر ۷, یکشنبه
۱۳۹۸ ۰۷ ۰۷
مهر
یکشنبه
تبدیل تاریخ میلادی به هجری قمری در جاوا با icu4j:
package finalCal; import java.time.ZoneId; import com.ibm.icu.text.DateFormat; import com.ibm.icu.text.SimpleDateFormat; import com.ibm.icu.util.Calendar; import com.ibm.icu.util.TimeZone; import com.ibm.icu.util.ULocale; public class From_Gregorian_to_Islamic { public From_Gregorian_to_Islamic () { ULocale islamic_LOCALE = new ULocale("ar@calendar=islamic-civil"); ULocale GREGORIAN_LOCALE = new ULocale("en@calendar=gregorian"); ZoneId IRAN_ZONE_ID = ZoneId.of("Asia/Tehran"); Calendar gregorianCalendar = Calendar.getInstance(GREGORIAN_LOCALE); gregorianCalendar.setLenient(false); gregorianCalendar.clear(); gregorianCalendar.setTimeZone(TimeZone.getTimeZone("Asia/Tehran")); // 29 september 2019 gregorianCalendar.set(2019, 8, 29); // full date DateFormat df = DateFormat.getDateInstance(DateFormat.FULL, islamic_LOCALE); System.out.println(df.format(gregorianCalendar.getTime())); // date in "yyyy MMM dd" format SimpleDateFormat df1 = new SimpleDateFormat ("yyyy MMM dd", islamic_LOCALE); System.out.println(df1.format(gregorianCalendar.getTime())); // name of month SimpleDateFormat df2 = new SimpleDateFormat (SimpleDateFormat.MONTH, islamic_LOCALE); System.out.println(df2.format(gregorianCalendar.getTime())); // name of weekday SimpleDateFormat df3 = new SimpleDateFormat (SimpleDateFormat.WEEKDAY, islamic_LOCALE); System.out.println(df3.format(gregorianCalendar.getTime())); } }
Output:
الأحد، ٢٩ محرم ١٤٤١ هـ
١٤٤١ محرم ٢٩
محرم
الأحد
نکته ی اول: در ULocale islamic_LOCALE = new ULocale("ar@calendar=islamic-civil") اگه ar رو حذف کنید، خروجی بصورت حروف انگلیسی در میاد. مثل زیر:
Output:
AH 1441 Muharram 29, Sun
1441 Muh. 29
Muharram
Sun
نکته ی دوم که بسیار مهمه: دوستان، برای تاریخ قمری، سه نوع locale داریم. ینی
"ar@calendar=islamic-civil" و "ar@calendar=islamic" و "ar@calendar=ISLAMIC_UMALQURA"
هر کدوم از اینها ممکنه خروجی متفاوت بده. در یکی از این ها، تعداد روزها در 6 ماهه اول ماه های قمری ثابت در نظر گرفته شده (ینی 31 روز) اما در دیگری ممکنه مثلا ماه دوم قمری یکبار 30 روز باشه و یک بار 29 روز! برای همین در ابتدای این مقاله گفتم که تبدیل تاریخ اصولا مبحث ساده و راحتی نیست. در اینجا فرق این سه locale رو توضیح داده. خودتون بخونید، من که کامل متوجه نشدم.
تبدیل تاریخ هجری شمسی به میلادی در جاوا با icu4j: (دوستان، در تبدیل تقویم فارسی به میلادی به مشکل برخوردم. برای بعضی تاریخ ها یک روز اختلاف میاره نمیدونم چرا. من حوصله نکردم دلیلشو پیدا کنم. شما اگه پیدا کردید به منم بگید در کامنت ها)
package finalCal; import java.time.ZoneId; import java.util.Date; import com.ibm.icu.text.DateFormat; import com.ibm.icu.text.SimpleDateFormat; import com.ibm.icu.util.Calendar; import com.ibm.icu.util.GregorianCalendar; import com.ibm.icu.util.TimeZone; import com.ibm.icu.util.ULocale; public class From_Persian_to_Gregorian { public From_Persian_to_Gregorian() { ULocale PERSIAN_LOCALE = new ULocale("fa_IR@calendar=persian"); ULocale GREGORIAN_LOCALE = new ULocale("en@calendar=gregorian"); ZoneId IRAN_ZONE_ID = ZoneId.of("Asia/Tehran"); Calendar persiancal = Calendar.getInstance(PERSIAN_LOCALE); persiancal.clear(); persiancal.setTimeZone(TimeZone.getTimeZone("Asia/Tehran")); persiancal.set(1365, 2, 29); System.out.println(persiancal.getTime()); // date in "yyyy MMM dd" format SimpleDateFormat df1 = new SimpleDateFormat ("yyyy MMM dd", GREGORIAN_LOCALE); System.out.println(df1.format(persiancal.getTime())); } }
Output:
Thu Jun 19 00:00:00 IRST 1986
1986 Jun 19
تبدیل تاریخ هجری شمسی به قمری در جاوا با icu4j:
package finalCal; import com.ibm.icu.text.SimpleDateFormat; import com.ibm.icu.util.Calendar; import com.ibm.icu.util.TimeZone; import com.ibm.icu.util.ULocale; public class From_Persian_to_Islamic { public From_Persian_to_Islamic() { ULocale PERSIAN_LOCALE = new ULocale("@calendar=persian"); ULocale islamic_LOCALE = new ULocale("@calendar=islamic-civil"); Calendar persiancal = Calendar.getInstance(PERSIAN_LOCALE); persiancal.setLenient(false); persiancal.clear(); persiancal.setTimeZone(TimeZone.getTimeZone("Asia/Tehran")); int persianMonth =9; // mehr دوستان دقت کنید. در این لایبرری، ماه ها از صفر شروع میشن. ینی صفر میشه فروردین persiancal.set(1398, persianMonth , 6); SimpleDateFormat df2 = new SimpleDateFormat ("yyyy MM dd", PERSIAN_LOCALE); System.out.println("Persian Calendar: " + df2.format( persiancal.getTime())); SimpleDateFormat df1 = new SimpleDateFormat ("yyyy MM dd", islamic_LOCALE); System.out.println("Islamic Calendar: " + df1.format(persiancal.getTime())); } }
Output:
Persian Calendar: 1398 10 06
Islamic Calendar: 1441 04 29
دوستان عزیز، مقاله همینجا تموم شد. امیدوارم این کدها بتونه راهنمای خوبی برای شما جهت کار کردن با این لایبرری باشه و تونسته باشم در وقت شما صرفه جویی کنم و کار شمارو اسان تر کرده باشم. اگر این مقاله به دردتون خورده، حتما واسم کامنت بزارید و نظرتونو بگید :)
و اما نکته ی آخر:
دوستان این لایبرری بسیار قوی هست، کار های زیادی میشه باهاش انجام داد، کارکردش فقط تبدیل تاریخ نیست (هر چند در زمینه تبدیل تاریخ هم میشه کد های دیگری رو به جز کدهای بالایی نوشت. مثلا میتونید بهش یک رنج از تاریخ بدید و بگید در این رنج چند تا سه شنبه وجود داره. دیگه کشف و نوشتن اون کدها رو به خود شما میسپارم :)
من همینقدر تونستم تبدیل تاریخ انجام بدم و کار خودمو راه بندازم، و وظیفه ی خودم دونستم به شما هم آموزش بدم :)
در مورد اون ایرادی که در تبدیل شمسی به میلادی هست و یک روز اختلاف میاره، در اون باره حتما سرچ کنید و دلیلشو پیدا کنید و به منم بگید در کامنت ها. ممنون از همتون :)
تشکر به کارم اومد