Java: Parse multiple String Date Time format into LocalDateTime/Date/Calender - java

I am trying to parse string date Time into LocalDateTime or Date in Java.
I may get the inputs Like 12/21/2020 12:12:12 PM, 1/12/2020 2:6:8 PM, 10/2/2020 10:50:8 AM
I tried to use different types of patterns under DateTimeFormatter or DateTimeFormatBuilder Which Can Work. Brute force method Where I am Hard coding different combination of pattern. Here's the code copied from the link:
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
class Main {
public static void main(String[] args) {
// many combinations
final String DATE_FORMATS = "[MM/dd/yyyy HH:mm:ss a][M/dd/yyyy H:mm:ss a]";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DATE_FORMATS);
System.out.println(dateFormatter.parse("12/21/2020 11:12:12 AM"));
System.out.println(dateFormatter.parse("1/12/2020 2:54:55 AM"));
}
}
Is there any other solution as I don't have control over a input? Single pattern which,I can use in Formatter Instead of multiple combination ? Or Is it better to modify the input after receiving before parsing?

tl;dr
LocalDateTime.parse(dateTimeString, DateTimeFormatter.ofPattern("M/d/yyyy h:m:s a");
is sufficient for all of your datetime examples.
The following example is able to parse all of your sample datetime Strings by using LocalDateTime.parse instead of DateTimeFormatter.parse, which are different:
public static void main(String[] args) {
String first = "12/21/2020 12:12:12 PM";
String second = "1/12/2020 2:6:8 PM";
String third = "10/2/2020 10:50:8 AM";
DateTimeFormatter dtf = DateTimeFormatter
.ofPattern("[MM/dd/yyyy HH:mm:ss a][M/dd/yyyy h:m:s a][MM/d/yyyy HH:mm:s a]");
System.out.println(LocalDateTime.parse(first, dtf));
System.out.println(LocalDateTime.parse(second, dtf));
System.out.println(LocalDateTime.parse(third, dtf));
}
Have a look at the second / middle pattern used, it uses h (lower case) for hours instead of an H (upper case) because the AM/PM of day will be derived from a capital H, too and that would lead to a conflict in the second datetime sample "1/12/2020 2:6:8 PM" where 02:06:08 is considered AM, but is followed by PM in the pattern.
The output of my solution is this:
2020-12-21T12:12:12
2020-01-12T14:06:08
2020-10-02T10:50:08
which correctly parses the time to PM (14 = 02 PM).
Note:
Don't use this DateTimeFormatter for output LocalDateTime.format(DateTimeFormatter) because it would return all three formattings defined...
Very ugly: 10/02/2020 10:50:08 AM10/02/2020 10:50:8 AM10/2/2020 10:50:8 AM

I dealt with a similar problem, where I needed to parse a String to date without knowing the format in advance. I wrote an article about the idea and the implementation. Here is a quote from the article:
So the solution I came up with is to have a set of formats stored in property file, and when a String needs to be parsed the formats are read from a file and attempts to parse the String are made sequentially with each format until it is parsed successfully or until we run out of formats. The advantages of this solution are that if you discover a valid String that was not parsed successfully, all you will need to do is to add new format to your properties file and no re-compilation and re-deployment is needed. Also this way you can set your priorities: say if US date format is preferable to European one just place US formats first and only after the European ones. Also in java 8 the format strings allow for optional format sections denoted by '[]'. So several formats actually may be combined into a single one with optional sections. For example instead of
MM/dd/yyyy
MM-dd-yyyy
MM.dd.yyyy
you can just write
MM['/']['-']['.']dd['/']['-']['.']yyyy
Here is the link to the full article

Related

How to resolve Unparseable date Exception in Java 8 with Date format yyyy-MM-dd hh:mm a

I have a text file from which I am reading and setting transaction POJO class data, to get the difference between start and end time I need to parse the time information in date object.
DateFormat format = new SimpleDateFormat(dateFormat);
System.out.println("Date format in play:"+dateFormat);
Transaction transaction = storageRepositroy.getTransaction(key);
Date start = format.parse(transaction.getStartDate() + " " + transaction.getStartTime());//line no. 29
Date end = format.parse(transaction.getEndDate() + " " + transaction.getEndTime());
I am getting exception while running this code
Exception is
Date format in play:yyyy-MM-dd hh:mm a
java.text.ParseException: Unparseable date: "2020–03–01 03:15 PM"
at java.text.DateFormat.parse(DateFormat.java:366)
at dc.tech.transaction.util.TimeUtil.calculateAverageTime(TimeUtil.java:29)
yyyy-MM-dd hh:mm a is the date format which I am passing to SimleDateFormat constructor. I am unable to understand and debug why I am getting this error.
java.time
I recommend that you use java.time, the modern Java date and time API, for your date and time work.
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu\u2013MM\u2013dd");
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a", Locale.ENGLISH);
String dateString = "2020–03–01";
String timeString = "03:15 PM";
LocalDate date = LocalDate.parse(dateString, dateFormatter);
LocalTime time = LocalTime.parse(timeString, timeFormatter);
LocalDateTime dateTime = date.atTime(time);
System.out.println(dateTime);
Output:
2020-03-01T15:15
With java.time it is straightforward to combine date and time after parsing, so I prefer to parse them individually.
What went wrong in your code?
Credits go to Ajeetkumar for noticing and reporting in comments: The hyphen in your date string is not a usual minus sign or hyphen with character value 2D hexadecimal (45 decimal), but a en dash with character value 2013 hexadecimal (8211 decimal). So when you specify a usual hyphen in your format pattern string, they don’t match, and parsing fails. Instead I am using a Unicode escape for putting the en dash into the format pattern string. Simply pasting it in there would have worked too (provided that you save your .java file with a character encoding that supports it), but I wanted to make the reader aware that something special was going on here, so I preferred the Unicode escape with \u.
There is another problem with your code: You are not providing any locale for your formatter. So it uses the default locale of your JVM. As long as that locale expects PM, parsing will work. If one day you change your locale setting or run your code on a computer or JVM with a different default locale, parsing will suddenly fail, at you may have a hard time figuring out why. I have specified English locale for parsing the time. Some would prefer doing it for the date too even though technically it isn’t necessary.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Unicode Character 'EN DASH' (U+2013) on FileFormat.info.
I always stick to this mantra: use exactly the same format as your date-time string.
In the solutions given below, I have copied your date-time string into the pattern that I've specified for SimpleDateFormat and DateTimeFormatter and replaced the numbers with the corresponding letters e.g. 2020 with yyyy while keeping the rest of things (symbols, space etc.) intact.
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class Main {
public static void main(String[] args) throws ParseException {
DateFormat sdf = new SimpleDateFormat("yyyy–MM–dd hh:mm a", Locale.ENGLISH);// Make sure to use Locale
String dateTimeString = "2020–03–01 03:15 PM";// The string copied from the exception you have got
Date date = sdf.parse(dateTimeString);
System.out.println(sdf.format(date));
}
}
Output:
2020–03–01 03:15 PM
Note: The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. I suggest you should stop using them completely and switch to the modern date-time API.
Using the modern date-time API:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy–MM–dd hh:mm a", Locale.ENGLISH);// Make sure to use Locale
String dateTimeString = "2020–03–01 03:15 PM";// The string copied from the exception you have got
System.out.println(LocalDateTime.parse(dateTimeString, dtf));
}
}
Output:
2020-03-01T15:15
Learn more about the modern date-time API at Trail: Date Time.
If you are doing it for your Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

