Lekce 14 - Datum a čas v Javě 8 - Parsování a porovnávání
V minulé lekci, Datum a čas od Javy 8 - Úprava a intervaly, jsme se věnovali převedení, úpravě hodnoty a časovým intervalům.
V dnešním tutoriálu se podíváme na čtení hodnoty, parsování a porovnávání.
Čtení hodnoty
Hodnotu čteme pomocí metod začínajících na get*()
, zde
nás ani nic nepřekvapí:
LocalDate halloween = LocalDate.of(2016, Month.OCTOBER, 31); System.out.println("Rok: " + halloween.getYear() + ", měsíc: " + halloween.getMonthValue() + ", den: " + halloween.getDayOfMonth());
a výsledek:
Konzolová aplikace
Rok: 2016, měsíc: 10, den: 31
Všimněte si, že pro získání čísla měsíce jsme použili metodu
getMonthValue()
, protože metoda getMonth()
by vrátil
hodnotu z výčtového typu Month
.
Kdybychom se setkali se starší třídou
Calendar
, musíme si dát pozor. Měsíce tam jsou číslované od
0
místo od 1
, jak je tomu ve třídách
LocalDate
/LocalDateTime
.
Parsování data a času
Datum a čas budeme samozřejmě často dostávat v textové podobě, např.
od uživatele z konzole, ze souboru nebo z databáze. Asi tušíte, že k
vytvoření LocalDateTime
z takovéto textové hodnoty použijeme
metodu parse()
přímo na datovém typu, jako to v Javě děláme
vždy.
Třída LocalDate
předpokládá datum ve formátu
yyyy-mm-dd
, třída LocalDateTime
datum a čas ve
formátu yyyy-mm-ddThh:mm:ss
a třída LocalTime
čas
ve formátu hh:mm:ss
. Všechna čísla před sebou musí mít nuly,
pokud jsou menší jak 10
. Písmeno T
uprostřed
formátu není překlep, ale opravdu oddělení data a času. To už můžeme
vědět z předchozích lekcí. Zkusme si datum a čas :
LocalDateTime datumCas = LocalDateTime.parse("2016-12-08T10:20:30"); LocalDate datum = LocalDate.parse("2016-12-08"); LocalTime cas = LocalTime.parse("10:20:30"); System.out.println(datumCas.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM))); System.out.println(datum.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM))); System.out.println(cas.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM)));
Nezapomeneme zase na importy:
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.FormatStyle;
Možný výsledek:
Konzolová aplikace
8. 12. 2016 10:20:30
8. 12. 2016
10:20:30
Výsledek je samozřejmě různorodý, záleží na lokalizaci systému. Proto můžete mít výstup jiný.
Vlastní formát
Mnohem často budeme ale samozřejmě chtít naparsovat datum a čas v
českém tvaru nebo v jakémkoli jiném tvaru, výchozí oddělování data a
času pomocí písmene T
není zrovna user-friendly
LocalDateTime datumCas = LocalDateTime.parse("12/08/2016 10:20:30", DateTimeFormatter.ofPattern("M/d/y HH:mm:ss")); LocalDate datum = LocalDate.parse("12/8/2016", DateTimeFormatter.ofPattern("M/d/y")); LocalTime cas = LocalTime.parse("10:20:30", DateTimeFormatter.ofPattern("H:m:ss")); System.out.println(datumCas.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM))); System.out.println(datum.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM))); System.out.println(cas.format(DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM)));
Výsledek:
Konzolová aplikace
8. 12. 2016 10:20:30
8. 12. 2016
10:20:30
Kdybychom chtěli třeba specifikovat vlastní formát v metodě
ofPattern()
a musel by formát obsahovat například klíčové
písmeno m
(normálně by to chtělo počet minut), můžeme ho
escapovat pomocí jednoduchých apostrofů '
. Zkusíme naparsovat
například 10h:20m:30s
:
LocalTime cas = LocalTime.parse("10h:20m:30s", DateTimeFormatter.ofPattern("H'h':m'm':ss's'")); System.out.println(cas);
Výstup:
Konzolová aplikace
10:20:30
Porovnávání instancí
Protože Java nepodporuje přetěžování operátorů, porovnáváme datumy
(píší schválně češtinsky nesprávně, daty je v IT zavádějící slovo)
pomocí metod k tomu určených. Metody začínají na is*()
,
vyjmenujme si je:
isAfter(datum)
- Vrací, zda je instance za datem/datem a časem předávané instance (zda je hodnota větší).isBefore(datum)
- Vrací, zda je instance před datem/datem a časem předávané instance (zda je hodnota menší).isEqual(datum)
- Vrací, zda je instance nastavena na stejný datum a/nebo čas jako je předávaná instance (zda je hodnota stejná).
To bylo jednoduché, že? Když už jsme u metod is*()
, uveďme
si i zbývající dvě:
isLeapYear()
- Vrací, zda je instance nastavena na přestupný rok či nikoli.isSupported(ChronoUnit)
- Vrací, zda instance podporuje práci s danou chrono jednotkou (např.LocalDate
nebude umětChronoUnit.HOURS
, protože neobsahuje informaci o čase).
Ukažme si příklad:
LocalDate halloween = LocalDate.of(2016, 10, 31); LocalDate vanoce = LocalDate.of(2016, 12, 25); System.out.println("Halloween po Vánocích: " + halloween.isAfter(vanoce)); System.out.println("Halloween před Vánocemi: " + halloween.isBefore(vanoce)); System.out.println("shodný Vánoce - Halloween: " + vanoce.isEqual(halloween)); System.out.println("shodný Halloween - Halloween: " + halloween.isEqual(halloween)); System.out.println("přestupný rok 2016: " + halloween.isLeapYear()); System.out.println("přestupný rok 2017: " + halloween.withYear(2017).isLeapYear()); System.out.println("podporuje hodiny: " + halloween.isSupported(ChronoUnit.HOURS)); System.out.println("podporuje roky: " + halloween.isSupported(ChronoUnit.YEARS));
Doplníme chybějící import:
import java.time.temporal.ChronoUnit;
Výsledek:
Konzolová aplikace
Halloween po Vánocích: false
Halloween před Vánocemi:: true
shodný Vánoce - Halloween: false
shodný Halloween - Halloween: true
přestupný rok 2016: true
přestupný rok 2017: false
podporuje hodiny: false
podporuje roky: true
Další třídy
Kromě tříd LocalDateTime
, LocalDate
a
LocalTime
se můžeme setkat také s několika dalšími třídami,
které využijeme spíše u aplikací, které jsou na práci s datem a časem
založené. Nemusíme se jich lekat, ve většině případů si vystačíme s
třídou LocalDateTime
, ale měli bychom o nich mít
povědomí.
Instant
Třída Instant
je pro datum a čas, ale ne v pojetí
kalendáře a přechodů na letní čas. Je to počet nanosekund od data
1.1.1970
, tedy jeden bod na časové ose UTC
(univerzálního času). Když si kdekoli na Zemi napíšete aplikaci s tímto
kódem:
Instant nyni = Instant.now(); System.out.println(nyni);
a importem:
import java.time.Instant;
Dostaneme na výstup vždy to samé, například:
Konzolová aplikace
2023-04-20T10:36:06.541863800Z
Třída Instant
zná jen univerzální čas a ten se bude lišit
od reálného času v dané oblasti.
Třídy
OffsetDateTime
a ZonedDateTime
Již víme, že Instant
je univerzální čas a
LocalDateTime
má v sobě ten čas, který je na daném území. Ze
samotného LocalDateTime
nedokážeme získat bod na univerzální
časové ose, protože nevíme na jakém je území.
Co nám chybí je tedy kombinace těchto dvou, lokálně korektní čas,
který by v sobě zároveň nesl informaci o území (časové zóně) a tím
pádem mohl být univerzálně převáděn mezi různými časovými zónami.
Právě k tomu slouží třída ZonedDateTime
.
V Javě nalezneme také třídu OffsetDateTim
, která je takový
mezičlánek, který umožňuje zaznamenat časový posun, ale nemá plnou
podporu zón.
Třída ZoneId
Časové zóny jsou v Javě reprezentovány třídou ZoneId
.
Pojďme si udělat jednoduchou ukázku datumu s časovou zónou.
V následující ukázce použijeme podtržítko _
.
Na české klávesnici ho pomocí Shift a následující
klávesy:
Datum s časovou zónou vypadá následovně:
ZonedDateTime lokalniDatumCas = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println(lokalniDatumCas);
Nezapomeneme na potřebné balíčky:
import java.time.ZoneId; import java.time.ZonedDateTime;
Výstup:
Konzolová aplikace
2017-02-02T06:37:11.026-05:00[America/New_York]
Ano, je to hodně tříd, berme to spíše jako informace, ke kterým se můžeme vrátit až je budeme potřebovat. Je dobré mít povědomí o tom, jak se s datumem pracuje. V Javě je tříd vždy více, než v ostatních jazycích. Zkusme si vypěstovat trpělivost a nějakou odolnost vůči této skutečnosti, zas jsme kvůli tomu lépe placení Příště budeme zas chvíli prakticky programovat, abychom si od teorie odpočinuli.
Epochy
Na úplný závěr si pojďme ještě představit několik posledních metod
na třídě LocalDateTime
.
ofEpochSecond()
- Statická metoda nám umožňuje vytvořit datum a čas z tzv. Unix timestamp, ve kterém se datum a čas často ukládal zejména v minulosti. Je to velké číslo označující počet vteřin od datumu1.1.1970
(začátek unixové epochy), musíme uvést i nanosekundy (většinou 0) a časovou zónu, což je nejčastějiZoneOffset.UTC
. Metoda je dostupná i na tříděLocalDate
jako metodaofEpochDay()
, kde přijímá počet dní místo sekund.toEpochSecond()
atoEpochDay()
- Metody opačné ke dvěma předchozím, převádí hodnotu na počet sekund/dní od roku1970
.
To je k datu a času od Javy verze 8 opravdu vše.
V následujícím cvičení, Řešené úlohy k 12. lekci OOP v Javě, si procvičíme nabyté zkušenosti z předchozích lekcí.