I need to be able to store a date (year/month/day) with no time component. It's an abstract concept of a date, such as a birthday - I need to represent a date in the year and not a particular instant in time.
I am using Java to parse the date from some input text, and need to store in a MySQL database. No matter what timezone the database, application, or any client is in, they should all see the same year/month/day.
My application will run on a machine with a different system timezone from the database server, and I don't have control over either. Does anyone have an elegant solution for ensuring I store the date correctly?
I can think of these solutions, neither of which seems very nice:
On the other hand, when you pass a java. sql. Date to the database, the driver will use the default time zone to separate the date and time components from the millisecond value. If you use 0 and you're in UTC+X, the date will be 1970-01-01 for X>=0 and 1969-12-31 for X<0.
MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME .) By default, the current time zone for each connection is the server's time.
You can use str_to_date to convert a date string to MySQL's internal date format for inserting.
SQL Date Data Types MySQL comes with the following data types for storing a date or a date/time value in the database: DATE - format YYYY-MM-DD. DATETIME - format: YYYY-MM-DD HH:MI:SS. TIMESTAMP - format: YYYY-MM-DD HH:MI:SS.
java.time
This was indeed a problem with java.util
date-time API until JSR-310 was incorporated in Java SE 8. The java.util.Date
object is not a real date-time object like the modern date-time types; rather, it represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT
(or UTC). When you print an object of java.util.Date
, its toString
method returns the date-time in the JVM's timezone, calculated from this milliseconds value.
The problem that you have described was addressed with the modern date-time API* where we have LocalDate
that represents just a date. In the following sentence, the Oracle tutorial describes its purpose nicely:
For example, you might use a LocalDate object to represent a birth date, because most people observe their birthday on the same day, whether they are in their birth city or across the globe on the other side of the international date line.
A couple of pages later, it describes it again as follows:
A LocalDate represents a year-month-day in the ISO calendar and is useful for representing a date without a time. You might use a LocalDate to track a significant event, such as a birth date or wedding date.
LocalDate
?It maps with DATE
ANSI SQL type. The mapping of ANSI SQL types with java.time
types have been depicted as follows in this Oracle's article:
ANSI SQL | Java SE 8 |
---|---|
DATE | LocalDate |
TIME | LocalTime |
TIMESTAMP | LocalDateTime |
TIME WITH TIMEZONE | OffsetTime |
TIMESTAMP WITH TIMEZONE | OffsetDateTime |
Given below is a sample code to insert a LocalDate
into columnfoo
(which is of DATE
type):
LocalDate localDate = LocalDate.now();
PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, localDate);
st.executeUpdate();
st.close();
Given below is a sample code to retrieve a LocalDate
from columnfoo
:
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM mytable WHERE <some condition>");
while (rs.next()) {
// Assuming the column index of columnfoo is 1
LocalDate localDate = rs.getObject(1, LocalDate.class));
System.out.println(localDate);
}
rs.close();
st.close();
Learn more about the modern date-time API* from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
You could zero out all time/timezone stuff:
public static Date truncateDate(Date date)
{
GregorianCalendar cal = getGregorianCalendar();
cal.set(Calendar.ZONE_OFFSET, 0); // UTC
cal.set(Calendar.DST_OFFSET, 0); // We don't want DST to get in the way.
cal.setTime(date);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.AM_PM, Calendar.AM);
return cal.getTime();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With