1

Firstly, I'm a C# dev learning Java. I'm converting a program I wrote in C# as an exercise and am having problems with parsing a date being submitted from an html form. The form is sent as an email and the java program reads the emails and parses the body. I have a drop down calendar for my peeps to select a date from but there's always some jerk who has to type it in and mess everything up. Currently I am doing this in my code:

public void SetDatePlayed(String datePlayed)
{
    this.datePlayed = LocalDate.parse(datePlayed);
}

datePlayed being passed in is a string usually formatted as yyyy-MM-dd but of course someone typed in 3/7 instead of using the calendar drop down on the form. this.datePlayed is a LocalDate. In C# I would just end up with a date that assumed 2020 for the year - no problem. LocalDate really wants it in the yyyy-MM-dd format and I don't know what the best practice here is with Java. I've been googling it all morning and haven't come across this as being an issue for anyone else. I don't care if I'm using LocalDate but I do need it to be a date datatype so I can do date checks, sorts, searches, etc later on.

4
  • "In C# I would just end up with a date that assumed 2020 for the year" What C# API handles both yyyy-MM-dd and 3/7 formats? I don't recall such a thing... Commented Apr 11, 2020 at 12:07
  • 1
    I'd look inside DateTimeFormatter and see how it does the parsing. Especially bits that use java.time.format.ResolverStyle#SMART Commented Apr 11, 2020 at 12:15
  • @Sweeper sure try DateTime.Parse("3/7") and you'll see you get a value of 3/7/2020 12:00:00AM. 99% of my input coming through my website uses yyyy-MM-dd so I know it's able to deal with both formats. Commented Apr 11, 2020 at 13:32
  • @user1572 Oh, you meant that... I never dared to let it guess the format like that... Commented Apr 11, 2020 at 14:51

2 Answers 2

4

You can use DateTimeFormatterBuilder and parseDefaulting() to supply default value for the year.

Building on the answer by Sweeper, it can be done like this:

static LocalDate parseLoosely(String text) {
    DateTimeFormatter fmt = new DateTimeFormatterBuilder()
            .appendPattern("[uuuu-M-d][M/d/uuuu][M/d]")
            .parseDefaulting(ChronoField.YEAR, Year.now().getValue())
            .toFormatter();
    return LocalDate.parse(text, fmt);
}

Warning: Do not cache the formatter in e.g. a static field, since it snapshots the year, if the program might be running across New Year's Eve, which a webapp would, unless you add logic to make the cache auto-refresh on year change.

Test

System.out.println(parseLoosely("2019-04-07"));
System.out.println(parseLoosely("2019-4-7"));
System.out.println(parseLoosely("4/7/2019"));
System.out.println(parseLoosely("4/7"));

Output

2019-04-07
2019-04-07
2019-04-07
2020-04-07
Sign up to request clarification or add additional context in comments.

Comments

2

I see two possible interpretations of your question. I'm not sure which one it is, so I'll answer both.

How do I parse a date string in a format that has no year, such as M/d (3/7), to a LocalDate?

You don't. A LocalDate by definition must have year, month, and day. If you only have a month and a day, that's a MonthDay:

MonthDay md = MonthDay.parse("3/7", DateTimeFormatter.ofPattern("M/d"));

If you want the current year added to it, you can do it later:

LocalDate ld = md.atYear(Year.now(/*optionally insert time zone*/).getValue());

How do I handle both yyyy-MM-dd and M/d patterns?

Here's one way: create a DateTimeFormatter that recognises both patterns, parse the string to a TemporalAccessor, check if the TemporalAccessor supports the "year" field:

TemporalAccessor ta = DateTimeFormatter.ofPattern("[M/d][yyyy-MM-dd]").parse("3/7");
if (ta.isSupported(ChronoField.YEAR_OF_ERA)) { // yyyy-MM-dd
    LocalDate ld = LocalDate.from(ta);
} else if (ta.isSupported(ChronoField.MONTH_OF_YEAR)) { // M/d
    MonthDay md = MonthDay.from(ta);
} else {
    // user has entered an empty string, handle error...
}

2 Comments

You actually can parse a date string in a format that has no year. See my answer, which builds on the solution you have at the bottom.
@Andreas Ahh~ parseDefaulting! I knew there has to be a method like this after writing my answer, I was going to add it at the end as an alternative way, but couldn't find what the method was called...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.