[java] java.sql.Timestamp 시간대는 특정입니까?

UTC dateTime을 DB에 저장해야합니다.
특정 시간대에 지정된 dateTime을 UTC로 변환했습니다. 이를 위해 아래 코드를 따랐습니다.
내 입력 dateTime은 “20121225 10:00:00 Z”입니다. 시간대는 “Asia / Calcutta”입니다.
내 서버 / DB (oracle)는 동일한 시간대 (IST) “Asia / Calcutta”에서 실행 중입니다.

이 특정 시간대의 Date 객체를 가져옵니다.

        String date = "20121225 10:00:00 Z";
        String timeZoneId = "Asia/Calcutta";
        TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);

        DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss z");
                    //This date object is given time and given timezone
        java.util.Date parsedDate = dateFormatLocal.parse(date + " "
                         + timeZone.getDisplayName(false, TimeZone.SHORT));

        if (timeZone.inDaylightTime(parsedDate)) {
            // We need to re-parse because we don't know if the date
            // is DST until it is parsed...
            parsedDate = dateFormatLocal.parse(date + " "
                    + timeZone.getDisplayName(true, TimeZone.SHORT));
        }

       //assigning to the java.sql.TimeStamp instace variable
        obj.setTsSchedStartTime(new java.sql.Timestamp(parsedDate.getTime()));

DB에 저장

        if (tsSchedStartTime != null) {
            stmt.setTimestamp(11, tsSchedStartTime);
        } else {
            stmt.setNull(11, java.sql.Types.DATE);
        }

산출

DB (oracle)는 dateTime: "20121225 10:00:00UTC가 아닌 동일한 내용을 저장했습니다 .

아래 SQL에서 확인했습니다.

     select to_char(sched_start_time, 'yyyy/mm/dd hh24:mi:ss') from myTable

내 DB 서버도 동일한 시간대 “Asia / Calcutta”에서 실행됩니다.

다음과 같은 모습을 보여줍니다.

  1. Date.getTime() UTC가 아닙니다.
  2. 또는 Timestamp는 DB에 저장하는 동안 시간대에 영향을 미칩니다. 여기서 내가 뭘 잘못하고 있습니까?

질문 하나 더 :

timeStamp.toString()같은 지역의 시간대로 인쇄 java.util.date합니까? UTC가 아닙니까?



답변

setTimestamp(int parameterIndex, Timestamp x)드라이버에 대해 명시 적으로 지정되어 있지는 않지만 setTimestamp(int parameterIndex, Timestamp x, Calendar cal)javadoc에서 설정 한 규칙을 따라야합니다 .

java.sql.Timestamp지정된 Calendar개체를 사용하여 지정된 매개 변수를 지정된 값으로 설정 합니다. 드라이버는 Calendar개체를 사용하여 SQL TIMESTAMP값 을 생성 한 다음 드라이버가 데이터베이스로 보냅니다. A를 Calendar사용자 정의 시간대를 계정에 객체, 드라이버는 타임 스탬프 복용을 계산할 수 있습니다. Calendar개체를 지정 하지 않으면 드라이버는 응용 프로그램을 실행하는 가상 머신의 기본 시간대를 사용합니다.

