Lekce 14 - Datum a čas od Javy 8 - Úprava a intervaly
V minulé lekci, Datum a čas v Javě 8 - Vytváření a formátování, jsme se naučili vytvářet instance tříd
LocalDate
, LocalTime
a LocalDateTime
a
formátovat jejich hodnotu.
V dnešním tutoriálu se budeme věnovat úpravě této hodnoty a zaběhneme i do časových intervalů.
Potřebné importy
Abych zbytečně neopakoval, o jaké importy v příkladech jde, naimportujte si je rovnou všechny, které použijeme:
import java.time.*; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAmount;
Převody
Na úvod si ukažme, jak můžeme převádět mezi datumy z instancí
LocalDate
, LocalTime
a LocalDateTime
.
Převod z LocalDateTime
Z datumu LocalDateTime
převádíme jednoduše pomocí jeho
metod toLocalDate()
a toLocalTime()
.
Převod na LocalDateTime
Instanci LocalDateTime
vytvoříme pomocí jedné z
of()
metod, kde uvedeme zvlášť datum a čas, např. takto:
LocalDateTime datumCas = LocalDateTime.of(LocalDate.of(1939, 9, 1), LocalTime.of(10, 0)); // nastavíme datum 1.9.1939 a 10. hodinu System.out.println(datumCas);
Výstup:
Konzolová aplikace
1939-09-01T10:00
Pokud chceme nastavit čas na začátek dne, můžeme využít metody
atStartOfDay()
. Další metodou, kterou můžeme vzít datum a
připojit k němu čas, je atTime()
. U této metody si můžeme
nastavit vlastní hodinu a minutu. Další varianta příkladu výše by tedy
byla:
LocalDate zacatek = LocalDate.of(1939, 9, 1); LocalDateTime zacatekDne = zacatek.atStartOfDay(); LocalDateTime danyZacatek = zacatek.atTime(10, 0); System.out.println(zacatekDne); System.out.println(danyZacatek);
Na výstupu bude tedy začátek dne (00:00 hodin) a další čas bude v 10 hodin jako u minulého příkladu:
Konzolová aplikace
1939-09-01T00:00
1939-09-01T10:00
Úprava hodnoty
K existující instanci můžeme přičítat určitý počet dní, hodin a
podobně. Slouží k tomu metody začínající na plus...()
nebo
minus...()
, snad netřeba vysvětlovat co dělají. Důležitá
poznámka je, že vracejí novou instanci s upravenou hodnotou.
Instance je ve všech ohledech neměnitelná
(immutable
) a jakákoli změna spočítá v nahrazení za novou
(např. podobně jako to je v Javě s textovými řetězci).
Zkusme si to a přidejme aktuálnímu datu další 3 dny v týdnu:
LocalDateTime datumCas = LocalDateTime.now();
datumCas = datumCas.plusDays(3);
System.out.println(datumCas.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
Pokud si představíme, že je dnes 19.4.2023
, výstupem
bude:
Konzolová aplikace
22. 4. 2023
Metody, které máme k dispozici, jsou následující:
minusDays()
minusHours()
minusMinutes()
minusMonths()
minusNanos()
- Odebere z času nanosekundy.minusSeconds()
minusWeeks()
minusYears()
plusDays()
plusHours()
plusMinutes()
plusMonths()
plusNanos()
plusSeconds()
plusWeeks()
plusYears()
Do metod můžeme dávat i záporné hodnoty. Pokud zavoláme
například metodu plusDays(-3)
, je tato metoda stejná jako
minusDays(3)
.
Třídy Period
a
Duration
Kromě výše zmíněných metod nalezneme i 4 obecné verze metod
minus()
a plus()
, které přijímají interval k
přidání/odebrání. Využijeme je zejména v případě, kdy dopředu
nevíme, zda budeme přidávat např. dny nebo roky, ušetří nám spoustu
podmínkování. Máme k dispozici třídy Duration
a
Period
, na kterých si můžeme nechat vrátit objekt
reprezentující takový interval.
Až se dostaneme k rozhraním, můžete se podívat, že obě
třídy implementující společné rozhraní TemporalAmount
.
Zatím si s nimi však nemotejme hlavu.
Upravený kód našeho příkladu by vypadal takto:
LocalDateTime datumCas = LocalDateTime.now();
datumCas = datumCas.plus(Period.ofDays(3));
System.out.println(datumCas.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
Výstup bude stejný, jako u příkladu výše. of...()
metody
mají stejné "přípony" jako měly plus/minus metody vyjmenované výše:
Konzolová aplikace
22. 4. 2023
Třída Duration
na rozdíl od třídy Period
označuje nějaký časový interval, který vůbec nesouvisí s kalendářem
(např. jak dlouho trvá vyrobit automobil), den trvá vždy 24 hodin. Třída
Period
počítá s přechodem na letní čas, den tedy může
někdy trvat 23 nebo 25 hodin. Period
použijeme při práci s
třídami LocalDate
/LocalDateTime
, třídu
Duration
při práci s časem.
Třída ChronoUnit
Pro snazší práci s různými jednotkami jako jsou dny, hodiny, minuty a
podobně je nám k dispozici třída ChronoUnit
. Vnitřně
používá třídu Duration
. Jedná se tedy pouze o jiný zápis
již předchozích úloh, ale jen pro jistotu, kdybyste jej někdy potkali,
ukažme si jej:
LocalDateTime datumCas = LocalDateTime.now();
datumCas = datumCas.plus(3, ChronoUnit.DAYS);
System.out.println(datumCas.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)));
Jen pro úplnost si dodejme, že třída ChronoUnit
implementuje rozhraní TemporalUnit
, kdybyste se s ním později
setkali. Nyní rozhraní řešit nebudeme.
Jak můžeme vidět, implementace data a času je v Javě poměrně složitá, nebudeme tu zabíhat do zbytečných detailů a zaměříme se zejména na praktickou stránku použití, jak to u nás na síti většinou děláme.
Další metody
Ukažme si ještě další užitečné metody.
Metoda between()
Další metoda, kterou máme k dispozici, je statická metoda
between()
na třídě Period
, která nám umožňuje
získat interval, tedy rozdíl mezi 2 datumy (přesněji objekty s rozhraním
Temporal
, to je společný typ pro třídy LocalDate
,
LocalDateTime
a LocalTime
):
LocalDate zacatek = LocalDate.of(1939, 9, 1); LocalDate konec = LocalDate.of(1945, 9, 2); TemporalAmount doba = Period.between(zacatek, konec); System.out.println("II. světová válka trvala " + doba.get(ChronoUnit.YEARS) + " let a " + doba.get(ChronoUnit.DAYS) + " dní");
Výstup:
Konzolová aplikace
II. světová válka trvala 6 let a 1 dní
Tu samou metodu nalezneme i na třídě Duration
, která ovšem
pracuje s třídou LocalDateTime
místo třídy
LocalDate
. Z intervalu nezjistíme počet let, protože roky
nemají pevný počet dnů a my již víme, že Duration
není
nijak spojená s kalendářním pojetím času.
Metoda until()
Jako poslední stojí za zmínku metoda until()
. Tato metoda se
volá přímo nad instancemi tříd LocalDate
,
LocalDateTime
a LocalTime
a vrací stejný výsledek,
jako statická metoda between()
.
LocalDate zacatek = LocalDate.of(1939, 9, 1); LocalDate konec = LocalDate.of(1945, 9, 2); // Doba mezi datumy 1.9.1939 a 2.9.1945 Period doba = zacatek.until(konec); System.out.println("II. světová válka trvala " + doba.get(ChronoUnit.YEARS) + " let a " + doba.get(ChronoUnit.DAYS) + " dní");
Výstup je stejný:
Konzolová aplikace
II. světová válka trvala 6 let a 1 dní
Nastavení hodnoty
Hodnotu nastavujeme pomocí metod with...*()
, mají opět ty
samé "přípony" jako metody doposud zmíněné. Jako vždy nezapomeňte, že
jako všechny podobné metody vracejí novou instanci:
LocalDateTime zacatek = LocalDateTime.of(1939, 9, 1, 0, 0); zacatek = zacatek.withHour(10); // Nastaví na 10. hodinu
Řetězení metod
Vracení nových instancí, které je zejména výsledkem toho, že jsou instance immutable, zároveň poskytuje tzv. fluent interface (česky někdy překládané jako plynulé rozhraní). Jedná se o řetězení metod, anglicky method chaining. Nehledejte v tom žádnou složitost, jde jen o to, že můžeme většinu metod volat po sobě na jednom řádku.
Zkusme si nastavit kalendář na programátorské vánoce, tedy na Halloween (ano, protože Oct 31 = Dec 25):
LocalDate zacatek = LocalDate.of(1939, 9, 1); zacatek = zacatek.withMonth(10).withDayOfMonth(31);
V příští lekci, Datum a čas v Javě 8 - Parsování a porovnávání, se podíváme na parsování a porovnávání datumu a času.