Подтвердить что ты не робот

Преобразование Joda LocalTime в java.sql.Date

Чтобы сделать запрос JDBC, мне нужно передать ему дату. Дата хранится в Date поле типа базы данных PostgreSql, которая представляет определенный день без какого-либо времени.

Поскольку мне нужна только дата, я решил использовать конкретный объект, который представляет только дату без времени, которая LocalDate из пакета Joda-Time. Я подумал, что это важно, потому что, если бы я использовал объект DateTime, он имел бы данные избыточного времени, а также может привести к ошибкам в конце летнего времени, когда часы будут отложены назад на один час (хотя ситуация беспрецедентно редка, невозможно).

Но когда я начал пытаться скомпоновать объект LocalDate с принятыми аргументами метода preparedStatement.setDate, я не нашел подходящего способа сделать это.

setDate принимает java.sql.Date как параметр. И единственным вариантом для построения объекта java.sql.Date является передача его в миллисекундах.

Но это побеждает все цели использования LocalDate из пакета Joda-Time, так как при этом преобразовании мы возвращаемся к миллисекундам, и, хотя эти преобразования происходят, часы могут быть возвращены на один час и изменить дату на предыдущий дата.

Итак, теперь у меня есть эта строка в моем коде:

preparedStatement.setDate(1, new java.sql.Date(localDate.toDate().getTime()));

Но это лучший способ конвертировать LocalDate в формат setDate?

Являются ли мои проблемы связанными с летним временем и соответствующими тактовыми сдвигами?

Есть ли лучший способ передать дату (и только дату без времени) в JDBC подготовленное утверждение?

4b9b3361

Ответ 1

Не следует использовать вашу технику, потому что все проблемы с часовым поясом будут учитываться с помощью LocalDate#toDate. Полученный миллисекундный момент, который у вас есть, не зависит от контекста: он однозначно относится к часовому поясу, действительному в тот момент времени в пределах локали, которую вы используете для преобразования. Другими словами, если вы повторите преобразование одного и того же миллисекундного значения в течение года, вы будете последовательно получать тот же самый ответ, даже если изменения в часовом поясе изменяются для вашего места тем временем, поскольку JDK ссылается на базу данных, документирующую полную историю всех изменений во времени в мире.

При анализе этих проблем важно помнить, что ваш текущий часовой пояс не влияет на преобразование, которое параметризуется вашей локалью и разрешает часовой пояс только в контексте моментального преобразования.

Я всем сердцем сочувствую тошноте, в которой вы падали все это: она превращает простую и напряженную операцию в сложный лабиринт расчетов, который ничего не делает, кроме как предлагать проблемы. Надеемся, что все будет иметь положительный поворот с Java 8 и новым (да, снова!) API Дата/Время, основанным на JodaTime.

Ответ 2

Сегодня у меня такая же проблема. Я использую JDK 8. Потратив несколько часов на поиск, я нашел ответ на Java SE 8 Documentation. Это решение:

statement.setDate(5, java.sql.Date.valueOf(personToUpdate.getBirthday()));
Оператор

является экземпляром PreparedStatement. "personToUpdate.getBirthday()" - тип LocalDate.

Ответ 3

Поскольку org.joda.time.toDateMidnight() и org.joda.time.toDateMidnight(зона DateTimeZone) устарели, это решение отлично работает для меня.

Мой типичный класс, который нужно сохранить:

    ...
    import org.joda.time.LocalDate;
    ...

    public class MyObject implements Serializable {

      ...
      private LocalDate startDate;

      ...
      private EndDate startDate;


      // Getters and Setters
      ...

      ...
    }

Im мой другой класс, где я сохраняю startDate, у меня есть:

    myObject.setStartDate(new LocalDate(myObject.getStartDate().toDateTimeAtStartOfDay(DateTimeZone.getDefault())));

Ответ 4

java.time

В Java 8 и более поздних версиях новая структура java.time теперь встроена. Этот преемник Joda-Time определяется JSR 310 и расширен ThreeTen-Extra.

