4

I'm trying to sort my arrayList by date. I stored the date on Firebase each time I receive a notification and is retrieved from there. I'm using Collections.sort but I don't know how to implement onto my codes as my date format starts with a number in a string format like "12 Jul 2017 at 12:00am".

I've seen some examples of this on stackoverflow but I don't know how to make it work in my case. I will post screenshots and my codes below.

The dates are not sorted.

enter image description here

NotificationFragment.java

public class NotificationFragment extends Fragment {
        prepareNotification1();
        sortDate();
        return v;
    }

       private void prepareNotification1() {
            FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
            String userID = user.getUid();

  mRef.child("customers").child(userID).child("Notification").addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                    Notification menu = dataSnapshot.getValue(Notification.class);
                    notificationList.add(menu);
                mAdapter.notifyDataSetChanged();
            }
 });
    }

    public void sortDate() {
        Collections.sort(notificationList, new Comparator<Notification>() {
            @Override
            public int compare(Notification lhs, Notification rhs) {
                return lhs.getDate().compareTo(rhs.getDate());
            }
        });
        mAdapter = new NotificationAdapter(getContext(), notificationList);
        mRecyclerView.setAdapter(mAdapter);
    }
}

MyFirebaseMessagingService.java

public class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Calendar cal = Calendar.getInstance();

        String currentDateTimeString = DateFormat.getDateInstance().format(new Date());

        SimpleDateFormat df = new SimpleDateFormat("hh:mm a");
        String currentTime = df.format(cal.getTime());

        String notificationTime = currentDateTimeString + " at " + currentTime;

        Notification newNotification = new Notification(remoteMessage.getData().get("body"), notificationTime);
        mRef.child("customers").child(userID).child("Notification").push().setValue(newNotification);
}

Notification.java

public class Notification {
    private String message;
    private String date;


    public Notification(){
    }

