How to prevent SimpleDateFormat to parse wrong formatted dates? - java

I use SimpleDateFormat to parse strings to Date objects and I wonder why the results are not what I expect.
For example:
DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date date = yyyyMMdd.parse("20100725");
System.out.println(date);
works as expected and outputs
Sun Jul 25 00:00:00 CEST 2010
But
Date date = yyyyMMdd.parse("2010-07-25");
System.out.println(date);
also works and outputs
Mon Dec 07 00:00:00 CET 2009
I expected a ParseException, but it seems that SimpleDateFormat interpretes the month part -07 and the day part -25 as a negative number. First I couldn't figure out how it comes to 7th of december. So I tried another value:
Date date = yyyyMMdd.parse("2010-7-25");
System.out.println(date);
and it outpus
Sun Apr 05 00:00:00 CEST 2009
So it seems that it somehow subtracts 7 month from the year 2010 which whould be 1th of may, and 25 days so the result is 5th of april 2009.
Image that you use the pattern yyyyMMdd in an service implementation and some client accidentially sends the date as yyyy-MM-dd. You will not get an exception. Instead you will get totally different dates. I guess this is not what you expect.
E.g.
String clientData = "2010-05-23";
DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date parsedDate = yyyyMMdd.parse(clientData);
System.out.println("Client : " + clientData);
System.out.println("Service : " + yyyyMMdd.format(parsedDate));
Do I miss something?
How do I prevent SimpleDateFormat to parse 'wrong' dates?
Sure I can use a regular expression to check first, but is there a better way?

Use SimpleDateFormat.setLenient(false); to get an exception. Otherwise it will try to parse the input as best as it can, which is usually wrong.
For some reason they decided that leniency should be true by default, but that is hardly a surprise.
Specify whether or not date/time parsing is to be lenient. With
lenient parsing, the parser may use heuristics to interpret inputs
that do not precisely match this object's format. With strict
parsing, inputs must match this object's format.

The accepted Answer by Cayman is correct: leniency in parsing by default is the problem.
java.time
You are using troublesome old date-time classes now supplanted by the java.time classes.
No such leniency-by-default problem in java.time. If the input does not strictly match the formatting pattern, a DateTimeParseException is thrown.
The LocalDate class represents a date-only value without time-of-day and without time zone.
ISO 8601 format
For standard ISO 8601 formatted inputs of YYYY-MM-DD, simply call parse directly.
String input = "2010-05-23";
try {
LocalDate ld = LocalDate.parse( input ); // Expects standard ISO 8601 input format.
} catch ( DateTimeParseException e ) {
…
}
“Basic” ISO 8601 format
The ISO 8601 standard allows for “basic” formats that minimize the use of separators. Not that I recommend these variations, but they exist.
Currently java.time predefines only a single one of these “basic” variations, DateTimeFormatter.BASIC_ISO_DATE.
String input = "20100725";
try {
LocalDate ld = LocalDate.parse( input , DateTimeFormatter.BASIC_ISO_DATE );
} catch ( DateTimeParseException e ) {
…
}
Custom format
For other formats, specify a formatter.
String input = "2010/07/25";
try {
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu/MM/dd" );
LocalDate ld = LocalDate.parse( input , f ); // Custom format.
} catch ( DateTimeParseException e ) {
…
}
Localized format
Or let java.time determine the localized format.
String input = … ;
try {
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( l );
LocalDate ld = LocalDate.parse( input , f ); // Localized format.
} catch ( DateTimeParseException e ) {
…
}

SimpleDateFormat.setLenient(false);
Is what needs to be done, or the input will be tried to be parsed well, and as you know, that doesn't always work. With the function above, the compiler will be strict about the format.

First, if you want to parse String "2010-05-23" your mask should be "yyyy-MM-dd" and not "yyyyMMdd". Second SimpleDateFormat has serious problems as it is not Thread safe. If you use java 8 then use learn and use new package "java.time". If you use any java earlier then version 8 then use some other frameworks for parsing date. One of the most popular is Joda time. Works much better.

