Вопрос-ответ

Dates with no time or timezone component in Java/MySQL

Даты без компонента времени или часового пояса в Java / MySQL

Мне нужно иметь возможность хранить дату (год / месяц / день) без компонента времени. Это абстрактное понятие даты, такое как день рождения - мне нужно представить дату в году, а не конкретный момент времени.

Я использую Java для анализа даты из некоторого входного текста, и мне нужно сохранить ее в базе данных MySQL. Независимо от того, в каком часовом поясе находится база данных, приложение или любой клиент, все они должны видеть один и тот же год / месяц / день.

Мое приложение будет работать на компьютере с системным часовым поясом, отличным от системного часового пояса сервера базы данных, и я не могу контролировать ни то, ни другое. Есть ли у кого-нибудь элегантное решение для обеспечения правильного сохранения даты?

Я могу придумать эти решения, ни одно из которых не кажется очень приятным:


  • Запросите у моего соединения MySQL его часовой пояс и проанализируйте входную дату в этом часовом поясе

  • Полностью обработайте дату в виде строки гггг-ММ-дд

Переведено автоматически
Ответ 1

java.time

Это действительно было проблемой с java.util API даты и времени, пока JSR-310 не был включен в Java SE 8. java.util.Date Объект не является реальным объектом даты и времени, как современные типы даты и времени; скорее, он представляет количество миллисекунд, прошедших со стандартного базового времени, известного как "эпоха", а именно January 1, 1970, 00:00:00 GMT (или UTC). Когда вы печатаете объект из java.util.Date, его toString метод возвращает дату-время в часовом поясе JVM, вычисленное на основе этого значения в миллисекундах.

Описанная вами проблема была решена с помощью современного API для определения даты и времени* где у нас есть LocalDate который представляет собой просто дату. В следующем предложении руководства по Oracle прекрасно описывается его назначение:


Например, вы могли бы использовать объект LocalDate для представления даты рождения, потому что большинство людей отмечают свой день рождения в один и тот же день, независимо от того, находятся ли они в своем городе рождения или по всему миру по другую сторону международной линии дат.


Пару страниц спустя это снова описывается следующим образом:


LocalDate представляет год, месяц, день в календаре ISO и полезен для представления даты без времени. Вы можете использовать LocalDate для отслеживания важного события, такого как дата рождения или свадьбы.


Для какого типа данных мне следует использовать LocalDate?

Он сопоставляется с DATE типом ANSI SQL. Сопоставление типов ANSI SQL с java.time типами было показано следующим образом в этой статье Oracle:































ANSI SQLJava SE 8
ДатаLocalDate
ВРЕМЯLocalTime
ВРЕМЕННАЯ МЕТКАLocalDateTime
ВРЕМЯ С ЧАСОВЫМ ПОЯСОМOffsetTime
ВРЕМЕННАЯ МЕТКА С ЧАСОВЫМ ПОЯСОМOffsetDateTime

Как использовать это в JDBC?

Ниже приведен пример кода для вставки LocalDate в columnfoo (который имеет DATE тип):

LocalDate localDate = LocalDate.now();
PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, localDate);
st.executeUpdate();
st.close();

Ниже приведен пример кода для извлечения LocalDate из 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();

Узнайте больше о современном API для определения даты и времени* из Trail: Date Time.


* По какой-либо причине, если вам нужно придерживаться Java 6 или Java 7, вы можете использовать ThreeTen-Backport, который переносит большую часть функциональности java.time в Java 6 и 7. Если вы работаете над проектом Android и ваш уровень API Android по-прежнему не совместим с Java-8, проверьте API Java 8 +, доступные через десугаринг и Как использовать ThreeTenABP в Android Project.

Ответ 2

Вы могли бы обнулить все данные о времени / часовом поясе:

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();
}
Ответ 3

Я пришел к выводу, что лучшим способом в моем текущем приложении (простой утилите, использующей jdbc напрямую) было вставить непосредственно в виде строки. Для приложения Hibernate большего размера я мог бы написать свой собственный пользовательский тип. Не могу поверить, что кто-то еще не решил эту проблему в каком-нибудь общедоступном коде...

Ответ 4

Не могли бы вы просто использовать тип MySQL DATE в своей таблице, а затем, по сути, использовать форматированную строку в вашем операторе insert? Я думаю, что что-то подобное позволит избежать каких-либо корректировок часового пояса.

INSERT INTO time_table(dt) VALUES('2008-12-31')
java sql date