Parsing date incorrectly - java

I'm trying to convert an input date string to a date format and then to a datetime format.
As a test, I gave an input of an incorrect date format, but this doesn't seem to be throwing any parse exceptions and gives me the wrong output. Any thoughts on what my code below is doing wrong?
String OLD_FORMAT ="MM/dd/yyyy";
String NEW_FORMAT ="yyyyMMdd HHmmss";
SimpleDateFormat sdf = new SimpleDateFormat(OLD_FORMAT);
String oldDateString = "03/01211/2012"; //Incorrect input
Date myOldDate;
Datetime myNewDate;
try {
myOldoldDate = sdf.parse(oldDateString);
//Returns Wed Jun 24 00:00:00 IST 2015...why??
//Shouldn't this be throwing a parse exception?
} catch (ParseException e) {
logger.error("Error while parsing Date");
}
sdf.applyPattern(NEW_FORMAT);
//Converting date to datetime format
try {
myNewDate= DateHelper.toDatetime(sdf.parse((sdf.format(myOldDate))));
//Returns 2015-06-24 00:00:00.0
} catch (ParseException e) {
logger.error("Error while parsing Date");
}

"03/01211/2012" => Jun 24 00:00:00 IST 2015 ... why?
My guess is that June 24th, 2015 is 1211 days from March 1st, 2012.
Excessive rollover, reads it as March 1211th.
You should be able to turn this off with:
sdf.setLenient(false)
public void setLenient(boolean lenient)
Specify whether or not date/time interpretation is to be lenient. With lenient interpretation, a date such as "February 942, 1996" will be treated as being equivalent to the 941st day after February 1, 1996. With strict interpretation, such dates will cause an exception to be thrown.

You can set strict format
SimpleDateFormat.setLenient(false)
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.

Without looking at the source code, I assume 01211 is parsed to 1211 days which are added to 2012-03-01 thus resulting in 2015-06-24. As #Thilo said sdf.setLenient(false) should help here.
The problem is that by default the parser is more tolerant to wrong input (lenient mode is on by default) and thus won't throw an exception here.

Related

Safe SimpleDateFormat parsing

I have a small block of code which parses response generation time from the response itself and turns it into a date for future purposes. It goes like this:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
Date responseTime = sdf.parse(RStime);
And it almost works like a charm. To be precise, it works 99.9% of the time, with the exception of one case: When the millisecond part is 000 then the Server doesn't append the .000 milliseconds at all, hence we have a problem.
Now, according to SimpleDateFormat docs if parsing fails, the function returns null. However, I probably misinterpreted it as it just throws an exception.
I am very new to Java and try-catch mechanisms, so could anyone please provide an elegant good-practice solution for handling such cases?
Thanks!
java.time
String rsTime = "2018-04-09T10:47:16.999-02:00";
OffsetDateTime responseTime = OffsetDateTime.parse(rsTime);
System.out.println("Parsed date and time: " + responseTime);
Output from this snippet is:
Parsed date and time: 2018-04-09T10:47:16.999-02:00
It works just as well for the version with the 000 milliseconds omitted:
String rsTime = "2018-04-09T10:47:16-02:00";
Parsed date and time: 2018-04-09T10:47:16-02:00
The classes you used, SimpleDateFormat and Date, are poorly designed and long outdated (the former in particular notoriously troublesome). So it is not only in this particular case I recommend using java.time, the modern Java date and time API, instead. However, the strings from your server are in ISO 8601 format, and OffsetDateTime and the other classes of java.time parse this format as their default, that is, without any explicit formatter, which already makes the task remarkably easier. Furthermore, in the standard the fractional seconds are optional, which is why both the variants of the string are parsed without any problems. OffsetDateTime also prints ISO 8601 back from it’s toString method, which is why in both cases a string identical to the parsed one is printed.
Only in case you indispensably need an old-fashioned Date object for a legacy API that you cannot change just now, convert like this:
Instant responseInstant = responseTime.toInstant();
Date oldfashionedDateObject = Date.from(responseInstant);
System.out.println("Converted to old-fashioned Date: " + oldfashionedDateObject);
Output on my computer in Europe/Copenhagen time zone is:
Converted to old-fashioned Date: Mon Apr 09 14:47:16 CEST 2018
Link: Oracle tutorial: Date Time explaining how to use java.time.
According to the SimpleDateFormat doc that you mentioned the parse method:
public Date parse(String text, ParsePosition pos)
Throws:
NullPointerException - if text or pos is null.
So one option is to catch that exception and do what you need inside the catch, for example:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
try {
Date responseTime = sdf.parse(RStime, position);
} catch (NullPointerException e) {
e.printStackTrace();
//... Do extra stuff if needed
}
Or the inherited method from DateFormat:
public Date parse(String source)
Throws:
ParseException - if the beginning of the specified string cannot be
parsed.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
try {
Date responseTime = sdf.parse(RStime);
} catch (ParseException e) {
e.printStackTrace();
//... Do extra stuff if needed
}
Is it actually an exceptional situation? If it is not then you probably shouldn't use exceptions in that case. In my opinion it is normal that time can end with .000ms. In this case you can check if the string contains . (dot) and if not append .000 to the end.
if(!RStime.contains(".")){
RStime+=".000";
}
Edit: I've forgot about time zone in the time String. You probably need something a little bit more complicated for that. Something like this should do it:
if(!RStime.contains(".")){
String firstPart = RStime.substring(0, 21);
String secondPart = RStime.substring(21);
RStime = firstPart + ".000" + secondPart;
}
You can check for a dot and then use the first or second format:
String timeString = "2018-04-09T10:47:16.999-02:00";
//String timeString = "2018-04-09T10:47:16-02:00";
String format = timeString.contains(".") ? "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" : "yyyy-MM-dd'T'HH:mm:ssXXX";
Date responseTime = new SimpleDateFormat(format).parse(timeString);
System.out.println("responseTime: " + responseTime);
If you comment-out the first line and comment-in the second and run it again, it will both print out:
responseTime: Mon Apr 09 14:47:16 CEST 2018
By the way:
Java 7 (the version you use obviously) returns a java.text.ParseException: Unparseable date: "2018-04-09T10:47:16-02:00"
Optionals are supported since Java 8.

