2

I have the following Java POJO, Start date is a string:

POJO Object:

public class Schedule {

    private String id;

    private String startDate = "";

    //const and getters and setters
}

I want to sort a list of these objects by the start date field:

Adding POJOS to list:

List<Schedule> schedules = new ArrayList<Schedule>();

Schedule s1 = new Schedule();
s1.setStartDate("2018-09-01T15:00:00+0000");

Schedule s2 = new Schedule();
s2.setStartDate("2018-09-15T15:00:00+0000");

schedules.add(s1);
schedules.add(s2);

I have tried writing a comparator but it does not seem to work, is there a way of sorting strings by date (earliest first)?

Edit: I am currently using Java 7

java123999
  • 6,974
  • 36
  • 77
  • 121
  • 1
    These timestamps have a timezone component. I think you will need to convert the strings to some sort of dates and then sort. – Tim Biegeleisen Nov 14 '17 at 10:01
  • 2
    Why not just change the type of your `startDate` field to `OffsetDateTime`? I should say that that is better and sounder modeling. – Ole V.V. Nov 14 '17 at 10:05
  • If — but only if — the zone offset is always `+0000`, you could just sort the strings. Dirty solution, though, not recommended. – Ole V.V. Nov 14 '17 at 10:08
  • My two cents: a. If you always need to have data in sorted format and data will be unique. You should go for SortedSet instead of ArrayList. You will need to implement Comparable interface in your Schedule class for that. Use following pattern to parse date DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssZ", Locale.ENGLISH); – akshaya pandey Nov 14 '17 at 10:12
  • 2
    @akshayapandey, `SortedSet` may be a good idea depending on the requirements and design. In any case avoid the long outdated `SimpleDateFormat` class and friends. The modern and much nicer equivalent is `DateTimeFormatter`. – Ole V.V. Nov 14 '17 at 10:15
  • @java123999 my example is for Java 7 – Sergiy Medvynskyy Nov 14 '17 at 10:37

4 Answers4

2

I think that you can create a custom comparator with an structure similar to the below:

