CodeGym /בלוג Java /Random-HE /איך לא ללכת לאיבוד בזמן: DateTime ולוח שנה
John Squirrels
רָמָה
San Francisco

איך לא ללכת לאיבוד בזמן: DateTime ולוח שנה

פורסם בקבוצה
היי! היום נתחיל לעבוד עם סוג נתונים חדש שעוד לא נתקלנו בו, כלומר תאריכים. איך לא ללכת לאיבוד בזמן: DateTime ולוח שנה - 1אני לא חושב שאני צריך להסביר מה זה דייט. :) באופן עקרוני, נוכל לאחסן את התאריך והשעה הנוכחיים במחרוזת Java רגילה.
public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
אבל לגישה זו יש חסרונות רבים. הכיתה Stringנועדה לעבוד עם טקסט, והשיטות שלה מתאימות למשימה זו. אם אנחנו צריכים לתמרן תאריך בדרך כלשהי (להוסיף שעתיים, למשל), Stringזה לא עובד כל כך טוב. או אם נרצה להציג את התאריך והשעה הנוכחיים כאשר התוכנית מורכבת. Stringגם כאן לא עוזר: עד שתכתוב את הקוד ותפעיל אותו, השעה תשתנה והקונסולה תציג את המידע השגוי. זו הסיבה שיוצרי Java סיפקו מספר שיעורים לעבודה עם תאריכים ושעה. הראשון שבהם הואjava.util.Date

שיעור תאריך

ציינו את השם המלא שלו, כי חבילת Java אחרת כוללת את java.sql.Dateהמחלקה. אל תערבב ביניהם! הדבר הראשון שאתה צריך לדעת על זה הוא שהוא מאחסן את התאריך כמספר האלפיות השניות שחלפו מאז 1 בינואר 1970. למערכת הזמן הזו יש אפילו שם משלה: " Unix-time " גישה מעניינת למדי, לא אתה מסכים? :) הדבר השני שכדאי לזכור הוא זה: אם אתה יוצר אובייקט Dateבאמצעות בנאי ברירת המחדל, התוצאה מייצגת את התאריך והשעה הנוכחיים ברגע שבו האובייקט נוצר . זוכרים שאמרנו שדייט המיוצג בתור Stringיתקשה במשימה כזו? הכיתה Dateמטפלת בזה בקלות.
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 GMT 2008 נקבל את ה-30 במאי 2008. "שי" מציין את היום בשבוע (שישי, דוה), ו-GMT הוא אזור הזמן (שעון גריניץ'). אלפיות השנייה מועברות כ- longs, מכיוון שמספר האלפיות השניות אינו מתאים בדרך כלל ל- int. אז, אילו פעולות עם תאריכים אולי נצטרך לבצע? ובכן, הברור ביותר, כמובן, הוא השוואה . כדי לקבוע אם דייט אחד בא לפני או אחרי אחר. זה יכול להיעשות בכמה דרכים. לדוגמה, אתה יכול לקרוא לשיטה Date.getTime(), שמחזירה את מספר האלפיות השניות שחלפו מאז חצות ב-1 בינואר 1970. פשוט קרא לה על שני אובייקטים של Date והשווה את התוצאות:
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));
   }
}
פלט מסוף: נכון באופן דומה, 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 שניות, כך ששני התאריכים מובטחים שונים. במחשבים מהירים, הזמן בין היצירה של date1לבין date2יכול להיות פחות מאלפית שנייה אחת, מה שגורם גם before()וגם after()להחזיר false. אבל במקרה זה, equals()השיטה תחזור אמיתית! אחרי הכל, הוא משווה את מספר האלפיות השניות מאז 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 (כלומר לא מומלץ לשימוש). הנה מה שיש ליוצרים של Java לומר על חלקים של מחלקות שהוצאו משימוש:
"רכיב תוכנית עם הערות @Deprecated הוא משהו למתכנתים לא מומלץ להשתמש, בדרך כלל בגלל שהוא מסוכן, או בגלל שיש אלטרנטיבה טובה יותר."
זה לא אומר שאי אפשר להשתמש בשיטות האלה בכלל. אם תנסה להריץ קוד באמצעות מתודות שהוצאו משימוש ב-IDE, סביר להניח שזה יעבוד. לדוגמה, שקול את Date.getHours()השיטה שהוצאה משימוש, המחזירה את מספר השעות המשויכות לאובייקט Date.
public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
אם תתחיל את הקוד בשעה 14:21 (14:21), הוא יציג את המספר 14. כפי שאתה יכול לראות, השיטה שהוצאה משימוש מחוצה, אך היא עדיין עובדת. שיטות אלו אינן מוסרות כדי לא לשבור את הגוף העצום של הקוד הקיים שמשתמש בהן. במילים אחרות, שיטות אלו אינן "שבורות" ולא "מוסרות". הם פשוט לא מומלצים לשימוש מכיוון שקיימת חלופה נוחה יותר. אגב, התיעוד מזכיר באופן ספציפי את החלופה הזו:
איך לא ללכת לאיבוד בזמן: DateTime ולוח שנה - 2
רוב Dateהשיטות של המחלקה הועברו למחלקה המשופרת והמורחבת Calendar. נכיר את השיעור הבא. :)