iso-8601 parser in Java/Spring with comma separator

According to
http://apiux.com/2013/03/20/5-laws-api-dates-and-times/
we should use ISO 8601 to format date.
The output on my system is:
$ date --iso-8601=ns
2020-10-29T10:38:59,112768965+01:00
Which java formatter parse this string correctly?
I've tried DateTimeFormatter.ISO_OFFSET_DATE_TIME and a few others, and they don't like this format. I expect that such a format should be supported out-of-the-box, as Oracle claims on its page https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html but it isn't.
Another thing is that Spring announced wide support for date format:
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#date-time-conversion-in-web-applications. I can then set all properties to "iso". But which exactly format is it, and where is it in use?
I see two problems here.
one is https://github.com/FasterXML/jackson-databind/issues/2643 (offset with or without semicolon)
the second one is existence of nano/milliseconds (I want to use them)
I would like to have consistent approach to date format across whole application, and I would like to use standard to easily integrate with the rest of the world. What should I use?
Edit: In the end it happened that there are a few things to consider:
Example from Linux console was artificial, as I didn't notice this format with "," in real world. It turned out, that it's slightly inconsistently supported in Java world
ObjectMapper, ModelMapper and OffsetDateTime makes Date object conversion different, especially conversion to ISO format ('Z' or '00:00')
Offset definition in form of "0000" also existed in Java, and I had to fix my conversion
ISO 8601-1 allows both , and . as decimal separators, though in practice this is the first time I've seen , actually in use. The default formats in JDK 11 only support parsing . (even though the documentation says the decimal separator is localised). To handle this format you have some options:
Modify the string before parsing:
OffsetDateTime.parse(value.replaceAll(",", "."));
Define a custom format:
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(ISO_LOCAL_DATE)
.appendLiteral('T')
.appendValue(HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2)
.optionalStart()
.appendLiteral(',')
.appendFraction(NANO_OF_SECOND, 0, 9, false)
.parseLenient()
.appendOffsetId()
.toFormatter()
.parse(value)
Use a locale that uses , separators (confirmed working in JDK 15):
DateTimeFormatter.ISO_OFFSET_DATE_TIME.localizedBy(Locale.FRANCE).parse(value);
There does not seem to be a way to define a format that allows either , or . but not both. You'd have to either try one catch the exception and try the other, or detect which format it's in (e.g. value.contains(",")) and use the appropriate formatter.
If your string always has got 9 decimals after the comma, just specify a comma in the format pattern string:
DateTimeFormatter isoWithCommaFormatter = DateTimeFormatter
.ofPattern("uuuu-MM-dd'T'HH:mm:ss,SSSSSSSSSXXX");
String iso = "2020-10-29T10:38:59,112768965+01:00";
OffsetDateTime odt = OffsetDateTime.parse(iso, isoWithCommaFormatter);
System.out.println(odt);
Output:
2020-10-29T10:38:59.112768965+01:00
If the number of decimals may vary, you need a DateTimeFormatterBuilder, but the basic trick is the same simple one: put a comma in the format pattern string.
DateTimeFormatter isoWithCommaFormatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendPattern("HH:mm:ss[,")
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, false)
.appendPattern("]XXX")
.toFormatter();
The ISO 8601 format recommends comma over dot as decimal separator, so it’s a little bit funny that the built in formatters and the one-arg parse methods only accept dot.
You can make the . or , as an optional pattern by putting them in a square bracket.
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter isoFormatter = new DateTimeFormatterBuilder()
.appendPattern("u-M-d'T'H:m:s[.][,]")
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, false)
.appendPattern("[XXX][XX][X]")
.toFormatter(Locale.ENGLISH);
// Test
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30,123456789+02:00", isoFormatter));
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30,123456789+0200", isoFormatter));
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30,123456789+02", isoFormatter));
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30.123456789+02:00", isoFormatter));
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30+02:00", isoFormatter));
}
}
Output:
2021-05-21T10:20:30.123456789+02:00
2021-05-21T10:20:30.123456789+02:00
2021-05-21T10:20:30.123456789+02:00
2021-05-21T10:20:30.123456789+02:00
2021-05-21T10:20:30+02:00
Alternatively,
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter isoFormatter = DateTimeFormatter.ofPattern(
"u[-M[-d]]['T'H[:m[:s[.][,][SSSSSSSSS][SSSSSSSS][SSSSSSS][SSSSSS][SSSSS][SSSS][SSS][SS][S]]]][XXX][XX][X]",
Locale.ENGLISH);
// Test
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30,123456789+02:00", isoFormatter));
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30,123456789+0200", isoFormatter));
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30,123456789+02", isoFormatter));
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30.123456789+02:00", isoFormatter));
System.out.println(OffsetDateTime.parse("2021-05-21T10:20:30+02:00", isoFormatter));
}
}
The output is the same.