    public Notification(String message, String date){
        this.message = message;
        this.date = date;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
arsenallavigne
  • 131
  • 1
  • 2
  • 16
  • Welcome to Stack Overflow! Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, _a specific problem or error_ and _the shortest code necessary_ to reproduce it **in the question itself**. Questions without a clear problem statement are not useful to other readers. See: [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve). – Joe C Jul 12 '17 at 05:51
  • Is getDate() returning String or Date foramt? – Usman Rana Jul 12 '17 at 05:55
  • @UsmanRana String format. I stored it as a string in Firebase each time I receive a notification. – arsenallavigne Jul 12 '17 at 05:55
  • 1
    I don't know Firebase, but assuming it is even remotely similar to other databases it must have a date type. Store your dates in this type. – Tim Biegeleisen Jul 12 '17 at 05:57
  • please print the value of getDate in Logs and post here, there might be some issue in format – Usman Rana Jul 12 '17 at 05:58
  • post your Notification.java – akhilesh0707 Jul 12 '17 at 06:04
  • Further to @TimBiegeleisen comment, if it is similar to other databases then selecting with an `order by datefield` would be better – Scary Wombat Jul 12 '17 at 06:04
  • [accepted answer](https://stackoverflow.com/a/15462829/9312502) you must check it maybe help – denizs May 28 '19 at 08:09

6 Answers6

11

Do sorting after parsing the date.

Collections.sort(notificationList, new Comparator<Notification>() {
            DateFormat f = new SimpleDateFormat("dd/MM/yyyy '@'hh:mm a");
            @Override
            public int compare(Notification lhs, Notification rhs) {
                try {
                    return f.parse(lhs.getDate()).compareTo(f.parse(rhs.getDate()));
                } catch (ParseException e) {
                    throw new IllegalArgumentException(e);
                }
            }
        })

If your date is in some other format, write the DateFormat accordingly.

Ritt
  • 3,181
  • 3
  • 22
  • 51
5

Consider storing the date in Notification as a long (unix time) or a Date (LocalDateTime if you're using Java 8 support) and formatting it as a String only when displaying it to the UI.

SpaceBison
  • 2,525
  • 1
  • 21
  • 38
2

Sort ArrayList by Date in Ascending and Descending order

In my answer I will use an ArrayList for debts named debtsArrayList. This answer will show how to sort an ArrayList with objects by date in both ascending and descending order.

Sort comparator class

First create a SortComparator class to hold the functions to sort by date in ascending and descending order.

public class SortComparator {

    /**
     * Class to sort list by Date
     */
    public static class SortBy_Date {

        /**
         * Class to sort list by Date in ascending order
         */
        public static class Ascending implements Comparator<JB_Debts> {

            @Override
            public int compare(JB_Debts jbDebts1, JB_Debts jbDebts2) {

                // Create DateFormat
                DateFormat dateFormat = DateFormat.getDateInstance(
                        DateFormat.FULL, Locale.ENGLISH);

                // Catch Parse errors
                try {

                    // Parse dates
                    Date date1 = dateFormat.parse(jbDebts1.getDate());
                    Date date2 = dateFormat.parse(jbDebts2.getDate());

                    // Check if dates are null
                    if ((date1 != null) && (date2 != null)) {

                        // Ascending
                        return (date1.getTime() > date2.getTime() ? 1 : -1);

                    } else {

                        return 0; // Return 0 to leave it at current index
                    }
                } catch (Exception exception) {

                    exception.printStackTrace();
                }

                return 0;
            }
        }

        /**
         * Class to sort list by Date in descending order
         */
        public static class Descending implements Comparator<JB_Debts> {

            @Override
            public int compare(JB_Debts jbDebts1, JB_Debts jbDebts2) {

                // Create DateFormat
                DateFormat dateFormat = DateFormat.getDateInstance(
                        DateFormat.FULL, Locale.ENGLISH);

                // Catch Parse and NullPointerExceptions
                try {

                    // Parse dates
                    Date date1 = dateFormat.parse(jbDebts1.getDate());
                    Date date2 = dateFormat.parse(jbDebts2.getDate());

                    // Check if dates are null
                    if ((date1 != null) && (date2 != null)) {

                        // Descending
                        return (date1.getTime() > date2.getTime() ? -1 : 1);

                    } else {

                        return 0; // Return 0 to leave it at current index
                    }
                } catch (Exception exception) {

                    exception.printStackTrace();
                }

                return 0;
            }
        }
    }
}

In my example I am using an ArrayList with objects ArrayList<JB_Debts> where JB_Debts is my Java bean class with getter and setter methods.

    public class JB_Debts {

    private String date; // Our date string

    /**
     * Default constructor
     */
    public JB_Debts2() {
    }

    /**
     * Function to get date
     */
    public String getDate() {

        return date;
    }

    /**
     * Function to set date
     *
     * @param date - Date
     */
    public void setDate(String date) {

        this.date = date;
    }
}

Using the class with collections

We will then use this class with Java collections to sort our ArrayList<JB_Debts> with objects based on Date in ascending and descending order.

// Create ArrayList
ArrayList<JB_Debts> debtsArrayList = new ArrayList();

Collections.sort(debtsArrayList,
            new SortComparator.SortBy_Date.Ascending());

Collections.sort(debtsArrayList,
            new SortComparator.SortBy_Date.Descending());

Note

Please note that I stored the date in my ArrayList as a string. To sort them, I parsed the string to date as below:

// Create DateFormat with Locale
DateFormat dateFormat = DateFormat.getDateInstance(
                DateFormat.FULL, Locale.ENGLISH);

// Create DateFormat without Locale
DateFormat dateFormat = DateFormat.getDateInstance(
                DateFormat.FULL);

You can replace the DateFormat.FULL with a date format of your choice e.g. "dd/MM/yyyy hh:mm a" which will then look as below:

// Create DateFormat with Locale
DateFormat dateFormat = new SimpleDateFormat(
            "dd/MM/yyyy hh:mm a", Locale.ENGLISH);

// Create DateFormat without Locale
DateFormat dateFormat = new SimpleDateFormat(
            "dd/MM/yyyy hh:mm a");

I hope this helps

David Kariuki
  • 1,522
  • 1
  • 15
  • 30
2

You can simply parse the date-time strings into LocalDateTime and perform a natural sorting.

Using Stream:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("d MMM u 'at' h:m a", Locale.UK);
        
        Stream.of(
                    "10 Jul 2017 at 10:59 pm",
                    "10 Jul 2017 at 10:59 pm",
                    "11 Jul 2017 at 11:15 pm",
                    "9 Jul 2017 at 11:15 pm"
                )
                .map(s -> LocalDateTime.parse(s, dtf))
                .sorted()
                .forEach(dt -> System.out.println(dtf.format(dt)));
    }
}

Output:

9 Jul 2017 at 11:15 pm
10 Jul 2017 at 10:59 pm
10 Jul 2017 at 10:59 pm
11 Jul 2017 at 11:15 pm

Non-Stream solution:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("d MMM u 'at' h:m a", Locale.UK);
        List<LocalDateTime> listLdt = new ArrayList<>();
        
        List<String> listDtStr = 
                Arrays.asList(
                                "10 Jul 2017 at 10:59 pm",
                                "10 Jul 2017 at 10:59 pm",
                                "11 Jul 2017 at 11:15 pm",
                                "9 Jul 2017 at 11:15 pm"
                            );
        
        // Add the strings, parsed into LocalDateTime, to listLdt
        for(String s: listDtStr) {
            listLdt.add(LocalDateTime.parse(s, dtf));
        }
        
        // Sort listLdt
        Collections.sort(listLdt);
        
        // Display
        for(LocalDateTime ldt: listLdt) {
            System.out.println(ldt.format(dtf));
        }
    }
}