Collections.sort(datestring, new Comparator<String>() {
    DateFormat df = new SimpleDateFormat("your format");
    @Override
    public int compare(String s1, String s2) {
       try {
            return df.parse(s1).compareTo(df.parse(s2));
        } catch (ParseException e) {
            throw new IllegalArgumentException(e);
        }
    }
});
canillas
  • 414
  • 3
  • 13
  • 2
    Please don’t teach the young ones to use the long outdated and notoriously troublesome `SimpleDateFormat` class. Today we have so much better. In this case the modern `OffsetDateTime` even parses the strings out of the box with no need for any explicit formatter or format pattern string. – Ole V.V. Nov 14 '17 at 10:07
  • Yes, but it's Java 8 and, for his question, I have suppossed that he is not using Java 8... – canillas Nov 14 '17 at 10:09
  • Java 8 is in widespread use since long. If the OP is using Java 7 (or 6), I still recommend to get [the ThreeTen Backport](http://www.threeten.org/threetenbp/), the backport of the modern date and time API to Java 6 and 7, and use `OffsetDateTime`. – Ole V.V. Nov 14 '17 at 10:11
  • @canillas you please show how I then use this to return the new sorted list? And yes you are correct I am using java 7 – java123999 Nov 14 '17 at 10:35
  • You must create the comparator and adapt it. My code is only for a String list, you need to create it for your custom object (but it will take you 1 or 2 minutes to change it... simply change Strings for Shedule and parse shedule.getStartDate). then you can call collection.sort(mySheduleList, new myCustomComparator()) – canillas Nov 14 '17 at 10:49
1

Basically you need to convert the timezones to dates as part of the sorting operation:

schedules.sort((s1, s2) -> {
  ZonedDateTime d1 = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(s1.getStartDate(), ZonedDateTime::from);
  ZonedDateTime d2 = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(s2.getStartDate(), ZonedDateTime::from);

  return d1.compareTo(d2);
});
Jeroen Steenbeeke
  • 3,884
  • 5
  • 17
  • 26
1

Try java 8 Comparator maybe ?

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
schedules.sort(Comparator.comparing(s -> dateTimeFormatter.parse(s.getStartDate(), ZonedDateTime::from)));

Java 8 ZonedDateTime requires a colon in the offset, +00:00. Code should work in Java 9!

Java 8 workaround, either use an explicit formatter or insert the colon yourself. – Ole V.V.

List<Schedule> schedules = new ArrayList<>();

Schedule s1 = new Schedule();
s1.setStartDate("2018-09-01T15:00:00+00:00"); //add a colon in the offset

Schedule s2 = new Schedule();
s2.setStartDate("2018-09-15T15:00:00+00:00"); //add a colon in the offset

schedules.add(s1);
schedules.add(s2);

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
schedules.sort(Comparator.comparing(s -> dateTimeFormatter.parse(s.getStartDate(), ZonedDateTime::from)));

Or you would need to create a formatter on your own and use it in the lambda above.

//final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("your format");
LazerBanana
  • 6,865
  • 3
  • 28
  • 47
  • 1
    How will this address the Zone component in the Date string? – Yash Nov 14 '17 at 10:16
  • In Java 8, this code will not work. `java.time.format.DateTimeParseException: Text '2018-09-15T15:00:00+0000' could not be parsed at index 19` with the OP's example. – Yash Nov 14 '17 at 10:28
  • Java 8 `ZonedDateTime` requires a colon in the offset, `+00:00`. @LazerBanana, your code should work in Java 9! Java 8 workaround, either use an explicit formatter or insert the colon yourself. – Ole V.V. Nov 14 '17 at 10:35
  • it works java 9 on my local indeed, sorry for a misleading. I Will edit the answer. – LazerBanana Nov 14 '17 at 10:36
  • I am using Java 7 unfortunately – java123999 Nov 14 '17 at 10:43
1

Best way is to convert your String to date in the class Schedule.

private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

public class Schedule {

    private String id;

    private String startDate = "";

    private Date startDateAsDate;

    public Date getStartDateAsDate() {
        if (startDateAsDate != null) {
            return startDateAsDate;
        }
        try {
            startDateAsDate = (Date) SDF.parseObject(startDate);
            return startDateAsDate;
        } catch (Exception e) {
            return null;
        }
    }
}

private class ScheduleComparator implements Comparator<Schedule> {

    @Override
    public int compare(Schedule o1, Schedule o2) {
        // TODO Auto-generated method stub
        return o1.getStartDateAsDate().compareTo(o2.getStartDateAsDate());
    }

}

Collections.sort(scheduleList, new ScheduleComparator());
// or
Arrays.sort(scheduleArray, new ScheduleComparator());
Sergiy Medvynskyy
  • 11,160
  • 1
  • 32
  • 48
  • 1
    Is it advisable to use the java.util.Date class? – Yash Nov 14 '17 at 10:19
  • It's preferrable to use equivalent from java.time when you use Java 8 or above. I've used `java.util.Date` because I don't know which java version you use. – Sergiy Medvynskyy Nov 14 '17 at 10:21
  • 1
    @Yash, thanks for the question, In 2017 I should say not. Better to use the modern Java data and time API, specifically `OffsetDateTime` (`ZonedDateTime` used in some of the other answers works too). – Ole V.V. Nov 14 '17 at 10:22
  • @OleV.V. As a matter of fact, I've been trying to parse using `ZonedDateTime` and it has been throwing an exception. That's why I was curious. – Yash Nov 14 '17 at 10:24
  • 1
    Ah, @Yash, I forgot, there’s a bug in Java 8 so it won’t parse the offset without a colon in it (`+00:00`). It works in Java 9! Java 8 workaround, either use an explicit formatter or insert the colon yourself. – Ole V.V. Nov 14 '17 at 10:28
  • 1
    @OleV.V. Exactly! in Java 9 it is working! Thanks a ton :) – Yash Nov 14 '17 at 10:29
  • @OleV.V. yes you're right, I've corrected my example. – Sergiy Medvynskyy Nov 14 '17 at 10:35
  • @SergiyMedvynskyy what is the SDF in this case? – java123999 Nov 14 '17 at 10:42
  • @java123999 it's converter used to parse date as string to date as `java.util.Date`, which make sense to compare – Sergiy Medvynskyy Nov 14 '17 at 11:03