1

I have a date list to which I would like to validate, I have a function that works only with a single date, but I now want a date list.

Rules:

1) When the list is empty, I return true

2) When a date is invalid, I switch to the following and delete it from the list of dates

3) Edit When all the execution is finished I return true (if at least one is valid) or false (all of them failed the test).

Edit: instead of having this isDateValid(String date) ==> isDateValid(List<LString> date)

List of date:

List<String> dateList= new ArrayList<>();

dateList.add("2016-10-02T04:00:00.000Z");
dateList.add("2017-02-15T14:32:32");
dateList.add("2017-01-23");

Function (only one date):

 public boolean isDateValid(String date ) {
        List<SimpleDateFormat> knownPatterns = new ArrayList<>();
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd"));
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
        knownPatterns.add(new SimpleDateFormat("MM/dd/yyyy"));

        for (SimpleDateFormat pattern : knownPatterns) {
            try {
               Date timestamp = pattern.parse(date);
               return true;                
            } catch (ParseException e) {
                continue;
            }
        }
        return false;
    }
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
David Edgar
  • 477
  • 8
  • 23
  • my actual function check only one date and I want to pass a list of date to it – David Edgar Jul 21 '17 at 05:22
  • So what do you want to return when there is one or more than that invalid dates in the list. It needs to return true or false – DhaRmvEEr siNgh Jul 21 '17 at 05:23
  • if there is one invalid and two valid. I remove the invalid and keep the others and return true. – David Edgar Jul 21 '17 at 05:24
  • 1
    What is the problem you're facing? Apparently you know how to use a loop. To remove from a collection you should use the [`Iterator`](https://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re). – Flown Jul 21 '17 at 05:32
  • I dont know which loop comes first, date list or the format list, maybe a nested loop. – David Edgar Jul 21 '17 at 05:36
  • A nested loop sounds right. I think it’s simpler to put the loop over your list as the outer loop and the loop over known formats inside. – Ole V.V. Jul 21 '17 at 05:38
  • 1
    As an aside, one, you should no longer use the outdated classes `Date` and `SimpleDateFormat`, the modern Java date and time API is much better. Two, most of your formats seem to conform with ISO 8601, a standard that the modern classes excel in. So you have every reason. See for example [the tutorial](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Jul 21 '17 at 08:41

4 Answers4

2

Just reuse your existing function and add another function providing the logic needed for the list.

public boolean isDateValid(List<String> dates) {
    if (dates == null || dates.isEmpty()) {
        return true;
    }

    for (Iterator<String> iter = dates.iterator(); iter.hasNext(); ) {
        String date = iter.next();
        if (!isDateValid(date)) {
            iter.remove();
        }
    }

    // return true if at least one date is valid? 
    return !dates.isEmpty();
}

Or even better, if you are already using Java 8 and want to use the new methods of Collection and Lambdas

public boolean isDateValid(List<String> dates) {
    if (dates == null || dates.isEmpty()) {
        return true;
    }

    dates.removeIf(date -> !isDateValid(date));

    // return true if at least one date is valid?
    return !dates.isEmpty();
}
Patrick
  • 915
  • 1
  • 8
  • 17
1
your isDateValid method can be modified as follow for list

    public  boolean isDateValid(List<String> list ) {
        //return true if list is null or empty
        if(list==null || list.isEmpty())
            return true;
        List<SimpleDateFormat> knownPatterns = new ArrayList<>();
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd"));
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
        knownPatterns.add(new SimpleDateFormat("MM/dd/yyyy"));
        //counter to keep counting valid dates
        int validCount=0;
        boolean flag=false;
        Iterator<String> itr=list.iterator();
        String date;
        while(itr.hasNext()){
            date=itr.next();
        for (SimpleDateFormat pattern : knownPatterns) {
            try {
                pattern.parse(date);
                validCount++;
                flag=true;
               break;               
            } catch (ParseException e) {
                continue;
            }
        }
        if(!flag)
            itr.remove();
        else
            flag=true;
        }
        //if there were valid dates in list return true else return false
        return validCount>0?true:false;
    }
DhaRmvEEr siNgh
  • 1,918
  • 2
  • 13
  • 17
  • Please explain how it solves the problem. And please indent your code properly. It’s not even clear to me as the code stands, are you using nested loops or not? – Ole V.V. Jul 21 '17 at 05:40
  • It looks good, I'll give it a try and find the best way to achieve what I want . – David Edgar Jul 21 '17 at 05:42
  • 2
    Yes, we are using nested loop. In first loop we are iterating on list of date with Iterator. in second loop we are checking for valid date. I'll add some more comments to it in a while to explain it.. – DhaRmvEEr siNgh Jul 21 '17 at 05:43
1

You can write a class for the validation:

class DateValidator {
    private String format;
    public DateValidator(String format) {
        this.format = format;
    }
    boolean validDate(String toParse) {
        try {
            new SimpleDateFormat(format).parse(toParse);
        } catch (ParseException ex) {
            return false;
        }
        return true;
    }
    static boolean validDate(List<DateValidator> validators, String toParse) {
        return validators.stream().anyMatch(v -> v.validDate(toParse));
    }
}
// and this is how you use it:
List<String> toValidate = createList(); // your list
List<String> validFormats = createValidFormats();
validator = formats.stream().map(DateValidator::new) // now we have a validator stream
    .collect(Collectors.reducing(Predicate::or)) // now all of them are ORed
    .orElse(s -> false); // default is not a valid format (?)
toValidate.removeIf(validator);

This is clean code in that the DateValidator class is reusable and has a clear, single usage. Maybe the stream takes getting used to, YMMV. You may also create a Composite validator class if you use this validation often:

class CompositeDateValidator {
    private Predicate<String> validator;
    public CompositeDateValidator(Collection<String> formats) {
        // or use getters/setters, or an addFormat method
        validator = formats.stream().map(DateValidator::new) // now we have a validator stream
            .collect(Collectors.reducing(Predicate::or)) // now all of them are ORed
            .orElse(s -> false); // default is not a valid format (?)
    }
    public boolean validate(String toValidate) {
        return validator.test(toValidate);
    }
}
// and then use it like this
List<String> validFormats = createValidFormats();
// this could be a member or Singleton somewhere
CompositeValidator validator = new CompositeValidator(validFormats);

List<String> toValidate = createList().removeIf(date -> !validator.validate(date));

This may be overengineered, but I learned a lot looking up on how to do that exactly ;)

daniu
  • 14,137
  • 4
  • 32
  • 53
0

I used iterator because when you are looping through a list you cant just delete an item and continue

 public boolean isDateValid(ArrayList<String> dates ) {
        if(dates.isEmpty()) return true;
        List<SimpleDateFormat> knownPatterns = new ArrayList<>();
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd"));
        knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
        knownPatterns.add(new SimpleDateFormat("MM/dd/yyyy"));
        //Iterator usage is for deleting
        for (Iterator<String> itarator = dates.iterator() ; iterator.hasNext()){
            String date = iterator.next();
            for (int i = 0; i< knownPatterns.size(); i++) {
                try {
                    Date timestamp = knownPatterns.get(i).parse(date);
                    return true;                
                } catch (ParseException e) {
                    if(i == knownPatterns.size()-1){
                    iterator.remove();
                    }
                    continue;
                }
            }
        }
        return false;
    }
Alper Fırat Kaya
  • 2,079
  • 1
  • 18
  • 18