Конвертер jooq: из java.sql.Date в java.time.LocalDate

Я пытался написать Converter<java.sql.Date, java.time.LocalDate>, но не могу заставить его работать со всеми настройками часового пояса.

Идея:

  • если клиентский код имеет LocalDate, скажем, 20 августа 2014 г., и сохраняет его в БД, он должен отображаться в БД как 20 августа 2014 г., независимо от часового пояса клиента.
  • если БД содержит дату 20 августа 2014 года, клиент должен получить LocalDate 20 августа 2014 года, независимо от часового пояса клиента.

Мой тест:

@Test public void dateConverter() {
  for (int offset = -12; offset <= 12; offset++) {
    TimeZone localTz = TimeZone.getTimeZone(ZoneOffset.ofHours(offset));
    TimeZone.setDefault(localTz);
    LocalDate ld = LocalDate.now();

    sql.insertInto(DATE_TEST).set(new DateTestRecord(ld)).execute();
    LocalDate savedLd = sql.selectFrom(DATE_TEST).fetchOne(DATE_TEST.DATE_);
    assertEquals(savedLd, ld, "offset=" + offset);
    sql.delete(DATE_TEST).execute();
  }
}

Мой преобразователь:

public class DateConverter implements Converter<Date, LocalDate>{
  @Override public LocalDate from(Date date) { return date.toLocalDate(); }
  @Override public Date to(LocalDate ld) { return Date.valueOf(ld); }
  @Override public Class<Date> fromType() { return Date.class; }
  @Override public Class<LocalDate> toType() { return LocalDate.class; }
}

Пробовал разные варианты, но ничего не помогло...


person assylias    schedule 21.08.2014    source источник


Ответы (1)


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

Таким образом, код в вопросе работает для конвертера Date to LocalDate (за исключением того, что он должен принимать значение null). Окончательный версия:

public class DateConverter implements Converter<Date, LocalDate> {
  @Override public LocalDate from(Date date) { return date == null ? null : date.toLocalDate(); }
  @Override public Date to(LocalDate ld) { return ld == null ? null : Date.valueOf(ld); }
  @Override public Class<Date> fromType() { return Date.class; }
  @Override public Class<LocalDate> toType() { return LocalDate.class; }
}

Преобразователь времени с часовым поясом в OffsetTime можно сделать аналогичным образом:

public class TimeConverter implements Converter<Time, OffsetTime> {
  @Override public OffsetTime from(Time time) {
    return time == null ? null : OffsetTime.ofInstant(Instant.ofEpochMilli(time.getTime()), ZoneOffset.systemDefault());
  }
  @Override public Time to(OffsetTime offsetTime) {
    return offsetTime == null ? null : new Time(offsetTime.atDate(LocalDate.ofEpochDay(0)).toInstant().toEpochMilli());
  }
  @Override public Class<Time> fromType() { return Time.class; }
  @Override public Class<OffsetTime> toType() { return OffsetTime.class; }
}
person assylias    schedule 21.08.2014
comment
Драйвер JDBC кэширует часовой пояс: Хм, а какой драйвер JDBC делает это? Обратите внимание, что обсуждение TIMESTAMP WITH TIMEZONE типов данных продолжается в группе пользователей jOOQ. . - person Lukas Eder; 22.08.2014
comment
У меня не получилось заставить работать аналогичный конвертер с отметками времени - может быть связано? (это последний драйвер postgres) - person assylias; 22.08.2014
comment
Вы уверены, что драйвер JDBC действительно кэширует часовой пояс? Я ожидаю, что драйвер использует текущий часовой пояс по умолчанию, когда он устанавливает соединение и устанавливает его для соединения. Этот параметр часового пояса остается в силе до тех пор, пока клиент подключения не изменит часовой пояс с помощью SET TIME ZONE и т.п. Простое изменение часового пояса по умолчанию вашей JRE не повлияет на установленное соединение и сеанс с БД в этом случае, но часовой пояс вообще не кэшируется. - person Thorsten Schöning; 22.08.2014
comment
Просто во время установки сеанса драйвер сообщает текущий часовой пояс, а затем клиент соединения может изменить часовой пояс по мере необходимости. В противном случае драйвер будет просто сообщать вещи и отправлять команды, используя ваш сеанс непредсказуемым образом. Я предполагаю, что ваш тест будет успешным, если вы также измените часовой пояс сеанса с помощью команд вашего клиента базы данных. - person Thorsten Schöning; 22.08.2014
comment
@ThorstenSchöning Вот что я имел в виду: часовой пояс JVM извлекается при установке соединения, и если после этого часовой пояс JVM изменяется, это не влияет на это соединение, но новое соединение будет учитывать это изменение. Благодарю за разъяснение. - person assylias; 22.08.2014
comment
@LukasEder К вашему сведению, проблема с конвертером timestamptz заключалась в том, что я не избегал пробелов в timestamp\ with\ time\ zone: stackoverflow.com/q/33173842/829571 . Это специально? (В обычной Java "timezone with time zone".matches("timezone with time zone") верно независимо от того, экранированы ли пробелы или нет) - person assylias; 20.10.2015
comment
@assylias: я заметил другую проблему и прокомментирую ее. Это нехорошо - мы проглядели этот эффект при включении флага COMMENTS в регулярных выражениях Java... - person Lukas Eder; 21.10.2015