Output:

9 Jul 2017 at 11:15 pm
10 Jul 2017 at 10:59 pm
10 Jul 2017 at 10:59 pm
11 Jul 2017 at 11:15 pm

Learn more about the modern date-time API from Trail: Date Time.

Using legacy date-time API:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class Main {
    public static void main(String[] args) throws ParseException {
        final SimpleDateFormat sdf = new SimpleDateFormat("d MMM u 'at' h:m a", Locale.UK);
        List<Date> listDate = new ArrayList<>();
        
        List<String> listDtStr = 
                Arrays.asList(
                                "10 Jul 2017 at 10:59 pm",
                                "10 Jul 2017 at 10:59 pm",
                                "11 Jul 2017 at 11:15 pm",
                                "9 Jul 2017 at 11:15 pm"
                            );
        
        // Add the strings, parsed into LocalDateTime, to listDate
        for(String s: listDtStr) {
            listDate.add(sdf.parse(s));
        }
        
        // Sort listDate
        Collections.sort(listDate);
        
        // Display
        for(Date date: listDate) {
            System.out.println(sdf.format(date));
        }
    }
}

Output:

9 Jul 4 at 11:15 pm
10 Jul 5 at 10:59 pm
10 Jul 5 at 10:59 pm
11 Jul 6 at 11:15 pm

Note: The java.util date-time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API* .


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

As I understand, Notification.getDate() returns String value. So, use

public static Date toDate(String value) throws ParseException {
    DateFormat format = new SimpleDateFormat("d MMMM yyyy 'at' HH:mm'am'", Locale.ENGLISH);
    return format.parse(value);
}

this method to det Date from your String. Just get two dates and use Date.compareTo method.

date1 = toDate(lhs.getDate());
date2 = toDate(rhs.getDate());
date1.compareTo(date2);
Artem Samokhin
  • 163
  • 3
  • 12
0

Instead of setting date like following in Notification model.

String notificationTime = currentDateTimeString + " at " + currentTime;

you can save time as System.currentTimeinMillis(); and while displaying then parse it to Date as following

DateFormat f = new SimpleDateFormat("dd/MM/yyyy hh:mm a");
f.parse(timeStamp);
Milan Pansuriya
  • 2,521
  • 1
  • 19
  • 33
Usman Rana
  • 2,067
  • 1
  • 21
  • 32