CodeGym /وبلاگ جاوا /Random-FA /چگونه در زمان گم نشویم: DateTime و Calendar
John Squirrels
مرحله
San Francisco

چگونه در زمان گم نشویم: DateTime و Calendar

در گروه منتشر شد
سلام! امروز کار را با نوع داده جدیدی که قبلاً با آن مواجه نشده بودیم، یعنی تاریخ ها، شروع می کنیم. چگونه در زمان گم نشویم: DateTime و Calendar - 1فکر نمی کنم نیازی به توضیح تاریخ باشد. :) در اصل، ما می توانیم تاریخ و زمان فعلی را در یک رشته جاوا معمولی ذخیره کنیم.
public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
اما این رویکرد دارای معایب بسیاری است. کلاس Stringبرای کار با متن طراحی شده است و روش های آن برای این کار مناسب است. اگر لازم باشد تاریخ را به نحوی دستکاری کنیم (مثلاً 2 ساعت اضافه کنیم)، Stringبه خوبی کار نمی کند. یا اگر بخواهیم تاریخ و ساعت فعلی هنگام کامپایل شدن برنامه نمایش داده شود. Stringدر اینجا هم کمکی نمی کند: زمانی که کد را بنویسید و آن را اجرا کنید، زمان تغییر کرده و کنسول اطلاعات اشتباهی را نمایش می دهد. به همین دلیل سازندگان جاوا چندین کلاس برای کار با تاریخ و زمان ارائه کردند. اولین مورد این استjava.util.Date

کلاس تاریخ