Related

java.time.format.DateTimeParseException: Text '13.05.2018' could not be parsed at index 2

I have a websocket that receives requests from clients as JSON strings. One of the requests I receive contains a date. I am retrieving the date as a String but I am unable to change it to LocalDate. This is the JSON request I receive {"type":"dataRequest","startDate":"13.05.2018","endDate":"20.05.2018","interval":"01:01:01"}
I am using Google's GSON to parse the JSON.
This is the Java code I am using to get the date as a String and to parse it as LocalDate.
private List<LocalDate> getStartEndDate(String message){
List<LocalDate> dates = new ArrayList();
JsonObject obj = parseJson(message);
JsonPrimitive date = obj.getAsJsonPrimitive("startDate");
String dateString = date.toString();
DateTimeFormatter formater = DateTimeFormatter.ofPattern("dd.MM.yyyy");
dates.add(LocalDate.parse(dateString, formater));
date = obj.getAsJsonPrimitive("endDate");
dateString = date.getAsString();
LocalDate end = LocalDate.parse(dateString, formater);
dates.add(end);
return dates;
}
No problem
The kind of code you present here should be working. You have not presented enough information here to solve the problem.
See this code run live at IdeOne.com.
String input = "20.05.2018" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.yyyy" );
LocalDate ld = LocalDate.parse( input , f ) ;
ld.toString(): 2018-05-20
Debugging
When debugging such a problem, reduce the problem to its essential core. In this case, this means stripping away all the JSON business, to focus on getting the date-parsing to work properly by itself.
This also makes for a much better Question on Stack Overflow.
ISO 8601
Best practice for exchanging date-time values as text is to always use the standard ISO 8601 formats.
The java.time classes use these standard formats by default when parsing/generating strings. See above for an example of this, YYYY-MM-DD format.

Simpledateformat unparseable date

