Now It's Time to Uncomplicate With the Not-So-New API in Java

Dealing with dates or any operations that deal with time as a unit of measurement is always a big challenge. It is no different within the Java world; however, since Java 8, the new date API brings several improvements.

This article will understand more about the complexity of working with dates and how useful Java's date API is.

After All, Why Is the Date So Complex?

Date APIs are considered very complex, but what is the reason for this complexity? Unlike other units of measurement, dates have physical, cultural, political, historical, and religious complexity.

The physical part of the time is already tricky. In his theory of relativity, Albert Einstein demonstrates that time dilation is possible. In this way, the movie Interstellar's time travels are proven, and it is possible to understand more about these theories in the book "The Science of Interstellar."

In addition to the natural complexity, there are several policy interventions, such as:

There are many external impacts on time, and this does not occur with other units of measurement; for example, there is no unit of measure of distance modified throughout the year, such as a “summer meter” or a unit that ceases to exist by decree.

Understanding the Concepts of Time

There is already a certain complexity within the data API. Still, you can also see another problem in most people who work with development: they don't understand the unit of time. Knowing these concepts is already complex; it makes them even more problematic to understand.

For example, if someone asks, "What day is today? Or what time is it?" the answer will be it depends, as the big problems in the history of computing or software architecture. After all, knowing the day or time depends on which place I'm referring to; they now can change if I'm referring to Lisbon or San Francisco.

The objective here is to talk about the minimum concepts to facilitate the daily life of devs:

TDTL:

UTC is the standard used today to control the world's time, and its unit of measure is the offset. On the other hand, daylight saving time comes down to putting the clock forward one hour, that is, increasing one in the offset of its time, and the time zone is responsible for containing the history of the times of a region.

Getting to Know the Java Date API

After contextualizing the business and the concept behind time, let's talk about the not-so-new Java 8 date API. There are two attempts with time in the Java world. However, my recommendation is for new systems and functionality does not import any API that is not in the "java.time" package.

The Entities

The first step is to know the types of entities the data API supports; this article will not cover all the details, but you can look for a more detailed reading.

An important point: the API brings representations that cover day, month, day of the week, year, timezone, and offset, in addition to combining all of them!


Java
 
DayOfWeek dayOfWeek = DayOfWeek.MONDAY;
Month month = Month.JANUARY;
MonthDay monthDay = MonthDay.now();
YearMonth yearMonth = YearMonth.now();
Year year = Year.now();
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
OffsetDateTime offsetDateTime = OffsetDateTime.now();
ZonedDateTime zonedDateTime = ZonedDateTime.now();
Clock clock = Clock.systemUTC();
Instant instant = Instant.now();
TimeZone timeZone = TimeZone.getDefault();

System.out.println("DayOfWeek: " + dayOfWeek);
System.out.println("month: " + month);
System.out.println("MonthDay: " + monthDay);
System.out.println("YearMonth: " + yearMonth);
System.out.println("Year: " + year);
System.out.println("LocalDate: " + localDate);
System.out.println("LocalTime: " + localTime);
System.out.println("LocalDateTime: " + localDateTime);
System.out.println("OffsetDateTime: " + offsetDateTime);
System.out.println("ZonedDateTime: " + zonedDateTime);
System.out.println("Clock: " + clock.getZone());
System.out.println("Instant: " + instant);
System.out.println("TimeZone: " + timeZone.getDisplayName());


We emphasize here the possibilities that we can use with types or entities representing time in the API instead of using just a single one, as was previously done. For example:

In addition to bringing better semantics into the code, these variables bring clarity and simplify how you perform validations. It is based on those earlier mentioned classic “When Make a Type” by Martin Fowler.

Java
 
Assertions.assertThrows(DateTimeException.class, () -> Year.of(1_000_000_000));


It is also possible to make combinations between types to create a final instance; for example, we start with year and go to date.


Java
 
LocalDate myBirthday = Year.of(1988).atMonth(Month.JANUARY).atDay(9);


So, as a first step, I suggest you explore and read a little more about the types that the API supports. Since the date API has timed validations, these APIs are much more efficient at representing time than more generic types like int, long, or String.

Operations With Date

In addition to bringing more semantics and validation to dealing with dates, the API also brings some fascinating operations that aim to make your day easier and save your time. Sorry for the tempting pun.

The most basic operations are adding or removing periods, such as months or days, using methods with the suffixes “plus” or “minus,” respectively.


Java
 
