EduBw
EduBw

Reputation: 942

Filter date with Java

I am trying filters with Java, I can't use "Select ..." for find results.

The problem is that my object always join in my array.

My datas are ->

Registro 1-> Fecha Inicio "2018-09-01" , Fecha Fin -> "2018-09-30"
Registro 2-> Fecha inicio "2018-10-01" , Fecha Fin -> "2018-10-05"
Registro 3-> Fecha inicio "2017-12-31" , Fecha Fin -> "2018-11-30"
Registro 4-> Fecha inicio "2018-12-01" , Fecha Fin -> "2019-10-01"
Registro 5-> Fecha inicio "2018-12-30" , Fecha Fin -> "2018-12-31"
Registro 6-> Fecha inicio "2018-11-30" , Fecha Fin -> "2018-12-01"

The user insert -> "2018-12-01" hasta el "9999-31-12" for filter.

Register 4 should be insert to array because my user insert "2018-12-01" and my register have the same Date Start.

Register 5º should be insert to array because my user insert "2018-12-01" and finish year->9999 THEN how my register is between, It should insert to array.

Register 6º -> shold be insert to array because the date End because its end date corresponds to the period of time between the start date and the end date of the user, that is, the user wants between "2018-12-01" until "9999-31-12" and the date End of registration is "2018-12-01"

public List<Article> filterResult(String paramSelect, String dateStart, String dateEnd) {
    List<Article> list = Collections.emptyList();

    try {
        // Sino me ponen fecha de fin, la establezco al máximo.
        if(dateStart != null && dateEnd == null) {
            dateEnd ="9999-31-12";
            list = (List<Article>) this.pgRepository.findAll();
            list =  this.getStart(list, dateStart, dateEnd);
        }
     } catch (Exception e) {
         LOGGER.error(e.getMessage());
     }

     return list;
}

private List<Article> getStart(List<Article> list, String dateStart, String dateEnd) throws ParseException {
    DateFomrat df = new SimpleDateFormat("yyyy-MM-dd"); 
    Date userDate = df.parse(dateStart);
    Date userEnd = df.parse(dateEnd);

    List<Article> filter = new ArrayList<Article>();

    for(Article param : list) {
        Date paramStart = df.parse(param.getStartdatevalidity());
        Date paramEnd = df.parse(param.getEnddatevalidity());

        if(paramStart.after(userDate) || paramEnd.before(userDate) && paramStart.after(userEnd) ||  paramEnd.before(userEnd)) {
            filter.add(param);
        }
    }

    return filter;
}

I have two problems -> 1º My object always is guarded in my array. 2º I don't know if my logic is correct...

Thanks and sorry for my English.

Upvotes: 1

Views: 5447

Answers (3)

Anonymous
Anonymous

Reputation: 86280

I think that you want this:

    if (! (paramEnd.before(userDate) || paramStart.after(userEnd))) {
        filter.add(param);
    }

Yes, this simple. You may say that I have reversed the condition. You don’t want the articles whose validity end before userDate, and you also don’t want those whose validity begin after userEnd. All other articles have an overlap with the user interval, so are included.

That said, the Date class that you are using is long outdated and has a number of design problems. And SimpleDateFormat is renowned for trouble. Furthermore a Date despite its name does not represent a date, but a point in time. I recommend you use LocalDate from java.time, the modern Java date and time API, for representing a date, both inside your Article class and in your search and filter code. As an added bonus LocalDate parses your date format of yyyy-MM-dd without any explicit formatter since this format is standard (it’s also known as ISO 8601). For example:

public class Article {

    LocalDate startDateValidity;
    LocalDate endDateValidity;

    // Note that this constructor accepts string arguments for convenience
    public Article(String startValidity, String endValidity) {
        this.startDateValidity = LocalDate.parse(startValidity);
        this.endDateValidity = LocalDate.parse(endValidity);
    }

    // getters etc.

}

In filterResult parse dateStart and dateEnd into LocalDate in the same say as in the constructor above. If dateEnd is missing, set userEnd tp LocalDate.MAX. Your if condition isn’t changed much when all the dates are LocalDate; there’s an is in front of the method names:

    if (! (paramEnd.isBefore(userDate) || paramStart.isAfter(userEnd))) {
        filter.add(param);
    }

EDIT: let’s test it.

Using the dates of the 6 registrations from your question:

    List<Article> list = Arrays.asList(
            new Article("2018-09-01", "2018-09-30"),
            new Article("2018-10-01", "2018-10-05"),
            new Article("2017-12-31", "2018-11-30"),
            new Article("2018-12-01", "2019-10-01"),
            new Article("2018-12-30", "2018-12-31"),
            new Article("2018-11-30", "2018-12-01"));

    LocalDate userDate = LocalDate.parse("2018-12-01");
    LocalDate userEnd = LocalDate.MAX;

    for(Article param : list) {
        LocalDate paramStart = param.getStartDateValidity();
        LocalDate paramEnd = param.getEndDateValidity();

        if (! (paramEnd.isBefore(userDate) || paramStart.isAfter(userEnd))) {
            System.out.println("Included " + param);
        }
    }

This snippet printed the following output:

Included Article [startDateValidity=2018-12-01, endDateValidity=2019-10-01]
Included Article [startDateValidity=2018-12-30, endDateValidity=2018-12-31]
Included Article [startDateValidity=2018-11-30, endDateValidity=2018-12-01]

You will recognize the dates of registrations number 4, 5 and 6, which were the ones that you said you wanted to be inserted into your filter array.

What went wrong in your code?

I have two problems -> 1º My object always is guarded in my array. 2º I don't know if my logic is correct...

You are correct that your logic is incorrect. Let’s look at your if condition:

if(paramStart.after(userDate)
        || paramEnd.before(userDate) && paramStart.after(userEnd)
        || paramEnd.before(userEnd)) …

&& has higher precedence than ||, so it is interpreted as though there were brackets around paramEnd.before(userDate) && paramStart.after(userEnd). This middle part of the condition will realistically never be true, though, but an article will be included if either the first or the last part is. Only registrations number 5 fulfils the first part of the condition because after means “strictly after”. However all 6 registrations fulfil the last part, paramEnd.before(userDate) && paramStart.after(userEnd). With || between the parts this is enough that all 6 articles are included in the filter result.

Links

Upvotes: 3

Haripriya
Haripriya

Reputation: 1000

Edit 2 - removed questions asked here for more clarification as got the concept by Ole V.V's answer and I have upvoted it being clear helpful answer.

My suggestion based on provided details -

  1. Using boolean java.util.Date.after(Date when)

    //Change suggestion in original code if(paramStart.after(userDate) || paramEnd.before(userDate) paramEnd.before(userEnd)) { filter.add(param); }

  2. Using int java.util.Date.compareTo(java.util.Date anotherDate)

Upvotes: 0

Oleg Cherednik
Oleg Cherednik

Reputation: 18245

I offer not to use date parsing from String to Date at all. In case data is stored and entered by the user in the same format yyyy-mm-dd, then to compare two dates it is enough just compare two strings.

P.S. Actually, to compare huge list of dates in same format, we use RadixSort for string.

In this case, your getStart() method becomes very simple:

public static List<Article> getStart(List<Article> list, String dateStart, String dateEnd) {
    final Predicate<String> isInRange = date -> date.compareTo(dateStart) >= 0 && date.compareTo(dateEnd) <= 0;
    final Predicate<Article> isIntersect = article -> isInRange.test(article.getStartdatevalidity()) || isInRange.test(article.getEnddatevalidity());
    return list.stream().filter(isIntersect).collect(Collectors.toList());
}

Upvotes: 0

Related Questions