2017-01-25 13 views
1

Я знаю, что есть много разных учебников по временному преобразованию, но это меня очень смутило. Моя задача - прочитать UTC DATE из Oracle DB и преобразовать его в BST-время (в более удобном для чтения формате).Oracle Timestamp to BST преобразование времени

Факты:

  • полей в БД имеет DATE типа.
  • Когда я выполняю запрос SELECT, он возвращает 2011-07-12 15:26:07 результат.
  • Я находится в Польше, поэтому в июле TimeZone здесь UTC+2

Что происходит:

На стороне Java я использую «классическое» подключение JDBC к БД.

Когда я выполняю Timestamp timestampDate = resultSet.getTimestamp(COLUMN_NAME) я получаю результат ... но ...

System.out.println(timestampDate) печатает на консоль 2011-07-12 15:26:07.0 (которая похожа на то, что я вижу в инструменте DB.

System.out.println(timestampDate.getTime()); печатает на консоль 1310477167000 (который интересно, потому что в соответствии с ms to date преобразователем я нашел в Интернете, это в основном 2011-07-12 13:26:07.0 (2ч раньше - что-то может быть связана с польским часовым поясом на тот момент)

Wh ен я выполнить преобразование в соответствии с этим кодом:

ukDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ukDateFormatter.setTimeZone(TimeZone.getTimeZone("BST")); return ukDateFormatter.format(timestampDate.getTime());

я 2011-07-12 19:26:07, которые я не могу объяснить.

я также пытался это

GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(timestampDate); calendar.setTimeZone(TimeZone.getTimeZone("BST")); return ukDateFormatter.format(calendar.getTime());

с тем же результатом.

Вопрос

Как правильно прочитать ДАТУ из БД Oracle в формате «часовой пояс агностик» и превратить его в BST?

+0

Я смущен. Если поле (действительно, «столбец») в БД имеет тип данных «DATE», то это - вообще - ** не ** a «ДАТА UTC». Тип данных DATE в Oracle не имеет привязанного часового пояса. Если вы, находясь в Польше, введите значения «insert into table_t (date_column)» (to_date ('2015-12-11 10:00:00', 'yyyy-mm-dd hh24: mi: ss)) 'в вашу базу данных, и я делаю то же самое из США, то есть дату, которая будет храниться для нас обоих, и это будет дата БЕЗ часового пояса. Пожалуйста, уточните, что вы имеете в виду; когда вы читаете 'DATE', как вы решаете, в какой часовой зоне это действительно принадлежит? – mathguy

+0

Предположим, что столбец DATE не привязан к временной зоне: - тогда почему после прочтения в «Timestamp» в Java и выполнения 'getTime' я получаю запутанный результат? - так что, как вы говорите, я должен быть в состоянии прочитать дату из БД и сделать простое преобразование с календарем ... но каким-то образом я не могу сделать это так прямолинейно. – amerykanin

+1

Примечание. Java будет интерпретировать '' BST '', как если бы вы имели в виду «стандартное время Бангладеш». Он буквально отображает его на «Азия/Дакка». Не используйте сокращения времени в качестве идентификаторов. См. Также http://stackoverflow.com/a/41683097/634824 –

ответ

0

То, что я сделал, и это, кажется, работает для меня:

ukDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); ukDateFormatter.setTimeZone(TimeZone.getTimeZone("Europe/London"));

и выполнение:

Timestamp timestampDate = rs.getTimestamp(...); DateTime dateTime = new DateTime(timestampDate).withZoneRetainFields(DateTimeZone.UTC); System.out.println(ukDateFormatter.format(dateTime.getMillis()));

печатает:

2011-07-12 16:26:07 от входа 2011-07-12 15:26:07

Почему здесь произошло?

Что было настолько проблематичным здесь, является то, что rs.getTimestamp(...) возвращался дату из базы данных «как есть» (с типом DATE колонки не сохраняет часовой пояс) неявно, но добавлял некоторую информацию о моей временной зоне - которая Я не хотел.

Простым решением было использовать joda и создать новый объект, сохраняя значения, но изменяя часовой пояс до UTC. С этого момента конвертация с SimpleDateFormat довольно проста.

1

Вот способ сделать это в стороне базы данных:

with dates as (select to_date('01/07/2016 10:39:29', 'dd/mm/yyyy hh24:mi:ss') dt from dual union all 
       select to_date('01/02/2016 09:18:41', 'dd/mm/yyyy hh24:mi:ss') dt from dual) 
select dt, 
     cast(dt AS TIMESTAMP) utc_dt_ts, 
     from_tz(cast(dt AS TIMESTAMP), 'UTC') AT time zone 'Europe/London' dt_as_ts_bst, 
     cast(from_tz(cast(dt AS TIMESTAMP), 'UTC') AT time zone 'Europe/London' AS DATE) dt_at_bst 
from dates; 

DT     UTC_DT_TS           DT_AS_TS_BST          DT_AT_BST 
------------------- ------------------------------------------------- ------------------------------------------------- ------------------- 
01/07/2016 10:39:29 01-JUL-16 10.39.29.000000       01-JUL-16 11.39.29.000000 EUROPE/LONDON   01/07/2016 11:39:29 
01/02/2016 09:18:41 01-FEB-16 09.18.41.000000       01-FEB-16 09.18.41.000000 EUROPE/LONDON   01/02/2016 09:18:41 

В четвертой колонке (dt_at_bst) является тот, который показывает, как взять дату и превратить его в другую дату в BST. Он делает это, введя дату как временную метку, а затем сообщив Oracle, чтобы рассматривать ее как отметку времени в UTC и выводить временную метку для региона «Европа/Лондон». Указание области, подобной этой (вместо того, чтобы передавать определенный часовой пояс +01: 00) означает, что результирующая временная метка будет знать о летнем свете. Указание области в виде трехбуквенного ярлыка не рекомендуется, так как это может представлять собой более одного региона - например, BST может быть британским летним временем или стандартным временем Беринга; обе очень разные вещи!

Я предположил, что на BST вы имеете в виду British Summer Time, поэтому я указал область для отметки времени, которую нужно переместить как Europe/London. Вам нужно будет отрегулировать это, если применимо, если вам нужен другой часовой пояс.

Я включил в свои образцы данные о зиме и дате лета, чтобы показать вам эффекты отбрасывания их в BST - ожидается, что летнее время будет изменено, а зимнее время - нет.

+0

ОП упомянул, что он/она находится в Польше (UTC + 2). Я не знаю, насколько это актуально. Если это так, возможно, он/она не имел в виду вход (дата, поэтому без привязки к ней информации о часовом поясе) должна интерпретироваться как UTC. Надеюсь, он/она сможет адаптировать ваш ответ, если ему нужно перевести с польского летнего часового пояса на все остальное. – mathguy

+0

@mathguy да, это не было впечатляюще ясно из поста OP, что они имели в виду; Я взял пунт и догадался, что даты хранятся в UTC, основываясь на том, что они хотят «читать UTC DATE из Oracle DB». Я думаю, они упомянули, что они были в Польше, потому что они получали результат на два часа раньше даты, что, по их мнению, могло быть результатом их пребывания в Польше. – Boneist

+0

Это приемлемое решение, но имеет некоторые недостатки для более сложных глобальных приложений. В качестве примера, когда сервер приложений должен предоставлять точное время в часовом поясе пользователя, но все три часовых пояса различаются (сервер БД, сервер приложений и пользователи) для таких случаев, как «Время отключения» в финансовом мире. – Vadim

0

На самом деле речь идет не о Oracle, а о Java. Прежде всего: При использовании

System.out.println(timestampDate) 

на выходе вы видите уже скорректировали время к времени компьютера зоны.

Он всегда корректируется при использовании Date (т.е.

Calendar.getTime() или Timestamp.getTime())

код, чтобы играть с:

SimpleDateFormat dtFmt = new SimpleDateFormat("HH:mm:ss"); 
    NumberFormat nFmt = NumberFormat.getIntegerInstance(); 
    nFmt.setMinimumIntegerDigits(2); 
    long currentTimeMs = System.currentTimeMillis(); 

    GregorianCalendar utcCalendar = new GregorianCalendar(
      TimeZone.getTimeZone("UTC")); 

    GregorianCalendar bstCalendar = new GregorianCalendar(
      TimeZone.getTimeZone("Europe/London")); 

    GregorianCalendar localCalendar = new GregorianCalendar(); 

    utcCalendar.setTimeInMillis(currentTimeMs); 
    bstCalendar.setTimeInMillis(currentTimeMs); 
    localCalendar.setTimeInMillis(currentTimeMs); 

    System.out.println("---- milliseconds ----"); 
    System.out.println("Current ms  : " + currentTimeMs); 
    System.out.println("Local Calendar ms: " + localCalendar.getTimeInMillis()); 
    System.out.println("UTC Calendar ms: " + utcCalendar.getTimeInMillis()); 
    System.out.println("BST Calendar ms: " + bstCalendar.getTimeInMillis()); 

    System.out.println("---- SimpleFormat Time ----"); 
    System.out.println("Current Time: " 
      + dtFmt.format(new Date(currentTimeMs))); 
    System.out.println("Local Time: " + dtFmt.format(localCalendar.getTime())); 
    System.out.println("UTC Time : " + dtFmt.format(utcCalendar.getTime())); 
    System.out.println("BST Time : " + dtFmt.format(bstCalendar.getTime())); 

    System.out.println("---- Calendar Zone Time ----"); 
    System.out.println("Local Zone Time: " 
      + nFmt.format(localCalendar.get(Calendar.HOUR_OF_DAY)) + ":" 
      + nFmt.format(localCalendar.get(Calendar.MINUTE)) + ":" 
      + nFmt.format(localCalendar.get(Calendar.SECOND))); 

    System.out.println("UTC Zone Time : " 
      + nFmt.format(utcCalendar.get(Calendar.HOUR_OF_DAY)) + ":" 
      + nFmt.format(utcCalendar.get(Calendar.MINUTE)) + ":" 
      + nFmt.format(utcCalendar.get(Calendar.SECOND))); 

    System.out.println("BST Zone Time : " 
      + nFmt.format(bstCalendar.get(Calendar.HOUR_OF_DAY)) + ":" 
      + nFmt.format(bstCalendar.get(Calendar.MINUTE)) + ":" 
      + nFmt.format(bstCalendar.get(Calendar.SECOND))); 

} 

Как вы увидите, каждый календарь возвращает поля Time (HOUR_OF_DAY, MINUTE, SECOND) в соответствии с его TimeZone, а не то, что вы печатаете или форматируете от Calendar.getTime())

+0

Что интересно, это то, что после выполнения 'getTimestamp (...)' на моем локальном компьютере и отладки, что я точно получаю, я вижу, что 'Timestamp timestampDate' имеет' 2011-07- 12T15: 26: 07.000 + 0200', который предложит некоторые автоматические улучшения с моим местным часовым поясом ... есть ли способ избежать этого? – amerykanin

+0

Нет, нет способа. Это то, о чем я рассказывал. Дата и временная метка, не заботятся о временной зоне. Фактическое значение - миллисекунды с 01.01.1970 по Гринвичу. Таким образом, то, что вы видите из метода toString(), настраивается с помощью строкового представления из ваших настроек TimeZone компьютера, а не из объекта Timestamp. – Vadim

+0

Просто имейте в виду. Объекты Date и Timestamp указывают на точный момент времени. Thta означает, что вы начали транзакцию в 8 часов вечера по местному времени. Это было для меня в 13 часов моего времени, но это был один момент времени. Итак ... эти классы не могут представить этот момент в разных часовых поясах - только в текущем времени компьютера. Календарь может показать, что момент (который является тем длинным значением) может отражать момент в нужном TimeZone. – Vadim