-2

For a booking monitoring system I'm using a simple date class(not showing the full class):

package DataBase;

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

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

    public int getDay() {
        return day;
    }

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

    public int getMonth() {
        return month;
    }

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

    public int getYear() {
        return year;
    }

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

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

I use this class because it simplifies a lot of my code when I need to process the bookings and put them in JTables.

I obtain the date from a JDatePicker which then put's it in an SQL database together with more info about the Booking. Then when the application is restarted the rows from the table are used to visualize the JTables.

My problem however is that I get strange behavior when converting the dates from the Java.util.Date which I get from the database to my own date class above. I use this piece of code to convert them:

//Java Util Date, dateString is a string formatted like this: dd-MM-yyyy
Date date = new Date();
DateFormat format = new SimpleDateFormat("dd-MM-yyyy");
date = format.parse(dateString);
//my own date
DataBase.Date dateTemp = new DataBase.Date(date.getDay(),date.getMonth() ,date.getYear());

When I print dateString & dateTemp.toString(), I obtain the following output:

19-03-2017
Date [day=0, month=2, year=117]

The date above is correct but it's values for day, month and year on the second line don't match up.

another example:

21-03-2017
Date [day=2, month=2, year=117]

there seems to be a pattern to it but I can't seem to find what I'm doing wrong here!

BRHSM
  • 854
  • 3
  • 13
  • 48
  • 2
    You're using deprecated methods, and without reading their documentation. Read the javadoc. And don't use Date. Use LocalDate (and read its documentation). – JB Nizet Mar 18 '17 at 15:27
  • 2
    For the love of god lock `java.util.Date` in a cage and use http://www.threeten.org/threetenbp/. – Eugen Pechanec Mar 18 '17 at 15:34
  • 2
    Also I suspect that `LocalDate` will be so convenient and offer the functionality that will convince you that you don’t need your own date class. It is available in Java 8 and in ThreeTen Backport (see @EugenPechanec’ link). – Ole V.V. Mar 18 '17 at 16:40

1 Answers1

2

The Date methods you use give the day according to your computer’s time zone setting, while the class internally stores midnight (0:00 hours) on the date in UTC timezone. If your time zone is east of UTC (as mine is), you are lucky that the date coincides; if you run the same code on a computer west of UTC, you will get the previous date. You will definitely not want your code to be as fragile as this.

Except: SimpleDateFormat too uses your computer’s time zone setting, so with the code you have posted you will not notice any problem; but with a date from your database you will.

JB Nizet mentioned in a comment that the methods are deprecated. And they are deprecated exactly because they give you such fragile code.

There are two solutions:

  • The poor one that the Date documentation refers you to, the Calendar class.
  • The good one: the java.time.LocalDate class that was introduced in Java 8 and has also been ported back to Java 6 and 7.

Let’s take the good one first:

    LocalDate local = date.toInstant().atOffset(ZoneOffset.UTC).toLocalDate();

To test this, you may use this way of producing a date equal to one that comes from your database:

    date = Date.from(LocalDate.of(2017, 3, 19).atStartOfDay().atOffset(ZoneOffset.UTC).toInstant());

The LocalDate class offers methods getDayOfMonth(), getMonth(), getYear() and many others, and they give you the expected results. I would strongly expect that you will find this class so attractive that you will not need your own date class. If you want to change each of the fields, you may use the withDayOfMonth, withMonth and withYear methods. Just notice that these return new LocalDate objects with the new dates rather than modifying the existing object. LocalDate instances are immutable.

If you cannot use Java 8 and do not want to adopt the ThreeTen Backport that Eugen Pechanec refers to in the comment, you may resort to the Calendar class introduced in Java 1.1 (that’s a long time ago now).

    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    cal.setTime(date);
    DataBase.Date dateTemp = new DataBase.Date(cal.get(Calendar.DAY_OF_MONTH),
            cal.get(Calendar.MONTH) + 1, cal.get(Calendar.YEAR));

To test with a date equal to one that comes from your database:

    DateFormat format = new SimpleDateFormat("dd-MM-yyyy");
    format.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date date = format.parse(dateString);

I am adding 1 to cal.get(Calendar.MONTH) because Calendar’s months are 0-based and you want a 1-based date.

Again, you may consider using Calendar instead of your own class. Its set method is quite powerful in case you need that.

For the sake of completeness, the output you got conforms precisely with what the documentation of the Date class says. If you want to understand what was going on (I would want that), see the documentation of its getDay, getMonth and getYear methods. And getDate().

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