Многое было сказано (и написано на SO) на части предмета, но не на всеобъемлющем, полном пути, поэтому у нас может быть одно "окончательное, охватывающее все" решение для всех, кто будет использовать.
У меня есть БД Oracle, где я храню дату + время + часовой пояс глобальных событий, поэтому оригинальная TZ должна быть сохранена и доставлена на клиентскую сторону по запросу. В идеале, он может хорошо работать с использованием стандартного формата ISO 8601 "T", который может быть хорошо сохранен в Oracle с использованием типа столбца "TIMESTAMP WITH TIME ZONE" ( "TSTZ" ).
Что-то вроде '2013-01-02T03: 04: 05.060708 + 09: 00'
Все, что мне нужно сделать, это получить вышеуказанное значение из БД и отправить его клиенту без каких-либо манипуляций.
Проблема в том, что Java не имеет поддержки ISO 8601 (или любого другого типа даты + времени + nano + tz), и ситуация еще хуже, потому что драйвер JDBC Oracle (ojdbc6.jar) имеет еще меньшую поддержку TSTZ ( в отличие от Oracle DB, где он хорошо поддерживается).
В частности, здесь я не должен или не могу делать:
- Любое отображение из TSTZ в java Date, Time, Timestamp (например, через вызовы JDBC getTimestamp()) не будет работать, потому что я теряю TZ.
- Драйвер Oracle JDBC не предоставляет какой-либо метод для сопоставления TSTZ с java-объектом Calendar (это может быть решение, но его там нет)
- JDBC getString() может работать, но драйвер Oracle JDBC возвращает строку в формате "2013-01-02 03: 04: 05.060708 +9: 00", которая не соответствует стандарту ISO 8601 (нет "T" ), отсутствие заднего 0 в TZ и т.д.). Более того, этот формат жестко закодирован (!) Внутри реализации драйвера JDBC Oracle, который также игнорирует настройки локали JVM и настройки форматирования сеанса Oracle (т.е. Игнорирует переменную сеанса NLS_TIMESTAMP_TZ_FORMAT).
- JDBC getObject() или getTIMESTAMPTZ() возвращают объект Oracle TIMESTAMPTZ, который практически бесполезен, поскольку он не имеет никакого преобразования в Календарь (только дата, время и временная метка), поэтому снова мы теряем информацию TZ.
Итак, вот варианты, с которыми мне осталось:
-
Используйте JDBC getString() и стройте-манипулируйте им, чтобы исправить и сделать ISO 8601 совместимым. Это легко сделать, но есть опасность умереть, если Oracle изменит внутреннее жестко закодированное getString() форматирование. Кроме того, если посмотреть на исходный код getString(), похоже, что использование getString() также приведет к некоторому снижению производительности.
-
Используйте Oracle DB toString для преобразования: "SELECT TO_CHAR (tstz...) EVENT_TIME...". Это прекрасно работает, но имеет 2 основных недостатка:
- Каждый SELECT теперь должен включать TO_CHAR-вызов, который является головной болью для запоминания и записи
- Каждый SELECT теперь должен добавить столбец EVENT_TIME "псевдоним" (необходимо, например, для сериализации результата для Json автоматически)
.
-
Используйте класс Java TIMESTAMPTZ Java и извлеките соответствующее значение вручную из его внутренней (документированной) структуры массива байтов (т.е. реализуйте мой собственный метод toString(), который Oracle забыл реализовать там). Это опасно, если Oracle изменяет внутреннюю структуру (маловероятно) и требует относительно сложной функции для ее реализации и поддержки.
-
Я надеюсь, что там 4-й, отличный вариант, но из просмотра по всему Интернету и SO - я не вижу ничего.
Идеи? Мнения?
UPDATE
Много идей было дано ниже, но похоже, что нет правильного способа сделать это. Лично я считаю, что использование метода # 1 является самым коротким и наиболее читаемым способом (и поддерживает достойную производительность, не теряя субмиллисекунд или SQL-запросы).
Вот что я в конечном итоге решил использовать:
String iso = rs.getString(col).replaceFirst(" ", "T");
Спасибо за хорошие ответы всем,
Б.