LocalDate: parse MM-yyyy - java

I get java.time.format.DateTimeParseException after attempt to do following:
LocalDate.parse( "09-2017" , DateTimeFormatter.ofPattern("MM-yyyy") )
What is wrong? Is there any utility in Java to check dateString formats?

A LocalDate needs the day, month and year to be built. Your input has only the month and year. You'll have to choose an arbitrary day and set it to the parsed object to create a LocalDate.
You can either parse it to a java.time.YearMonth, and then choose the day:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM-yyyy");
YearMonth ym = YearMonth.parse("09-2017", fmt);
LocalDate dt = ym.atDay(1); // choose whatever day you want
Or you can use a java.time.format.DateTimeFormatterBuilder with a java.time.temporal.ChronoField to define a default value for the day:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// month-year
.appendPattern("MM-yyyy")
// default value for day
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
// create formatter
.toFormatter();
LocalDate dt = LocalDate.parse("09-2017", fmt);
PS: If you just want to check if the input is correct, just parsing it to a YearMonth is enough (it already checks if the parsed values are valid).

Related

Including wildcard in a SimpleDateFormat

I want a DateFormatter in java so that i can specify some special character as well as digits in a date expression. For ex :
String dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz";
Here dd is used to specify the day of month which is numeric.
But i have a requirement to create a date as below :
String stringDate = "2017-12-??T00:00Z";
SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);
formatter.parse(stringDate);
I get an unparseable exception as the DAY specified here is ?? . Is there any workaround for this or shall i have to write a new parser ?
Thanks
Try escaping the additional literals using single quote
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-'??T'HH:mm:ss.SSS zzz");
Also the value and the format given should match(Can edit the string date as required), in your case following syntax will work.
String stringDate = "2017-12-??T00:00Z";
Date date = (new SimpleDateFormat("yyyy-MM-'??T'HH:mmZ")).parse(stringDate.replaceAll("Z$", "+0000"));
System.out.println("date: " + (new SimpleDateFormat("yyyy-MM-dd'??T'HH:mmZ")).format(date));
Please note that 'Z' indicates that the timezone conforms to the RFC 822 time zone standard as well.
Edit: Consider a scheduler. Your comment may sound like what you need is a scheduler, for example Quartz scheduler. I include a link at the bottom. Then convert user input not to a YearMonth, OffsetDateTime or any other date-time object (because they don’t fit), but into a syntax that your scheduler can accept.
Original answer
I am giving you a couple of suggestions. It’s with reservation though: I don’t understand why you want this, not even exactly what you want, so these suggestions may not be the right ones for you.
One suggestion I am pretty sure of, though: do use java.time, the modern java date and time API, for your date and time work. It is so much nicer to work with than the old, poorly designed and long outdated date-time classes that include the notoriously troublesome SimpleDateFormat class.
Parsing year and month: If you just want the year and the month from a string that has question marks instead of the day of month, parse into a YearMonth:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-'??T'HH:mmX");
String stringDate = "2017-12-??T00:00Z";
YearMonth ym = YearMonth.parse(stringDate, formatter);
System.out.println("Year and month are " + ym);
Output from this snippet is:
Year and month are 2017-12
Parsing all information from the string: If you need time of day and offset from the same string too, just parse the string once and get the various information from the parse result:
TemporalAccessor parsed = formatter.parse(stringDate);
YearMonth ym = YearMonth.from(parsed);
System.out.println("Year and month are " + ym);
LocalTime time = LocalTime.from(parsed);
System.out.println("Time of day is " + time);
ZoneOffset offset = ZoneOffset.from(parsed);
System.out.println("UTC offset is " + offset);
Year and month are 2017-12
Time of day is 00:00
UTC offset is Z
Using a default day of month: If you know what day of month you want instead of the question marks, specify it as a default value:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-'??T'HH:mmX")
.parseDefaulting(ChronoField.DAY_OF_MONTH, 23)
.toFormatter();
String stringDate = "2017-12-??T00:00Z";
OffsetDateTime dateTime = OffsetDateTime.parse(stringDate, formatter);
System.out.println("Date and time is " + dateTime);
Date and time is 2017-12-23T00:00Z
Accepting both numbers and question marks: If the date can be given as either numeric or question marks, use optional parts in the format pattern strings. Such are enclosed in square brackets:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-[??][dd]'T'HH:mmX")
.parseDefaulting(ChronoField.DAY_OF_MONTH, 23)
.toFormatter();
String stringDate = "2017-12-??T00:00Z";
OffsetDateTime dateTime = OffsetDateTime.parse(stringDate, formatter);
System.out.println("Date and time is " + dateTime);
stringDate = "2018-02-16T00:00Z";
dateTime = OffsetDateTime.parse(stringDate, formatter);
System.out.println("Date and time is " + dateTime);
Date and time is 2017-12-23T00:00Z
Date and time is 2018-02-16T00:00Z
Tutorial links
Cron Trigger Tutorial from the Quartz Scheduler documentation.
Oracle tutorial: Date Time explaining how to use java.time.