שיעור לוח שנה

JDK 1.1 הציג מחלקה חדשה: Calendar. זה הפך את העבודה עם תאריכים ב-Java לקלה יותר מבעבר. היישום היחיד של הכיתה 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 ולוח שנה - 3 תאריך הוא רק כמה מספרים שמציינים מרווח זמן מסוים. לוח שנה הוא מערכת שלמה שמאפשרת לך לעשות הרבה דברים עם תאריכים. :) זה בולט לעין אם אתה מנסה להציג את Calendarהאובייקט: פלט: java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id= "אירופה/לונדון",offset=0,dstSavings=0,useDaylight=false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=? ,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,SECOND=0,MILLISECOND_OFFSET=0,MILLISECOND_OFFSET=? =?] ראה כמה מידע אתה מקבל! ללוח שנה יש חבורה של מאפיינים שאין לתאריך רגיל, וכולם מוצגים (כך 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);
}
פלט: Wed Jan 25 00:00:00 GMT 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());
}
פלט: Wed Jan 25 19:42:12 GMT 2017 אנו קוראים לשיטה set(), מעבירים קבוע (בהתאם לשדה שאנו רוצים לשנות) ואת הערך החדש עבור השדה. מסתבר ששיטה זו set()היא מעין "סופר-מגדיר" שיודע להגדיר את הערך לא רק לשדה אחד, אלא להרבה שדות. :) Calendarהמחלקה משתמשת בשיטה add()כדי להוסיף ולהחסיר ערכים. אתה מעביר בשדה שאתה רוצה לשנות, ומספר (בדיוק כמה אתה רוצה להוסיף/להחסיר מהערך הנוכחי). לדוגמה, בוא נקבל תאריך שהוא חודשיים לפני התאריך שיצרנו:
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 טוב מאוד! קיבלנו את התאריך לפני חודשיים. זה לא רק גרם לשינוי החודש: גם השנה השתנתה מ-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());
}
עשינו בדיוק את אותו הדבר כמו בדוגמה הקודמת: לקחנו חודשיים מהתאריך הנוכחי. אבל עכשיו הקוד עושה משהו שונה: החודש השתנה מינואר לנובמבר, אבל השנה נשארת ללא שינוי - 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-מגדיר" של הכיתה, יש גם "סופר-גטר" ". :) כמובן, היבט מעניין נוסף של השיעור הזה הוא עבודה עם תקופות. כדי ליצור תאריך "לפני הספירה", תצטרך להשתמש בשדה Calendar.ERA. לדוגמה, בואו ניצור תאריך לקרב קנאי, שבו חניבעל הביס את הצבא הרומי. זה קרה ב-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 GMT 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