Does the java.util.Date object verifies the date validity?

I just wrote this unit tests :
#Test
public void testGetDateFromString() throws ParseException{
String date = "52/29/2500";
Date dateFromString = DateHelper.getDateFromString(date, DateHelper.DD_MM_YYYY_FORMAT);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateHelper.DD_MM_YYYY_FORMAT);
Date dateWithSimpleFormat = simpleDateFormat.parse(date);
Assert.assertNotNull(dateFromString);
Assert.assertNotNull(dateWithSimpleFormat);
Assert.assertTrue(dateFromString.equals(dateWithSimpleFormat));
System.out.println("dateFromString " + dateFromString);
System.out.println("dateWithSimpleFormat " + dateWithSimpleFormat);
}
And the output is :
dateFromString Wed Jun 21 00:00:00 CEST 2502
dateWithSimpleFormat Wed Jun 21 00:00:00 CEST 2502
The DateHelper.DD_MM_YYYY_FORMAT pattern is dd/MM/yyyy and getDateFromString is a method that parses a String date to a Date object using commons-lang library.
Why des the java.util.Date object verifies the date validity?
You need to set simpleDateFormat.setLenient(false); to make the SimpleDateFormat to validate your input strictly.
You can refer the setLenient documentation for further understanding. By the definition,
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.
Use simpleDateFormat.setLenient(false); to enable strict parsing.
java.time
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/uuuu");
String date = "52/29/2500";
try {
LocalDate dateWithJavaTime = LocalDate.parse(date, dateFormatter);
System.out.println("dateWithJavaTime " + dateWithJavaTime);
} catch (DateTimeParseException dtpe) {
System.out.println("Invalid date. " + dtpe);
}
The output from this code is:
Invalid date. java.time.format.DateTimeParseException: Text
'52/29/2500' could not be parsed: Invalid value for MonthOfYear (valid
values 1 - 12): 29
Please enjoy not only that the validation works, but also the precision of the error message.
Other results:
For the string 52/11/2500 the result is “Invalid date. java.time.format.DateTimeParseException: Text '52/11/2500' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 52”.
For the string 29/02/2019 we get “dateWithJavaTime 2019-02-28”, which may surprise. To have this string rejected use
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/uuuu")
.withResolverStyle(ResolverStyle.STRICT);
Now we get
Invalid date. java.time.format.DateTimeParseException: Text
'29/02/2019' could not be parsed: Invalid date 'February 29' as '2019'
is not a leap year
Again enjoy how precise the message is.

Having trouble format a string into a date object

