Home » Java » Java: How to save a date into MySQL without timezone conversion?-Exceptionshub

Java: How to save a date into MySQL without timezone conversion?-Exceptionshub

Posted by: admin February 25, 2020 Leave a comment

Questions:

I’m saving a birthday with Java8 + MyBatis + MySQL5.7. My code is like this:

// 'birthday' is a DATE column
@Insert("insert into MyTable (id, birthday) values (#{id"}, #{date})")
void saveDate(int id, LocalDate date);

@Select("select birthday from MyTable where id=#{id"}")
LocalDate loadDate(@Param("id") int id);

And the code to save and load a date.

final int id = 1;
final LocalDate date = LocalDate.of(2020, 1, 3);

repo.saveDate(id, date);
LocalDate dateFromDB = repo.loadDate(id);

System.out.println(date);        // 2020-1-3
System.out.println(dateFromDB);  // 2020-1-2

I noticed that the value of “dateFromDB” is always one day before the “date”. I think it’s caused by the timezone conversion. Since my application is at UTC+8, while DB is using UTC. By using DATETIME instead of DATE, I can see the date ‘2020-1-3’ is saved as ‘2020-1-2 16:0:0’.

So the question is how to avoid timezone conversion when saving a date like a birthday?

—————– Updated 2020-02-20 ————————————

So far, I found 4 ways to do it:

  1. Add a parameter noTzConversionForDateType=true to avoid the conversion only for date type. This is the best way but it only applies to version 5.1. I really don’t understand why it’s removed from version 8.0… (document)
  2. Add a parameter serverTimezone=GMT%2B8 use the client timezone (UTC+8) as the server timezone. The conversion still happens but it changes nothing. The problem is TIMESTAMP columns would also be affected.
  3. Add a parameter -Duser.timezone=UTC to JVM. The problem is similar to #2.
  4. Send LocalDate value as String. Update the saveDate function as below (only change the type from LocalDate to String):
@Insert("insert into MyTable (id, birthday) values (#{id"}, #{date})")
void saveDate(int id, String date);

Then convert the date to a String before calling saveDate:

String dateString = date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
repo.saveDate(id, dateString);

Note that the data type in DB is still DATE. The benefit of this way is only the birthday column will be affected. The problem is that the code is not elegant and it might lead to performance issues for a large data set.

How to&Answers:

Assuming you are connecting to MySQL using a connection string like the following

jdbc:mysql://localhost/mydb?useLegacyDatetimeCode=false&serverTimezone=YOUR_TZ

Should do some conversion for you