ما نام کامل آن را مشخص کردیم، زیرا بسته جاوا دیگری دارای java.sql.Dateکلاس است. آنها را با هم قاطی نکنید! اولین چیزی که باید در مورد آن بدانید این است که تاریخ را به عنوان تعداد میلی ثانیه هایی که از اول ژانویه 1970 گذشته است ذخیره می کند . موافق نیستی؟ :) دومین چیزی که ارزش یادآوری دارد این است: اگر یک شی را با استفاده از سازنده پیش فرض ایجاد کنید ، نتیجه تاریخ و زمان فعلی را در لحظه ایجاد شیء نشان می دهد . به یاد داشته باشید که گفتیم تاریخی که به عنوان یک نشان داده می شود با چنین وظیفه ای دست و پنجه نرم می کند؟ کلاس به راحتی آن را اداره می کند. DateStringDate
public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
این کد را چندین بار اجرا کنید و شاهد تغییر مکرر زمان خواهید بود. :) این امکان پذیر است زیرا زمان به صورت میلی ثانیه ذخیره می شود: آنها واحدهای بسیار کوچکی از زمان هستند، بنابراین نتایج بسیار دقیق هستند. کلاس Dateسازنده دیگر: می‌توانید تعداد دقیق میلی‌ثانیه‌ها را از ساعت 00:00 در 1 ژانویه 1970 به تاریخ مورد نیاز منتقل کنید، و یک شی تاریخ مربوطه ایجاد می‌شود:
public class Main {
   public static void main(String[] args) {

       Date date = new Date(1212121212121L);
       System.out.println(date);
   }
}
خروجی کنسول: جمعه 30 می 04:20:12 به وقت گرینویچ 2008 ما 30 می 2008 را دریافت می کنیم. "جمعه" روز هفته را نشان می دهد (جمعه، دوشنبه)، و GMT منطقه زمانی (به وقت گرینویچ) است. میلی‌ثانیه‌ها به‌عنوان s ارسال می‌شوند long، زیرا تعداد میلی‌ثانیه‌ها معمولاً در یک عدد قرار نمی‌گیرد int. بنابراین، چه عملیاتی با تاریخ ممکن است لازم باشد انجام دهیم؟ خوب، واضح ترین، البته، مقایسه است . برای تعیین اینکه آیا یک تاریخ قبل یا بعد از دیگری است. این را از راه های گوناگون می توان انجام داد. برای مثال، می‌توانید Date.getTime()روشی را فراخوانی کنید که تعداد میلی‌ثانیه‌هایی را که از نیمه‌شب ۱ ژانویه ۱۹۷۰ گذشته است را برمی‌گرداند.
public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "date1 is later than date2" : "date1 is earlier than date2");
   }
}
خروجی: date1 زودتر از date2 است ، اما راه راحت‌تری نیز وجود دارد، یعنی استفاده از روش‌های ویژه ارائه‌شده توسط کلاس Date before()after()و equals(). همه آنها یک مقدار بولی برمی گردانند. این before()روش بررسی می کند که آیا تاریخ ما زودتر از تاریخی است که به عنوان آرگومان ارسال شده است:
public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.before(date2));
   }
}
خروجی کنسول: true به طور مشابه، after()متد بررسی می کند که آیا تاریخ ما دیرتر از تاریخ ارسال شده به عنوان آرگومان است یا خیر:
public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
خروجی کنسول: false در مثال‌هایمان، برنامه را به مدت 2 ثانیه به حالت Sleep قرار می‌دهیم تا دو تاریخ متفاوت باشند. در رایانه‌های سریع، زمان بین ایجاد date1و date2می‌تواند کمتر از یک میلی‌ثانیه باشد، که باعث می‌شود هر دو before()اشتباه after()شوند. اما در این صورت equals()روش true بر می گردد! به هر حال، تعداد میلی ثانیه ها از ساعت 00:00 در 1 ژانویه 1970 برای هر تاریخ مقایسه می شود. اشیاء فقط در صورتی برابر در نظر گرفته می شوند که با میلی ثانیه مطابقت داشته باشند :
public static void main(String[] args) {

   Date date1 = new Date();
   Date date2 = new Date();

   System.out.println(date1.getTime());
   System.out.println(date2.getTime());

   System.out.println(date1.equals(date2));
}
در اینجا نکته دیگری است که باید به آن توجه کنید. اگر مستندات کلاس را Dateدر وب سایت اوراکل باز کنید ، خواهید دید که بسیاری از متدها و سازنده های آن به عنوان منسوخ شده علامت گذاری شده اند (یعنی برای استفاده توصیه نمی شود). در اینجا چیزی است که سازندگان جاوا در مورد بخش هایی از کلاس ها که منسوخ شده اند می گویند:
"یک عنصر برنامه با حاشیه نویسی @Deprecated چیزی است که برنامه نویسان استفاده از آن را توصیه نمی کنند، معمولاً به دلیل خطرناک بودن یا به دلیل وجود جایگزین بهتر."
این بدان معنا نیست که اصلاً نمی توان از این روش ها استفاده کرد. اگر بخواهید کد را با استفاده از روش های منسوخ شده در یک IDE اجرا کنید، به احتمال زیاد کار خواهد کرد. برای مثال، متد منسوخ شده را در نظر بگیرید Date.getHours()که تعداد ساعت های مرتبط با یک Dateشی را برمی گرداند.
public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
اگر کد را در ساعت 14:21 (2:21 بعد از ظهر) شروع کنید، عدد 14 را نمایش می دهد. همانطور که می بینید، روش منسوخ شده خط خورده است، اما همچنان کار می کند. این روش ها حذف نمی شوند تا حجم عظیمی از کدهای موجود که از آنها استفاده می کنند شکسته نشوند. به عبارت دیگر، این روش ها نه «شکسته» هستند و نه «حذف». آنها به سادگی برای استفاده توصیه نمی شوند زیرا جایگزین راحت تری در دسترس است. اتفاقاً، اسناد به طور خاص به این جایگزین اشاره می کند:
چگونه در زمان گم نشویم: DateTime و Calendar - 2
اکثر Dateمتدهای کلاس به کلاس بهبود یافته و توسعه یافته منتقل شده اند Calendar. در ادامه با آن کلاس آشنا می شویم. :)

کلاس تقویم

JDK 1.1 کلاس جدیدی را معرفی کرد: Calendar. کار با خرما در جاوا را تا حدودی آسانتر از قبل کرد. تنها پیاده سازی کلاسی Calendarکه با آن کار خواهیم کرد GregorianCalendarکلاس است. تقویم میلادی را اجرا می کند که توسط اکثر کشورهای جهان رعایت می شود. مزیت اصلی آن این است که می تواند با تاریخ ها در قالب راحت تری کار کند. به عنوان مثال، می تواند:
  • یک ماه یا یک روز به تاریخ فعلی اضافه کنید
  • بررسی کنید که آیا سال یک سال کبیسه است یا خیر.
  • اجزای جداگانه تاریخ را برگردانید (به عنوان مثال، شماره ماه را از یک تاریخ کامل استخراج کنید)
  • همچنین شامل یک سیستم بسیار راحت از ثابت ها است (که بسیاری از آنها را در زیر خواهیم دید).
