SRSR333
SRSR333

Reputation: 266

Returning a ZonedDateTime always from a Java TemporalAccessor with missing fields

I have a certain class that parses a (fairly) wide variety of dates:

// imports omitted for brevity

public final class DateParser
{
    private static final String DATE_TIME_FORMAT_PATTERN 
            = "[h][hh][[:][.]mm][[ ]a][ v][ ][[EEEE ][E ][d][dd][/][-][LLLL][L][/][-][uuuu]]";

    private static final DateTimeFormatter formatter 
            = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT_PATTERN).localizedBy(Locale.ENGLISH);

    public static ZonedDateTime parseDateTimeString(final String dateTimeString)
    {
        TemporalAccessor parsedString = formatter.parse(dateTimeString);

        ZonedDateTime returnable = ZonedDateTime.now();

        // Return a ZonedDateTime with sane defaults if some fields are missing from parsedString
    }
}

I would like to return a ZonedDateTime that has sane defaults if parsedString is not a complete ZonedDateTime.

  1. Say, for instance, that the string given to the method is "5:45pm" (which will parse successfully); I would like the ZonedDateTime to reflect 5:45 pm today, at the current time zone. Of course, a more pre-emptive way would be to reflect 5:45pm the next day if it is already past quarter to six today, but I'd like to get this implemented first.

  2. Other possible inputs are just the day of week, for instance, "Monday": this would return a ZonedDateTime set for the next Monday after the current day, at 12 pm, at the local time zone.

  3. An input of just a date "4/2/2020" would return a ZonedDateTime set at the local time zone, the date in the input (the 4th of February, 2020), with time set at 12 pm.

How would I go about doing this? I currently have bit of code, that just consists of a very long chain of ifs:

if (parsedString.isSupported(ChronoField.YEAR))
{
    returnable = returnable.withYear(parsedString.get(ChronoField.YEAR));
}            
if (parsedString.isSupported(ChronoField.DAY_OF_MONTH))
{
    returnable = returnable.withMonth(parsedString.get(ChronoField.MONTH_OF_YEAR));
}
if (parsedString.isSupported(ChronoField.DAY_OF_MONTH)) 
{
    returnable = returnable.withDayOfMonth(parsedString.get(ChronoField.DAY_OF_MONTH));
}
// And so on.

However, this merely modifies the current date-time group. I am not sure how to implement things like (2) and (3).

Furthermore, it possible to build up a ZonedDateTime from parsedString, by iterating over its fields, somehow? Or is there some other way to achieve this entire parsing idea?

Thanks, everyone.

Upvotes: 0

Views: 188

Answers (1)

VGR
VGR

Reputation: 44404

I don’t think you can do this with only a pattern, but you can create a DateTimeFormatterBuilder and call its parseDefaulting method:

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern(DATE_TIME_FORMAT_PATTERN);

ZonedDateTime defaults = ZonedDateTime.now();

builder.parseDefaulting(ChronoField.YEAR,
       defaults.getLong(ChronoField.YEAR));
builder.parseDefaulting(ChronoField.MONTH_OF_YEAR,
       defaults.getLong(ChronoField.MONTH_OF_YEAR));
builder.parseDefaulting(ChronoField.DAY_OF_MONTH,
       defaults.getLong(ChronoField.DAY_OF_MONTH));
// etc.

DateTimeFormatter formatter = builder.toFormatter();

ZonedDateTime returnable = ZonedDateTime.parse(dateTimeString, formatter);

Upvotes: 1

Related Questions