1

I'm experiencing some frustrating problems when converting to java.sql.Date objects.

Let's say I've picked a date with JavaFx's `DatePicker’, specifically 1-1-2020. The java.sql.Date object will correctly reflect the selected date:

System.out.println("d: " + date1DatePicker.getValue().getDayOfMonth()); // Prints 1
System.out.println("m: " + date1DatePicker.getValue().getMonth()); // Prints 1
System.out.println("y: " + date1DatePicker.getValue().getYear()); // Prints 2020

// This date will be 1-1-2020
java.sql.Date date = Date.valueOf(date1DatePicker.getValue());

If, however, I choose one day before, that is, 12-31-2019, the converted Date object will be wrong:

System.out.println("d: " + date1DatePicker.getValue().getDayOfMonth()); // Prints 31
System.out.println("m: " + date1DatePicker.getValue().getMonth()); // Prints 12
System.out.println("y: " + date1DatePicker.getValue().getYear()); // Prints 2019

// This date will be 12-31-2020 !!!!!!!!!!!!!!!!
java.sql.Date date = Date.valueOf(date1DatePicker.getValue());

If I choose 12-30-2019, the conversion will still fail:

System.out.println("d: " + date1DatePicker.getValue().getDayOfMonth()); // Prints 30
System.out.println("m: " + date1DatePicker.getValue().getMonth()); // Prints 12
System.out.println("y: " + date1DatePicker.getValue().getYear()); // Prints 2019

// This date will be 12-30-2020 !!!!!!!!!!!!!!!!
java.sql.Date date = Date.valueOf(date1DatePicker.getValue());

Finally, if I choose 12-29-2019, the conversion will be correct:

System.out.println("d: " + date1DatePicker.getValue().getDayOfMonth()); // Prints 29
System.out.println("m: " + date1DatePicker.getValue().getMonth()); // Prints 12
System.out.println("y: " + date1DatePicker.getValue().getYear()); // Prints 2019

// This date will be 12-29-2019 ** FINALLY CORRECT **
java.sql.Date date = Date.valueOf(date1DatePicker.getValue());

I print java.sql.Date dates using:

System.out.println(new SimpleDateFormat("YYYY-MM-dd").format(date));

What's the problem here?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
ismarlowe
  • 119
  • 2
  • 13
  • How do you *know* `Date` contains those values? Do you print the object (thus calling `toString`)? – MC Emperor Feb 06 '20 at 22:17
  • 1
    I print using: new SimpleDateFormat("YYYY-MM-dd").format(date) – ismarlowe Feb 06 '20 at 22:21
  • To add to Lard's answer - don't trust the `toString` implementation of date objects - use an appropriate `DateTimeFormatter` and ensure you know the timezone that's been used – MadProgrammer Feb 06 '20 at 22:28
  • 1
    FYI… The terrible `java.sql.Date` Class was supplanted years ago by `java.time.LocalDate` with the adoption of JSR 310. – Basil Bourque Feb 06 '20 at 23:44
  • I recommend you don’t use `SimpleDateFormat` and `java.sql.Date`. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. Especially since you can obviously use `LocalDateTime` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). For formatting use either a `DateTimeFormatter` or the `toString()`method of your `LocalDate`. – Ole V.V. Feb 07 '20 at 15:41

3 Answers3

4

The problem is that you are printing it through YYYY-MM-dd. The uppercase Y represents the week year, that means that the year returned is the year that belongs to the week number of that particular date.

Since 30th and 31st of december fall in week 1 of 2020, that year number is returned.

The value contained in Date is correct; its formatted output is simply confusing. You should use yyyy-MM-dd instead.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
2

A JavaFX DatePicker returns its value as a java.time.LocalDate. This is, as its documentation indicates (emphasis mine):

A date without a time-zone in the ISO-8601 calendar system, such as 2007-12-03.

So when you call datePicker.getValue(), you get back one of these.

Then you feed it into java.sql.Date#valueOf(LocalDate), whose documentation indicates that a certain kind of conversion will occur:

The provided LocalDate is interpreted as the local date in the local time zone.

This is probably related to what you're observing.

To eliminate time and time zone concerns you might wish to either work with the LocalDate directly, thus simply dropping all time-related concerns on the floor, or deliberately converting to some well-known-for-your-system time zone. The class that may help you out here is java.time.ZonedDateTime. Hope this is helpful.

Laird Nelson
  • 15,321
  • 19
  • 73
  • 127
  • While what you say is true and would certainly be helpful to some, it doesn’t seem to bear much relation to the issue asked about here. – Ole V.V. Feb 07 '20 at 15:42
2

The other two Answers are correct. I’ll add a few more thoughts.

java.sql.Date is obsolete

The terrible class java.sql.Date was supplanted years ago by the modern java.time classes defined in JSR 310.

These is no need to ever use java.sql.Date again. Now replaced by java.time.LocalDate. With JDBC 4.2 and later, we can exchange java.time objects with the database.

Write to database.

LocalDate ld = myDatePicker.getValue() ;  // Extract `LocalDate` from JavaFX widget.
myPreparedDatabase.setObject( … , ld ) ;  // Write to database.

Retrieve from database.

LocalDate ld =  myResultSet.getObject( … , LocalDate.class ) ;

Convert

If you absolutely must have a java.sql.Date to interoperate with old code not yet updated to java.time, convert. Look to new methods added to the old classes.

java.sql.Date myJSD = Date.valueOf( myLocalDate ) ;

Going the other direction.

LocalDate myLocalDate = myJSD.toLocalDate() ;
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154