To make a JDBC query I need to pass date to it. The date is kept in Date field type of PostgreSql database, which represents specific day without any time.
As I need only date, I decided to use specific object which represent only date without time, which is LocalDate from Joda-Time package. I thought it is important because if I used DateTime object, it would carry redundant time data as well as it might lead to bugs at end of daylight saving time when the clock are put backward one hour (though the situation is unprecedentedly rare, it's not impossible).
But when I started trying to square LocalDate object with accepted arguments of preparedStatement.setDate method, I didn't find a proper way to do it.
setDate accepts java.sql.Date as parameter. And the only option to construct java.sql.Date object is to pass it time in milliseconds.
But this defeats all the purpose of using LocalDate from Joda-Time package, as on this conversion we get back to milliseconds and, while these conversions happen, clock may be put back one hour and change the date to the previous date.
So, now I have this line in my code:
preparedStatement.setDate(1, new java.sql.Date(localDate.toDate().getTime()));
But is this the best way to convert LocalDate to accepted by setDate format?
Are my concerns related to daylight saving time and corresponding clock-shifts justified?
Is there a better way to pass date (and only date without time) to JDBC preparedStatement?
It should be safe to use your technique because all the timezone issues will be taken into account by LocalDate#toDate. The resulting millisecond instant you have is context-independent: it uniquely relates to a timezone valid at that point in time within the locale you are using for conversion. In other words, if you repeat the conversion of the exact same millisecond value throughout a year, you will consistently get the exact same answer, even if timezone regulations change for your place in the meantime, since JDK refers to a database documenting the complete history of all timezone changes around the world.
When reasoning about these issues it is important to remember that your current timezone has no effect on the conversion, which is parameterized by your locale and resolves the timezone only within the context of the instant being converted.
I wholeheartedly sympathize with the queasiness you fell about all this: it is turning a simple and straigtforward operation into a complex maze of calculations which does nothing but invite trouble. Hopefully things will take a positive turn with Java 8 and its new (yes, again!) Date/Time API, based firmly on JodaTime.
I got the same problem today. I'm using JDK 8. After spending some hours searching finally I found the answer at Java SE 8 Documentation. This is the solution :
statement.setDate(5, java.sql.Date.valueOf(personToUpdate.getBirthday()));
statement is PreparedStatement instance. "personToUpdate.getBirthday()" is type of LocalDate.
Since org.joda.time.toDateMidnight() and org.joda.time.toDateMidnight(DateTimeZone zone) have been deprecated, this is the solution that works perfectly for me.
My typical Class, to be persisted:
...
import org.joda.time.LocalDate;
...
public class MyObject implements Serializable {
...
private LocalDate startDate;
...
private EndDate startDate;
// Getters and Setters
...
...
}
Im my other Class where I persist startDate, I have:
myObject.setStartDate(new LocalDate(myObject.getStartDate().toDateTimeAtStartOfDay(DateTimeZone.getDefault())));
tl;dr
myPreparedStatement.setObject( // Pass java.time objects directly to database with JDBC 4.2 or later.
… ,
LocalDate.now() // Get current date today. Better to pass optional `ZoneId` time zone object explicitly than rely implicitly on JVM’s current default.
)
java.time
In Java 8 and later, the new java.time framework is now built-in. This successor to Joda-Time is defined by JSR 310 and extended by the ThreeTen-Extra project.
Hopefully we well eventually see the JDBC drivers updated to directly handle the new java.time types. But until then we continue to need the java.sql.* types. Fortunately, new methods have been added to conveniently convert between the types.
For a date-only, with no time-of-day and no time zone, the Java type is LocalDate (quite similar to Joda-Time).
As for your concern about time zone related to a LocalDate, it matters when your are translating a date-only to a date-time, to a moment on the timeline. A date-only is just a vague idea, with no real meaning, until you translate it to a time-span of moment on the timeline (midnight to midnight in some time zone). For example, determining "today" requires a time zone. In java.time we use the ZoneId class.
LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
If omitted, your JVM’s current default time zone is used in determining the date. In other words, the following two lines are equivalent.
LocalDate today = LocalDate.now();
LocalDate today = LocalDate.now( ZoneId.systemDefault() );
I consider the first version, now(), to be a poor API design choice. This implicit application of the JVM’s current default time zone causes no end of confusion, bugs, and misery among naïve developers. For one thing, the JVM's current default varies by machine, by host OS settings, and by sysadmins. Worse, the JVM’s current default can change at any moment, during runtime, by any code in any thread of any app within that JVM. So best practice is to always specify your desired/expected time zone.
Now that we have a java.time object for "today", how to get it into the database?
With JDBC 4.2 or later, directly exchange java.time objects with your database.
myPreparedStatement.setObject( … , today ) ;
To retrieve:
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
If you cannot upgrade yet to JDBC 4.2 or later: Use a java.sql.Date object. In Java 8, that old class gained new methods, toLocalDate and valueOf. The latter is our bridge from the java.time type to the java.sql type.
java.sql.Date sqlToday = java.sql.Date.valueOf( today );
From there do the usual PreparedStatement handling.
myPreparedStatement.setDate( 1 , sqlToday );
Date-only vs Date-time
Perhaps you have concerns about such a date-only fitting your business needs.
If you need to know, for example, if a contract was signed by the end of the day for legal reasons, then date-only is the wrong data-type if mean a specific moment such as the stroke of midnight in Montréal. A new day dawns earlier in Paris than in Montréal, so "today" in Paris is "yesterday" in Montréal. If your contract deadline is defined legally as the end of the day in Montréal, then you must apply a time zone. To apply a time zone, you must have a date-time rather than a date-only. You can make a jump from the LocalDate into a ZonedDateTime, but I consider that overly complex. Your database should have used a date-time type from the beginning.
In Postgres, a date-time type means the TIMESTAMP WITH TIME ZONE type. That name is a misnomer as the time zone is not actually stored. Think of it as “timestamp with respect for time zone”. Postgres uses any offset-from-UTC or time zone information accompanying incoming data to adjust to UTC, and that offset/zone info is then discarded. The other type, TIMESTAMP WITHOUT TIME ZONE, ignores the offset/zone info entirely, and this is the wrong behavior for most any business app.
I suspect many developers or DBAs may make the naïve mistake of thinking by intuition that the date-only has obvious meaning. But in fact if you have specific or strict moment-oriented needs, such as legalities regarding events such as “contract signed”, “invoice received”, or “company executive hired”, then a date-time value should be used rather than date-only.
In other words, regarding the Question’s author’s comment:
And I expected there should be a way to work with dates without resorting to time instances.
No, I would argue that is asking for trouble. If moments matter, use a date-time rather than a date-only.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Try to Use LocalDate#toDateMidnight() which sets time to 0 and then DateMidnight#toDate().
Date date = localDate.toDateMidnight().toDate();
Or if you're using JodaTime 1.5 or newer, use LocalDate#toDateTimeAtStartOfDay() and then DateTime#toDate()
Hope that help
Related
This question is related to this but this specific question focuses on the why. So no, this isn't a duplicate.
Quoting the answer:
The problem is that Java Date objects don't store a time zone. The
value is always in UTC, and is parsed and formatted in a given time
zone, usually the JVM's default time zone.
Oracle DATE columns are also stored without time zone, but should
represent the date as seen by the user. In 99.99% of cases, that means
the date in the JVM's default time zone.
So, the JDBC driver takes the Timestamp / Date value, which is in UTC,
converts it to the default time zone, and saves that to the database.
What's exactly wrong with NOT adjusting and saving the value (UTC) as it is?
What is it trying to solve by adjusting the value before saving it to the database?
The answers to these questions are the whys.
I couldn't see the benefit of the design and I can only see are the problems associated with it. Case in point is when saving is done in a specific timezone and retrieval is done in another timezone. The amount of questions being thrown at this specific topic just proves my point.
So ultimate question is, why it was designed that way? What are the reasons?
Date-time handling is a surprisingly complicated topic. Our intuitive understanding of time works against us as programmers, making this topic difficult to master. Furthermore, poor date-time handling in old databases and old classes make the job even more confusing.
java.time
Firstly, avoid the wretched old date-time classes bundled with the earliest versions of Java. Never use java.util.Date, java.util.Calendar, java.sql.Timestamp, nor other related classes. Use only the java.time classes. If you must interface with old code not yet updated to java.time, call on new conversions methods added to the old classes.
Date was replaced by Instant.
The problem is that Java Date objects don't store a time zone.
Not true. An Instant (and a Date) is always in UTC. Both the modern and legacy class represent a a count of fractional seconds since the first moment of 1970 in UTC, 1970-01-01T00:00:00Z. Always in UTC, easy-peasy.
Oracle DATE columns are also stored without time zone,
True.
The Oracle DATE data type represents only a date with time zone but lacks any concept of time zone or offset-from-UTC. This is apparently a legacy type, created before the SQL standard defined some basic date-time types. In the standard, the TIMESTAMP WITHOUT TIME ZONE might map close to Oracle DATE.
but should represent the date as seen by the user. In 99.99% of cases, that means the date in the JVM's default time zone.
I have no idea what the author meant by that. I think that is their clumsy way of saying that any type similar to the SQL-standard TIMESTAMP WITHOUT TIME ZONE simply takes any given date or date-with-time-of-day as-is, with no attempt at adjusting between zones or offsets. So if you pass January 21, 2018 at noon, it stores a value equivalent to this string 2018-01-23T12:00 without any regard to whether that was noon in Montréal Québec or noon in Kolkata India (two different moments, hours apart).
So, the JDBC driver takes the Timestamp / Date value, which is in UTC, converts it to the default time zone, and saves that to the database.
While the JDBC driver is unspecified here, I doubt this is its behavior. Such behavior would be a contradiction of the behavior of the Oracle DATE type which is no such zone adjustment. The Oracle DATE type (as I read the documentation; I'm not an Oracle user) is agnostic or unaware of zones/offsets.
In Java, the class mapping to SQL-standard TIMESTAMP WITHOUT TIME ZONE and Oracle DATE is LocalDateTime. You should use these zone-less types only in these three situations:
The zone or offset is unknown.This is bad. This is faulty data. Analogous to having a price/cost without knowing the currency. You should be rejecting such data, not storing it.
The intention is “everywhere”, as in, every time zone.For example, a corporate policy that states “All our factories will break for lunch at 12:30" means the factory in Delhi will break hours before the factory in Düsseldorf which breaks hours before the factory in Detroit.
A specific moment in the future is intended, but we are afraid of politicians redefining the time zone.Governments change the rules of their time zones with surprising frequency and with surprisingly little warning or even no warning at all. So if you want to book an appointment at 3 PM on a certain date, and you really mean 3 PM regardless of any crazy decision a government might make in the interim, then store a LocalDateTime. To print a report or display a calendar, dynamically apply a time zone (ZoneId) to generate a specific moment (ZonedDateTime or Instant). This must be done on-the-fly rather than storing the value.
What's exactly wrong with NOT adjusting and saving the value (UTC) as it is?
The JDBC driver should not be doing any adjustments to UTC for a type of Oracle Date or SQL-standard TIMESTAMP WITHOUT TIME ZONE.
If two users at 2018-06-06T21:53Z, one in Québec and one in India, both simultaneously save the current moment of their own parochial wall-clock time into a column of type SQL-standard TIMESTAMP WITHOUT TIME ZONE or Oracle DATE, then we should see two rows with values:
2018-06-06T17:53 (notice the date is “yesterday”)
2018-06-07T03:23 (notice the date is “tomorrow”)
The values are different because America/Montreal is four hours behind UTC while Asia/Kolkata is five and a half hours ahead of UTC, and no adjustment for time zones was made. To repeat myself yet again, the stored values here represent only a date and a time-of-day, but without any context of time zone or offset-from-UTC, they do not represent a moment.
The confusion may be coming from the fact that some databases such a Postgres do adjust incoming values into UTC for values heading to a column of a different type, the TIMESTAMP WITH TIME ZONE type (note the WITH versus WITHOUT). Postgres and something other databases use any passed zone/offset info to adjust into UTC value, then discard the zone/offset info. So the type name is something of a misnomer, and you can think of it as TIMESTAMP WITH RESPECT FOR TIME ZONE.
If those same two users seen above at 2018-06-06T21:53Z were saving the current moment into a SQL-standard column of type TIMESTAMP WITH TIME ZONE, then the two rows would appear as:
2018-06-06T21:53Z
2018-06-06T21:53Z
The Z at the end is pronounced Zulu and means UTC.
What is it trying to solve by adjusting the value before saving it to the database?
Generally, the best practice in date-time handling is to work in UTC rather than in other zones/offsets.
When working as a programmer or sysadmin, forget about your own parochial time zone. Translating back-and-forth from your own zone to UTC or other zones will drive you batty. Think of UTC as the One True Time; all other zones are but mere variations.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Store.
String sql = "INSERT INTO tbl ( event ) VALUES ( ? ) ;" ; // Writing a moment into a column of type `TIMESTAMP WTH TIME ZONE`.
myPreparedStatement.setObject( 1 , instant ) ; // As of JDBC 4.2 and later, we can directly exchange java.time objects with our database.
Retrieve.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Present that moment in a particular time zone.
ZoneId z = ZoneId.of( "Asia/Kolkat" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, same point on the timeline, different wall-clock time.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I have read all of the other Q/A about Date Manipulation, but none of them seems to deliver a satisfactory answer to my concern.
I have a project with geographically diverse users which uses Date in some of its classes and data. The thing is that I am looking for an efficient way to manipulate the Dates for the different users in their respective timezone, most of the answers suggest using Joda library for Date manipulation, which quite don't understand yet because I still have not found any operation you cannot do with traditional Java, so if someone can explain what can I do with Joda that can't be done with traditional Java, then I may consider using it.
I finally came to the approach of using System.currentTimeMillis() to save my dates into the database (any database). This would avoid me to worry about what timezone is using the database to store the dates. If I want to query the database for an specific date or range of dates, I would perform the queries using the long value of the Date I want to query:
SELECT * FROM table1 WHERE date1>=1476653369000
And when retrieving a ResultSet I would then format the long value retrieved from database to a readable Date using the timezone of the user requesting the data.
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(resultSet.getLong(1));
cal.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"));
Date myDate = cal.getTime();
According to some opinions I have read, some people say emphatically that storing System.currentTimeMillis() is definitely not the best practice, nevertheless, for some reason they all miss to say WHY it is not recommendable. Am I missing something? Does this cause a performance issue for the conversions Long->Date/Date->Long? Is there any use case that cannot be accomplished when using Long instead Date in database? Can someone post a rationale explanation about this?
In the other hand, assuming that I keep using Date values to store dates in database, is there a way to avoid worrying about time-zones while handling database Date?
Thanks in advance.
I have read all of the other Q/A about Date Manipulation
No, you certainly did not read them all.
You would have learned that both the legacy date-time classes (such as java.util.Date & java.util.Calendar) and the Joda-Time project are supplanted by the java.time classes (1,890 results for search on 'java.time').
You would have learned not to track date-time values as a count-from-epoch. Debugging and logging becomes very difficult with bugs going undiscovered as humans cannot decipher the meaning of a long integer as a date-time. And because many granularities of counting (whole seconds, milliseconds, microseconds, nanoseconds, whole days, and more) and at least a couple dozen of epochs are employed in various software projects create ambiguity about your data with assumptions leading to errors, misinterpretation, and confusion.
You would have learned to use date-time types in your database to track date-time values.
You would have learned to work and store date-time values in UTC. Adjust into a time zone only where required by logic or as expected by the user for presentation. “Think global, present local.”
You would have learned that while a valiant industry-first effort, the legacy date-time classes are poorly designed, confusing, and troublesome. See What's wrong with Java Date & Time API? for some discussion. Joda-Time was the first good date-time library in the industry, and inspired its replacement, the java.time classes built into Java 8 and later.
I'll be somewhat brief as all of this has been covered many times already on Stack Overflow.
Work in UTC. In Java that means the Instant class is commonly used. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now();
Any serious database such as Postgres tracks date-time values in UTC. Your JDBC driver handles the detail of converting from database internally-stored data to a Java type. JDBC drivers that comply with JDBC 4.2 and later can directly handle java.time types via PreparedStatement::setObject & ResultSet::getObject methods.
myPreparedStatement.setObject( … , instant );
For non-compliant drivers, fall back to using java.sql types such as java.sql.Timestamp to communicate with database, and convert to/from java.time types via new methods added to the old classes. The internal details of how the database handles date-time values may be quite different than how java.time does. For the most part the JDBC driver hides all the nitty-gritty details from you. But one critical issue is resolution, which you should study in your database. The java.time classes handle date-times with a resolution up to nanoseconds but your database may not. For example, Postgres uses a resolution of microseconds. So going back-and-forth means data-loss. You want to use the truncation methods on the java.time classes to match your database.
myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) );
So, no time zone involved. So no “worrying about time-zones while handling database Date”.
When you want to see the same moment through the lens of a region’s wall-clock time, apply a ZoneId to get a ZonedDateTime.
ZoneId z = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = instant.atZone( z );
When taking a zoned date-time back to the database, extract an Instant.
Instant instant = zdt.toInstant();
Be aware that for any given moment, the date as well as the time-of-day varies around the globe by time zone. So if an exact moment matters, such as when a contract expires, beware of using a date-only value. Either use a date-time value for the exact moment, or store the intended time zone alongside the date-only so the exact moment can be calculated later.
LocalDate ld = LocalDate.of( 2016, 1 , 1 );
// Determine the first moment of 2016-01-01 as it happens in Kolkata.
ZonedDateTime zdt = ld.atStartOfDay( ZoneId.of( "Asia/Kolkata" ) );
Instant instant = zdt.toInstant(); // Adjust to UTC and store.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
what can I do with Joda that can't be done with traditional Java
It's not really about what you can or cannot do with traditional Java in the general case. It's more about how the library API works to make you write better (more robust and correct) code easier than traditional Java does.
So much so that as of Java 8 the Joda API was more or less copied/adopted verbatim with just package names changed and incorporated into the Java 8 SE standard library.
Therefore if you are using Java 8 you should pick the new API, and if not you should consider that using Joda will at least buy you a smooth path towards upgrading/porting to Java 8 when you are able to.
A few examples:
Consistent API for date and time types.
Date/time objects are immutable, manipulations return new instances of the type representing the altered value. (Like Java Strings). This makes it easier to reason about reuse of date/time objects.
By design avoids mixing DST & timezone dependent values/operations with DST & timezone agnostic ones. This makes it a lot easier to write code that works consistently and correctly and doesn't have corner cases dependent on timezone/locale/date/time-of-day.
Sane defaults for things like toString() so serialising/deserialising can be expected to work correctly with minimal effort.
Culture/locale dependent corner cases and things you weren't aware of yet (for example, did you know about the traditional Korean calendar?) which saves you a whole lot of hassle when converting your date times between locales/calendaring systems. Also: a wealth of formatting options.
The concept of Instants to represent 'absolute' time stamps which is useful when working with geographically distributed systems (when the system default clocks/timezones & DST rules may differ) or for interop because it uses UTC.
EDIT to add:
According to some opinions I have read, some people say emphatically that storing System.currentTimeMillis() is definitely not the best practice, nevertheless, for some reason they all miss to say WHY it is not recommendable. Am I missing something?
System.currentTimeMillis() has a few downsides. The big drawback is that the type of clock is ill defined. It could be a monotonic clock, it could be clock subject to DST and timezone or it could be a UTC time. It is also not necessarily an accurate clock, it is not actually guaranteed to be accurate down to the millisecond. Just whatever happens to be to hand for something that will work as a semblance of the current time at the current time, basically.
This means that if you want to use multiple servers to handle incoming requests, for instance, it gets tricky when you have to consider working with the output of System.currentTimeMillis() from server A in the context of your program running on a different server B the next day, say.
java.util.Date vs java.sql.Date: when to use which and why?
Congratulations, you've hit my favorite pet peeve with JDBC: Date class handling.
Basically databases usually support at least three forms of datetime fields which are date, time and timestamp. Each of these have a corresponding class in JDBC and each of them extend java.util.Date. Quick semantics of each of these three are the following:
java.sql.Date corresponds to SQL DATE which means it stores years, months and days while hour, minute, second and millisecond are ignored. Additionally sql.Date isn't tied to timezones.
java.sql.Time corresponds to SQL TIME and as should be obvious, only contains information about hour, minutes, seconds and milliseconds.
java.sql.Timestamp corresponds to SQL TIMESTAMP which is exact date to the nanosecond (note that util.Date only supports milliseconds!) with customizable precision.
One of the most common bugs when using JDBC drivers in relation to these three types is that the types are handled incorrectly. This means that sql.Date is timezone specific, sql.Time contains current year, month and day et cetera et cetera.
Finally: Which one to use?
Depends on the SQL type of the field, really. PreparedStatement has setters for all three values, #setDate() being the one for sql.Date, #setTime() for sql.Time and #setTimestamp() for sql.Timestamp.
Do note that if you use ps.setObject(fieldIndex, utilDateObject); you can actually give a normal util.Date to most JDBC drivers which will happily devour it as if it was of the correct type but when you request the data afterwards, you may notice that you're actually missing stuff.
I'm really saying that none of the Dates should be used at all.
What I am saying that save the milliseconds/nanoseconds as plain longs and convert them to whatever objects you are using (obligatory joda-time plug). One hacky way which can be done is to store the date component as one long and time component as another, for example right now would be 20100221 and 154536123. These magic numbers can be used in SQL queries and will be portable from database to another and will let you avoid this part of JDBC/Java Date API:s entirely.
LATE EDIT: Starting with Java 8 you should use neither java.util.Date nor java.sql.Date if you can at all avoid it, and instead prefer using the java.time package (based on Joda) rather than anything else. If you're not on Java 8, here's the original response:
java.sql.Date - when you call methods/constructors of libraries that use it (like JDBC). Not otherwise. You don't want to introduce dependencies to the database libraries for applications/modules that don't explicitly deal with JDBC.
java.util.Date - when using libraries that use it. Otherwise, as little as possible, for several reasons:
It's mutable, which means you have to make a defensive copy of it every time you pass it to or return it from a method.
It doesn't handle dates very well, which backwards people like yours truly, think date handling classes should.
Now, because j.u.D doesn't do it's job very well, the ghastly Calendar classes were introduced. They are also mutable, and awful to work with, and should be avoided if you don't have any choice.
There are better alternatives, like the Joda Time API (which might even make it into Java 7 and become the new official date handling API - a quick search says it won't).
If you feel it's overkill to introduce a new dependency like Joda, longs aren't all that bad to use for timestamp fields in objects, although I myself usually wrap them in j.u.D when passing them around, for type safety and as documentation.
tl;dr
Use neither.
java.time.Instant replaces java.util.Date
java.time.LocalDate replaces java.sql.Date
Neither
java.util.Date vs java.sql.Date: when to use which and why?
Both of these classes are terrible, flawed in design and in implementation. Avoid like the Plague Coronavirus.
Instead use java.time classes, defined in in JSR 310. These classes are an industry-leading framework for working with date-time handling. These supplant entirely the bloody awful legacy classes such as Date, Calendar, SimpleDateFormat, and such.
java.util.Date
The first, java.util.Date is meant to represent a moment in UTC, meaning an offset from UTC of zero hours-minutes-seconds.
java.time.Instant
Now replaced by java.time.Instant.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
java.time.OffsetDateTime
Instant is the basic building-block class of java.time. For more flexibility, use OffsetDateTime set to ZoneOffset.UTC for the same purpose: representing a moment in UTC.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
You can send this object to a database by using PreparedStatement::setObject with JDBC 4.2 or later.
myPreparedStatement.setObject( … , odt ) ;
Retrieve.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
java.sql.Date
The java.sql.Date class is also terrible and obsolete.
This class is meant to represent a date only, without a time-of-day and without a time zone. Unfortunately, in a terrible hack of a design, this class inherits from java.util.Date which represents a moment (a date with time-of-day in UTC). So this class is merely pretending to be date-only, while actually carrying a time-of-day and implicit offset of UTC. This causes so much confusion. Never use this class.
java.time.LocalDate
Instead, use java.time.LocalDate to track just a date (year, month, day-of-month) without any time-of-day nor any time zone or offset.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate ld = LocalDate.now( z ) ; // Capture the current date as seen in the wall-clock time used by the people of a particular region (a time zone).
Send to the database.
myPreparedStatement.setObject( … , ld ) ;
Retrieve.
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The only time to use java.sql.Date is in a PreparedStatement.setDate. Otherwise, use java.util.Date. It's telling that ResultSet.getDate returns a java.sql.Date but it can be assigned directly to a java.util.Date.
I had the same issue, the easiest way i found to insert the current date into a prepared statement is this one:
preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));
The java.util.Date class in Java represents a particular moment in time (e,.g., 2013 Nov 25 16:30:45 down to milliseconds), but the DATE data type in the DB represents a date only (e.g., 2013 Nov 25). To prevent you from providing a java.util.Date object to the DB by mistake, Java doesn’t allow you to set a SQL parameter to java.util.Date directly:
PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work
But it still allows you to do that by force/intention (then hours and minutes will be ignored by the DB driver). This is done with the java.sql.Date class:
PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work
A java.sql.Date object can store a moment in time (so that it’s easy to construct from a java.util.Date) but will throw an exception if you try to ask it for the hours (to enforce its concept of being a date only). The DB driver is expected to recognize this class and just use 0 for the hours. Try this:
public static void main(String[] args) {
java.util.Date d1 = new java.util.Date(12345);//ms since 1970 Jan 1 midnight
java.sql.Date d2 = new java.sql.Date(12345);
System.out.println(d1.getHours());
System.out.println(d2.getHours());
}
java.util.Date represents a specific instant in time with millisecond precision. It represents both date and time information without timezone. The java.util.Date class implements Serializable, Cloneable and Comparable interface. It is inherited by java.sql.Date, java.sql.Time and java.sql.Timestamp interfaces.
java.sql.Date extends java.util.Date class which represents date without time information and it should be used only when dealing with databases. 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.
It inherits all public methods of java.util.Date such as getHours(), getMinutes(), getSeconds(), setHours(), setMinutes(), setSeconds(). As java.sql.Date does not store the time information, it override all the time operations from java.util.Dateand all of these methods throw java.lang.IllegalArgumentException if invoked as evident from their implementation details.
I sometimes get confused by the different Date types in java and their practical usage. Here
i am trying to summarize my understanding
java.sql.Date :- A thin wrapper around a millisecond value that allows JDBC to identify this as an SQL DATE value
java.sql.Timestamp :- A thin wrapper around java.util.Date that allows the JDBC API to identify this as an SQL
TIMESTAMP value. It adds the ability to hold the SQL TIMESTAMP fractional seconds value, by allowing the specification
of fractional seconds to a precision of nanoseconds
I have seen most of the projects prefer Timestamp instead of date. I think the main reason for this is that Timestamp can hold the value till
nano seconds precision whereas Data can hold till milli seconds. Correct?
Calendar :- This class is designed for date manipulation for example :- for converting between a specific instant in time and a
set of calendar fields such as YEAR, MONTH, DAY_OF_MONTH, HOUR, and so on, and for manipulating the calendar fields, such as getting
the date of the next week.Though i dont know why this class is abstract when only one implementation exists i.e GregorianCalendar.
java.sql.Timestamp A thin wrapper around java.util.Date that allows the JDBC API to identify this as an SQL TIMESTAMP value.
If you check java.sql.Timestamp JavaDoc, it is very explicit that this class extends from java.util.Date (as java.sql.Date does). And in real world projects you must plain java.util.Date when storing the data in your database and mostly java.sql.Timestamp since it stores date and time value, while java.sql.Date just stores date value.
On the other hand, java.util.Calendar is abstract since there are more implementations of this apart from java.util.GregorianCalendar. If you see the code of Calendar#getInstance from HotSpot, you will see that it calls createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT)), and this method code uses 3 different calendars: BuddhistCalendar, JapaneseImperialCalendar and GregorianCalendar. This code is copied from JDK 7 source:
private static Calendar createCalendar(TimeZone zone,
Locale aLocale) {
Calendar cal = null;
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype == null) {
// Calendar type is not specified.
// If the specified locale is a Thai locale,
// returns a BuddhistCalendar instance.
if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
} else if (caltype.equals("japanese")) {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else if (caltype.equals("buddhist")) {
cal = new BuddhistCalendar(zone, aLocale);
} else {
// Unsupported calendar type.
// Use Gregorian calendar as a fallback.
cal = new GregorianCalendar(zone, aLocale);
}
return cal;
}
Now, why to work directly with Calendar instead of GregorianCalendar? Because you must work with abstract classes and interfaces when provided instead of working directly with implementations. This is better explained here: What does it mean to "program to an interface"?
Apart from this, if you will work with date and times, I recommend using a library like Joda-Time that already handles and solves lot of the problems with the current Java Date API and also provides methods to retrieve this date and times object in java.util.Date flavor.
java.time
You must first understand that those old date-time classes bundled with early versions of Java are a confusing mess of badly designed classes with hacks. They were the industry's first attempt at a sophisticated facility for date-time handling, and deserve credit for that. But ultimately they failed.
They have been supplanted by the new java.time framework built into Java 8 and later.
java.sql.Date — Use java.time.LocalDate instead
java.sql.Timestamp — Use java.time.Instant instead
java.util.Calendar & GregorianCalendar — Use java.time.ZonedDateTime instead
For date only, without time-of-day nor time zone, use java.time.LocalDate. For a moment on the timeline in UTC, use java.time.Instant. To assign a different time zone to an Instant, use java.time.ZonedDateTime.
Understand that an offset-from-UTC is merely a number of hours and minutes ahead of, or behind, UTC. A time zone is a history of past, present, and future changes to the offset used by the people of a certain region.
If you have a date-time value that has an offset-from-UTC rather than a time zone, represent that with the OffsetDateTime class. Then call its toInstant method to obtain a Instant object to be sent to your database in a column of type akin to the SQL-standard TIMESTAMP WITH TIME ZONE.
The SQL-standard type TIMESTAMP WITHOUT TIME ZONE (without, not with) purposely lacks any concept of time zone or offset-from-UTC. The legacy date-time classes had no way to represent such a value. Now, in java.time, we have LocalDateTime.
If your JDBC driver complies with JDBC 4.2 or later, you can directly exchange java.time objects with your database. No need to ever use the java.sql date-item types again.
myPreparedStatement.setObject( … , instant ) ;
And retrieval.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Adjust into the wall-clock time used by the people of a particular region (a time zone).
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
The first line in the original question contains the phrase " the different Date types in java and their practical usage"
The practical usage of the timestamp data type is exactly as it says - an timestamp used by the SQL to record a precise chronological value typically used for transactional ordering. Timestamps are usually only used internally... where nanoseconds count. There are use cases for external timestamp data, but they are relatively rare.
The date type handles 99% of external nonscientific data needs with millisecond precision.
I need to remove time from a Date Object. Here is my try,
Code:
System.out.println("date " + dbDate);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("formatter.format(dbDate) " + formatter.format(dbDate));
System.out.println("final " + formatter.parse(formatter.format(dbDate)));
Output:
date 2011-12-03 23:59:59.0
formatter.format(dbDate) 2011-12-03
final Sat Dec 03 00:00:00 IST 2011
I want to the final date to display in 2011-12-03. But after conversion toString() of that Date is in different format. I am missing something. Please help.
Update:
In my application, I have two different methods to get dbDate. EXPIRY_DATE column is type of DATE.
First query uses dbDate = (java.util.Date) rs.getDate("EXPIRY_DATE");.
For this dbDate, System.out.println("date " + dbDate); gives date 2011-12-03
Second query uses dbDate = rs.getTimestamp("EXPIRY_DATE");
For this dbDate, System.out.println("date " + dbDate); gives date 2011-12-03 23:59:59.0.
This is my problem. As I thought toString() was giving problem, I didn't mention the full problem.
Solution:
I did not have choices to avoid java.sql.Date as my application methods have multiple usages.
I tried the below and worked,
dbDate = new java.sql.Date(dbDate.getTime());
I need to remove time from a Date Object
You can't. The java.util.Date object contains both the date and time. Its toString() is also in a fixed format. If you want to represent it without time to humans, then you need to convert it to a String like as you already did. Or, if you intend to store it in the DB without the time (as the db part in the variable name dbDate suggests), then you need to convert it to java.sql.Date.
preparedStatement.setDate(1, new java.sql.Date(dbDate.getTime()));
// ...
Update as per your update, the ResultSet#getDate() returns an instance of java.sql.Date, not java.util.Date (but it is a subclass of java.util.Date, that's why the unnecessary cast worked; please note that casting is not the same as converting, a real conversion would be new java.util.Date(dbDate.getTime())). As you can read in the javadoc of the toString() method of java.sql.Date, it's indeed in yyyy-MM-dd format.
So, your concrete problem is that you're confusing java.sql.Date with java.util.Date and that you're misgrasping the internal workings of java.util.Date and been mislead by the toString() method. Everything is working as intented.
Related:
Handling MySQL datetimes and timestamps in Java
If what you want to do is remove the time part of the Date object:
Use a Calendar to remove the time part of your Date object. As pointed out in this question: Java Date cut off time information.
If you only want to obtain a String representation without the time part of the Date object:
You've got to use SimpleDateFormat.format(). You can't make Date.toString() return a different value, it will always use that pattern. Look at its source code.
When you last call formatter.parse() you get back a Date object; the concatenation then makes an implicit call to Date.toString(): the format returned by this call is the default for the locale set in the JVM.
What you must understand is that the Date object has no knowledge of the string representation, internally it's just an aggregate of inte
I have encountered similar problem for those who encounters the same problem as mine I write this entry:
The problem is the date value that is taken from database and passed to the web client is in format yyyy-mm-dd but in the application for the first entry there is not database value so we create date object and passed the value to web client which gives us timestamp value. The value that will be passed to web client must be in date format so SimpleDateFormat is not a good choice for me
So from this post ı understand the difference of java.sql.date and java.util.date and then create first object as
Date date = new java.sql.Date(1430454600000L);
which gives yyyy-mm-dd value for toString method.
java.time
The Answer by BalusC is correct: You cannot eliminate a time-of-day from a class object defined to hold a date plus a time-of-day.
Also, you are using troublesome old classes (java.util.Date and java.sql.Date) that are now obsolete, supplanted by the java.time classes.
Instead, use a date-only class for a date-only value. The LocalDate class represents a date-only value without time-of-day and without time zone. The java.sql.Date pretends to do the same, but actually does carry a time of day due to very poor design decision of inheriting from java.util.Date. Avoid java.sql.Date, and use only java.time.LocalDate instead.
You are starting with a java.util.Date object apparently. That represents a point on the timeline in UTC with a resolution in milliseconds. So using that to determine a date requires a time zone. The LocalDate class represents a date-only value without time-of-day and without time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment, so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the JVM’s current default is applied implicitly. Better to be explicit, as the default may be changed at any moment during runtime by any code in any thread of any app within the JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
To get a date-only value from your java.util.Date, first convert to its java.time replacement, Instant. To convert back and forth, call new methods added to the old classes.
Instant instant = myJavaUtilDate.toInstant() ;
That value is in UTC by definition. Apply your desired time zone (ZoneId) to generate a ZonedDateTime.
ZonedDateTime zdt = instant.atZone( z ) ;
Finally, extract your desired LocalDate object from ZonedDateTime.
LocalDate ld = zdt.toLocalDate() ;
As of JDBC 4.2 and later, you can directly exchange java.time classes with your database. So no need to use the the java.sql classes such as java.sql.Date and java.sql.Timestamp.
myPreparedStatement.setObject( … , ld ) ;
Retrieval.
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.