I have a String in a database (match.getDate) that has the following date format:
01/04/2018
This is the date I want to format, stored as day/month/year. I want to format this for my Android app.
I want to format the date into:
Sun 01 Apr 2018
My code below:
SimpleDateFormat fDate = new SimpleDateFormat("dd/MM/yyyy");
try {
textViewDate.setText(fDate.parse(match.getDate()).toString());
} catch (ParseException ex) {
System.out.println(ex.toString());
}
This outputs:
Sun Apr 08 00:00:00 GMT+00:00 2018.
I have also tried "EE, MM d, yyyy", but it gives me:
java.text.ParseException: Unparseable date: "01/04/2018"
The other answers solved your problem, but I think it's important to know some concepts and why your first attempt didn't work.
There's a difference between a date and a text that represents a date.
Example: today's date is March 9th 2018. That date is just a concept, an idea of "a specific point in our calendar system".
The same date, though, can be represented in many formats. It can be "graphical", in the form of a circle around a number in a piece of paper with lots of other numbers in some specific order, or it can be in plain text, such as:
09/03/2018 (day/month/year)
03/09/2018 (monty/day/year)
2018-03-09 (ISO8601 format)
March, 9th 2018
9 de março de 2018 (in Portuguese)
2018年3月5日 (in Japanese)
and so on...
Note that the text representations are different, but all of them represent the same date (the same value).
With that in mind, let's see how Java works with these concepts.
a text is represented by a String. This class contains a sequence of characters, nothing more. These characters can represent anything; in this case, it's a date
a date was initially represented by java.util.Date, and then by java.util.Calendar, but those classes are full of problems and you should avoid them if possible. Today we have a better API for that.
In Android, you can use the java.time classes if available in the API level you're using, or the threeten backport for API levels lower than that (check here how to use it). You'll have easier and more reliable tools to deal with dates.
In your case, you have a String (a text representing a date) and you want to convert it to another format. You must do it in 2 steps:
convert the String to some date-type (transform the text to numerical day/month/year values) - that's called parsing
convert this date-type value to some format (transform the numerical values to text in a specific format) - that's called formatting
Why your attempts didn't work:
the first attempt gave you the wrong format because you called Date::toString() method, which produces an output (a text representation) in that format (Sun Apr 08 00:00:00 GMT+00:00 2018) - so the parsing was correct, but the formatting wasn't
in the second attempt, you used the output pattern (EE dd MMM yyyy, the one you should use for formatting) to parse the date (which caused the ParseException).
For step 1, you can use a LocalDate, a type that represents a date (day, month and year, without hours and without timezone), because that's what your input is:
String input = "01/04/2018";
DateTimeFormatter inputParser = DateTimeFormatter.ofPattern("dd/MM/yyyy");
// parse the input
LocalDate date = LocalDate.parse(input, inputParser);
That's more reliable than SimpleDateFormat because it solves lots of strange bugs and problems of the old API.
Now that we have our LocalDate object, we can do step 2:
// convert to another format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EE dd MMM yyyy", Locale.ENGLISH);
String output = date.format(formatter);
Note that I used a java.util.Locale. That's because the output you want has the day of week and month name in English, and if you don't specify a locale, it'll use the JVM's default (and who guarantees it'll always be English? it's better to tell the API which language you're using instead of relying on the default configs, because those can be changed anytime, even by other applications running in the same JVM).
And how do I know which letters must be used in DateTimeFormatter? Well, I've just read the javadoc.
Use this date formatter method I have created
public static String dateFormater(String dateFromJSON, String expectedFormat, String oldFormat) {
SimpleDateFormat dateFormat = new SimpleDateFormat(oldFormat);
Date date = null;
String convertedDate = null;
try {
date = dateFormat.parse(dateFromJSON);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(expectedFormat);
convertedDate = simpleDateFormat.format(date);
} catch (Exception e) {
e.printStackTrace();
}
return convertedDate;
}
and call this method like
dateFormater(" 01/04/2018" , "EE dd MMM yyyy" , "dd/MM/yyyy")
and you will get the desired output
You need two date formatters here. One to parse the input, and a different formatter to format the output.
SimpleDateFormat inDateFmt = new SimpleDateFormat("dd/MM/yyyy");
SimpleDateFormat outDateFmt = new SimpleDateFormat("EEE dd MMM yyyy");
try {
Date date = inDateFmt.parse(match.getDate());
textViewDate.setText(outDateFmt.format(date));
} catch (ParseException ex) {
System.out.println(ex.toString());
}
Try this, you can create any date format you want with this
public String parseTime(String date){
SimpleDateFormat format = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss");
try {
Date date1 = format.parse(date.replace("T"," "));
String d= new SimpleDateFormat("yyyy/dd/MM HH:mm:ss").format(date1);
return d;
}catch (Exception e){
e.printStackTrace();
}
return "";
}
Try with new SimpleDateFormat("EEE dd MMM yyyy", Locale.ENGLISH);
Sample Code:
DateFormat originalFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH);
DateFormat targetFormat = new SimpleDateFormat("EEE dd MMM yyyy", Locale.ENGLISH);
Date date = originalFormat.parse("01/04/2018");
String formattedDate = targetFormat.format(date); // Sun 01 Apr 2018
tl;dr
LocalDate
.parse(
"01/04/2018" ,
DateTimeFormatter // Parses & generates text in various formats
.ofPattern( "dd/MM/uuuu" ) // Define a formatting pattern to match your input.
) // Returns a `LocalDate` object.
.toString() // Generates text in standard ISO 8601 format.
2018-04-01
Use data types appropriately
I have a String in a database (match.getDate) that has the following date format:
Do not store date-time values as text.
You should be storing date-time values in a database using date-time data types. In standard SQL, a date-only value without time-of-day and without time zone is stored in a column of type DATE.
Another problem is that you are trying to represent a date-only value in Java class that represents a moment, a date with time-of-day in context of time zone or offset-from-UTC. Square peg, round hole. Using a date-only data types makes your problems go away.
java.time
The other Answers used outmoded classes, years ago supplanted by the modern java.time classes built into Java 8 and later, and built into Android 26 and later. For earlier Java & Android, see links below.
In Java, a date-only value without time-of-day and without time zone is represented by the LocalDate class.
LocalDate ld = LocalDate.parse( "2020-01-23" ) ; // Parsing a string in standard ISO 8601 format.
For a custom formatting pattern, use DateTimeFormatter.
String input = "01/04/2018" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" ) ;
LocalDate ld = LocalDate.parse( input , f ) ;
Generate a string in standard ISO 8601 format.
String output = ld.toString() ;
Generate a string in your custom format.
String output = ld.format( f ) ;
Tip: Use DateTimeFormatter.ofLocalizedDate to automatically localize your output.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
first of check your match.getDate() method which format given date if is given above define format date then used below code and show date in define above format ...
String date="09/03/2018";
SimpleDateFormat parseDateFormat = new SimpleDateFormat("dd/MM/yyyy"); // if your match.getDate() given this format date.and if is given different format that time define that format.
DateFormat formatdate = new SimpleDateFormat("EEE dd MMM yyyy");
try {
Date date1=parseDateFormat.parse(date);
Log.d("New Date",formatdate.format(date1));
} catch (ParseException e) {
e.printStackTrace();
}
output:: Fri 09 Mar 2018

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.