Мы надеемся, что в конечном итоге мы увидим, что драйверы JDBC обновлены, чтобы напрямую обрабатывать новые типы java.time. Но до тех пор нам по-прежнему нужны типы java.sql. *. К счастью, новые методы были добавлены для удобного преобразования между типами.

Для даты только, без времени суток и без часового пояса, тип Java LocalDate (аналогично к Joda-Time).

Что касается вашей озабоченности по поводу часового пояса, связанного с LocalDate, это имеет значение, когда вы переводите дату только на дату-время, на мгновение на временной шкале. Только для даты - это просто неопределенная идея, без реального смысла, пока вы не переведете ее на временной промежуток времени на шкале времени (от полуночи до полуночи в какой-то часовой пояс). Например, для определения "сегодня" требуется часовой пояс. В java.time мы используем класс ZoneId.

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );

Если этот параметр опущен, ваш текущий часовой пояс JVMs используется для определения даты. Другими словами, следующие две строки эквивалентны.

LocalDate today = LocalDate.now();
LocalDate today = LocalDate.now( ZoneId.systemDefault() );

Я считаю, что первая версия now() является плохим выбором дизайна API. Это неявное применение текущего часового пояса JVMs по умолчанию не приводит к путанице, ошибкам и страданиям среди наивных разработчиков. Во-первых, текущее значение JVM зависит от машины, настроек хост-системы и системных администраторов. Хуже того, текущее значение по умолчанию JVM может измениться в любой момент, во время выполнения, любым кодом в любом потоке любого приложения в этой JVM. Поэтому лучше всего указать желаемый/ожидаемый часовой пояс.

Теперь, когда у нас есть объект java.time для "today", как его получить в базе данных? Через объект java.sql.Date. В Java 8 этот старый класс получил новые методы, toLocalDate и valueOf. Последний является нашим мостом от типа java.time до типа java.sql.

java.sql.Date sqlToday = java.sql.Date.valueOf( today );

Оттуда выполняется обычная обработка PreparedStatement.

myPreparedStatement.setDate( 1 , sqlToday );

Date-only vs Date-time

Возможно, у вас есть опасения по поводу такой даты - только для удовлетворения потребностей вашего бизнеса.

Если вам нужно знать, например, если контракт был подписан к концу дня по юридическим причинам, то дата-только является неправильным типом данных, если это означает определенный момент, такой как удар в полночь в Монреале, Новый день наступает раньше в Париже, чем в Монреале, поэтому "сегодня" в Париже "вчера" в Монреале. Если конечный срок вашего контракта определяется юридически в конце дня в Монреале, вы должны применить часовой пояс. Чтобы применить часовой пояс, вы должны иметь дату-время, а не только дату. Вы можете сделать переход из LocalDate в ZonedDateTime, но я считаю это слишком сложным. Ваша база данных должна была использовать тип даты с начала.

В Postgres тип даты-времени означает тип TIMESTAMP WITH TIME ZONE. Это имя является неправильным, поскольку часовой пояс фактически не хранится. Подумайте об этом как о "временной отметке с уважением к часовому поясу". Postgres использует любое смещение-from- UTC или информацию о часовом поясе, сопровождающую входящие данные, для настройки на UTC, и эта информация о смещении/зоне затем отбрасывается. Другой тип TIMESTAMP WITHOUT TIME ZONE полностью игнорирует информацию о смещении/зоне, и это неправильное поведение для большинства бизнес-приложений.

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

Другими словами, в отношении комментария авторов вопросов:

И я ожидал, что должен быть способ работать с датами, не прибегая к экземплярам времени.

Нет, я бы сказал, что просит неприятностей. Если момент имеет значение, используйте дату-время, а не только дату.

Ответ 5

Попробуйте использовать LocalDate#toDateMidnight(), который устанавливает время на 0, а затем DateMidnight#toDate().

Date date = localDate.toDateMidnight().toDate();

Или, если вы используете JodaTime 1.5 или новее, используйте LocalDate#toDateTimeAtStartOfDay(), а затем DateTime#toDate()

Надеюсь, что поможет