joda time unexpected format

I'm trying to use Joda-Time library to convert a String date and time to Date but the result I get is not the expected.
From the server I get:
08/11/2017 12:30
10/11/2017 12:30
Joda converts it to:
2017-01-08T12:30:00.000+02:00
2017-01-10T12:30:00.000+02:00
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/mm/yyyy HH:mm:ss");
// add two :00 at the end for the seconds
startDate = startDate +":00";
DateTime start = formatter.parseDateTime(startDate);
System.out.println(start.toString());
endDate= endDate + ":00";
DateTime end = formatter.parseDateTime(endDate);
That's because you're using mm for the month, but the correct pattern is uppercase MM. Check the documentation for more details.
One more thing. If your input doesn't have the seconds (:00), you don't need to append it in the end of the input strings. You can simply create a pattern without it:
// "MM" for month, and don't use "ss" for seconds if input doesn't have it
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm");
// parse input (without ":00" for the seconds)
DateTime start = formatter.parseDateTime("08/11/2017 12:30");
System.out.println(start.toString());
The output will be:
2017-11-08T12:30:00.000-02:00
Notice that the offset (-02:00) is different from yours. That's because DateTime uses the default timezone if you don't specify one.

Java: Unable to obtain LocalDate from TemporalAccessor

I am trying to change the format of a String date from EEEE MMMM d to MM/d/yyyy by, first, converting it into a LocalDate and then applying a formatter of a different pattern to the LocalDate before parsing it into String again.
Here's my code:
private String convertDate(String stringDate)
{
//from EEEE MMMM d -> MM/dd/yyyy
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
.toFormatter();
LocalDate parsedDate = LocalDate.parse(stringDate, formatter);
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");
String formattedStringDate = parsedDate.format(formatter2);
return formattedStringDate;
}
However, I get this exception message that I don't really understand:
Exception in thread "main" java.time.format.DateTimeParseException: Text 'TUESDAY JULY 25' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {DayOfWeek=2, MonthOfYear=7, DayOfMonth=25},ISO of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
As the other answers already said, to create a LocalDate you need the year, which is not in the input String. It has only day, month and day of the week.
To get the full LocalDate, you need to parse the day and month and find a year in which this day/month combination matches the day of the week.
Of course you could ignore the day of the week and assume that the date is always in the current year; in this case, the other answers already provided the solution. But if you want to find the year that exactly matches the day of the week, you must loop until you find it.
I'm also creating a formatter with a java.util.Locale, to make it explicit that I want month and day of week names in English. If you don't specify a locale, it uses the system's default, and it's not guaranteed to always be English (and it can be changed without notice, even at runtime).
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
// use English Locale to correctly parse month and day of week
.toFormatter(Locale.ENGLISH);
// parse input
TemporalAccessor parsed = formatter.parse("TUESDAY JULY 25");
// get month and day
MonthDay md = MonthDay.from(parsed);
// get day of week
DayOfWeek dow = DayOfWeek.from(parsed);
LocalDate date;
// start with some arbitrary year, stop at some arbitrary value
for(int year = 2017; year > 1970; year--) {
// get day and month at the year
date = md.atYear(year);
// check if the day of week is the same
if (date.getDayOfWeek() == dow) {
// found: 'date' is the correct LocalDate
break;
}
}
In this example, I started at year 2017 and tried to find a date until back to 1970, but you can adapt to the values that fits your use cases.
You can also get the current year (instead of some fixed arbitrary value) by using Year.now().getValue().
The documentation for LocalDate says, that
LocalDate is an immutable date-time object that represents a date,
often viewed as year-month-day. For example, the value "2nd October
2007" can be stored in a LocalDate.
In your case, the input String is missing an important component of LocalDate , i.e the year. What you have basically is month and day. So, you can use a class suited to that MonthDay. Using that your code can be modified to :
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("EEEE MMMM d"))
.toFormatter();
MonthDay monthDay = MonthDay.parse(stringDate, formatter);
LocalDate parsedDate = monthDay.atYear(2017); // or whatever year you want it at
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");
String formattedStringDate = parsedDate.format(formatter2);
System.out.println(formattedStringDate); //For "TUESDAY JULY 25" input, it gives the output 07/25/2017
Here is the minor change which you need to implement:
private static String convertDate(String stringDate)
{
//from EEEE MMMM d -> MM/dd/yyyy
DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("EEEE MMMM dd")
.parseDefaulting(ChronoField.YEAR, 2017)
.toFormatter();
LocalDate parsedDate = LocalDate.parse(stringDate, formatter);
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/d/yyyy");
String formattedStringDate = parsedDate.format(formatter2);
return formattedStringDate;
}
Add the default chronological year in the formatter using .parseDefaulting(ChronoField.YEAR, 2017)
Call the method using the argument "Tuesday July 25" like this convertDate("Tuesday July 25");
Another option is to do the following (just like the other answers a bit hacky), assuming of course you want the date to fall in the current year:
LocalDate localDate = LocalDate.parse(stringDate + " " +LocalDate.now().getYear(), DateTimeFormatter.ofPattern("EEEE MMMM d");

Java 8: How to parse expiration date of debit card?

It is really easy to parse expiration date of debit/credit card with Joda time:
org.joda.time.format.DateTimeFormatter dateTimeFormatter = org.joda.time.format.DateTimeFormat.forPattern("MMyy").withZone(DateTimeZone.forID("UTC"));
org.joda.time.DateTime jodaDateTime = dateTimeFormatter.parseDateTime("0216");
System.out.println(jodaDateTime);
Out: 2016-02-01T00:00:00.000Z
I tried to do the same but with Java Time API:
java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("MMyy").withZone(ZoneId.of("UTC"));
java.time.LocalDate localDate = java.time.LocalDate.parse("0216", formatter);
System.out.println(localDate);
Output:
Caused by: java.time.DateTimeException: Unable to obtain LocalDate
from TemporalAccessor: {MonthOfYear=2, Year=2016},ISO,UTC of type
java.time.format.Parsed at
java.time.LocalDate.from(LocalDate.java:368) at
java.time.format.Parsed.query(Parsed.java:226) at
java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
... 30 more
Where I made a mistake and how to resolve it?
A LocalDate represents date that is composed of a year, a month and a day. You can't make a LocalDate if you don't have those three fields defined. In this case, you are parsing a month and a year, but there is no day. As such, you can't parse it in a LocalDate.
If the day is irrelevant, you could parse it into a YearMonth object:
YearMonth is an immutable date-time object that represents the combination of a year and month.
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMyy").withZone(ZoneId.of("UTC"));
YearMonth yearMonth = YearMonth.parse("0216", formatter);
System.out.println(yearMonth); // prints "2016-02"
}
You could then transform this YearMonth into a LocalDate by adjusting it to the first day of the month for example:
LocalDate localDate = yearMonth.atDay(1);

