Is there a way to determine if a DateTimeFormatter is only a date or time only after building?

With Java 8's new time date library, a way to parse strings in dates is to use DateTimeFormatter . LocalDate , LocalTime and LocalDateTime all have a static parsing method that accepts a string and formatter. The potential getcha is that if your DateTimeFormat does not contain a temporary part (or for DateTime, a part of the date), you will receive an analysis error, even if your template matches.

Example

 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-DD"); LocalDateTime dt = LocalDateTime.parse("2016-01-11", formatter); 

This will DateTimeParseException (as discussed here ) with the message

 java.time.format.DateTimeParseException: Text '2016-01-11' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor 

This is a somewhat useless error message since the text can be parsed as it matches the pattern. Rather, the error is due to the fact that it cannot create a temporary part of LocalDateTime. Code refactoring below:

 LocalDateTime ldt = LocalDate.parse("2016-01-11", formatter).atStartOfDay()); 

My question is: let's say you had a general method like

  public static LocalDateTime getDate(String s, DateTimeFormatter format) { ... } 

Is there a way to determine which static parsing method you will need to call to force a string into LocalDateTime using some default logic? for example, if the date is used only at midnight, if the time is used only today, etc.

+5
source share
3 answers

I think you have a question in reverse order. You do not want to determine if the formatter uses only date or date / time. You create a formatter based on what you need to parse and on what you intend to store the result. Obviously, if you create a formatter that does not handle the temporary part, using it for analysis in LocalDateTime is a misconception.

If you need to parse dates that may appear with two formats, such as "yyyy-MM-dd" or "yyyy-MM-dd HH:mm:ss" (with the time part), it is not parsed to determine what he should do, but up to the format to provide a default in case there is no time.

With Java Time, this is done with additional sections and default values. For example, the pattern "yyyy-MM-dd[ HH:mm:ss]" will be able to parse a String date (for example, "2016-01-11" ) and a String date / time (for example, "2016-01-11 20:10:10" ). If you store this value in LocalDateTime , you will need to specify default values ​​if there is no time component. This is done using parseDefaulting(field, value) : this will say that formatting will return the default value for this chrono field, if it is not already set.

The following code creates such a formatter and by default sets the time until midnight.

 public static void main(String[] args) { DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd[ HH:mm:ss]") .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) .toFormatter(); LocalDateTime dt1 = LocalDateTime.parse("2016-01-11", formatter); LocalDateTime dt2 = LocalDateTime.parse("2016-01-11 20:10:10", formatter); } 

Of course, this logic can only be extended for parsing only String and by default for the date component to the current date.

+6
source

To analyze the use of formatting created by someone else is more complex than the parsing you can control (where Tunaki parseDefaulting() correct answer). However, this can be done:

 public static LocalDateTime getDate(String s, DateTimeFormatter format) { TemporalAccessor dt = parser.parseBest( str, LocalDateTime::from, LocalDate::from, LocalTime::from, YearMonth::from); if (dt instanceof LocalDate) { return ((LocalDate) dt).atStartOfDay(); } else if (dt instanceof LocalTime) { return ((LocalTime) dt).atDate(LocalDate.now()); } else if (dt instanceof YearMonth) { return ((YearMonth) dt).atDay(1).atStartOfDay(); } else { return LocalDateTime.from(dt); } } 

I have not tested the code above, but this is what was developed for the parseBest() method.

+6
source

According to your main question in the title , DateTimeFormatter returns only the generic TemporalAccessor and not the specific desired result type. Instead, users should indirectly use the analyzer result to feed static from(parsed) methods to a specific type. Therefore, you will find in the documentation of these methods which fields are expected for successful analysis:

LocalDate requires EPOCH_DAY

LocalTime requires NANO_OF_DAY

So it’s enough to request the raw analysis data for these fields to decide if the formatter is like a date, time, or a combination.

 DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ENGLISH); TemporalAccessor tacc = dateFormatter.parse("2015-07-24"); System.out.println("date-like: " + tacc.isSupported(ChronoField.EPOCH_DAY)); // true System.out.println("time-like: " + tacc.isSupported(ChronoField.NANO_OF_DAY)); // false DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm", Locale.ENGLISH); tacc = timeFormatter.parse("17:45"); System.out.println("date-like: " + tacc.isSupported(ChronoField.EPOCH_DAY)); // false System.out.println("time-like: " + tacc.isSupported(ChronoField.NANO_OF_DAY)); // true DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm", Locale.ENGLISH); tacc = dateTimeFormatter.parse("2015-07-24 17:45"); System.out.println("date-like: " + tacc.isSupported(ChronoField.EPOCH_DAY)); // true System.out.println("time-like: " + tacc.isSupported(ChronoField.NANO_OF_DAY)); // true 

But I have to agree with @Tunaki that using default values ​​for fields is a better idea from a design point of view . To complete the above code, you will also need special exception handling if the input does not match the pattern, etc.

+3
source

Source: https://habr.com/ru/post/1240625/


All Articles