전화 할 때 setTimestamp(int parameterIndex, Timestamp x)JDBC 드라이버로 가상 머신의 시간대를 사용하여 해당 시간대의 시간 소인 날짜 및 시간을 계산합니다. 이 날짜와 시간은 데이터베이스에 저장되며, 데이터베이스 열에 시간대 정보가 저장되지 않으면 해당 영역에 대한 정보가 손실됩니다 (즉, 데이터베이스를 사용하는 응용 프로그램이 동일한 시간대를 일관되게 사용하거나 시간대를 식별하기위한 다른 체계를 마련합니다 (예 : 별도의 열에 저장).

예 : 현지 시간대는 GMT + 2입니다. “2012-12-25 10:00:00 UTC”를 저장합니다. 데이터베이스에 저장된 실제 값은 “2012-12-25 12:00:00″입니다. 다시 검색합니다. “2012-12-25 10:00:00 UTC”로 다시 가져 오지만을 사용하여 검색하는 경우에만 해당 getTimestamp(..)되지만 다른 응용 프로그램이 GMT + 0 시간대의 데이터베이스에 액세스하면 타임 스탬프를 “2012-12-25 12:00:00 UTC”로 검색합니다.

다른 시간대에 저장 setTimestamp(int parameterIndex, Timestamp x, Calendar cal)하려면 필요한 시간대의 캘린더 인스턴스와 함께 를 사용해야합니다 . 값을 검색 할 때 동일한 시간대의 동등한 getter도 사용하는지 확인하십시오 ( TIMESTAMP데이터베이스에서 시간대 정보없이 를 사용하는 경우 ).

따라서 실제 GMT 시간대를 저장한다고 가정하면 다음을 사용해야합니다.

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
stmt.setTimestamp(11, tsSchedStartTime, cal);

JDBC 4.2 호환 드라이버가 지원해야한다 java.time.LocalDateTime(과 java.time.LocalTime)에 대한 TIMESTAMP(그리고 TIME를 통해) get/set/updateObject. java.time.Local*(코드는 특정 시간대를 가정 않은 경우 그 문제의 새로운 세트를 열 수도 있지만) 더 변환을 적용 할 필요가 없다, 그래서 클래스는, 시간대 않고 있습니다.


답변

정답은 java.sql.Timestamp가 시간대별로 다르지 않습니다. 타임 스탬프는 java.util.Date와 별도의 나노초 값의 조합입니다. 이 클래스에는 시간대 정보가 없습니다. 따라서 Date와 마찬가지로이 클래스는 1970 년 1 월 1 일 00:00:00 GMT + nanos 이후의 밀리 초 수를 단순히 보유합니다.

PreparedStatement.setTimestamp (int parameterIndex, Timestamp x, Calendar cal)에서 Calendar는 드라이버에서 기본 시간대를 변경하는 데 사용됩니다. 그러나 타임 스탬프는 여전히 GMT로 밀리 초를 유지합니다.

API는 JDBC 드라이버가 캘린더를 사용하는 방법에 대해 명확하지 않습니다. 공급자는 그것을 해석하는 방법에 대해 자유롭게 느끼는 것 같습니다.


답변

Mysql의 경우 제한이 있습니다. 에서 드라이버의 MySQL 문서 , 우리는이 :

다음은 MySQL Connector / J에 대한 몇 가지 알려진 문제 및 제한 사항입니다. Connector / J가 결과 집합에서 getTimeStamp () 메서드를 사용하여 DST (일광 절약 시간) 전환 날짜에 대한 타임 스탬프를 검색하면 반환 된 값 중 일부가 잘못 될 수 있습니다. 데이터베이스에 연결할 때 다음 연결 옵션을 사용하여 오류를 방지 할 수 있습니다.

useTimezone=true
useLegacyDatetimeCode=false
serverTimezone=UTC

따라서이 매개 변수를 사용 setTimestamp or getTimestamp하지 않고 캘린더를 사용하거나 사용하지 않고 호출 하면 jvm 시간대에 타임 스탬프가 있습니다.

예 :

jvm 시간대는 GMT + 2입니다. 데이터베이스에는 타임 스탬프가 있습니다. 1461100256 = 19/04/16 21 : 10 : 56,000000000 GMT

Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "");
props.setProperty("useTimezone", "true");
props.setProperty("useLegacyDatetimeCode", "false");
props.setProperty("serverTimezone", "UTC");
Connection con = DriverManager.getConnection(conString, props);
......
Calendar nowGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Calendar nowGMTPlus4 = Calendar.getInstance(TimeZone.getTimeZone("GMT+4"));
......
rs.getTimestamp("timestampColumn");//Oracle driver convert date to jvm timezone and Mysql convert date to GMT (specified in the parameter)
rs.getTimestamp("timestampColumn", nowGMT);//convert date to GMT
rs.getTimestamp("timestampColumn", nowGMTPlus4);//convert date to GMT+4 timezone

첫 번째 메서드는 다음을 반환합니다. 1461100256000 = 19/04/2016-21 : 10 : 56 GMT

두 번째 메서드는 다음을 반환합니다. 1461100256000 = 19/04/2016-21 : 10 : 56 GMT

