0

I'm trying to sort a list of files by date in custom format that appears in a file name itself. I'm not sure why, but this not works as expected.

This is how i get my files:

    ArrayList<File> inFiles = new ArrayList<>();
    File[] sortedByDate = parentDir.listFiles();

    for (File file : sortedByDate) {
        if(file.getName().endsWith(".txt")){
            inFiles.add(file);
        }
    }
}
Collections.sort(inFiles, new FileNameCompare());

The sort class:

public class FileNameCompare implements Comparator<File> {

    @Override
    public int compare(File o1, File o2) {

        String name1 = o1.getName();
        String name2 = o2.getName();

        String substr1 = name1.substring(name1.length()-24, name1.lastIndexOf('.'));
        String substr2 = name2.substring(name2.length()-24, name2.lastIndexOf('.'));

        //This is a file name format for example:
        //XXXXX_XXXX_24_Mar_2019_13_02_25.txt

        Date d1 = null, d2 = null;
        SimpleDateFormat sdf = new SimpleDateFormat( "dd_MMM_yyyy_hh_mm_ss" , Locale.ENGLISH);

        try {
            d1 = sdf.parse(substr1);
            d2 = sdf.parse(substr2);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return (int)(d1.getTime() - d2.getTime());
    }
}

For some reason this not working and return a non-sorted list. What I'm doing wrong?

Edited: Adding logs before and after the sorting.

Before:

25_Mar_2019_01_03_53.txt"
25_Mar_2019_01_21_44.txt"
25_Mar_2019_04_59_02.txt"
25_Mar_2019_05_57_06.txt"
25_Mar_2019_06_37_35.txt"
25_Mar_2019_07_10_07.txt"
18_Jan_2019_10_31_25.txt"
18_Jan_2019_10_25_25.txt"
24_Jan_2019_13_02_25.txt"
19_Feb_2019_13_02_25.txt"
18_Mar_2019_10_31_25.txt"
18_Mar_2019_10_25_25.txt"
24_Mar_2019_13_02_25.txt"
09_Jan_2019_09_36_25.txt"
09_Jan_2019_09_02_25.txt"

After:

18_Jan_2019_10_25_25.txt"
18_Jan_2019_10_31_25.txt"
24_Jan_2019_13_02_25.txt"
18_Mar_2019_10_25_25.txt"
18_Mar_2019_10_31_25.txt"
24_Mar_2019_13_02_25.txt"
25_Mar_2019_01_03_53.txt"
25_Mar_2019_01_21_44.txt"
25_Mar_2019_04_59_02.txt"
25_Mar_2019_05_57_06.txt"
25_Mar_2019_06_37_35.txt"
25_Mar_2019_07_10_07.txt"
19_Feb_2019_13_02_25.txt"
09_Jan_2019_09_02_25.txt"
09_Jan_2019_09_36_25.txt"
Fábio Nascimento
  • 2,644
  • 1
  • 21
  • 27
iluxa.b
  • 375
  • 1
  • 6
  • 16
  • you have to debug it to see what is wrong. Just set a breakpoint inside `compare` and see – Vladyslav Matviienko Mar 27 '19 at 09:54
  • Hi @VladyslavMatviienko, Already done this for couple of hours. The SimpleDateFormat works well and catch the right date, but the result of sort is wrong. – iluxa.b Mar 27 '19 at 10:04

3 Answers3

1
int result = 0;
if(d1.getTime() - d2.getTime() > 0){
   result = 1;
} else if (d1.getTime() - d2.getTime() < 0) {
   result = -1;
}
return result;

long cast to int may be wrong

fancyyou
  • 965
  • 6
  • 7
1

Java 8 or later and java.time

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd_MMM_uuuu_HH_mm_ss");

    Collections.sort(inFiles, Comparator.comparing(f -> {
        String name = f.getName();
        String substr = name.substring(name.length() - 24, name.lastIndexOf('.'));
        return LocalDateTime.parse(substr, formatter);
    }));

It’s not just short, it’s first of all a lot harder to get wrong.

Earlier Android, java.time and ThreeTenABP

class FileNameCompare implements Comparator<File> {

    static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd_MMM_uuuu_HH_mm_ss");

    @Override
    public int compare(File o1, File o2) {

        String name1 = o1.getName();
        String name2 = o2.getName();

        String substr1 = name1.substring(name1.length()-24, name1.lastIndexOf('.'));
        String substr2 = name2.substring(name2.length()-24, name2.lastIndexOf('.'));

        LocalDateTime ldt1 = LocalDateTime.parse(substr1, formatter);
        LocalDateTime ldt2 = LocalDateTime.parse(substr2, formatter);

        return ldt1.compareTo(ldt2);
    }
}

What went wrong in your code?

  1. int overflow
  2. Wrong date-time format pattern string

I think it has been said already: You had an int overflow. As soon as two dates are a month a part, the difference in milliseconds is greater than what can be held in an int (the limit goes by 24 days 20 hours 31 minutes 23.647 seconds to be precise). So in this line you sometimes returned the wrong value:

        return (int)(d1.getTime() - d2.getTime());

To compare the Date objects you might just have used:

        return d1.compareTo(d2);

The correct way to compare long values is:

        return Long.compare(d1.getTime(), d2.getTime());

A further tip: When you do need to convert a long to an int and provided that the long value does not overflow the int, make it a habit to use Math.toIntExact. In this case it would have been

        return Math.toIntExact(d1.getTime() - d2.getTime());

It catches any overflow or underflow and reports it through an exception, so this would have told you what your problem was.

There is a further bug in your program: The use of lowercase hh for hour in your format pattern string. hh is for hour within AM or PM from 01 through 12 and only useful with an AM/PM marker. For hour of day from 00 through 23 you need uppercase HH. I bet, in your code, if you had a file stamped with hour 12, it would have been sorted as though the hour was 00.

Question: Can I use java.time on Android?

Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.

  • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

You can sort your files using the lastModified like this

    ArrayList<File> inFiles = new ArrayList<>();
    Collections.sort(inFiles , new Comparator<File>() {

    @Override
    public int compare(File file1, File file2) {
        long k = file1.lastModified() - file2.lastModified();
        if(k > 0){
           return 1;
        }else if(k == 0){
           return 0;
        }else{
          return -1;
       }
    }
});

Note that if you change files name or any properties in your code this logic won't work and you would need to change your logic of how to sort your files.

Amine
  • 2,241
  • 2
  • 19
  • 41
  • Thank you for your comment @Amine, `lastModified` will not work in my case, as I can add a file with older date in the name than a current newest file. In this case "new" file will be first, while I need it to be sorted by date in the filename. – iluxa.b Mar 27 '19 at 10:02
  • You are always welcome. Can you `Log` the files dates before and after the sort logic using your method ? and notice the difference, maybe some dates are wrong. – Amine Mar 27 '19 at 10:09