LocalDate myBirthday = Year.of(1988).atMonth(Month.JANUARY).atDay(9);
LocalDate yesterday = myBirthday.minusDays(1);
LocalDate oneYear = myBirthday.plusDays(365);


The TemporalAdjuster interface allows custom adjustments and some more complex operations, and from there, it is possible to create many customizable and reusable procedures. As a convention, this utility class brings several features; for example, checking the following Monday from the current date, I refer to the TemporalAdjusters class.


Java
 
LocalDate myBirthday = Year.of(1988).atMonth(Month.JANUARY).atDay(9);
LocalDate newYear = myBirthday.with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDayOfMonth = myBirthday.with(TemporalAdjusters.lastDayOfMonth());
LocalDate nextMonday = myBirthday.with(TemporalAdjusters.next(DayOfWeek.MONDAY));


It is also possible to make comparisons between dates, nothing very recent compared to the older APIs. However, it is always important to point out that this exists.


Java
 
LocalDate myBirthday = Year.of(1988).atMonth(Month.JANUARY).atDay(9);
LocalDate now = LocalDate.now();
Assertions.assertTrue(now.isAfter(myBirthday));
Assertions.assertFalse(now.isBefore(myBirthday));


The primary operations are beneficial. However, it is possible to go further with Period and also ChronoUnit. With them, you can see the difference between a specific period.


Java
 
LocalDate today = LocalDate.now();
LocalDate tomorrow = LocalDate.now().plusDays(1);
Assertions.assertEquals(1, ChronoUnit.DAYS.between(today, tomorrow));

LocalDate dateA = LocalDate.of(2012, Month.APRIL, 7);
LocalDate dateB = LocalDate.of(2015, Month.DECEMBER,5);

Period period = Period.between(dateA, dateB);

Assertions.assertEquals(3, period.getYears());
Assertions.assertEquals(7, period.getMonths());
Assertions.assertEquals(28, period.getDays());
Assertions.assertEquals(43, period.toTotalMonths());


The last step for comparison and verification is the TemporalQuery, which aims to extract some time information.


Java
 
TemporalQuery<Boolean> weekend = temporal -> {
    int dayOfWeek = temporal.get(ChronoField.DAY_OF_WEEK);
    return dayOfWeek == DayOfWeek.SATURDAY.getValue()
           || dayOfWeek == DayOfWeek.SUNDAY.getValue();
};
LocalDate date = LocalDate.of(2018, 5, 4);
LocalDateTime sunday = LocalDateTime.of(2018, 5, 6, 17, 0);
Assertions.assertFalse(date.query(weekend));
Assertions.assertTrue(sunday.query(weekend));


The last step for comparison and verification is the TemporalQuery, which aims to extract some time information.


Java
 
ZoneId saoPaulo = ZoneId.of("America/Sao_Paulo");
ZonedDateTime adenNow = ZonedDateTime.now(saoPaulo);
ZoneOffset offset = adenNow.getOffset();
Assertions.assertEquals(saoPaulo, adenNow.getZone());
Assertions.assertEquals("-03:00", offset.getId());


It is possible to make comparisons of different timezones, for example, comparing a time in Brazil and another in Portugal, which on May 3 are four hours apart.


Java
 
ZoneId saoPaulo = ZoneId.of("America/Sao_Paulo");
ZoneId portugal = ZoneId.of("Portugal");
LocalDateTime timeSP = Year.of(2021).atMonth(Month.MAY).atDay(3)
.atTime(12,0,0);

LocalDateTime timeLisbon = Year.of(2021).atMonth(Month.MAY).atDay(3)
.atTime(16,0,0);

ZonedDateTime zoneSaoPaulo = ZonedDateTime.of(timeSP, saoPaulo);
ZonedDateTime zoneLisbon = ZonedDateTime.of(timeLisbon, portugal);
Assertions.assertTrue(zoneSaoPaulo.isEqual(zoneLisbon));


It is important to emphasize that what we mentioned is just an overview of the API, and it is worth reading the project documentation, which is extremely rich and detailed.

Conclusion

Despite not being a new API, the Java date API is still little used and little explored for several reasons.

Either because of the complexity in dealing with time, the number of available documents is still tiny compared to legacy APIs or not being a well-known API.

It is essential to mention that this API brings many benefits, from its clarity with the methods to several operation features that are well worth knowing. Our article aims to revive this topic and get you on the rock's path for you to explore the API further.

 

 

 

 

Top