یکی دیگر از پیشرفت‌های مهم کلاس ثابت Calendar.ERACalendar آن است : می‌توانید تاریخی را قبل از دوران مشترک (پیش از میلاد - قبل از میلاد مسیح) یا در دوره مشترک (AD - Anno Domini) نشان دهید. بیایید به همه اینها با مثال نگاه کنیم. بیایید یک شی با تاریخ 25 ژانویه 2017 ایجاد کنیم: calendar
public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
در Calendarکلاس (و همچنین Dateکلاس برای آن موضوع)، ماه ها از صفر شروع می شوند ، بنابراین عدد 0 را به عنوان آرگومان دوم پاس می کنیم. هنگام کار با Calendarکلاس، درک این نکته مهم است که این فقط یک تقویم است ، نه یک تاریخ فردی. چگونه در زمان گم نشویم: DateTime و Calendar - 3 تاریخ فقط چند عدد است که یک فاصله زمانی خاص را نشان می دهد. تقویم یک سیستم کامل است که به شما امکان می دهد کارهای زیادی را با تاریخ انجام دهید. :) اگر بخواهید Calendarشیء را نمایش دهید این کاملاً آشکار است: خروجی: java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id= "Europe/London",offset=0,dstSavings=0,useDaylight=false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,YEEK_OF ,WEEK_OF_MONTH=?,DAY_OF_MONTH=25,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECONDZET=FFF=D,SECONDZET=FFF? ET =؟] ببینید چقدر اطلاعات به دست می آورید! یک تقویم دارای تعدادی ویژگی است که تاریخ معمولی ندارد و همه آنها نمایش داده می شوند (این روش toString()در Calendarکلاس کار می کند). اگر فقط نیاز دارید یک تاریخ ساده از تقویم، یعنی یک Dateشیء، بدست آورید، از روش استفاده کنید Calendar.getTime()(نام منطقی ترین نیست، اما چه کاری می توانید انجام دهید؟):
public static void main(String[] args) {

   Calendar calendar = new GregorianCalendar(2017, 0 , 25);
   Date date = calendar.getTime();
   System.out.println(date);
}
خروجی: چهارشنبه 25 ژانویه 00:00:00 به وقت گرینویچ 2017 اکنون ما تقویم را انتخاب کرده و به یک تاریخ عادی "کاهش" داده ایم. بیایید جلوتر برویم. علاوه بر تعیین ماه ها با تعداد آنها، می توانید از مقادیر فیلد ثابتCalendar کلاس استفاده کنید . این ثابت ها فیلدهای ثابت کلاس با مقدار از پیش تعیین شده هستند که قابل تغییر نیستند. این در واقع یک گزینه حتی بهتر است، زیرا استفاده از آنها خوانایی کد شما را بهبود می بخشد. Calendar
public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY یکی از ثابت هایی است که ماه های سال را نشان می دهد. با استفاده از این ثابت های نامگذاری شده، هیچ کس فراموش نمی کند که مثلاً عدد 3 به معنای آوریل است و نه ماه سوم که ما دوست داریم آن را مارس بنامیم. فقط Calendar.APRIL را بنویسید و کارتان تمام شد. :) همه فیلدهای تقویم (تعداد، ماه، دقیقه، ثانیه و غیره) را می توان به طور جداگانه با استفاده ازset()روش مشخص کرد. این روش بسیار راحت است، زیراCalendarکلاس برای هر فیلد یک ثابت دارد و کد به دست آمده بسیار راحت خوانده می شود. در مثال آخر، ما یک تاریخ ایجاد کردیم، اما زمانی برای آن تعیین نکردیم. بیایید زمان را 19:42:12 تنظیم کنیم
public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar();
   calendar.set(Calendar.YEAR, 2017);
   calendar.set(Calendar.MONTH, 0);
   calendar.set(Calendar.DAY_OF_MONTH, 25);
   calendar.set(Calendar.HOUR_OF_DAY, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println(calendar.getTime());
}
خروجی: چهارشنبه 25 ژانویه 19:42:12 به وقت گرینویچ 2017 ما set()متد را فراخوانی می کنیم، یک ثابت (بسته به فیلدی که می خواهیم تغییر دهیم) و مقدار جدید را برای فیلد ارسال می کنیم. به نظر می رسد که این set()روش نوعی "ابر تنظیم کننده" است که می داند چگونه مقدار را نه فقط برای یک فیلد، بلکه برای بسیاری از فیلدها تنظیم کند. :) کلاس از متد برای جمع و تفریق مقادیر Calendarاستفاده می کند . add()شما در فیلدی که می خواهید تغییر دهید، و یک عدد (دقیقاً چقدر می خواهید از مقدار فعلی اضافه یا کم کنید) ارسال می کنید. به عنوان مثال، بیایید تاریخی را دریافت کنیم که 2 ماه قبل از تاریخی است که ایجاد کردیم:
public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.add(Calendar.MONTH, -2); // To subtract, pass a negative number
   System.out.println(calendar.getTime());
}
خروجی: جمعه 25 نوامبر 19:42:12 GMT 2016 بسیار خوب! ما تاریخ را 2 ماه پیش گرفتیم. این نه تنها باعث تغییر ماه نشد، بلکه سال نیز از سال 2017 به 2016 تغییر کرد. البته، هنگام تبدیل تاریخ، سال جاری به طور خودکار محاسبه می شود بدون اینکه نیازی به پیگیری دستی آن باشد. اما اگر به دلایلی نیاز به غیرفعال کردن این رفتار دارید، می توانید این کار را انجام دهید. این روش می تواند مقادیر را بدون تأثیر بر مقادیر باقیماندهroll() اضافه و کم کند . به عنوان مثال، مانند این:
public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(calendar.getTime());
}
ما دقیقاً همان کاری را که در مثال قبلی انجام دادیم انجام دادیم: از تاریخ فعلی 2 ماه طول کشید. اما اکنون کد کار متفاوتی انجام می دهد: ماه از ژانویه تا نوامبر تغییر کرده است، اما سال بدون تغییر باقی می ماند - 2017! خروجی: شنبه 25 نوامبر 10:42:12 GMT 2017 در حال حرکت. همانطور که در بالا گفتیم، می توانیم تمام Calendarفیلدها را جداگانه دریافت کنیم. ما این کار را با get()روش زیر انجام می دهیم:
public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println("Year: " + calendar.get(Calendar.YEAR));
   System.out.println("Month: " + calendar.get(Calendar.MONTH));
   System.out.println("Week in the month: " + calendar.get(Calendar.WEEK_OF_MONTH));// Week in this month?

   System.out.println("Day: " + calendar.get(Calendar.DAY_OF_MONTH));

   System.out.println("Hours: " + calendar.get(Calendar.HOUR));
   System.out.println("Minutes: " + calendar.get(Calendar.MINUTE));
   System.out.println("Seconds: " + calendar.get(Calendar.SECOND));
   System.out.println("Milliseconds: " + calendar.get(Calendar.MILLISECOND));

}
خروجی: سال: 2017 ماه: 0 هفته در ماه: 5 روز: 25 ساعت: 10 دقیقه: 42 ثانیه: 12 میلی ثانیه: 0 بنابراین، علاوه بر Calendar"سوپر تنظیم کننده" کلاس، یک "سوپرگیرنده" نیز وجود دارد. ". :) البته یکی دیگر از جنبه های جالب این کلاس کار با دوره هاست. برای ایجاد یک تاریخ "BC"، باید از فیلد Calendar.ERA استفاده کنید، به عنوان مثال، اجازه دهید یک تاریخ برای نبرد Cannae ایجاد کنیم، جایی که هانیبال ارتش روم را شکست داد. این در 2 اوت 216 قبل از میلاد اتفاق افتاد:
public static void main(String[] args) {
   GregorianCalendar cannae = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannae.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("MMM dd, yyy GG");
   System.out.println(df.format(cannae.getTime()));
}
در اینجا ما از SimpleDateFormatکلاس برای چاپ تاریخ در قالبی استفاده کردیم که درک آن برای ما آسان تر باشد (حروف "GG" نشان می دهد که ما می خواهیم دوره نمایش داده شود). خروجی: 02 اوت 216 ق.م. کلاس Calendarمتدها و ثابت های بیشتری دارد. شما می توانید در مورد آنها در اسناد بخوانید . اگر از این قالب تاریخ شنبه 25 نوامبر 10:42:12 به وقت گرینویچ 2017 خوشتان نمی آید ، می توانید از SimpleDateFormatآن استفاده کنید تا به راحتی آن را همانطور که می خواهید بسازید.
public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy");
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(dateFormat.format(calendar.getTime()));
}
خروجی: شنبه 25 نوامبر 2017 خیلی بهتر است، اینطور نیست؟ :)
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION