Cześć! Dzisiaj zaczniemy pracować z nowym typem danych, którego dotąd nie znaliśmy, a mianowicie datami.
Chyba nie muszę wyjaśniać, czym jest data. :) W zasadzie możemy przechowywać aktualne dane i czas w zwykłym stringu Java.
Nie oznacza to, że tych metod nie można używać w ogóle. Jeśli spróbujesz uruchomić kod przy użyciu przestarzałych metod w IDE, najprawdopodobniej zadziała.
Weźmy pod uwagę, na przykład przestarzałą metodę
public class Main {
public static void main(String[] args) {
String date = "June 11, 2018";
System.out.println(date);
}
}
Lecz to podejście ma wiele wad. Klasa String
zaprojektowana została do pracy z tekstem i jej metody są odpowiednie do tego zadania. Jeśli musimy w jakiś sposób manipulować datą (np. dodać 2 godziny), String
nie działa tak dobrze. Lub jeśli chcemy wyświetlić aktualną datę i godzinę kompilacji programu. String
tutaj też nie pomaga: zanim napiszesz kod i uruchomisz go, czas ulegnie zmianie, a konsola wyświetli błędne informacje.
Dlatego twórcy Javy udostępnili kilka klas do pracy z datami i godzinami.
Pierwszą z nich jest java.util.Date
Klasa Date w Java (Java Date)
Podaliśmy jego pełną nazwę, ponieważ inny pakiet Java ma klasęjava.sql.Date
. Nie pomieszaj ich!
Pierwszą rzeczą, o której musisz wiedzieć, jest to, że zapisuje datę jako liczbę milisekund, które upłynęły od 1 stycznia 1970 r. Ten system czasu ma nawet swoją własną nazwę: "Unix-time"
Całkiem interesujące podejście, prawda? :)
Drugą rzeczą wartą zapamiętania jest: Jeśli utworzysz obiekt Date
przy użyciu domyślnego konstruktora, wynik reprezentuje bieżącą datę i godzinę w momencie utworzenia obiektu.
Pamiętasz, gdy powiedzieliśmy, że data reprezentowana jako String
będzie miała problemy z takim zadaniem? Klasa Date
z łatwością sobie z tym radzi.
public class Main {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
}
}
Uruchom ten kod kilka razy, a zobaczysz wielokrotną zmianę czasu. :) Jest to możliwe, ponieważ czas przechowywany jest w milisekundach: są to bardzo małe jednostki czasu, więc wyniki są bardzo dokładne.
Inny konstruktor klasy Date
: możesz przekazać dokładną liczbę milisekund od godziny 00:00 1 stycznia 1970 do wymaganej daty, a utworzony zostanie odpowiedni obiekt daty:
public class Main {
public static void main(String[] args) {
Date date = new Date(1212121212121L);
System.out.println(date);
}
}
Wydruk konsoli:
Fri May 30 04:20:12 GMT 2008
Otrzymujemy 30 maja 2008. "Fri" oznacza dzień tygodnia ("Pt" - piątek), a GMT to strefa czasowa (Greenwich Mean Time - czas Greenwich).
Milisekundy mają typ long
, ponieważ liczba milisekund zazwyczaj nie mieści się w int
.
Więc jakie operacje z datami możemy mieć potrzebę wykonać?
Cóż, najbardziej oczywistą jest z pewnością porównanie. Do określenia, czy jedna data przypada przed, czy po drugiej.
Można to zrobić na kilka sposobów.
Na przykład można wywołać metodę Date.getTime()
, zwracającą liczbę milisekund, które upłynęły od północy 1 stycznia 1970 r.
Wywołaj to po prostu dla dwóch obiektów Date i porównaj wyniki:
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");
}
}
Wydruk:
date1 jest wcześniejsza niż date2
Ale jest też wygodniejszy sposób, np. przy użyciu specjalnych metod dostarczonych przez klasę Date: before()
, after()
i equals()
.
Wszystkie zwracają wartość logiczną.
Metoda before()
sprawdza, czy nasza data jest wcześniejsza niż data podana jako argument:
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));
}
}
Wydruk konsoli:
true
Podobnie, metoda after()
sprawdza, czy nasza data jest późniejsza niż data przekazana jako argument:
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));
}
}
Wydruk konsoli:
false
W naszych przykładach "usypiamy program" na 2 sekundy, aby zagwarantować, że te dwie daty będą różne. Na szybkich komputerach czas między utworzeniem date1
i date2
może być krótszy niż jedna milisekunda, powodując, że zarówno before()
i after()
zwracają wartość false.
Ale w tym przypadku metoda equals()
zwróci wartość true!
W końcu porównuje ona liczbę milisekund od godziny 00:00 1 stycznia 1970 dla każdej daty. Obiekty te uważane są za równe tylko wtedy, gdy pasują do milisekundy:
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));
}
Oto kolejna rzecz, na którą musisz zwrócić uwagę.
Jeśli otworzysz dokumentację klasy Date
na stronie Oracle, zobaczysz, że wiele z jej metod i konstruktorów zostało oznaczonych jako Deprecated (tzn. nie zalecane do stosowania).
Oto, co twórcy Javy mają do powiedzenia na temat części klas, które zostały wycofane:Element programu z adnotacją @Deprecated to coś, czego nie zaleca się używać programistom, dlatego, że zazwyczaj są niebezpieczne lub istnieje lepsza alternatywa”. |
Date.getHours()
, która zwraca liczbę godzin związanych z obiektem Date
.
public static void main(String[] args) {
Date date1 = new Date();
System.out.println(date1.getHours());
}
Jeśli uruchomisz kod o 14:21 (2:21 PM), wyświetli się liczba 14.
Jak widać, przestarzała metoda jest przekreślona, ale wciąż działa.
Te metody nie są usuwane, aby nie zniszczyć ogromnej ilości istniejącego kodu, który z nich korzysta.
Innymi słowy, metody te nie są ani "zepsute" ani "usunięte". Po prostu nie są zalecane do użytku, ponieważ wygodniejsza alternatywa jest dostępna.
Nawiasem mówiąc, w dokumentacji wyraźnie wspomina się o tej alternatywie:
Większość metod klasy
Date
została przeniesiona do ulepszonej i rozszerzonej klasy Calendar
. Następnie zapoznamy się z tą klasą. :)Klasa Calendar w Java (Java Calendar)
JDK 1.1 wprowadził nową klasę:Calendar
. Dzięki temu praca z datami w Javie stała się nieco łatwiejsza niż dotychczas.
Jedyną implementacją klasy Calendar
, z którą będziemy pracować, jest klasa GregorianCalendar
. Implementuje kalendarz gregoriański, który jest uznawany przez większość krajów świata.
Jego główną zaletą jest to, że może pracować z datami w wygodniejszym formacie.
Na przykład, może:
- Dodać miesiąc lub dzień do aktualnej daty
- Sprawdzić, czy dany rok jest rokiem przestępnym;
- Zwrócić poszczególne składniki daty (na przykład wyodrębnić numer miesiąca z całej daty)
- Zawiera on również bardzo wygodny system stałych (wiele z nich zobaczymy poniżej).
Calendar
jest jej stała Calendar.ERA: można wskazać datę przed naszą erą (BC - Before Christ) lub z naszej ery (AD - Anno Domini).
Zobaczmy to wszystko na przykładach.
Stwórzmy obiekt calendar
z datą 25 stycznia 2017:
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
W klasie Calendar
(a także w klasie Date
) miesiące zaczynają się od zera, więc podajemy liczbę 0 jako drugi argument.
Podczas pracy z klasą Calendar
należy pamiętać, że jest to właśnie kalendarz, a nie indywidualna data.
Data to tylko kilka liczb wskazujących konkretny przedział czasu. Kalendarz to cały system, który pozwala robić wiele rzeczy z datami. :)
Jest to bardzo widoczne, jeśli spróbujesz wyświetlić obiekt Calendar
:
Wydruk:
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=?]
Zobacz, ile informacji uzyskasz! Kalendarz ma kilka właściwości, których zwykła data nie ma i wszystkie z nich są wyświetlane (tak działa metoda toString()
w klasie Calendar
).
Jeśli potrzebujesz tylko prostej daty z kalendarza, czyli obiektu Date
, użyj metody Calendar.getTime()
(nazwa nie brzmi zbyt logicznie, no ale co zrobić?):
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2017, 0 , 25);
Date date = calendar.getTime();
System.out.println(date);
}
Wydruk:
Śr. Stycz. 25 00:00:00 GMT 2017
Teraz zajęliśmy się kalendarzem i "zredukowaliśmy go" do zwykłej daty.
Przejdźmy dalej.
Oprócz oznaczania miesięcy według ich liczby można użyć wartości pól stałych klasy Calendar
.
Te stałe to pola statyczne klasy Calendar
z wstępnie ustawioną wartością, która nie może być zmieniona.
W rzeczywistości jest to jeszcze lepsza opcja, ponieważ korzystanie z nich poprawia czytelność kodu.
public static void main(String[] args) {
GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY to jedna ze stałych reprezentujących miesiące w roku.
Używając tych nazwanych stałych, nikt nie zapomni na przykład, że liczba 3 oznacza kwiecień, a nie trzeci miesiąc, który lubimy nazywać marcem. Po prostu napisz Calendar.APRIL i gotowe. :)
Wszystkie pola kalendarza (liczba, miesiąc, minuty, sekundy itp.) można określić osobno używając metody set()
.
Metoda ta jest bardzo wygodna, ponieważ klasa Calendar
ma stałą dla każdego pola, a wynikowy kod jest bardzo łatwy do odczytania.
W ostatnim przykładzie utworzyliśmy datę, ale nie ustawiliśmy dla niej czasu. Ustawmy czas 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());
}
Wydruk:
Śr. Stycz. 25 19:42:12 GMT 2017
Wywołujemy metodę set()
, przekazując stałą (w zależności od pola, które chcemy zmienić) i nową wartość dla pola. Okazuje się, że ta metoda set()
jest rodzajem "super-settera", który wie, jak ustawić wartość nie tylko dla jednego pola, ale dla wielu pól. :)
Klasa Calendar
używa metody add()
do dodawania i odejmowania wartości. Podajesz pole, które chcesz zmienić oraz liczbę (dokładnie, ile chcesz dodać/odjąć od aktualnej wartości).
Na przykład weźmy datę, która jest 2 miesiące przed datą, którą utworzyliśmy:
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());
}
Wydruk:
Pt. List. 25 19:42:12 GMT 2016
Bardzo dobrze! Mamy datę sprzed 2 miesięcy. Spowodowało to nie tylko zmianę miesiąca: zmienił się również rok z 2017 na 2016. Oczywiście podczas konwersji dat aktualny rok obliczany jest automatycznie, bez konieczności jego ręcznego śledzenia.
Ale jeśli z jakiegoś powodu musisz wyłączyć te działania, możesz to zrobić.
Metoda roll()
może dodawać i odejmować wartości bez wpływu na wartości pozostałe. Jak na przykład tutaj:
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());
}
Zrobiliśmy dokładnie to samo, co w poprzednim przykładzie: odjęliśmy 2 miesiące od obecnej daty. Ale teraz kod robi coś innego: miesiąc zmienił się od stycznia do listopada, ale rok pozostał niezmieniony - 2017!
Wydruk:
Sob. List. 25 10:42:12 GMT 2017
Idziemy dalej.
Jak powiedzieliśmy powyżej, możemy wszystkie pola Calendar
uzyskać oddzielnie. Robimy to za pomocą metody 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));
}
Wydruk:
Rok: 2017
Miesiąc: 0
Tydzień w miesiącu: 5
Dzień: 25
Godzin: 10
Minut: 42
Sekund: 12
Milisekund: 0
Tak więc, oprócz "super-settera" klasy Calendar
, istnieje również "super-getter". :)
Oczywiście innym interesującym aspektem tej klasy jest praca z epokami.
Aby stworzyć datę "BC" (p.n.e), należy użyć pola Calendar.ERA.
Stwórzmy na przykład datę Bitwy pod Kanną, gdzie Hannibal pokonał armię rzymską. Stało się to 2 sierpnia 216 r. p.n.e.:
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()));
}
Tutaj użyliśmy klasy SimpleDateFormat
do wydrukowania daty w formacie, który jest dla nas łatwiejszy do zrozumienia (litery ”GG” wskazują, że chcemy, aby data była wyświetlana).
Wydruk:
Sierp. 02, 216 p.n.e.
Klasa Calendar
ma znacznie więcej metod i stałych. Możesz o nich przeczytać w dokumentacji.
Jeśli nie podoba Ci się ten format daty Sob. List. 25 10:42:12 GMT 2017, możesz użyć SimpleDateFormat
, aby łatwo ustawić go tak, jak chcesz.
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()));
}
Wydruk:
Sobota, Listopad 25, 2017
O wiele lepiej, prawda? :)Ten artykuł przeczytasz także po angielsku. |
---|
Read the English version of this article about the DateTime and Calendar classes. Programmers need to be good at time management! |
GO TO FULL VERSION