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

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

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

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

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

  • Запросите мое соединение MySQL для его часового пояса и проанализируйте входную дату в этом часовом поясе
  • Обработать дату полностью как строку yyyy-MM-dd

6 ответов

Решение

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

java.time

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

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

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

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

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

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

Он отображается с типом ANSI SQL. Отображение типов ANSI SQL с java.timeВ типы были изображены следующим образом :

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

Ниже приведен пример кода для вставки в (который имеет 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 columm index of columnfoo is 1
    LocalDate localDate = rs.getObject(1, LocalDate.class));
    System.out.println(localDate);
}
rs.close();
st.close();

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


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

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

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();
    }

Класс java.sql.Date существует именно по этой причине, к сожалению, его очень неудобно использовать, так как он просто расширяется java.util.Date и вам нужно вручную установить время на ноль.

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

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

Поскольку вы указываете, что ваш сервер и база данных находятся в разных часовых поясах, мне кажется, что у вас есть проблемы с интерпретацией. Если вы обнулите дату или сохраните ее как MM-dd-YYYY, что равносильно тому, сохраняете ли вы дату сервера или дату в БД? Если это 23:00 на сервере и 1:00 следующего дня на БД, какая дата является "правильной"? Когда вы посмотрите на даты через год, вспомните ли вы, от каких часовых поясов машин зависят даты?

Эти соображения могут быть немного излишними. Но почему бы просто не хранить даты в GMT (обнулять секунды, если хотите)?

Другие вопросы по тегам