I have a java.sql.Date instance and I want to use the java.util.Date.toInstant() method instead of java.sql.Date.toInstant(), for example, is it possible?
java.time and JDBC 4.2
I recommend that you stick to java.time, the modern Java date and time API to which Instant belongs and forget about the two Date classes mentioned in the question. They are both poorly designed and both long outdated. Since JDBC 4.2 we can retrieve java.time types from a ResultSet.
I am assuming that your SQL query is returning SQL datatype date.
PreparedStatement stmt = yourDatabaseConnection
.prepareStatement("select your_date_column from your_table;");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
LocalDate date = rs.getObject("your_date_column", LocalDate.class);
// do something with date
}
You want to convert the date to an Instant? That conversion doesn’t readily make sense. An SQL date usually is a calendar date defined by year, month and day of month, without time zone. An Instant is a point in time, a completely different beast. If you can decide on a time of day and a time zone to use, a conversion is possible. One option is:
Instant inst = date.atStartOfDay(ZoneId.systemDefault()).toInstant();
If you cannot avoid getting a java.sql.Date
Having a java.sql.Date, again we need a time of day and a time zone if we want to convert to Instant. There is a reason why java.sql.Date.toInstant() unconditionally throws UnsupportedOperationException (which I guess caused you to ask the question). I would convert the Date to a LocalDate and then proceed as before:
java.sql.Date oldfashionedSqlDate = getFromSomewhere();
Instant inst = oldfashionedSqlDate.toLocalDate()
.atStartOfDay(ZoneId.systemDefault())
.toInstant();
A shorter but more low-level and cryptic alternative is:
Instant inst = Instant.ofEpochMilli(oldfashionedSqlDate.getTime());
The two ways may not always give the same result, which should be your first guidance for choosing. In case the Date contrary to the specification holds a time of day other than the start of day, the latter method will give you that time, whereas the former will give you the start of the day as the code says. BTW the latter is what java.util.Date.toInstant() does.
In general is it possible to circumvent the method implementation in the subclass and call the superclass method directly?
Is it possible to choose which overridden method to use in Java?
No, that is not possible in the language. Java doesn’t offer any syntax for such a trick. Holger’s comment under this answer seems to suggest that it is possible through reflection under some circumstances such as you being able to open the java.base module. See the last link at the bottom for more details.
Links
Related question: Insert & fetch java.time.LocalDate objects to/from an SQL database such as H2
Oracle tutorial: Date Time explaining how to use java.time.
Related question: I want to print hi GrandFather;but it seems to print hi father
To answer your question as asked:
It is not possible for your code that uses a java.sql.Date to call an method in java.util.Date which java.sql.Date overrides.
That would break data abstraction, and the Java language doesn't allow it. You can't even do it reflectively.
(As other answers and comments indicate, there are alternative approaches that avoid this problem. Some more practical than others1.)
1 - The idea of creating a custom subtype of java.sql.Date is not practical. You don't want to be modifying JDBC drivers to try to solve this.
Related
I should preface this with I use Apache Spark, which uses java.sql.Date, in case anyone suggests I should use dates from java.time. The example below is in Scala.
The API that I use (which is deprecated) to get the month for a date is as follows:
val date: java.sql.Date = ???
val month = date.getMonth()
However if I look at how it appears I should do this based on the deprecation, the above code would be re-written as follows:
val date: java.sql.Date = ???
val cal = Calendar.getInstance()
cal.setTime(date)
cal.get(Calendar.MONTH)
The simplicity and readability of the code is significantly different, and the date being a side effect on the calendar is not terribly nice from a functional programming point of view. Can someone explain why they think this change was made?
Prior to JDK 1.1, the class Date had two additional functions. It
allowed the interpretation of dates as year, month, day, hour, minute,
and second values. It also allowed the formatting and parsing of date
strings. Unfortunately, the API for these functions was not amenable
to internationalization. As of JDK 1.1, the Calendar class should be
used to convert between dates and time fields and the DateFormat class
should be used to format and parse date strings. The corresponding
methods in Date are deprecated.
The JavaDoc explains. Internationalization.
"in case anyone suggests I should use dates from java.time"
There is nothing to stop you from converting to java.time classes as soon as possible, performing whatever calculations/modifications you need and, if you need to re-insert, converting back to java.sql.Date again.
val date: java.sql.Date = ???
val month = date.toLocalDate().getMonthValue()
You said it yourself, and I still think: You should use java.time, the modern Java date and time API. When you get an old-fashioned java.sql.Date from a legacy API not yet upgraded to java.time, convert it to a modern LocalDate and enjoy the natural code writing with java.time.
Why were getMonth() and the other getXxx methods deprecated?
While Michael has already answered the question with respect to java.util.Date, I have something to add when it comes to java.sql.Date. For this class the situation is quite a bit worse than what Michael reported.
What is left undeprecated (apprecated?) of java.util.Date after the deprecations is that a Date is a point in time. java.sql.Date on the other hand was never meant to be a point in time. One way to illustrate this fact is that its toInstant method — which should convert it to an Instant, a point in time — unconditionally throws an UnsupportedOperationException. A java.sql.Date was meant to be a calendar date to be used with an SQL database and its date datatype, which in most cases is also a date, defined by year, month and day of month. Since a Date is no longer year, month and day of month, they have virtually deprecated everything that a java.sql.Date was supposed to be. And they didn’t give us a replacement until with JDBC 4.2 we can exchange LocalDate objects with SQL databases.
The observations that lead to deprecation have got very practical consequences. Let’s try this (in Java because it is what I can write):
void foo(java.sql.Date sqlDate) {
System.out.println(sqlDate);
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Pacific/Samoa")));
System.out.println(sqlDate.getMonth());
}
In one call the method printed:
2020-11-02
9
So we had the 2nd day of the 11th month, and month prints as 9? There are two things going on:
Confusingly the month number that getMonth() returns is 0-based, so 9 means October.
The Date is internally represented as a count of milliseconds since the epoch to the start of the day in the default time zone of the JVM. 2020-11-02 00:00:00 in my original time zone (set to Pacific/Kiritimati for this demonstration) is the same point in time as 2020-10-31 23:00:00 in Samoa. Therefore we get October.
You don’t have to change the time zone yourself for this to happen. Situations where it can happen include:
The default time zone of the JVM can be changed from any part of your program and from any other program running in the same JVM.
The date may be serialized in a program running in one JVM and deserialized in a different JVM with a different time zone setting.
BTW the first snippet I presented at the top often won’t help against unexpected results in these situations. If things go off track before you convert from java.sql.Date to LocalDate, the conversion too will give you the wrong date. If you can make it, convert to LocalDate before anyone messes with the JVM time zone setting and be on the safe side.
In a somewhat legacy project we were using the version 2 of the cassandra driver in Spring application.
This version, and in particular the class BoundStatement exposed a method getDate which returns a java Date. We all know that the old java date api was pretty horrible, but when used with caution it did the job.
Now, because of some necessity we decided to upgrade the cassandra driver to version 3.4. The first thing that was noted was that in this version, the same method getDate now returns a date of type LocalDate which a class that datastax team implemented to repleace the java's one. The interesting thing about this class is noted in documentation:
A date with no time components, no time zone, in the ISO 8601
calendar. Note that ISO 8601 has a number of differences with the
default gregorian calendar used in Java: it uses a proleptic gregorian
calendar, meaning that it's gregorian indefinitely back in the past
(there is no gregorian change); there is a year 0. This class
implements these differences, so that year/month/day fields match
exactly the ones in CQL string literals.
So basically, this class truncates the time information. This change caused some failures in the unit tests that were based on date comparison and it required some test modification. To me it seems strange actually, but I guess there must be a good reason for such choice by the datastax team. I would be happy to hear the opinion of someone who knows more in regard.
getDate from driver 2 was moved to getTimestamp in driver 3.0, as explained in the upgrade guide:
Getters and setters have been added to “data-container” classes for new CQL types:
getByte/setByte for the TINYINT type
getShort/setShort for the SMALLINT type
getTime/setTime for the TIME type
getDate/setDate for the DATE type
The methods for the TIMESTAMP CQL type have been renamed to getTimestamp and setTimestamp.
This affects Row, BoundStatement, TupleValue and UDTValue.
The main justification for this was the addition of a date type in Cassandra 3.0. To prevent future confusion, we moved the existing getDate to getTimestamp so the get methods match their cql type name.
I'm getting a SqlException on a PreparedStatement for violating a uniqueness constraint on a table (dupe key). Essentially my table looks like this:
mytable
=======
mytable_id PRIMARY KEY INT NOT NULL
fizz_id INT NOT NULL
buzz_timestamp DATETIME
The uniqueness constraint is on the buzz_timestamp; that is, no 2 records may have the same date/time "timestamp".
The PreparedStatement that inserts records into this table looks like this:
PreparedStatement ps = conn.prepareStatement(insertQuery);
ps.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
So you can see I'm taking java.util.Date() ("now") and converting it to a java.sql.Date instance.
The exact error I'm seeing (the dupe key) keeps complaining that I'm trying to insert 2015-05-27 00:00:00.0000000 into the table for buzz_timestamp, but that it already exists. So, obviously, I'm using the Date API wrong, and I'm inserting dates that have nulled-out time components, and thereby producing dupe key exceptions.
So I ask: How do I correct this so that I'm truly inserting the date and time for "now"?
Use
ps.setTimestamp(new java.sql.Timestamp(System.currentTimeMillis());
See this: Using setDate in PreparedStatement
The accepted answer is correct. I'll add an explanation.
Confusing Hack
In SQL, a DATE is a date-only value lacking a time-of-day or time zone.
The mess that is the old date-time classes bundled with Java lack any such class to represent date-only values.
Instead, the Java team created a hack. They created java.sql.Date by extending java.util.Date which, despite its confusing name, has a date portion and a time-of-day portion. For the SQL-oriented subclass, they set time-of-day portion to zero values. You might think of that as "midnight". So a java.sql.Date has a time-of-day but pretends not to.
To quote the java.SQL.Date doc:
To conform with the definition of SQL DATE, the millisecond values wrapped by a java.sql.Date instance must be 'normalized' by setting the hours, minutes, seconds, and milliseconds to zero in the particular time zone with which the instance is associated.
For a date-and-time value in SQL use the java.sql.Timestamp class as shown in accepted answer.
java.time
These poorly designed date-time classes have been supplanted by the new java.time package built into Java 8 and later. This package was inspired by the Joda-Time library.
The Java.time package includes classes to represent date-only and time-only classes. Eventually the JDBC drivers will be upgraded to directly support the new data types. Until then, use the several conversion methods added to the old and new classes. Search StackOverflow.com for many examples and discussion, as this Question is largely a duplicate.
java.sql.Date extends java.util.Date but works differently: In SQL, DATE has no time. Therefore, the Java object also ignores hours, minutes, etc.
Try java.sql.Timestamp instead but you may need a cast (in SQL) to convert it to DATETIME.
Related:
mssql 2005 datetime and jdbc
Instead of setDate you need to try setTimestamp
java.sql.Date - A date only (no time part)
java.sql.Time - A time only (no date part)
java.sql.Timestamp - Both date and time
I am trying to read several dates from my database but under certain circumstances I get a ' java.sql.SQLException Bad format for DATE'. Here is my code :
Date entryDateD = res.getDate("entryDate");
In debug mode I see that the content of entryDateD '1996-9-15' as is in my database..
Although I would have to mention that I read other dates too from my database which I notice are of the format 'xxxx-0y-zz'. What I want to say is that in case of a month being less than 10 there is a zero added in front of it which in this case is not added. I suspect that this might have something to do with it.
(this zero does not appear in the database itself though not only in this date but in any date)
thanx in advance :)
Just a guess, but what MySQL column type do you have for entryDate??
By default a date type in MySQL will generate an yyyy-mm--dd format; the missing zero leads me to believe that the column type may be varchar or other non-date type at DB level.
This could be the cause of your problems at Java level...
If you represent it as java.util.Date, you have the advantage of allowing the JDBC driver to worry about handling any format issues with the database.
As for how it's rendered in your display, that's up to you and your use of the java.text.DateFormat class.
You're doing the right thing by representing a date as java.util.Date in your app, but you need to understand that formatting is a separate issue from the type.
Java has a child class of java.util.Date, called java.sql.Date
The easiest way to create a Java SQL Date is by calling the constructor with a Utility Date.
java.util.Date uDate = res.getDate("entryDate");
java.sql.Date sDate = new java.sql.Date(uDate.getTime());
If you want to get res.getDate("entryDate") as YYYY-MM-DD then you need to cast as date in your query like this:
SELECT
...
CAST(entryDate as DATE)as entryDate,
...
FROM your_table;
If you feel this is helpful then do upVote to make it useful for others.
Previously, when I first design a stock application related software, I decide to use java.util.Date to represent date/time information of a stock.
Later, I realize most of the methods in java.util.Date is deprecated. Hence, very soon, I refactor all my code to make use of java.util.Calendar
However, there is 2 shortcomings I encounter.
Construct java.util.Calendar is comparative slower than java.util.Date
Within the accessors getCalendar method of Stock class, I need to clone a copy, as Calendar is a mutable class
Here is the current source code for Stock.java
Recently, I discover Joda-Time. I do the following benchmarking, by creating 1,000,000 java.util.Date, java.util.Calendar and org.joda.time.DateTime. I found org.joda.time.DateTime performs better than java.util.Calendar, during instantiation.
Here is the benchmarking result.
This instantiation speed is important, especially many instance of Stocks will be created, to represent a long price history of a stock.
Do you think is it worth to migrate from Java Calendar to Joda Date Time, to gain application speed performance? Is there any trap I need to pay attention to?
Note that java.util.Date is mutable too - so if it's a problem now you're using Calendar, it would have been a problem using Date too. That said, using Joda Time is definitely worth doing just because it's a much, much better API.
How certain are you that performance is actually an issue in your particular app? You say there will be "many instances" of Stock created... how many? Have you profiled it? I wouldn't expect it to actually make a significant difference in most situations. It's not obvious from your benchmarking graph what you're actually measuring.
Moving to Joda Time is a good idea in general, but I would measure the performance to see how much difference it really makes for you.
Why do you need a Calendar in your Stock class? I think using a Date to represent a point in time is fine. This seems to be what you want, because you want the Calendar object in the a stock to be immutable, which the Date class should be, if you ignore the deprecated methods.
You can use a temporary Calendar when you need to do time operations on a Date outside the Stock class:
Calendar calendar = Calendar.getInstance();
calendar.setTime(stock.getDate());
System.out.println(calendar.getYear());
Like this you can still store a Date in your Stock class, which should have the best performance when only store and retrieve Stock objects from a data storage. If you do several operations at once you can also reuse the same Calendar object.
If you don't like the Calendar interface you could still use Joda Time to do the time operations. You can probably convert dates to and from Joda Time if needed, to do time operations, and still store Date objects in your Stock class.
If java.util.Calendar instances to be replaced with org.joda.time.DateTime are parsed and/or formatted to a particular pattern, e.g.
String format = "YYYY-MM-dd HH:mm:ss";
Within method signatures for parameter and return types, as well as variable declarations
and instantiations, repace whole word occurrences of the class names Calendar (with
DateTime) and SimpleDateFormat (with DateTimeFormatter), respectively
Replace the formatter instantiation statement, e.g.
formatter = new SimpleDateFormat(format);
with
formatter = DateTimeFormat.forPattern(format);
Replace calls to methods of `SimpleDateFormat', e.g.
String dateStr = formatter.format(Calendar.getInstance().getTime());
with
String dateStr = DateTime.now().toString(formatter);
and
formatter.parse(dateStr);
with
DateTime.parse(dateStr, formatter);
I used Joda in the past, and it is an awesome library.
In terms of performance, you'll have to test it, unfortunately. But refactoring your code seems too much. Personally, I used Date throughout my whole application (including DB store and retrieve), and used Joda only when I needed data manipulation. Joda calculates fields only when needed, so I guess the overhead will be much lower than using Java API classes; furthermore, you won't have object version issues to transfer and handle Date objects in your DB, serialize objects and such. I don't expect Joda to break such compatibility, but it is less likely using Date.
It is always better to move to joda-time in general. But it it is really worth to move to the joda-time for your project is based on your use-cases pertaining to the date usage. Look at slide number 46 in the presentation for performance related numbers for some of the operationslink text