How to convert ISO 8601 Datetime into a specific datetime format in Java using Joda package

I am using org.joda.time.DateTime; package to convert ISO 8601 datetime for Eg "2017-02-07T00:00:00.000+05:30" to a format "yyyy-MM-dd HH:mm:ss.SSS".
Code is :
String dateTimePattern = "yyyy-MM-dd HH:mm:ss.SSS";
DateTimeFormatter dtf = DateTimeFormat.forPattern(inputDateTimePattern);
DateTime jodatime = dtf.parseDateTime("2017-02-07T00:00:00.000+05:30");;
System.out.println("Converted datetime is: ",jodatime.toString(dtf))
But i get error mentioning
java.lang.IllegalArgumentException: Invalid format: is malformed at ".T00:00:00.000+05:30"
How to convert ISO 8601 datetime format in required format in java ?
tl;dr
Joda-Time is replaced by the java.time classes.
OffsetDateTime.parse( "2017-02-07T00:00:00.000+05:30" )
java.time
The Joda-Time project is now in maintenance mode, with its team advising migration to the java.time classes.
In java.time, your input string can be parsed directly as a OffsetDateTime object. No need to specify a formatting pattern.
OffsetDateTime odt = OffsetDateTime.parse( "2017-02-07T00:00:00.000+05:30" );
A time zone is a history of offsets for a particular region. So always better to use if you are certain of the intended time zone.
ZoneId z = ZoneId.of( "Asia/Kolkata" ); // Or "America/Montreal", etc.
ZonedDateTime zdt = odt.atZoneSameInstant();
Joda-Time
In Joda-Time, you can parse a string in standard ISO 8601 format with an offset-from-UTC in either of two ways:
Constructornew DateTime( "2017-02-07T00:00:00.000+05:30" ) ;
Static parse methodDateTime.parse( "2017-02-07T00:00:00.000+05:30" )
These two routes are not the same! See the class doc from the parse method:
However, when this method is passed a date-time string with an offset, the offset is directly parsed and stored. As such, DateTime.parse("2010-06-30T01:20+02:00") and new DateTime("2010-06-30T01:20+02:00")) are NOT equal. The object produced via this method has a zone of DateTimeZone.forOffsetHours(2). The object produced via the constructor has a zone of DateTimeZone.getDefault().
Well... the solution might be a mix between 2 already existing answers here at Stack Overflow
First - look at this post How to parse and generate DateTime objects in ISO 8601 format which describes how to write such a code using C# (C# and JAVA are very similar)
Second - please use the SimpleDateFormat as described here How to parse a date?
This combination should do the trick
Looks like you got confused while using DateTimeFormat. Given time string is not in yyyy-MM-dd HH:mm:ss.SSS format. So you are getting the exception which is expected.
SimpleDateFormat target = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date date=source.parse("2017-02-07T00:00:00+05:30");
System.out.println(target.format(date)); // prints 2017-02-07 00:00:00.000
This code will format the date into yyyy-MM-dd HH:mm:ss.SSS.
Use the below method
public static Calendar toCalendar(String iso8601string) {
DateTime dt = new DateTime(iso8601string);
Date date = new Date(dt.getMillis());
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar;
}

