Lesson 12 - Date and Time in Kotlin - Modifying and intervals
In the previous lesson, Date and Time in Kotlin - Creating and formatting, we learned to create
LocalDate
, LocalTime
, and LocalDateTime
instances and format their value. In today's tutorial of our Kotlin course,
we're going to modify the value and introduce time intervals.
Conversions
First of all, we'll learn how to convert between the LocalDate
,
LocalTime
, and LocalDateTime
data types.
Converting from LocalDateTime
We convert from LocalDateTime
easily by using the
toLocalDate()
and
toLocalTime()
methods.
Converting to LocalDateTime
We create LocalDateTime
instances using one of the
of*()
methods where we specify the date and time separately:
val beginning = LocalDate.of(1939, 9, 1) val dateTime = LocalDateTime.of(beginning, LocalTime.of(10, 0))
If we want to set the time at the very beginning of the day, we can use the
atStartOfDay()
method. Another method, which allows us to take a
date and attach a time to it, is atTime()
. Here’s an alternative
approach for the example above:
val beginning = LocalDate.of(1939, 9, 1) val dateTime = beginning.atStartOfDay() val dateTime2 = beginning.atTime(0, 0)
Modifying the value
We can add a particular number of days, hours and so on to an existing
instance. We use the methods with the plus...()
or
minus...()
prefix to do just that. I'm sure we
don't have to explain which does what. The important thing to remember is that
they return a new instance with the modified value. The
instance is completely immutable (unchangeable) and any changes
are made by replacing the original instance with a new one (as we do with
Strings).
Here’s an example where we add three more days to the current date:
val dateTime = LocalDateTime.now()
dateTime = dateTime.plusDays(3)
println(dateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
The result:
Dec 9, 2016
Here's a list of the available methods:
minusDays()
minusHours()
minusMinutes()
minusMonths()
minusNanos()
minusSeconds()
minusWeeks()
minusYears()
plusDays()
plusHours()
plusMinutes()
plusMonths()
plusNanos()
plusSeconds()
plusWeeks()
plusYears()
Periods and Durations
Aside from the methods we've already mentioned, there are also four general
minus()
and plus()
methods which receive an interval
that is to be added or subtracted. They come in handy when we don't know whether
we're going to add days or years at a specific point in the application, so they
spare us from having to implement complex branching. There are also
Duration
and Period
classes available, which provide
us objects representing time intervals.
When we get to interfaces, you can check that both classes
implement the TemporalAmount
interface. However, don't worry about
it too much for now.
Here's a modified version of the example code from before:
val dateTime = LocalDateTime.now()
dateTime = dateTime.plus(Period.ofDays(3))
println(dateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
Because Kotlin allows us to overload operators (see further in the course), we can rewrite the code above like this:
var dateTime = LocalDateTime.now() dateTime += Period.ofDays(3) println(dateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
The output will remain the same as with the previous example. The
of...*()
methods have the same "suffixes" as the
plus()
/minus()
methods listed above.
The difference between Period
and Duration
is that
Duration
represents a time interval which isn't related to the
calendar anyhow (e.g. how long it takes to manufacture a car), a day always
lasts 24 hours there. Whereas, Period
also takes daylight saving
time into consideration, so a day may sometimes be 23 or 25 hours long. We use
Period
to work with
LocalDate
/LocalDateTime
and Duration
to
work exclusively with time.
ChronoUnit
To make units (such as days, hours, minutes and similar) easier to work with,
there's the ChronoUnit
class. It uses the Duration
class internally, so all it provides is a different syntax for the tasks we
demonstrated above. For completeness' sake, we'll go over an example of its
use:
var dateTime = LocalDateTime.now() dateTime = dateTime.plus(3, ChronoUnit.DAYS) println(dateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)))
ChronoUnit
implements the TemporalUnit
interface, in case you meet it later on. We'll introduce interfaces later in the
course.
As you can see, the implementation of date and time in Kotlin is quite complex. We won't go into unnecessary details. Instead, we'll focus on practical use, as we always do here at ICT.social.
Between()
The last method worth mentioning is the static between()
method
in the Period
class. It allows us to compute the difference
(interval) between 2 dates. More accurately, it computes the difference between
two objects implementing the Temporal
interface, which is the
common data type for the LocalDate
, LocalDateTime
, and
LocalTime
classes.
val started = LocalDate.of(1939, 9, 1) val ended = LocalDate.of(1945, 9, 2) val period = Period.between(started, ended) println("The World War II lasted for " + period.get(ChronoUnit.YEARS) + " years and " + period.get(ChronoUnit.DAYS) + " days")
The output:
The World War II lasted for 6 years and 1 days
The same method is available in the Duration
class, however,
that one works with LocalDateTime
rather than LocalDate. We
wouldn’t be able to extract the number of years from an interval since years
don't have a fixed amount of days and Duration
isn't related to
calendar-based time.
Setting the value
We set the value using the with...*()
methods, they all have the
same suffixes as the methods mentioned earlier. As always, remember that they
only return a new instance and don't modify the current one.
val started = LocalDate.of(1939, 9, 1) started = started.withYear(1945) // Sets the year to 1945
Method chaining
The fact that all of the methods return new instances, so as to keep classes immutable, also allows us to use the fluent interface, aka "method chaining". It really isn’t all that complicated, all it means is that we’re able to call multiple methods on a single line.
Let's set the value to programmer Christmas, i.e. Halloween (since, 31 OCT = 25 DEC).
var started = LocalDate.of(1939, 9, 1) started = started.withMonth(9).withDayOfMonth(31)
We'll continue in the next lesson, Date and Time in Kotlin- Parsing and comparing, where we'll finish up with date and time in Kotlin. We'll learn how to parse date and time and how to access inner values.