Lekce 12 - Datum a čas v Kotlin - Úprava a intervaly
V minulé lekci, Datum a čas v Kotlin - Vytváření a formátování, jsme se naučili vytvářet instance
LocalDate
, LocalTime
a LocalDateTime
a
formátovat jejich hodnotu.
V dnešním Kotlin tutoriálu se budeme věnovat úpravě této hodnoty a zaběhneme i do časových intervalů.
Převody
Na úvod si ukažme, jak můžeme převádět mezi LocalDate
,
LocalTime
a LocalDateTime
.
Převod z LocalDateTime
Z LocalDateTime
převádíme jednoduše pomocí jeho metod
toLocalDate()
a
toLocalTime()
.
Převod na LocalDateTime
LocalDateTime
vytvoříme pomocí jedné z of*()
metod, kde uvedeme zvlášť datum a čas, např. takto:
val zacatek = LocalDate.of(1939, 9, 1) val datumCas = LocalDateTime.of(zacatek, LocalTime.of(10, 0))
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()
. Další varianta příkladu
výše by tedy byla:
val zacatek = LocalDate.of(1939, 9, 1) val datumCas = zacatek.atStartOfDay() val datumCas2 = zacatek.atTime(0, 0)
Ú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 Kotlinu s textovými
řetězci).
Zkusme si to a přidejme aktuálnímu datu další 3 dny v týdnu:
{KOTLIN_OOP}
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.Month
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
fun main(args: Array<String>) {
var datumCas = LocalDateTime.now()
datumCas = datumCas.plusDays(3)
println(datumCas.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
}
{/KOTLIN_OOP}
Výsledek:
9.1.2017
Metody, které máme k dispozici, jsou následující:
minusDays()
minusHours()
minusMinutes()
minusMonths()
minusNanos()
minusSeconds()
minusWeeks()
minusYears()
plusDays()
plusHours()
plusMinutes()
plusMonths()
plusNanos()
plusSeconds()
plusWeeks()
plusYears()
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:
{KOTLIN_OOP}
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.Month
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.time.Period
fun main(args: Array<String>) {
var datumCas = LocalDateTime.now()
datumCas = datumCas.plus(Period.ofDays(3))
println(datumCas.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
}
{/KOTLIN_OOP}
Protože nám Kotlin umožňuje tzv. přetěžování operátorů, více dále v kurzu, můžeme kód výše zapsat i tímto způsobem:
{KOTLIN_OOP}
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.Month
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.time.Period
fun main(args: Array<String>) {
var datumCas = LocalDateTime.now()
datumCas += Period.ofDays(3)
println(datumCas.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
}
{/KOTLIN_OOP}
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.
Rozdíl mezi Period
a Duration
je ten, že Duration
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. 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
LocalDate
/LocalDateTime
, Duration
při
práci s časem.
ChronoUnit
Pro snažší 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:
{KOTLIN_OOP}
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.Month
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.time.temporal.ChronoUnit
fun main(args: Array<String>) {
var datumCas = LocalDateTime.now()
datumCas = datumCas.plus(3, ChronoUnit.DAYS)
println(datumCas.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
}
{/KOTLIN_OOP}
Jen pro úplnost si dodejme, že ChronoUnit
implementuje rozhraní TemporalUnit
, kdybyste se s ním později
setkali. Nyní rozhraní řešit nebudeme.
Jak vidíte, implementace data a času je v Kotlinu 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 na ITnetwork většinou děláme.
Between()
Jako poslední stojí za zmínku 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 LocalDate
, LocalDateTime
a
LocalTime
). Záměrně zde nebudu psát češtinsky správně "mezi
dvěma daty", jelikož je to v IT zavádějící slovo.
{KOTLIN_OOP}
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.Month
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.time.temporal.ChronoUnit
import java.time.Period
fun main(args: Array<String>) {
val zacatek = LocalDate.of(1939, 9, 1)
val konec = LocalDate.of(1945, 9, 2)
val doba = Period.between(zacatek, konec)
println("II. světová válka trvala " + doba.get(ChronoUnit.YEARS) + " let a " + doba.get(ChronoUnit.DAYS) + " dní")
}
{/KOTLIN_OOP}
Tu samou metodu nalezneme i na třídě Duration
, která ovšem
pracuje s LocalDateTime
místo s 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.
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.
var zacatek = LocalDate.of(1939, 9, 1) zacatek = zacatek.withYear(1945) // Nastaví rok na 1945
Ř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 31 OCT = 25 DEC).
var zacatek = LocalDate.of(1939, 9, 1) zacatek = zacatek.withMonth(9).withDayOfMonth(31)
V příští lekci, Datum a čas v Kotlin - Parsování a porovnávání, dokončíme datum a čas v Kotlin. Ukážeme si jak hodnoty číst a jak datum a čas v Kotlinu parsovat.