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


85

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가 아닙니까?


4
제목의 질문에 답하기 위해 java.sql.Timestamp는 UTC를 기반으로합니다. 타임 스탬프 표시는 시간대별로 다릅니다.
Gilbert Le Blanc

@Gilbert Le Blanc 감사합니다!. "new java.sql.Timestamp (parsedDate.getTime ())"이 내 입력에 대해 GMT 타임 스탬프 객체를 반환 할 것이라고 대답 해 주시겠습니까?
Kanagavelu Sugumar

1
@GilbertLeBlanc java.sql.TimestampUTC로되어 있지만 시간대 정보없이 TIMESTAMP 필드에 저장할 때 가상 머신의 현재 시간대에 해당하는 날짜 / 시간을 사용합니다
Mark Rotteveel

@Kanagavelu Sugumar : 네. Date 클래스의 getTime 메서드는 1970 년 1 월 1 일 00:00:00 GMT 이후의 밀리 초 수를 반환합니다.
Gilbert Le Blanc

@MarkRotteveel Mark "시간대 정보없이 TIMESTAMP 필드에 저장, 가상 머신의 현재 시간대에 해당하는 날짜 / 시간을 사용합니다"라는 점을 알지 못했습니다. 답변을 자세히 설명해 주시겠습니까? 나에게 더 도움이 될 것입니다.
Kanagavelu Sugumar

답변:


105

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*(코드는 특정 시간대를 가정 않은 경우 그 문제의 새로운 세트를 열 수도 있지만) 더 변환을 적용 할 필요가 없다, 그래서 클래스는, 시간대 않고 있습니다.


GMT로 시간을 저장하고 싶습니다. GMT로 된 타임 스탬프를 믿습니다. 그래서 내가 무엇을해야하니? 그것은 setTimestamp (int parameterIndex, Timestamp tzGmtObj, Calendar calGmtObj)입니까?
Kanagavelu Sugumar

"setTimestamp (int parameterIndex, Timestamp x)"가 가상 머신의 시간대를 사용하는 경우. 코드에서 x의 역할은 무엇입니까?
Kanagavelu Sugumar

1
@KanagaveluSugumar 설정할 실제 타임 스탬프 값입니다. 드라이버는 해당 값이 아닌 시간대 정보에 대해서만 Calendar 개체를 사용합니다!
Mark Rotteveel

1
좋아요, 작동하고 있습니다 :) (Date / Timestamp) .getTime ()이 시간대 특정 시간을 밀리 초 단위로 유지하고 있음을 증명하는 데 많은 시간을 보냅니다. 이제 나는 그것이 아니라는 것을 이해했습니다. 시간대에 관계없이 항상 UTC 밀리 초를 제공합니다. 그러나 (Date / Timestamp) .toString ()은 시간대에 따라 다릅니다. 감사합니다.
Kanagavelu Sugumar

4
toString()java.lang.Date(과 java.lang.Timestamp) 언제든지 해당 지역의 시간대를 발표하는 것입니다.
Mark Rotteveel

35

정답은 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의 5.5에 대한 관심 주석
알렉스

2
나는 이것이 자바 8 이상에서 잘못되었다고 생각합니다. 타임 스탬프 (java.util.Date에서 확장되기 때문에) 안에는 실제로 시간대를 포함하는 zoneinfo 속성이있는 캘린더 속성이 있습니다. .getTime ()을 사용하여 UTC 시간을 얻을 수 있습니다. 타임 스탬프도이를 저장하지만 시간대 정보가 있기 때문입니다.
Jorge.V

1
즉, GMT가 아닌 1970 년 1 월 1 일 00:00:00 UTC 이후의 밀리 초 수를 유지합니다. GMT에는 일광 절약 시간이 있습니다. UNIX epoch는 1970 년 1 월 1 일 00:00 UTC 이후의 밀리 초입니다.
Kirby

6

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에 대한 매개 변수를 지정할 필요가 없습니다.


1
오라클의 경우 세션 시간대를 UTC로 설정하여 MySQL이 캘린더가 아닌 경우와 동일한 변환을 수행 할 수 있다고 생각합니다. " ALTER SESSION SET TIME_ZONE = 'UTC'".
eckes 2011

5

드라이버에 따라 다릅니다. 사용하려는 시간대를 알려주려면 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')

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


stackoverflow.com/questions/2858182/… 를 따르 겠다는 뜻 입니까? 아니면 내 JVM 애플리케이션을 GMT에서 실행하도록 설정 하시겠습니까?
Kanagavelu Sugumar

@KanagaveluSugumar 잘 다릅니다. 모든 쿼리에 대한 타임 스탬프를 수정하려면 시작시 설정하고 SOME (모든 것보다 작은 것을 의미) 만 수정하려는 경우 참조한 게시물에 명시된 방식으로 각 쿼리를 수정합니다.
Woot4Moo

5

대답은 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);

아주 잘 말했습니다. 감사합니다.
Ole VV

2

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

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

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

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


솔루션에 감사드립니다. 작동했습니다 !!!
Karthik SA
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.