Why does non-lenient SimpleDateFormat parse dates with letters in? - java

When I run the following code I would expect a stacktrace, but instead it looks like it ignores the faulty part of my value, why does this happen?
package test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public static void main(final String[] args) {
final String format = "dd-MM-yyyy";
final String value = "07-02-201f";
Date date = null;
final SimpleDateFormat df = new SimpleDateFormat(format);
try {
df.setLenient(false);
date = df.parse(value.toString());
} catch (final ParseException e) {
e.printStackTrace();
}
System.out.println(df.format(date));
}
}
The output is:
07-02-0201

The documentation of DateFormat.parse (which is inherited by SimpleDateFormat) says:
The method may not use the entire text of the given string.
final String value = "07-02-201f";
In your case (201f) it was able to parse the valid string till 201, that's why its not giving you any errors.
The "Throws" section of the same method has defined as below:
ParseException - if the beginning of the specified string cannot be parsed
So if you try changing your string to
final String value = "07-02-f201";
you will get the parse exception, since the beginning of the specified string cannot be parsed.

You can look whether the entire string was parsed as follows.
ParsePosition position = new ParsePosition(0);
date = df.parse(value, position);
if (position.getIndex() != value.length()) {
throw new ParseException("Remainder not parsed: "
+ value.substring(position.getIndex()));
}
Furthermore when an exception was thrown by parse the position will also yield getErrorIndex().

Confirmed... I also found that "07-02-201", "07-02-2012 is the date" compiles. However, "bar07-02-2011" does not.
From the code in SimpleDateFormat, it seems like the parsing terminates the moment an illegal character is found that breaks the matching. However, if the String that has already been parsed up to that point is valid, it is accepted.

Related

How to solve this error "String Cannot be converted to Date"?

Hello I am trying to store the birthdate of the user in database with the code below:
private void btnActionPerformed(java.awt.event.ActionEvent evt) {
String username = txtUserName.getText();
String password = txtPassword.getText();
String email = txtEmail.getText();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String birthdate = sdf.format(JDateChooser.getDate());
Users user = new Users();
user.setUserName(cin);
user.setPassWord(firstName);
user.setEmail(email);
user.setBirthDate(birthdate);
try {
int count = Users.getInstance().insert(user);
if(count == 1){
JOptionPane.showMessageDialog(null,"success");
reset();
}else{
JOptionPane.showMessageDialog(null,"Faild");
}
} catch (Exception ex) {
Logger.getLogger(AddNewPatient.class.getName()).log(Level.SEVERE, null, ex);
}
}
I got an error which says String connot be converted to Date in the line "user.setBirthDate(birthdate);"
Because the parameter birthdate is assigned as Date type in the encapsulation(setBirthDate)
is there any way to solve this issue, I am new in java programming and I am trying to improve my skills in java.
If this returns a Date:
JDateChooser.getDate()
And what you need is a Date, then don't convert it to a String. Just keep it as a Date:
Date birthdate = JDateChooser.getDate();
// later...
user.setBirthDate(birthdate);
Note that you can then also remove this line, since you're not using the variable it declares:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
In general you want to keep data types in their raw form pretty much as often as possible. Unless there's a specific need for something to be represented as a string (displaying it to the user, sending it over a serialized API of some kind, etc.) then just use the data as-is instead of converting it to something else.
After you get the date with JDateChooser.getDate(), you are immediately converting it to a string: sdf.format(JDateChooser.getDate());
You should store the returned Date from JDateChooser.getDate() as an actual Date object.
Date birthdate = JDateChooser.getDate();
Then you can use it in your other function directly:
user.setBirthDate(birthdate);
If you do need the date as a string for some other purpose (perhaps display to the user), you can store a formatted string version in a different variable:
String birthdateString = sdf.format(birthdate);
Otherwise, if you don't need a string version, you can delete the line where you create sdf.

Issue with Simple Date format Java [duplicate]

