SimpleDateFormat Unexpected Success with Phone Number - java

Accidentally passing in a phone number string into the format method of SimpleDateFormat sometimes results in a valid date being parsed.
As an example, passing the number "518-123-4567" (literal, with hyphens) somehow results in the date "11/23/0517 05:27 AM"
We are using this in an area where a String could represent a number of different things, and we were under the assumption that a string with digits and hyphens in the way that a phone number is typically written would fail when parsed as a date. Our code simply checks to ParseException, and accepts anything that does not throw such an exception as valid. Why doesn't this sort of string fail parsing? Is there a better way to check to see if a string could potentially be or not be a date?
private static Date getPromisedDate(String promisedText) {
SimpleDateFormat promsiedDateTimeFormat = new SimpleDateFormat("yyyyMMddHHmm");
if(null != promisedText) {
try {
return promsiedDateTimeFormat.parse(promisedText);
}
catch (ParseException e) { }
}
return null;
}

Your SimpleDateFormat is in "lenient" mode - which is very lenient indeed. If you use
promsiedDateTimeFormat.setLenient(false);
it will throw an exception as you'd expect when you try to parse the bogus data.
Personally I think it should be strict by default, but...

From DateFormat#parse(String):
Parses text from the beginning of the given string to produce a date. The method may not use the entire text of the given string.
So, the method might not parse the entire string. It will stop at the position, where the pattern stops matching. In your case, the matching is done in these ways:
yyyy MM dd HH mm
518 -1 23 -4 567
The year parsing yyyy stops at first -, as it can't be parsed as year. So, the year is 518. Then month is taken as -1, then 23 goes as dd, so on.
You can use the overloaded version of parse method and pass a ParsePosition instance to see the details.
From DateFormat#parse(String, ParsePosition):
By default, parsing is lenient: If the input is not in the form used by this object's format method but can still be parsed as a date, then the parse succeeds. Clients may insist on strict adherence to the format by calling setLenient(false)
So, just set the leniency to false, to stop it from parsing date not matching the format:
promsiedDateTimeFormat.setLenient(false);
For example, on using ParsePosition, suppose you pass the date string as - "518-123-4567abc". Surprisingly, it would also be parsed with leniency set to true, because the last part abc would not be parsed at all. To test this, you can try the following code:
private static Date getPromisedDate(String promisedText) throws Exception {
ParsePosition pp = new ParsePosition(0);
SimpleDateFormat promsiedDateTimeFormat = new SimpleDateFormat("yyyyMMddHHmm");
if(null != promisedText) {
try {
Date date = promsiedDateTimeFormat.parse(promisedText);
// If complete string is not parsed, throw ParseException
if (pp.getIndex() != promisedText.length()) {
throw new ParseException("Unparseable date given: " + promisedText, pp.getIndex());
}
return date;
}
catch (ParseException e) { throw e; }
}
return null;
}

To explain what happened: Year 581, Month -1, day 23, Hour -4, Minute 567. Sum everything up and you will get the resulting date. To avid such results, see Post of JonSkeet

Related

String to Date Conversion mm/dd/yy to YYYY-MM-DD in java [duplicate]

