Why do we need a new date and time library?
A
long-standing bugbear of Java developers has been the inadequate support for
the date and time use cases of ordinary developers. For example, the existing
classes (such as java.util.Date and SimpleDateFormatter) aren’t thread-safe, leading to
potential concurrency issues for users—not something the average developer
would expect to deal with when writing date-handling code. Some of the date and
time classes also exhibit quite poor API design. For example, years in java.util.Date start at 1900, months start at 1,
and days start at 0—not very intuitive.
These
issues, and several others, have led to the popularity of third-party date and
time libraries, such as Joda-Time. In order to address these problems and
provide better support in the JDK core, a new date and time API, which is free
of these problems, has been designed for Java SE 8.
The project
has been led jointly by the author of Joda-Time (Stephen Colebourne) and
Oracle, under JSR 310, and will appear in the new Java SE 8 package java.time.
Core Ideas
The new API
is driven by three core ideas:
·
Immutable-value
classes. One of
the serious weaknesses of the existing formatters in Java is that they aren’t
thread-safe. This puts the burden on developers to use them in a thread-safe
manner and to think about concurrency problems in their day-to-day development
of date-handling code. The new API avoids this issue by ensuring that all its
core classes are immutable and represent well-defined values.
·
Domain-driven
design. The
new API models its domain very precisely with classes that represent different
use cases for Date andTime closely.
This differs from previous Java libraries that were quite poor in that regard.
For example, java.util.Daterepresents an instant on the timeline—a
wrapper around the number of milli-seconds since the UNIX epoch—but if you calltoString(), the result suggests that it has a
time zone, causing confusion among developers.
This emphasis on domain-driven design
offers long-term benefits around clarity and understandability, but you might
need to think through your application’s domain model of dates when porting
from previous APIs to Java SE 8.
·
Separation
of chronologies. The
new API allows people to work with different calendaring systems in order to
support the needs of users in some areas of the world, such as Japan or
Thailand, that don’t necessarily follow ISO-8601. It does so without imposing
additional burden on the majority of developers, who need to work only with the
standard chronology.
LocalDate and LocalTime
The first
classes you will probably encounter when using the new API are LocalDate and LocalTime. They are local in the sense that they
represent date and time from the context of the observer, such as a calendar on
a desk or a clock on your wall. There is also a composite class called LocalDateTime, which is a pairing of LocalDate and LocalTime.
The existing classes aren’t thread-safe, leading to potential
concurrency issues for users—not something the average developer would expect.
Time zones,
which disambiguate the contexts of different observers, are put to one side
here; you should use these local classes when you don’t need that context. A
desktop JavaFX application might be one of those times. These classes can even
be used for representing time on a distributed system that has consistent time
zones.
Creating Objects
All the core
classes in the new API are constructed by fluent factory methods. When
constructing a value by its constituent fields, the factory is called of; when converting from another type, the factory is called from. There are also parse methods that take strings as parameters. See
Listing 1.
Paste your text here.LocalDateTime timePoint = LocalDateTime.now(); //
The current date and time
LocalDate.of(2012, Month.DECEMBER, 12); // from values
LocalDate.ofEpochDay(150); //
middle of 1970
LocalTime.parse("10:15:30"); // From a String
LocalTime.of(17, 18); // the train I took home today
Listing
1
Standard
Java getter conventions are used in order to obtain values from Java SE 8
classes, as shown in Listing 2.
LocalDate theDate =
timePoint.toLocalDate();
Month month = timePoint.getMonth();
timePoint.getSecond();
int day = timePoint.getDayOfMonth();
Listing
2
You can also
alter the object values in order to perform calculations. Because all core
classes are immutable in the new API, these methods are called with and return new objects, rather than using setters (see Listing 3).
There are also methods for calculations based on the different fields.
// Set the value, returning a new
object
LocalDateTime thePast =
timePoint.withDayOfMonth(10).withYear(2010);
or pass a value and field pair */
/* You can use direct manipulation
methods,
LocalDateTime yetAnother =
thePast.plusWeeks(3).plus(3, ChronoUnit.WEEKS);
Listing
3
The new API
also has the concept of an adjuster—a block of code that can be used to wrap up common processing logic.
You can either write a WithAdjuster, which is used to set one or more
fields, or a PlusAdjuster, which is used to add or subtract some
fields. Value classes can also act as adjusters, in which case they update the
values of the fields they represent. Built-in adjusters are defined by the new
API, but you can write your own adjusters if you have specific business logic
that you wish to reuse. See Listing 4.
import static
java.time.temporal.TemporalAdjusters.*;
LocalDateTime timePoint = ...
bar =
timePoint.with(previousOrSame(ChronoUnit.WEDNESDAY));
foo = timePoint.with(lastDayOfMonth());
timePoint.with(LocalTime.now());
// Using value classes as adjusters
Listing 4
Truncation
The new API
supports different precision time points by offering types to represent a date,
a time, and date with time, but obviously there are notions of precision that
are more fine-grained than this.
The truncatedTo method exists to support such use
cases, and it allows you to truncate a value to a field, as shown in Listing
5.
LocalTime truncatedTime =
time.truncatedTo(ChronoUnit.SECONDS);
Listing
5
Time Zones
The local
classes that we looked at previously abstract away the complexity introduced by
time zones. A time zone is a set of rules, corresponding to a region in which
the standard time is the same. There are about 40 of them. Time zones are
defined by their offset from Coordinated Universal Time (UTC). They move
roughly in sync, but by a specified difference.
Time zones
can be referred to by two identifiers: abbreviated, for example, “PLT,” and
longer, for example, “Asia/Karachi.” When designing your application, you
should consider what scenarios are appropriate for using time zones and when
offsets are appropriate.
·
ZoneId is an identifier for a region
(see Listing 6). Each ZoneId corresponds to some rules that
define the time zone for that location. When designing your software, if you
consider throwing around a string such as “PLT” or “Asia/Karachi,” you should
use this domain class instead. An example use case would be storing users’
preferences for their time zone.
// You can specify the zone id when
creating a zoned date time
ZoneId id =
ZoneId.of("Europe/Paris");
assertEquals(id, ZoneId.from(zoned));
ZonedDateTime zoned =
ZonedDateTime.of(dateTime, id);
Listing 6
·
ZoneOffset is the period of time
representing a difference between Greenwich/UTC and a time zone. This can be resolved
for a specific ZoneId at a specific moment in time, as
shown in Listing 7.
ZoneOffset offset =
ZoneOffset.of("+2:00");
Listing 7
Time Zone Classes
ZonedDateTime is a date and time with a fully
qualified time zone (see Listing 8). This can resolve an offset at any point in
time. The rule of thumb is that if you want to represent a date and time
without relying on the context of a specific server, you should useZonedDateTime.
ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]");
Listing
8
·
OffsetDateTime is a date and time with a
resolved offset. This is useful for serializing data into a database and also
should be used as the serialization format for logging time stamps if you have
servers in different time zones.
·
OffsetTime is a time with a resolved offset,
as shown in Listing 9.
OffsetTime time = OffsetTime.now(); // changes offset, while keeping the
same point on the timeline
OffsetTime sameTimeDifferentOffset = time.withOffsetSameInstant(offset);
OffsetTime changeTimeWithNewOffset = time.withOffsetSameLocal(offset);//
changes the offset, and updates the point on the timeline
changeTimeWithNewOffset.withHour(3).plusSeconds(2);
// Can also create new object with altered fields as before
Listing 9
There is an existing time zone class in Java—java.util.TimeZone—but it isn’t used by Java SE 8 be-cause all JSR 310 classes are immutable and time zone is mutable.
Periods
A Period represents a value such as “3 months and 1 day,” which is a
distance on the timeline. This is in contrast to the other classes we’ve looked
at so far, which have been points on the timeline. See Listing 10.
// 3 years, 2 months, 1 day
Period period = Period.of(3, 2, 1);
// You can modify the values of dates
using periods
LocalDate newDate =
oldDate.plus(period);
ZonedDateTime newDateTime =
oldDateTime.minus(period);
// Components of a Period are
represented by ChronoUnit values
assertEquals(1, period.get(ChronoUnit.DAYS));
Listing 10
Java SE 8 will ship with a new date and time API in java.time that offers greatly improved
safety and functionality for developers. The new API models the domain well,
with a good selection of classes for modeling a wide variety of developer use
cases.
Durations
A Duration is a distance on the timeline
measured in terms of time, and it fulfills a similar purpose to Period, but with different precision, as shown in Listing 11.
// A duration of 3 seconds and 5
nanoseconds
Duration duration =
Duration.ofSeconds(3, 5);
Duration oneDay =
Duration.between(today, yesterday);
Listing
11
It’s
possible to perform normal plus, minus, and “with” operations on a Durationinstance and also to modify the value
of a date or time using the Duration.
Chronologies
In order to
support the needs of developers using non-ISO calendaring systems, Java SE 8
introduces the concept of a Chronology, which
represents a calendaring system and acts as a factory for time points within
the calendaring system. There are also interfaces that correspond to core
time point classes, but are parameterized by
Chronology:
ChronoLocalDate
ChronoLocalDateTime
ChronoZonedDateTime
These classes are there purely for
developers who are working on highly internationalized applications that need
to take into account local calendaring systems, and they shouldn’t be used by
developers without these requirements. Some calendaring systems don’t even have
a concept of a month or a week and calculations would need to be performed via
the very generic field API.
Java SE 8 also has classes for some other
common use cases. There is the MonthDay class, which contains a pair of Month and Day and is
useful for representing birthdays. The YearMonth class covers the credit card start date
and expiration date use cases and scenarios in which people have a date with no
specified day.The Rest of the API
JDBC in Java
SE 8 will support these new types, but there will be no public JDBC API
changes. The existing generic setObject andgetObject methods will be sufficient.
These types
can be mapped to vendor-specific database types or ANSI SQL types; for example,
the ANSI mapping looks like Table 1.
ANSI SQL
|
Java SE 8
|
DATE
|
LocalDate
|
TIME
|
LocalTime
|
TIMESTAMP
|
LocalDateTime
|
TIME WITH
TIMEZONE
|
OffsetTime
|
TIMESTAMP
WITH TIMEZONE
|
OffsetDateTime
|
Table 1
Conclusion
Java SE 8
will ship with a new date and time API in java.time that
offers greatly improved safety and functionality for developers. The new API
models the domain well, with a good selection of classes for modeling a wide
variety of developer use cases.
Reference: http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html
0 comments:
Post a Comment