This question already has answers here:
SimpleDateFormat.parse() ignores the number of characters in pattern
(5 answers)
Closed 7 years ago.
I am working on a project where I need to validate multiple dates based on length and patterns. I am using simple date format and found many issues with that. My requirement is to strictly allow if date string matches "yyyy/MM/dd" and strictly 10 characters.
The below code is not giving expected results for various testing input strings.
public static boolean checkformat(String dateString){
boolean flag = false;
Date d1 = null;
SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
format.setLenient(false);
try {
d1 = format.parse(dateString);
flag=true;
} catch (ParseException ex) {
ex.printStackTrace();
return false;
}
return flag;
}
the above code is returning "true" for various inputs like "99/03/1" (should be 0099/03/01) and 99/1/1( should be 0099/01/1). Since the input strings are not coming from a from so I cant perform validations before passing them to this method. Please suggest any implementation which should act very strict towards the dateformat("yyyy/MM/dd").
I suggest that you should try to validate date with regex before format it.
user below code for validate
public static boolean checkformat(String dateString){
boolean flag = false;
Date d1 = null;
SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
format.setLenient(false);
try {
if (dateString.matches("([0-9]{4})/([0-9]{2})/([0-9]{2})")) { // use this regex
d1 = format.parse(dateString);
flag=true;
}
} catch (ParseException ex) {
ex.printStackTrace();
return false;
}
return flag;
}
Okay, first: You know what format you're expection. So why just parse it and catch an exception rather than checking preconditions ?
if(dateString.size() > 10) {
...
What you are actually doing is not checking your input format but rather parsing it - though the method is not expressing this contract -
so if your method is just for checking you could:
1. Use a regex
2. ... ?
I know that are quiet a lot of answers on the net which propose using SimpleDateFormat, but - to be frank -they are wrong.
If I am expecting a given format, e.g. as I know that conversions have been made on some user input, I can start parsing a string, and considering that something may have gone wrong, catch the exception. If I don't know which format is passed to me, I am at the validation layer and this layer should not try to perform a conversion but rather proof that the conversion would be valid.
You could try using the new java.time package from Java 8 and later. You could use it as so to replace the SimpleDateFormat:
public static boolean checkformat(String dateString){
boolean flag = false;
try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("yyyyMMdd").parse(strDate);
flag=true;
} catch (DateTimeParseException ex) {
ex.printStackTrace();
return false;
}
return flag;
}
This would also limit the values from making no sense (e.g. month value being 18).
String[] removeSlashes=new String[3];
removeSlashes = enteredDate.split("/");
if(removeSlashes[0].length()!=4)
throw new IncorrectDateFormatException(); // user defined exception
if(removeSlashes[1].length()!=2)
throw new IncorrectDateFormatException();
if(removeSlashes[2].length()!=2)
throw new IncorrectDateFormatException();
//Then use SimpleDateFormat to verify

Parsing ASP.NET MVC returned date using Jackson JSON library in Java