This question already has answers here:
Java Date Error
(8 answers)
Closed 4 years ago.
I want to convert String values in the format of mm/dd/yy to YYYY-MM-DD Date. how to do this conversion?
The input parameter is: 03/01/18
Code to convert String to Date is given below
public static Date stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
try {
date = formatter.parse(dateVlaue);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
When tried to convert using this method it shows the following error
java.text.ParseException: Unparseable date: "03/01/18"
As you say the input is in a different format, first convert the String to a valid Date object. Once you have the Date object you can format it into different types , as you want, check.
To Convert as Date,
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yy");
date = formatter.parse(dateVlaue);
To Print it out in the other format,
SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd");
dateString = formatter1.format(date)
You are writing it the wrong way. In fact, for the date you want to convert, you need to write
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yy");
The format you are passing to SimpleDateFormat is ("yyyy-MM-dd") which expects date to be in form 2013-03-01 and hence the error.
You need to supply the correct format that you are passing your input as something like below
public static Date stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yy");
try {
date = formatter.parse(dateVlaue);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
The solution for the above problem
Convert the String date value in the Format of "dd/mm/yy" to Date.
By using the converted Date can able to frame the required date format.
The method has given below
public static String stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("dd/mm/yy");
String dateString = null;
try {
// convert to Date Format From "dd/mm/yy" to Date
date = formatter.parse(dateVlaue);
// from the Converted date to the required format eg : "yyyy-MM-dd"
SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd");
dateString = formatter1.format(date);
} catch (ParseException e) {
e.printStackTrace();
}
return dateString;
}
EDIT: Your question said “String values in the format of mm/dd/yy”, but I understand from your comments that you meant “my input format is dd/mm/yy as string”, so I have changed the format pattern string in the below code accordingly. Otherwise the code is the same in both cases.
public static Optional<LocalDate> stringToDateLinen(String dateValue) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yy");
try {
return Optional.of(LocalDate.parse(dateValue, dateFormatter));
} catch (DateTimeParseException dtpe) {
return Optional.empty();
}
}
Try it:
stringToDateLinen("03/01/18")
.ifPresentOrElse(System.out::println,
() -> System.out.println("Could not parse"));
Output:
2018-01-03
I recommend you stay away from SimpleDateFormat. It is long outdated and notoriously troublesome too. And Date is just as outdated. Instead use LocalDate and DateTimeFormatter from java.time, the modern Java date and time API. It is so much nicer to work with. A LocalDate is a date without time of day, so this suites your requirements much more nicely than a Date, which despite its name is a point in time. LocalDate.toString() produces exactly the format you said you desired (though the LocalDate doesn’t have a format in it).
My method interprets your 2-digit year as 2000-based, that is, from 2000 through 2099. Please think twice before deciding that this is what you want.
What would you want to happen if the string cannot be parsed into a valid date? I’m afraid that returning null is a NullPointerException waiting to happen and a subsequent debugging session to track down the root cause. You may consider letting the DateTimeParseException be thrown out of your method (just declare that in Javadoc) so the root cause is in the stack trace. Or even throw an AssertionError if the situation is not supposed to happen. In my code I am returning an Optional, which clearly signals to the caller that there may not be a result, which (I hope) prevents any NullPointerException. In the code calling the method I am using the ifPresentOrElse method introduced in Java 9. If not using Java 9 yet, use ifPresent and/or read more about using Optional elsewhere.
What went wrong in your code?
The other answers are correct: Your format pattern string used for parsing needs to match the input (not your output). The ParseException was thrown because the format pattern contained hyphens and the input slashes. It was good that you got the exception because another problem is that the order of year, month and day doesn’t match, neither does the number of digits in the year.
Link
Oracle tutorial: Date Time explaining how to use java.time.

Why do I get a date back when I pass a date string with a "HH" pattern to the SimpleDateFormat?

Here's my code:
try {
DateFormat dateFormat = new SimpleDateFormat(pattern);
dateFormat.setLenient(false);
Date date = dateFormat.parse(value);
if (date != null) {
return true;
}
} catch (ParseException e) {}
1.) When I pass value as "01/07/2015" and pattern as "HH:mm" I correctly get an exception.
2.) However when I pass value as "01/07/2015" and pattern as "HH" I get a "Thu Jan 01 01:00:00 EST 1970" Date object.
I would except scenario #2 to also throw an exception since the given string completely doesn't match the given pattern. Why do I get that strange date even when setLenient(false) is set?
http://download.java.net/jdk6/archive/b104/docs/api/java/text/Format.html#parseObject(java.lang.String)
parseObject
public Object parseObject(String source)
throws ParseException
Parses text from the beginning of the given string to produce an object. The method may not use the entire text of the given string
I suppose #1 doesn't match the separator. You put / in it although the pattern is :.
And #2 stops matching right after HH because it parses text from the beginning of the given string and DOESN'T USE the entire text of the given string.
The JavaDoc beautifully sums up why you do not get an exception:
Throws: ParseException - if the beginning of the specified string cannot be parsed.
01 can be parsed with HH, therefore no exception.
I found a solution to this problem.
In order to resolve my issue I just wrapped my entire code in an if statement where I check if the length of the pattern is the same as the length of the value because they should be when I use this code for validation:
if(StringUtils.length(pattern) == StringUtils.length(value)) {
try {
DateFormat dateFormat = new SimpleDateFormat(pattern);
dateFormat.setLenient(false);
Date date = dateFormat.parse(value);
if (date != null) {
return true;
}
} catch (ParseException e) {}
}
return false;

Why java SimpleDateFormat can parse formatted text with extra characters?