I have a string that looks like this
Aug 16, 2013,11:30:10
The comma can be replaced by a different separator and the date & time position can be switched.
I am trying to use this format for my SimpleDateFormat(dtString is the String above):
Date d = null;
try {
d = new SimpleDateFormat("MMMM dd, yyyy,hh:mm:ss", Locale.ENGLISH).parse(dtString);
} catch (ParseException ex) {
Logger.getLogger(MonKaiClientImpl.class.getName()).log(Level.SEVERE, null, ex);
}
return d;
but when I run d.getYear() the result is 113.
All of the other Date methods return the correct result except .getYear(). Am I missing something? Is my SimpleDateFormatter wrong?
You should not use Date#getYear. It's deprecated.
As for the result you get, it's as specified in the Javadoc:
Returns a value that is the result of subtracting 1900 from the year that contains or begins with the instant in time represented by this Date object, as interpreted in the local time zone.
Use Calendar API instead. Or even better, if you can use 3rd party library, then I would really suggest you to try Joda Time. Or wait for Java 8 to come next year, that has a new Date Time API.
Simple way to check the result of SimpleDateFormat parsing is this
System.out.println(d);
and it shows correct result
Fri Aug 16 11:30:10 EEST 2013
the problems is that Date methods interpreting year, month, day, hour, minute, and second are deprecated. See Date API

SimpleDateFormat ParseException : Unparseable date

I'm trying to execute this code:
Date date = null;
if (detailsBean.getDiscoveryProjectBean ().getCreatedDate ()==null ||
detailsBean.getDiscoveryProjectBean ().getCreatedDate ().equalsIgnoreCase(""))
{
projectDetails.getDiscoveryProject().setCreationTime(new Date());
}
else
{
try
{
date = new SimpleDateFormat (FormatUtils.simpleFormat).
parse (detailsBean.getDiscoveryProjectBean ().getCreatedDate ());
} catch (Exception e) {
throw new PanDaApplicationException (e.getMessage ());
}
projectDetails.getDiscoveryProject().setCreationTime(date);
}
in the try block a ParseException exception is thrown. I don't know the cause of that as the code seems fine, however. the definition of the FormatUtils.simpleFormat is public static final String simpleFormat = "dd-MMM-yyyy" and detailsBean.getDiscoveryProjectBean().getCreatedDate() have value like 28-Feb-2013
I really don't have any clues why this exception is thrown and I need help.
My guess is that the problem is the way that SimpleDateFormat uses your default locale - if your locale doesn't use "Feb" as an abbreviated month name, you'll have problems. So if all your data is actually in English, you might want:
DateFormat format = new SimpleDateFormat(FormatUtils.simpleFormat, Locale.US);
format.setTimeZone(...); // See below
date = format.parse(detailsBean.getDiscoveryProjectBean().getCreatedDate());
Note the part about setting the time zone. Again, SimpleDateFormat will use your system default if you don't specify anything else. (You'll get the instant of "midnight in the specified time zone" as the Date value.)
I would also strongly urge you to consider using Joda Time instead of the built-in Date/Calendar types - it's a much better date/time API.
Locale.setDefault (Locale.ROOT);
System.out.println (new SimpleDateFormat ("dd-MMM-yyyy").parse ("28-Feb-2013"));
Locale.setDefault (Locale.forLanguageTag ("ru"));
System.out.println (new SimpleDateFormat ("dd-MMM-yyyy").parse ("28-Feb-2013"));
For me output is:
Thu Feb 28 00:00:00 MSK 2013
Exception in thread "main" java.text.ParseException: Unparseable date: "28-Feb-2013"
at java.text.DateFormat.parse(DateFormat.java:357)
at DateFormat.main(DateFormat.java:19)
So the same date successfully parsed with ROOT locale, but failed with Russian.

Date parsing from string to long gives wrong result

I got simple code, maybe the problem relies on the given format string or on the timezone. So here is the code:
public static void main(String[] args) {
SimpleDateFormat df = new SimpleDateFormat("HH:mm");
try {
Date added = df.parse("00:00");
System.out.println(added);
System.out.println(added.getTime());
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The result is:Thu Jan 01 00:00:00 EET 1970
-10800000 --> should be 0 as we give 00:00 hours in and the other time elements remain default.
//Edit
Yes the problem is with timezone to fix this use
df.setTimeZone(TimeZone.getTimeZone("UTC")); before parsing.
The value 10800000 is exactly 3 hours (in milliseconds), which I'm gathering is roughly the offset between EET and UTC (actually, it's only 2 hours according to this, but I guess the extra hour's down to DST or something).
Therefore, the difference is probably due to your timezone.
Your timezone appears to be EET. That difference would be the offset from 1st Jan 1970 00:00:00.000 UTC
Since you didn't specify the date, only the hour, you actually created a Date object with default values, as specified in the DateFormat API (which SimpleDateFormat implements):
The date is represented as a Date object or as the milliseconds since January 1, 1970, 00:00:00 GMT.

Categories

Resources