I am parsing JSON from server in my Android application by using Jackson JSON library. However, parsing requests fail whenever I receive DateTime since it's in this format:
"/Date(1277931782420)/"
I know I should do something like:
ObjectMapper om = new ObjectMapper();
om.setDateFormat(new TicksSinceFormat());
But I have no idea if I can use SimpleDateFormat at all (and what format string would I use?) or I need to write my own DateFormat parser. So, I would seriously appreciate if somebody could help with code example.
EDIT:
OK, see my answer for complete code.
This proved to be tougher then I expected:
public class TicksSinceFormat extends DateFormat {
#Override
public StringBuffer format(Date date, StringBuffer buffer, FieldPosition field) {
long millis = date.getTime();
return new StringBuffer("/Date(" + millis + ")/");
}
#Override
public Date parse(String string, ParsePosition position) {
int start = string.indexOf("(") + 1;
int end = string.indexOf(")");
String ms = string.substring(start, end);
Date date = new Date(Long.parseLong(ms));
position.setIndex(string.length() - 1); // MUST SET THIS
return date;
}
#Override
public Object clone() {
return new TicksSinceFormat(); // MUST SET THIS
}
}
Using class is then extremely simple, just do:
ObjectMapper om = new ObjectMapper();
om.setDateFormat(new TicksSinceFormat())
I presume that this can be coded better + that I'll need to deal with differences when it comes to .NET Ticks VS Java ticks - but for now this'll do. If somebody has better solution or more insight into mentioned problems I'll deal with later - feel free to post and I'll mark your answer as correct one if it's better.
EDIT: As I've explained in this question & answer I've switched to ServiceStack.Text library on the server and it returns different, ISO8601 format. For that format I'm using slightly different parsing (since Jackson has trouble parsing ISO8601 that contains milliseconds). Of course, as with other code I'm posting - let me know if you have better version (just please post code / edit this post, rather than resorting to philosophical rhetoric on how it should be done):
#SuppressLint("SimpleDateFormat")
public class JacksonSimpleDateFormat extends SimpleDateFormat {
public JacksonSimpleDateFormat() {
if (mParser == null) {
mParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
mParser.setTimeZone(TimeZone.getTimeZone("UTC"));
}
}
#Override
public StringBuffer format(Date date, StringBuffer buffer, FieldPosition field) {
return mParser.format(date, buffer, field);
}
private static SimpleDateFormat mParser;
#Override
public Date parse(String string, ParsePosition position) {
String str = string.split("\\.")[0];
Date date = null;
try {
date = mParser.parse(str);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
position.setIndex(string.length() - 1);
return date;
}
#Override
public Object clone() {
return new JacksonSimpleDateFormat();
}
}
I may be wrong on this, as I haven't gotten very far into Android development, but the format you presented:
"/Date(1277931782420)/"
Appears to be Unix epoch time.
If that is the case, you would not want/need to use SimpleDateFormat. Instead, try creating a Long from it and passing to the Date constructor, accounting for whether it is seconds or milliseconds-based epoch value.
Here is a StackOverflow post that provides the code for doing so: https://stackoverflow.com/a/535017/463196

Proving that SimpleDateFormat is not threadsafe

I want to show to a colleague that SimpleDateFormat is not thread-safe through a simple JUnit test. The following class fails to make my point (reusing SimpleDateFormat in a multi-threaded environment) and I don't understand why. Can you spot what is preventing my use of SDF from throwing a runtime exception?
public class SimpleDateFormatThreadTest
{
#Test
public void test_SimpleDateFormat_MultiThreaded() throws ParseException{
Date aDate = (new SimpleDateFormat("dd/MM/yyyy").parse("31/12/1999"));
DataFormatter callable = new DataFormatter(aDate);
ExecutorService executor = Executors.newFixedThreadPool(1000);
Collection<DataFormatter> callables = Collections.nCopies(1000, callable);
try{
List<Future<String>> futures = executor.invokeAll(callables);
for (Future f : futures){
try{
assertEquals("31/12/1999", (String) f.get());
}
catch (ExecutionException e){
e.printStackTrace();
}
}
}
catch (InterruptedException e){
e.printStackTrace();
}
}
}
class DataFormatter implements Callable<String>{
static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Date date;
DataFormatter(Date date){
this.date = date;
}
#Override
public String call() throws RuntimeException{
try{
return sdf.format(date);
}
catch (RuntimeException e){
e.printStackTrace();
return "EXCEPTION";
}
}
}
Lack of thread safety doesn't necessarily mean that the code will throw an exception. This was explained in Andy Grove's article, SimpleDateFormat and Thread Safety, which is no longer available online. In it, he showed SimpleDateFormat's lack of thread safety by showing that the output would not always be correct, given different inputs.
When I run this code, I get the following output:
java.lang.RuntimeException: date conversion failed after 3 iterations.
Expected 14-Feb-2001 but got 01-Dec-2007
Note that "01-Dec-2007" isn't even one of the strings in the test data. It is actually a combination of the dates being processed by the other two threads!
While the original article is no longer available online, the following code illustrates the issue. It was created based on articles that appeared to have been based on Andy Grove's initial article.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class SimpleDateFormatThreadSafety {
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
public static void main(String[] args) {
new SimpleDateFormatThreadSafety().dateTest(List.of("01-Jan-1999", "14-Feb-2001", "31-Dec-2007"));
}
public void dateTest(List<String> testData) {
testData.stream()
.map(d -> new Thread(() -> repeatedlyParseAndFormat(d)))
.forEach(Thread::start);
}
private void repeatedlyParseAndFormat(String value) {
for (int i = 0; i < 1000; i++) {
Date d = tryParse(value);
String formatted = dateFormat.format(d);
if (!value.equals(formatted)) {
throw new RuntimeException("date conversion failed after " + i
+ " iterations. Expected " + value + " but got " + formatted);
}
}
}
private Date tryParse(String value) {
try {
return dateFormat.parse(value);
} catch (ParseException e) {
throw new RuntimeException("parse failed");
}
}
}
Sometimes this conversion fails by returning the wrong date, and sometimes it fails with a NumberFormatException:
java.lang.NumberFormatException: For input string: ".E2.31E2"
Isn't this part from javadoc of SimpleDateFormatter has sufficent proof about it?
Synchronization
Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.
And the major observation of not being thread safe is to get unexpected results and not an exception.
It is not thread safe because of this code in SimpleDateFormat (in sun JVM 1.7.0_02):
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
....
}
Each call to format stores the date in a calendar member variable of the SimpleDateFormat, and then subsequently applies the formatting to the contents of the calendar variable (not the date parameter).
So, as each call to format happens the data for all currently running formats may change (depending on the coherence model of your architecture) the data in the calendar member variable that is used by every other thread.
So if you run multiple concurrent calls to format you may not get an exception, but each call may return a result derived from the date of one of the other calls to format - or a hybrid combination of data from many different calls to format.