I am parsing date strings from user input with MM-dd-yyyy HH:mm:ss format, and I found 12-20-2012 10:10:10 abcdexxxx could be pasred as well. How can this happen? Here is my code:
SimpleDateFormat df = new SimpleDateFormat( "MM-dd-yyyy HH:mm:ss" );
String currColValue = "12-20-2012 10:10:10 abcdexxxx";
try{
d=df.parse( currColValue );
}catch( ParseException e ){
System.out.println("Error parsing date: "+e.getMessage());
}
But there is no exception, the String value is parsed to be a Date. Why?
Per the Javadoc of the parse method:
Parses text from the beginning of the given string to produce a date. The method may not use the entire text of the given string.
(emphases mine).
Contrary to the implication of comments above, this has nothing to do with lenient parsing; rather, it's just that this method is not intended to consume the whole string. If you wish to validate that it consumed the whole string, I suppose you could set up a ParsePosition object and use the two-arg overload, and then examine the ParsePosition afterward to see if it parsed to the end of the string.
java.time
I should like to contribute the modern answer. This question was asked just the month before java.time, the modern Java date and time API, came out, which we all should be using now.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm:ss");
String currColValue = "12-20-2012 10:10:10 abcdexxxx";
try {
LocalDateTime ldt = LocalDateTime.parse(currColValue, formatter);
// Do something with ldt
} catch (DateTimeParseException e) {
System.out.println("Error parsing date and time: " + e.getMessage());
}
Output:
Error parsing date and time: Text '12-20-2012 10:10:10 abcdexxxx' could not be parsed, unparsed text found at index 19
Contrary to the old SimpleDateFormat class the parse methods of the modern classes do insist on parsing the entire string (there is a way to parse only part of the string if that is what you require). Also please note the precision and clarity of the exception message. By the way, SimpleDateFormat is not only long outdated, it is also notoriously troublesome. You found just one of many surprising problems it has. I recommend that you no longer use SimpleDateFormat and Date.
Link: Oracle tutorial: Date Time explaining how to use java.time.
Check SimpleDateFormat.parse(String) doc. It clearly says it.
Parses text from the beginning of the given string to produce a date. The method may not use the entire text of the given string.
http://docs.oracle.com/javase/7/docs/api/java/text/DateFormat.html#parse(java.lang.String)
I want to contibute to the above correct answers with examples, using the method overload
public Date parse(String text, ParsePosition pos);
To parse the exact whole string just create a new ParsePosition object (with index 0, indicating that parse needs to start from begin), pass it to the method, and inspect its index property after parse.
Index is where the parse did end. If matches with string length then the string matches exactly form start to end.
Here is a unit test demonstrating it
public class DateParseUnitTest {
#Test
public void testParse(){
Date goodDate = parseExact("2019-11-05");
Date badDate1 = parseExact("foo 2019-11-05");
Date badDate2 = parseExact("2019-11-05 foo");
assert(goodDate != null);
assert(badDate1 == null);
assert(badDate2 == null);
}
#Nullable
private Date parseExact(#NonNull String text){
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
ParsePosition pos = new ParsePosition(0);
Date date = formatter.parse(text, pos);
if (pos.getIndex() != text.length())
return null;
return date;
}
}

Parsing String to Date when sometimes year is missing and sometimes not

I am parsing multiple String objects containing date in format of mm/dd/yyyy into Date objects using SimpleDateFormatter
Problem I am facing is that, some String contain complete date means it is mm/dd/yyyy while some Strings do not contain year and comes in this format mm/dd
I am using this format to parse dates MM/dd/yyyy but everytime I get a String with only month and day, I get parsing exception.
I want to know, is there any way by which if a year field is missing then I can use something like 0000 or I need to tackle it into exception body only?
Just create 2 separate SimpleDateFormat instances, one that you have and a second one with day and month only. You analyze the string first and pass it to the correct formatter.
Or pass it to the first one and if an exception is thrown, pass it to the second one.
as far as i know you can create an instance of SimpleDateFormat that does not "need" an year.
So just use an try-catch-Block
try{
//try formatting with simpledate instance with year
}catch (Exception thrown when no year){
try{
//try formatting with simple date instace without year
} catch (Exception e)
//something went wrong both did not accept it
}
}
ParseDate(String yourDate);
{
try {
Date reqDate=sdf.parse(yourDate);//sdf is instance of you date formatter
return reqDate;
} catch (ParseException e) {
return null;
e.printStackTrace();
}
}
try Using this function. I think it will work. if thrown exception then null will return. In this way by checking null we know if it is correctly parsed or not.
A code is worth thousand words....
DateFormat df;
Calendar c = Calendar.getInstance();
String s = new String("12/25/2012");
String[] arr = s.split("/");
if(arr.length>2){
c.set(Integer.parseInt(arr[0]), Integer.parseInt(arr[1]),Integer.parseInt(arr[2]));
}
else{
c.set(Integer.parseInt(arr[0]), Integer.parseInt(arr[1]));
}
df = DateFormat.getDateInstance(DateFormat.SHORT);
System.out.println(df.format(c.getTime()));
You can later convert it back to Date object using the df.parse() method...

Java : Impossible to parse "23/10/1973" with "dd/MM/yyyy HH:mm" format

I'm trying to parse many string dates to Date(s), some with time part, others without, with the "dd/MM/yyyy HH:mm" format.
public static Date StringToDate (String format, String theDate) {
SimpleDateFormat df = new SimpleDateFormat(format);
Date retDate = null;
try {
df.setLenient(true);
retDate = df.parse(theDate);
}
catch (ParseException e) {
e.printStackTrace();
}
return (retDate);
}
(here, format is always "dd/MM/yyyy HH:mm").
But this causes an exception, even with setLenient forced at true. Do you know how I may convert to Date a lot of strings formatted like "dd/MM/yyyy HH:mm:ss", but with someones without time, some others without secondes, and still other one with everything ?
If you know that some strings have a time and some don't, and there are no other cases, I'd just check the length of the string. However, if you have many different formats available, I'd try each one in some order that makes sense, until you get a valid date.
I always have two parse strings, and I parse twice; once with date/time and once with date only.

Categories