Why my pattern("yyyyMM") cannot parse with DateTimeFormatter (java 8)

When I using SimpleDateFormat, it can parse.
SimpleDateFormat format = new SimpleDateFormat("yyyyMM");
format.setLenient(false);
Date d = format.parse(date);
But When I use Java 8 DateTimeFormatter,
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
LocalDate localDate = LocalDate.parse(date, formatter);
it throws
java.time.format.DateTimeParseException: Text '201510' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {Year=2015, MonthOfYear=10},ISO of type java
.time.format.Parsed
String value for date is "201510".
Ask yourself the question: which day should be parsed with the String "201510"? A LocalDate needs a day but since there is no day in the date to parse, an instance of LocalDate can't be constructed.
If you just want to parse a year and a month, you can use the YearMonth object instead:
YearMonth localDate = YearMonth.parse(date, formatter);
However, if you really want to have a LocalDate to be parsed from this String, you can build your own DateTimeFormatter so that it uses the first day of the month as default value:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("yyyyMM")
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
.toFormatter();
LocalDate localDate = LocalDate.parse(date, formatter);
You can use a YearMonth and specify the day you want (say the first for example):
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
LocalDate localDate = YearMonth.parse(date, formatter).atDay(1);
Or if the day is irrelevant, just use a YearMonth.

Categories