java.text.ParseException: Unparseable date: "8:30 AM"

I am trying to Parse "8:30 AM" but I am getting Unparseable Date Exception.
From my UI side I am getting "8:30 AM" and "6:30 PM" kind of values but I have to convert that String into Date format and save that date in my database.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public static void main(String[] args) {
SimpleDateFormat timingFormat = new SimpleDateFormat("h a",
Locale.US);
String dateInString = "8:30 AM";
try {
// This line throws Unparseable exception
Date date = timingFormat.parse(dateInString);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
From Documentation
the year value of the parsed Date is 1970 with GregorianCalendar if no
year value is given from the parsing operation. The TimeZone value may
be overwritten, depending on the given pattern and the time zone value
in text.
try this
SimpleDateFormat timingFormat = new SimpleDateFormat("h a", Locale.US);
Date date = timingFormat.parse("8 AM");
System.out.println(date.toString());
Output
Thu Jan 01 08:00:00 IST 1970
UPDATE
To get today date,you can try something like this after parsing
int hours = date.getHours();
Date today = new Date();
today.setHours(hours);
System.out.println(today);
Note getHours() and setHours are deprecated methods.Its recommended to go for Calendar.You will have to set hours, minutes explicitly.
UPDATE
if input is 8:30 or so,then you will have to parse it like this
SimpleDateFormat timingFormat = new SimpleDateFormat("h:mm a", Locale.US);
Date date = timingFormat.parse("8:30 AM");
System.out.println(date.toString());
Output
Thu Jan 01 08:30:00 IST 1970
Depending on the input,you need to select which kind of format you are insterested.You can check that whether string contains : or not,based on that you can use SimpleDateFormat.
Just to Parse 8:30 AM just change the formatter above with
SimpleDateFormat timingFormat = new SimpleDateFormat("h:mm a");
Regards
The accepted answer is correct but outdated.
As mentioned, the Question has an input string which means only a time-of-day while the java.util.Date class represents both a date plus a time-of-day.
What you need is a class that represents only a time-of-day. No such class in the older versions of Java before Java 8.
java.time
The java.time framework built into Java 8 and later supplants the troublesome old java.util.Date/.Calendar classes. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.
LocalTime
The java.time classes include the LocalTime class. This class represents a time-of-day without any date nor time zone.
Note that for the formatter we specified a Locale using the English language to correctly identify the strings AM and PM which could vary by human language.
String input = "8:30 AM";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "h:m a" , Locale.ENGLISH );
LocalTime localTime = LocalTime.parse ( input , formatter );
Dump to console.
System.out.println ( "localTime: " + localTime );
localTime: 08:30
Database
Next, the database. Eventually we should see JDBC drivers updated to directly handle the java.time types. Until then, we must convert from java.time types to the old java.sql types.
Convert From java.time to java.sql
For this Question, that means the java.sql.Time class. That old java.sql.Time class has a new method for convenient conversions, valueOf.
java.sql.Time sqlTime = java.sql.Time.valueOf ( localTime );
From there, the JDBC driver converts from the java.sql time to the database type. For this Question, that probably means the standard TIME SQL type.
Pass java.sql.* Object To Database Via JDBC Driver
Use a PreparedStatement to insert or update your data by passing that sqlTime variable seen above. Search StackOverflow.com for countless examples of such insert/update work in SQL.

Categories