CarterZhang
CarterZhang

Reputation: 49

Java Using SimpleDateFormat to compare dates

Now I am doing a small project. And this project includes the compare among several dates. So I convert "yyyy-MM-dd" to millisecond by using SimpleDateFormat. However, it did not work out. By the way, when I use more easy way, that is, compare year, month and day one by one, it works out! So the problem is about the SimpleDateFormat.

Here are the code:

    package GenericsPractice;

public class Employee {
    private String name;
    private int age;
    private MyDate birthday;

    public Employee(String name, int age, MyDate birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday.toString();
    }

}
    package GenericsPractice;

import java.text.ParseException;
import java.text.SimpleDateFormat;

public class MyDate {
    private int month;
    private int day;
    private int year;

    public MyDate(int month, int day, int year) {
        this.month = month;
        this.day = day;
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public long dateToMills() throws ParseException {
        String date = (getYear())+"-"+(getMonth())+"-"+(getDay());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        long time = sdf.parse(date).getTime();
        return time;
    }

    @Override
    public String toString() {
        return "month=" + month +
                ", day=" + day +
                ", year=" + year +
                '}';
    }
}

    import GenericsPractice.Employee;
import GenericsPractice.MyDate;

import java.text.ParseException;
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class EmployeeTest {
    public static void main(String[] args) {
//        TreeSet<Employee> set = new TreeSet<>(new Comparator<Employee>() {
//            @Override
//            public int compare(Employee o1, Employee o2) {
//                int year = o1.getBirthday().getYear() - o2.getBirthday().getYear();
//                int month = o1.getBirthday().getMonth() - o2.getBirthday().getMonth();
//                int day = o1.getBirthday().getDay() - o2.getBirthday().getDay();
//                if(year != 0){
//                    return year;
//                }
//                if(month != 0){
//                    return month;
//                }
//                if(day != 0){
//                    return day;
//                }
//                return o1.getName().compareTo(o2.getName());
//            }
//        });

        TreeSet<Employee> set = new TreeSet<>(new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                try {
                    return (int)(o1.getBirthday().dateToMills()-o2.getBirthday().dateToMills());
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                throw new RuntimeException("输入类型不一致");
            }
        });

        set.add(new Employee("Chandler",23,new MyDate(10,18,1997)));
        set.add(new Employee("Ross",25,new MyDate(10,18,1992)));
        set.add(new Employee("Joey",20,new MyDate(3,15,1999)));
        set.add(new Employee("Monica",35,new MyDate(12,31,1990)));
        set.add(new Employee("Rachel",21,new MyDate(6,2,1998)));



        Iterator<Employee> iterator = set.iterator();
        while(iterator.hasNext()){
            Employee obj = iterator.next();
            System.out.println(obj);
        }
    }
}

When running, it did not sort by date:

Employee{name='Chandler', age=23, birthday=month=10, day=18, year=1997}
Employee{name='Monica', age=35, birthday=month=12, day=31, year=1990}
Employee{name='Ross', age=25, birthday=month=10, day=18, year=1992}  
Employee{name='Joey', age=20, birthday=month=3, day=15, year=1999}
Employee{name='Rachel', age=21, birthday=month=6, day=2, year=1998}

Upvotes: 0

Views: 257

Answers (1)

Basil Bourque
Basil Bourque

Reputation: 338835

You said:

So I convert "yyyy-MM-dd" to millisecond by using SimpleDateFormat

No, do not represent a date as a count of milliseconds. And, No, never use SimpleDateFormat. These terrible date-time classes were supplanted years ago. Use only the modern java.time classes defined in JSR 310.

Your code:

private int age;

No, do not store age. You can dynamically calculate an age by comparing their date of birth to the current date.

Also, you need a time zone to calculate age. For any given moment, the current date varies around the globe by time zone. It is "tomorrow" in Tokyo Japan while simultaneously "yesterday" in Toledo Ohio US.

Your code:

class MyDate

No, do not invent your own class to represent a date. We have the java.time.LocalDate class for that.

Note that LocalDate implements Comparable. So LocalDate objects know how to compare and sort amongst themselves. This behavior will be useful in code seen below.

To simplify your class definition, let's make it a record. Records are a new feature in Java 16, for a class whose main purpose is transparently and immutably carrying data. The compiler implicitly creates the constructor, the getters, hashCode & equals, and toString. You could just as well define a conventional class.

package org.example;

import java.time.LocalDate;

public record Person( String name , LocalDate dateOfBirth )
{
}

If you want an age-calculator as mentioned above, add a age method.

package org.example;

import java.time.LocalDate;
import java.time.Period;
import java.time.ZoneId;

public record Person(String name , LocalDate dateOfBirth)
{
    public int age ( ZoneId zoneId ) { return Period.between( this.dateOfBirth , LocalDate.now( zoneId ) ).getYears(); }
}

In real work, I would add an explicit constructor that used Objects.requireNonNull to ensure no null arguments were received. Ditto for age method, verify ZoneId argument is not null.

For specifying our input objects, let's use Set.of syntax. Then later feed the resulting unmodifiable Set object to our TreeSet.

Set < Person > peopleInput =
        Set.of(
                new Person( "Chandler" , LocalDate.of( 1997 , 10 , 18 ) ) ,
                new Person( "Ross" , LocalDate.of( 1992 , 10 , 18 ) ) ,
                new Person( "Joey" , LocalDate.of( 1999 , 3 , 15 ) ) ,
                new Person( "Monica" , LocalDate.of( 1990 , 12 , 31 ) ) ,
                new Person( "Rachel" , LocalDate.of( 1998 , 6 , 2 ) )
        );

Much easier nowadays to use lambda/streams syntax for specifying a Comparator. You could write a conventional comparator as seen in your Question, but doing so is more likely to be incorrect, and less readable.

As discussed in my Answer to your similar Question yesterday, the Comparator/Comparable implementation used in your NavigableSet must be consistent with equals. If two objects being compared are named a and b, then “consistent with equals” means that where a.equals(b) returns true, so must a.compareTo(b) == 0 return true. So we must include both member fields name & dateOfBirth as those fields are used in our record’s default implementation of equals.

Comparator < Person > comparingByDateOfBirthAndThenName =
        Comparator
                .comparing( Person :: dateOfBirth )
                .thenComparing( Person :: name );

Construct our TreeSet using that Comparator object.

NavigableSet < Person > people = new TreeSet <>( comparingByDateOfBirthAndThenName );

Populate with the objects we instantiated via Set.of. As the objects are added, the TreeSet, being a NavigableSet, uses our Comparator object to sort them.

people.addAll( peopleInput );

Dump to console.

System.out.println( "peopleInput = " + peopleInput );
System.out.println( "people = " + people );

When run.

peopleInput = [Person[name=Chandler, dateOfBirth=1997-10-18], Person[name=Rachel, dateOfBirth=1998-06-02], Person[name=Joey, dateOfBirth=1999-03-15], Person[name=Ross, dateOfBirth=1992-10-18], Person[name=Monica, dateOfBirth=1990-12-31]]
people = [Person[name=Monica, dateOfBirth=1990-12-31], Person[name=Ross, dateOfBirth=1992-10-18], Person[name=Chandler, dateOfBirth=1997-10-18], Person[name=Rachel, dateOfBirth=1998-06-02], Person[name=Joey, dateOfBirth=1999-03-15]]

Upvotes: 2

Related Questions