Wednesday, March 29, 2023

Quiz yourself: Java’s date and time TemporalAccessor generation interface

Core Java, Java Tutorial and Materials, Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation

By the way, how’s your working knowledge of the Liskov substitution principle?


Given the following code fragment

String pattern = "dd MMM yyyy HH:mm";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
LocalDate x = LocalDate.of(2022, 11, 1);
System.out.println(formatter.format(x));

What is the result if the default locale is en_US? Choose one.


A. 01 Nov 2022 is printed.
B. 01 11 2022 is printed.
C. 01 N 2022 is printed.
D. 01 November 2022 is printed.
E. Compilation fails.
F. A runtime exception occurs.

Answer. The Date and Time API added to Java 8 seeks to provide a generalizable API for calendars and time systems that might vary substantially across cultures. For example, the Gregorian calendar is widely used throughout the world, and it has 12 months, most of which have 30 or 31 days.

However, some calendars have different numbers of months in a year, and some have different numbers of days in their months. This means that the concepts of date and time are surprisingly complex.

When faced with complexity, software engineers typically try to find generalizations that allow simplifications and abstractions, and they use them to allow client code to be simpler.

The Date and Time API generalizes many representations of date and time to an interface called TemporalAccessor. In particular, this interface is implemented by LocalDate, LocalTime, LocalDateTime, and ZonedDateTime, among many other classes. The interface generalizes access to fields for the hour of the day, the month of the year, and so forth. The most common of these fields are enumerated in the constants of the enum type ChronoField.

The formatting mechanisms offered by DateTimeFormatter are generalized in terms of TemporalAccessor as well. It’s at this point that a problem arises: The formatter is capable of formatting all manner of fields, including the fields for the hour and minute of time, the day, and the month and year of the date. In the template string, all five of those elements are presented with their placeholders. The pattern characters H and m correspond to the hour of the day (0 through 23) and the minute of the hour, respectively.

However, a LocalDate does not carry any time information, so an attempt to access these values cannot be fulfilled in any sensible way. Rather than present dummy values, the implementation of LocalDate throws an exception in response to requests it cannot properly fulfill.

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay

Therefore, option F is correct.

The generalization TemporalAccessor, which is an interface, declares the same access methods regardless of the capabilities of the implementing class. Therefore, you can conclude that any code that attempts to access potentially unsupported fields through this interface will not generate compilation errors. This means that option E is incorrect.

It’s interesting to note that this approach, while allowing a powerful generalization over date and time types, contravenes the Liskov substitution principle (LSP) (which represents the L in SOLID, the set of five object-oriented design principles).

Very loosely, this principle suggests that if you believe an object is a generalization of something (in this case, that a LocalDate is a TemporalAccessor), that thing should substitute for the generalization in all uses without causing unexpected results. Surely throwing an exception on an otherwise valid method invocation qualifies as an unexpected result!

However, the benefit of breaking this principle in this situation is that the resulting API is much simpler and, most of the time, easier to use. As a further note, it’s possible to ask the TemporalAccessor whether it supports any given field using the method isSupported(TemporalField tf). However, the formatter does not check; if you use a given pattern character, the formatter requests that field directly, and the exception arises if that field is not supported by the target.

The suggested output shown in options A through D can be extracted successfully from a LocalDate—provided an appropriate formatter is used. For the sake of illustration, consider the following format strings, which would be needed to create those four output variations.

Option A would be correct for this pattern.

String pattern = "dd MMM yyyy";

Option B would be correct for this pattern.

String pattern = "dd L yyyy";

Option C would be correct for this pattern.

String pattern = "dd MMMMM yyyy";

And option D would be correct for this pattern.

String pattern = "dd MMMM yyyy";

Conclusion. The correct answer is option F.

Source: oracle.com

Related Posts

0 comments:

Post a Comment