String to date with no format specified

I want to convert a string into a date, this is simple. But what I'd like to do it without knowing the date format.
Here is a situation: say I have 100 dates and all are in the same format but I'd like to write a Java program to find out this format for me. The result of this program should give me a list of all the possible formats.
For example:
06-06-2006
06-06-2009
...
06-13-2001 <- 99th record
the result of this will give me date format can be mm-dd-yyyy
If the 99th record also was 06-06-2006 the result should be mm-dd-yyyy and dd-mm-yyyy.
Can someone please help me with an example?
Seems sensible to create a set of formats you know about (DATE_FORMATS) and then test each line to see which formats understand every line. You should end up with a set of possibilities.
public class DateFormatDetector {
private static final Set<String> DATE_FORMATS = new HashSet<String>();
static {
DATE_FORMATS.add("yyyy-MM-dd");
DATE_FORMATS.add("dd-MM-yyyy");
DATE_FORMATS.add("MM-dd-yyyy");
}
public static Set<String> getPossibleDateFormats(List<String> dates) {
Set<SimpleDateFormat> candidates = new HashSet<SimpleDateFormat>();
for (String df : DATE_FORMATS) {
SimpleDateFormat candidate = new SimpleDateFormat(df);
candidate.setLenient(false);
candidates.add(candidate);
}
for (String date : dates) {
Iterator<SimpleDateFormat> it = candidates.iterator();
while (it.hasNext()) {
SimpleDateFormat candidate = it.next();
try {
// try to parse the string as a date
candidate.parse(date);
}
catch (ParseException e) {
// failed to parse, so this format is not suitable
it.remove();
}
}
}
Set<String> results = new HashSet<String>();
for (SimpleDateFormat candidate : candidates)
results.add(candidate.toPattern());
return results;
}
}
Try to use SimpleDateFormat prepare all possible formats and calculate parsed result.
The solution could be functional Java as described for example in the stack overflow

Categories

Resources