Java date handling in improper format

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.
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
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...
}

Date Function is trimming seconds where seconds is 00

OffsetDateTime odtB = OffsetDateTime.parse("2019-02-02T13:55:00Z");
odtB.toString()
prints 2019-02-02T13:55 as output. As because of this my conversion function is throwing error!!
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM\''YY HH:mm aa");
String parsedDate = odtB.format(otdB);
How to stop OffsetDateTime or anyOther Java DateTime class from trimming seconds off when seconds are 00??
In java8, you do not need SimpleDateFormat any more, it's troublesome.
I suggest to use ISO_OFFSET_DATE_TIME:
The ISO date-time formatter that formats or parses a date-time with an
offset, such as '2011-12-03T10:15:30+01:00'.
Example:
import java.util.*;
import java.time.*;
import java.time.format.*;
public class HelloWorld{
public static void main(String []args){
OffsetDateTime odtB = OffsetDateTime.parse("2019-02-02T13:55:00Z");
DateTimeFormatter f = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.print(f.format(odtB)); // 2019-02-02T13:55:00Z
}
}
If you use java.time.LocalDateTime (which you should since Java 8), you can use different DateTimeFormatters, which you can configure (give them a pattern) to not trimming trailing zeros. See the following example using your date String with a slightly adjusted pattern:
LocalDateTime ldt = LocalDateTime.parse("2019-02-02T13:55:00Z", DateTimeFormatter.ISO_DATE_TIME);
System.out.println(ldt.format(DateTimeFormatter.ofPattern("dd-MMM\''YY HH:mm:ss")));
This prints 02-Feb'19 13:55:00, which hopefully is what you want.
SimpleDateFormat is from an old and obsolete way of working with Dates. It is also not Thread-safe and has a lot of other problems. In other words don't use it. You need to use DateTimeFormatter Please read the javadoc (link provided). It gives detailed explanation how to use it. However the cause of your problem is that in your format mask you are missing placeholder for seconds, thus when your String has seconds it doesn't conform with your format. Change the format to dd-MMM-YY HH:mm:ss aa emphases on "ss" - the missing seconds placeholder and it will work

