CodeGym /Java блог /Случаен /Как да не се изгубите във времето: DateTime и Calendar
John Squirrels
Ниво
San Francisco

Как да не се изгубите във времето: DateTime и Calendar

Публикувано в групата
здрасти Днес ще започнем да работим с нов тип данни, който не сме срещали досега, а именно дати. Как да не се изгубите във времето: DateTime и Calendar - 1Мисля, че няма нужда да обяснявам Howво е дата. :) По принцип можем да съхраним текущата дата и час в обикновен Java String.

public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
Но този подход има много недостатъци. Класът Stringе предназначен за работа с текст и неговите методи са подходящи за тази задача. Ако трябва да манипулираме дата по няHowъв начин (да добавим 2 часа, например), това Stringне работи толкова добре. Или ако искаме да покажем текущата дата и час, когато програмата е компorрана. Stringне помага и тук: докато напишете codeа и го стартирате, времето ще се е променило и конзолата ще показва грешна информация. Ето защо създателите на Java предоставиха няколко класа за работа с дати и час. Първият от тях еjava.util.Date

Дата клас

Уточнихме пълното му име, защото друг Java пакет има класа java.sql.Date. Не ги смесвайте! Първото нещо, което трябва да знаете за нея е, че тя съхранява датата като брой мorсекунди , изминали от 1 януари 1970 г. Тази система за време дори има собствено име: " Unix-време " Доста интересен подход, нали съгласни ли сте? :) Второто нещо, което си струва да запомните, е следното: Ако създадете Dateобект, използвайки конструктора по подразбиране, резултатът представлява текущата дата и час в момента, в който обектът е създаден . Помните ли, че казахме, че дата, представена като a, Stringще се бори с такава задача? Класът Dateсе справя с лекота.

public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
Изпълнете този code няколко пъти и ще видите, че времето се променя многократно. :) Това е възможно, защото времето се съхранява като мorсекунди: те са изключително малки единици за време, така че резултатите са много точни. Класът Dateдруг конструктор: можете да подадете точния брой мorсекунди от 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 е часовата зона (средно време по Гринуич). Мorсекундите се предават като longs, тъй като броят мorсекунди обикновено не се побира в int. И така, Howви операции с дати може да трябва да извършим? Е, най-очевидното, разбира се, е сравнението . За да определи дали една дата идва преди or след друга. Това може да стане по няколко начина. Например, можете да извикате Date.getTime()метода, който връща броя мorсекунди, изминали от полунощ на 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));
   }
}
Конзолен изход: 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));
   }
}
Конзолен изход: невярно В нашите примери ние "поставяме програмата да заспи" за 2 секунди, така че двете дати са гарантирано различни. На бързи компютри времето между създаването на date1и date2може да бъде по-малко от една мorсекунда, което кара before()и двете after()да връщат невярно. Но в този случай equals()методът ще върне true! В крайна сметка той сравнява броя мorсекунди от 00:00 часа на 1 януари 1970 г. за всяка дата. Обектите се считат за равни само ако съвпадат с точност до мorсекунда :

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));
}
Ето още нещо, на което трябва да обърнете внимание. Ако отворите documentацията за Dateкласа на уебсайта на Oracle , ще видите, че много от неговите методи и конструктори са маркирани като Deprecated (т.е. не се препоръчват за употреба). Ето Howво имат да кажат създателите на Java за части от класове, които са отхвърлени:
„Програмен елемент с анотация @Deprecated е нещо, което програмистите не се препоръчва да използват, обикновено защото е опасно or защото има по-добра алтернатива.“
Това не означава, че тези методи изобщо не могат да се използват. Ако се опитате да изпълните code, използвайки остарели методи в IDE, той най-вероятно ще работи. Например, помислете за остарелия Date.getHours()метод, който връща броя часове, свързани с Dateобект.

public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
Ако стартирате codeа в 14:21 (14:21), той ще покаже числото 14. Както можете да видите, отхвърленият метод е зачеркнат, но все още работи. Тези методи не се премахват, за да не се наруши огромното тяло от съществуващ code, който ги използва. С други думи, тези методи не са нито "счупени", нито "премахнати". Те просто не се препоръчват за употреба, защото има по-удобна алтернатива. Между другото, documentацията изрично споменава тази алтернатива:
Как да не се изгубите във времето: DateTime и Calendar - 2
Повечето от Dateметодите на класа са преместени в подобрения и разширения Calendarклас. След това ще се запознаем с този клас. :)

Календар клас

JDK 1.1 въведе нов клас: Calendar. Това направи работата с дати в Java малко по-лесна от преди. Единственото изпълнение на класа Calendar, с което ще работим, е класът GregorianCalendar. Той прилага григорианския календар, който се спазва от повечето страни по света. Основното му предимство е, че може да работи с дати в по-удобен формат. Например може:
  • Добавете месец or ден към текущата дата
  • Проверете дали годината е високосна;
  • Връщане на отделни компоненти на датата (например извличане на номера на месеца от цяла дата)
  • Той също така съдържа много удобна система от константи (много от които ще видим по-долу).
Друго важно подобрение на Calendarкласа е неговата константа Calendar.ERA : можете да посочите дата преди общата ера (пр.н.е. - преди Христа) or в общата ера (AD - Anno Domini). Нека да разгледаме всичко това с примери. Нека създадем calendarобект с дата 25 януари 2017 г.:

public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
В Calendarкласа (Howто и 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,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=?,ZONE_OFFSET=?,DST_OFFSET=?] Вижте колко информация получавате ! Календарът има куп свойства, които нормалната дата няма, и всички те се показват (ето HowtoString()работи методът вCalendarкласа). Ако просто трябва да получите проста дата от календара, т.е. обектDate, използвайтеCalendar.getTime()метод (името не е най-логичното, но Howво можете да направите?):

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 GMT 2017 Сега взехме календара и го „намалихме“ до обикновена дата. Да отидем по-нататък. В допълнение към обозначаването на месеци по техния номер, можете да използвате постоянните стойности на полетоCalendar на класа . Тези константи са статични полета на класа с предварително зададена стойност, която не може да бъде променяна. Това всъщност е още по-добър вариант, защото използването им подобрява четливостта на вашия code. Calendar

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY е една от константите, които представляват месеците в годината. Използвайки тези именувани константи, никой няма да забрави например, че числото 3 означава април, а не третия месец, който обичаме да наричаме март. Просто напишете Calendar.APRIL и сте готови. :) Всички календарни полета (число, месец, minutesи, секунди и т.н.) могат да бъдат зададени отделно чрез методаset(). Този метод е много удобен, защотоCalendarкласът има константа за всяко поле и полученият code е много лесен за четене. В последния пример създадохме дата, но не зададохме час за нея. Нека настроим часа 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()метод е един вид "супер-задавач", който знае How да зададе стойността не само за едно поле, а за много полета. :) 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. Разбира се, когато преобразувате датите, текущата година се изчислява автоматично, без да е необходимо да я следите ръчно. Но ако по няHowва причина трябва да деактивирате това поведение, можете да го направите. Методът 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 месеца от текущата дата. Но сега codeът прави нещо различно: месецът е променен от януари на ноември, но годината остава непроменена — 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 Мorсекунди: 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" показват, че искаме ерата да бъде показана). Резултат: 2 август 216 г. пр.н.е. Класът Calendarима много повече методи и константи. Можете да прочетете за тях в documentацията . Ако не харесвате този формат на датата, събота, 25 ноември 10:42:12 GMT 2017 г. , тогава можете да използвате SimpleDateFormat, за да я направите лесно такава, Howвато искате да бъде.

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