세 번째 방법은 다음을 반환합니다. 1461085856000 = 19/04/2016-17 : 10 : 56 GMT

Oracle 대신 동일한 호출을 사용할 때 다음이 있습니다.

첫 번째 메서드는 다음을 반환합니다. 1461093056000 = 19/04/2016-19 : 10 : 56 GMT

두 번째 메서드는 다음을 반환합니다. 1461100256000 = 19/04/2016-21 : 10 : 56 GMT

세 번째 방법은 다음을 반환합니다. 1461085856000 = 19/04/2016-17 : 10 : 56 GMT

주의 :
Oracle에 대한 매개 변수를 지정할 필요가 없습니다.


답변

드라이버에 따라 다릅니다. 사용하려는 시간대를 알려주려면 Java 프로그램에 매개 변수를 제공해야합니다.

java -Duser.timezone="America/New_York" GetCurrentDateTimeZone

더 나아가 :

to_char(new_time(sched_start_time, 'CURRENT_TIMEZONE', 'NEW_TIMEZONE'), 'MM/DD/YY HH:MI AM')

전환을 올바르게 처리하는데도 가치가있을 수 있습니다. 여기 에서 찍은


답변

대답은 java.sql.Timestamp엉망이므로 피해야합니다. java.time.LocalDateTime대신 사용하십시오 .

그렇다면 왜 엉망입니까? 로부터 java.sql.Timestamp의 JavaDoc, a는 java.sql.Timestamp은 “얇은 래퍼 주위에 java.util.DateJDBC API는 이것을 SQL TIMESTAMP 값으로 식별 할 수 있습니다.” 로부터 java.util.Date의 JavaDoc “고 Date클래스는 협정 세계시 (UTC)를 반영하기위한 것입니다.” ISO SQL 스펙에서 TIMESTAMP WITHOUT TIME ZONE은 “시간대가없는 datetime 인 데이터 유형입니다”. TIMESTAMP는 TIMESTAMP WITHOUT TIME ZONE의 짧은 이름입니다. 따라서 java.sql.TimestampSQL TIMESTAMP는 “시간대 없음”인 반면 UTC를 “반영”합니다.

java.sql.TimestampUTC를 반영 하기 때문에 그 방법은 변환을 적용합니다. 이것은 혼란의 끝이 없습니다. SQL 관점에서 TIMESTAMP에는 변환 할 표준 시간대가 없으므로 SQL TIMESTAMP 값을 다른 표준 시간대로 변환하는 것은 의미가 없습니다. 42를 화씨로 변환한다는 것은 무엇을 의미합니까? 42에는 온도 단위가 없기 때문에 의미가 없습니다. 그냥 숫자 일뿐입니다. 마찬가지로 2020-07-22T10 : 38 : 00의 TIMESTAMP는 2020-07-22T10 : 30 : 00이 시간대가 아니기 때문에 아메리카 / 로스 앤젤레스로 변환 할 수 없습니다. UTC 나 GMT 또는 그 밖의 어떤 것도 아닙니다. 맨날 데이트 시간입니다.

java.time.LocalDateTime또한 맨날 데이트 시간입니다. SQL TIMESTAMP와 똑같은 시간대가 없습니다. 그 어떤 방법도 어떤 종류의 시간대 변환도 적용하지 않아 행동을 훨씬 쉽게 예측하고 이해할 수 있습니다. 따라서 java.sql.Timestamp. 사용 java.time.LocalDateTime.

LocalDateTime ldt = rs.getObject(col, LocalDateTime.class);
ps.setObject(param, ldt, JDBCType.TIMESTAMP);


답변

아래 방법을 사용하여 원하는 영역 / 영역 ID에 해당하는 데이터베이스에 타임 스탬프를 저장할 수 있습니다.

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Calcutta")) ;
Timestamp timestamp = Timestamp.valueOf(zdt.toLocalDateTime());

사람들이 흔히하는 실수 LocaleDateTime는 그 순간의 타임 스탬프를 얻기 위해 사용 하는 것인데, 나중에 변환을 시도하더라도 해당 영역에 대한 특정 정보를 버리는 것입니다. 영역을 이해하지 못합니다.

Timestamp클래스의 주의 사항 입니다 java.sql.Timestamp.


답변