Parsing from SimpleDateFormat to Date not working?

SimpleDateFormat df = new SimpleDateFormat();
Date lastLogin = null;
try {
String troubleChild = lineScanner.next();
lastLogin = df.parse(troubleChild);
} catch (ParseException e) {
System.out.println("ohnoes");
}
Hi I'm quite new to using the date functions and I've come up with a problem. I have a file that is being parsed into various variables and they all work except this one i can never get it so that it passes the try/catch clause i've looked up similar problems but none of them work on my code.(The date i am inputting is in the format: Mon, Oct 30 22:20:11 GMT 2017) please can I get some help and thanks for it!
Solution: java.time
Please don’t take the trouble with the long outmoded classes Date and SimpleDateFormat. Instead use java.time, the modern Java date and time API also known as JSR-310:
DateTimeFormatter dtf
= DateTimeFormatter.ofPattern("E, MMM d H:mm:ss z uuuu", Locale.UK);
String inputDate = "Mon, Oct 30 22:20:11 GMT 2017";
ZonedDateTime lastLogin = ZonedDateTime.parse(inputDate, dtf);
System.out.println(lastLogin);
This prints
2017-10-30T22:20:11Z[GMT]
Since dates and times may come in so many different textual formats, I am using a format pattern string to specify your particular format. For which letters you may use, and what difference it makes whether you use 1, 3 or 4 of the same letter, see the documentation. Beware that format pattern strings are case sensitive.
Problem: SimpleDateFormat
You used the no-arg SimpleDateFormat constructor. The way I read the documentation, this gives you the default date format for your locale. If your JVM is running UK locale, I believe the format goes like 28/11/17 10:57 — not much like the input format you were trying to parse. You can use System.out.println(df.format(new Date())); to find out. The usual SimpleDateFormat constructor to use would be SimpleDateFormat(String, Locale) so that you may again supply a format pattern string and a locale.

Categories