복붙노트

[SQL] java.sql.Date 대 SQL 날짜의 시간대

SQL

java.sql.Date 대 SQL 날짜의 시간대

나는 java.sql.Date의는 SQL DATE 데이터 유형 대의 행동에 혼란을 조금 얻고있다. 예를 들어 다음과 같은 문장을 보자 :

select cast(? as date)           -- in most databases
select cast(? as date) from dual -- in Oracle

의 준비하자 자바와 함께 문을 실행

PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setDate(1, new java.sql.Date(0)); // GMT 1970-01-01 00:00:00
ResultSet rs = stmt.executeQuery();
rs.next();

// I live in Zurich, which is CET, not GMT. So the following prints -3600000, 
// which is CET 1970-01-01 00:00:00
// ... or   GMT 1969-12-31 23:00:00
System.out.println(rs.getDate(1).getTime());

즉, GMT 타임 스탬프 나는 문에 내가 돌아올 CET 타임 스탬프가된다 바인드. 어떤 단계에서 시간대를 추가하는 이유는 무엇입니까?

노트 :

해결법

  1. ==============================

    1.JDBC 스펙은 시간대에 관하여 모든 세부 사항을 정의하지 않습니다. 그럼에도 불구하고, 우리의 대부분은 JDBC 시간대 불일치가 처리해야하는 고통을 알고; 단지 모든 StackOverflow의 질문 봐!

    JDBC 스펙은 시간대에 관하여 모든 세부 사항을 정의하지 않습니다. 그럼에도 불구하고, 우리의 대부분은 JDBC 시간대 불일치가 처리해야하는 고통을 알고; 단지 모든 StackOverflow의 질문 봐!

    궁극적으로, 날짜 / 시간 데이터베이스 유형에 대한 시간 영역의 처리는 데이터베이스 서버 사이의 JDBC 드라이버 및 모든 것을 요약된다. 당신은 JDBC 드라이버 버그의 자비에있어; PostgreSQL는 버전 8.3의 버그를 수정

    새로운 날짜 (0)를 사용하여 새 날짜를 만들 때 오라클 JavaSE java.sql.Date을 사용하는 주장 (하자, 당신의 날짜가 생성됩니다

    그래서, 새로운 날짜 (0) GMT를 사용해야합니다.

    당신이 ResultSet.getDate (int)를 호출 할 때, 당신은 JDBC 구현을 실행하고 있습니다. JDBC 스펙은 JDBC 구현이 시간대 세부 사항을 처리하는 방법을 지시하지 않는다; 그래서 당신은 구현의 자비입니다. 그것은을 java.sql.Date에 날짜를 얻기 위해 자신의 변환을 수행하도록 오라클 11g oracle.sql.DATE의 JavaDoc를 보면, 그것은, 오라클 DB를 저장 시간대 정보를 보이지 않는다. 나는 오라클 DB와 경험이 없지만, 나는 JDBC 구현 java.sql.Date에 oracle.sql.DATE에서 변환을 수행하는 서버의 및 해당 지역의 JVM의 시간대 설정을 사용하고 생각합니다.

    당신은 SQLite는 제외하고, 제대로 그 여러 RDBMS 구현 핸들 시간대를 언급. 당신이 JDBC 드라이버에 날짜 값을 전송하는 방법 H2와 SQLite는 직장에서 살펴 보자 당신은 JDBC 드라이버에서 날짜 값을 얻을 때.

    H2 JDBC 드라이버 PrepStmt.setDate (INT, 날짜) 시간대 변환을 수행합니다 (긴) DateTimeUtils.dateValueFromDate를 호출 ValueDate.get (날짜)를 사용합니다.

    이 SQLite는 JDBC 드라이버를 사용 PrepStmt.setDate (INT, 날짜) PrepStmt.setObject (INT, Object)를 호출하고 어떤 시간대 변환을 수행하지 않습니다.

    H2 JDBC 드라이버 JdbcResultSet.getDate (int)를 반환 (columnIndex에) .getDate ()를 얻는다. 수 (INT)는 H2 값은 지정된 열에 대해 반환합니다. 열 유형 DATE이므로, H2는 ValueDate를 이용한다. ValueDate.getDate ()는 궁극적으로 시간대 변환 후 java.sql.Date를 생성 DateTimeUtils.convertDateValueToDate을 (long)를 호출합니다.

    이 SQLite는 JDBC 드라이버를 사용하여 RS.getDate (INT) 코드는 훨씬 간단합니다; 그냥 데이터베이스에 저장된 긴 날짜 값을 사용하여 java.sql.Date을 반환합니다.

    SQLite는 JDBC 드라이버가없는 동안 우리는 H2 JDBC 드라이버가 날짜와 시간대 변환 처리에 대한 스마트되고있는 것을 볼 그래서 (이 결정은 스마트 아니다 대답이 아니라 SQLite는 디자인 결정에 맞게 할 수 없습니다). 당신은 당신이 언급 다른 RDBMS JDBC 드라이버 소스를 쫓아 경우, 당신은 아마 H2가하는 방법과 유사한 방식으로 대부분 접근하고 날짜와 시간대를 찾을 수 있습니다.

    비록 JDBC 사양하지 상세 시간대 처리, 그것은 RDBMS와 JDBC 구현 설계자가 고려 시간대를 가져다가 제대로 처리 할 수 ​​있다는 좋은 의미가 있습니다 할; 그들이 원하는 특히 이들 제품은 세계 무대에서 시장성이 될 수 있습니다. 이 디자이너는 무척 똑똑하고 나는 그들의 대부분도 구체적인 사양이없는 상태에서,이 권리를 얻을 놀라게하고 있지 않다.

    나는 시간대가 일을 복잡하게하는 방법을 설명 SQL 서버 2008, 시간 영역 데이터를 사용하여,이 마이크로 소프트 SQL 서버의 블로그를 발견 :

  2. ==============================

    2.이 변환을 수행하는 JDBC 드라이버입니다. 그것은 DB / 와이어 형식과 그 형식은 시간대를 포함하지 않는 경우, 경향 날짜를 해석 할 때 로컬 컴퓨터의 시간대 설정을 기본값으로하는 것입니다으로 수용 할 수있는 형식으로 날짜 개체를 변환 할 필요가있다. 그래서, 가능성이 가장 높은 시나리오는 사용자가 지정한 드라이버 목록 제공, 당신은 CET 1970년 1월 1일 있던 문을 설정할 때 GMT 1970년 1월 1일 00:00:00 날짜 만 해석 날짜를 설정한다는 것입니다 1시 0분 0초. 날짜는 날짜 부분이기 때문에, 당신은 (시간대 없음) 1970년 1월 1일가 서버로 전송하고 다시 당신에게 반향 얻을. 드라이버가 날짜 등을 얻을, 당신이 날짜로 액세스하면 1970년 1월 1일와 해석을보고 그 다시 현지 시간대, 즉 CET 1970년 1월 1일 0시 0분 0초 또는 GMT 1천9백69에서 12 사이에 -31 23시 0분 0초. 따라서, 당신은 원래 날짜에 비해 시간을 "손실"했다.

    이 변환을 수행하는 JDBC 드라이버입니다. 그것은 DB / 와이어 형식과 그 형식은 시간대를 포함하지 않는 경우, 경향 날짜를 해석 할 때 로컬 컴퓨터의 시간대 설정을 기본값으로하는 것입니다으로 수용 할 수있는 형식으로 날짜 개체를 변환 할 필요가있다. 그래서, 가능성이 가장 높은 시나리오는 사용자가 지정한 드라이버 목록 제공, 당신은 CET 1970년 1월 1일 있던 문을 설정할 때 GMT 1970년 1월 1일 00:00:00 날짜 만 해석 날짜를 설정한다는 것입니다 1시 0분 0초. 날짜는 날짜 부분이기 때문에, 당신은 (시간대 없음) 1970년 1월 1일가 서버로 전송하고 다시 당신에게 반향 얻을. 드라이버가 날짜 등을 얻을, 당신이 날짜로 액세스하면 1970년 1월 1일와 해석을보고 그 다시 현지 시간대, 즉 CET 1970년 1월 1일 0시 0분 0초 또는 GMT 1천9백69에서 12 사이에 -31 23시 0분 0초. 따라서, 당신은 원래 날짜에 비해 시간을 "손실"했다.

  3. ==============================

    3.모두 java.util.Date와 Oracle / MySQL의 날짜 객체는 단순히 위치에 관계없이 시점의 표현이다. 매우 가능성이 내부적으로 "시대"이후 밀리 / 나노 초, GMT 1970-01-01 00:00:00 저장할 수 있음이 의미합니다.

    모두 java.util.Date와 Oracle / MySQL의 날짜 객체는 단순히 위치에 관계없이 시점의 표현이다. 매우 가능성이 내부적으로 "시대"이후 밀리 / 나노 초, GMT 1970-01-01 00:00:00 저장할 수 있음이 의미합니다.

    당신이 결과 집합에서 읽을 때, 당신의 전화 "rs.getDate ()는"시점을 포함하는 내부 데이터를 가지고 가고, 자바 Date 객체로 변환 할 수없는 결과를 알려줍니다. 자바는 취리히에 대한 CET입니다 해당 지역의 시간대를 선택할 수 있도록이 날짜 객체는, 로컬 컴퓨터에 생성됩니다.

    당신이보고있는 차이는 표현의 차이가 아니라 시간의 차이입니다.

  4. ==============================

    4.시간 또는 시간대 정보를 저장하지 않는 SQL 날짜에 클래스 java.sql.Date에 해당한다. 이 수행되는 방법은 javadoc에 둔다을 같은 '정상화'날짜입니다 :

    시간 또는 시간대 정보를 저장하지 않는 SQL 날짜에 클래스 java.sql.Date에 해당한다. 이 수행되는 방법은 javadoc에 둔다을 같은 '정상화'날짜입니다 :

    당신은 UTC + 1에서 작동 및 날짜에 대한 데이터베이스를 요청할 때 준수 구현 당신이 관찰 한 정확히 않는이 수단 : 밀리 세컨드의 값을 java.sql.Date을 돌려주는 0시에서 해당 날짜에 해당 : 00 UTC + 1 독립적으로 데이터가 처음에 데이터베이스에 도착하는 방법.

    데이터베이스 드라이버는 당신이 원하는 아니라면 옵션을 통해이 동작을 변경 할 수 있습니다.

    데이터베이스에 java.sql.Date을 통과 할 때 반면에, 드라이버는 밀리 초 값에서 날짜와 시간 구성 요소를 분리하는 기본 표준 시간대를 사용합니다. 0을 사용하고 UTC + X의 경우, 날짜가 X <0 X> = 0 1969년 12월 31일에 대한 1970-01-01 될 것입니다.

    (!) 참고 :이보고 이상한 그 구현의 날짜 (긴) 생성자 다릅니다에 대한 문서. javadoc의이 말한다 :

    그러나 무엇 실제로 오픈 JDK에 구현하는 것은입니다 :

    public Date(long date) {
        // If the millisecond date value contains time info, mask it out.
        super(date);
    
    }
    

    분명히이 "밖으로 마스킹"구현되지 않습니다. 그냥뿐만 아니라 지정된 동작이 잘 지정되어 있지 않기 때문에, 예를 들어, SHOULD 1970-01-01 00:00:00 GMT-1970-01-01 6은 그리니치 표준시 06시 00분 0초 1970-01-01 그리니치 표준시 00시 00분 0초 1969년 12월 31일 = 18:00 매핑 될 = 그리니치 표준시 00시-6, 1970-01-01 그리니치 표준시 18시 00분 0초-6?

  5. ==============================

    5.달력을 받아 GETDATE의 과부하는이 문제를 해결할 수 있습니다 사용 할 수 있습니까? 아니면 정보를 변환하는 Calendar 객체를 사용할 수 있습니다.

    달력을 받아 GETDATE의 과부하는이 문제를 해결할 수 있습니다 사용 할 수 있습니까? 아니면 정보를 변환하는 Calendar 객체를 사용할 수 있습니다.

    즉, 그 검색을 가정하는 것은 문제입니다.

    Calendar gmt = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    PreparedStatement stmt = connection.prepareStatement(sql);
    stmt.setDate(1, new Date(0)); // I assume this is always GMT
    ResultSet rs = stmt.executeQuery();
    rs.next();
    
    //This will output 0 as expected
    System.out.println(rs.getDate(1, gmt).getTime());
    

    대안 적으로, 그 저장을 가정하는 것은 문제이다.

    Calendar gmt = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    PreparedStatement stmt = connection.prepareStatement(sql);
    gmt.setTimeInMillis(0) ;
    stmt.setDate(1, gmt.getTime()); //getTime returns a Date object
    ResultSet rs = stmt.executeQuery();
    rs.next();
    
    //This will output 0 as expected
    System.out.println(rs.getDate(1).getTime());
    

    올바른 가정 인 발견 할 것이다, 그래서 나는이에 대한 테스트 환경이 없습니다. 또한, 저는 그렇지 않으면 당신은 빠른 시간대 변환을 수행하는 방법을 찾아 볼 것이다, getTime를 현재의 로케일을 돌려줍니다 있다고 생각합니다.

  6. ==============================

    6.

    PreparedStatement stmt = connection.prepareStatement(sql);
    stmt.setObject(1, LocalDate.EPOCH); // The epoch year LocalDate, '1970-01-01'.
    ResultSet rs = stmt.executeQuery();
    rs.next();
    
    // It doesnt matter where you live or your JVM’s time zone setting
    // since a LocalDate doesn’t have or use time zone
    System.out.println(rs.getObject(1, LocalDate.class));
    

    내가 테스트하지 못했지만, JDBC 드라이버에 버그가 아니라면, 모든 시간대의 출력은 다음과 같습니다

    LOCALDATE 하루 중 및 시간대없이 시간이없는 날짜입니다. 그래서 대해 혼동 얻을 수있는 더 밀리 초 값과 일의 시간이 없습니다. LOCALDATE는 java.time, 현대 자바 날짜 및 시간 API의 일부입니다. 당신이 java.time가와 방법을 통해 SQL 데이터베이스에서 LOCALDATE 등의 setObject와 조각에서으로 getObject I 사용 (그렇게하지하여 setDate도 GETDATE) 객체 전송할 수있는 JDBC 4.2 지정합니다.

    나는 우리의 거의 모든 지금까지 JDBC 4.2 드라이버를 사용하고 있다고 생각합니다.

    java.sql.Date 하루 중 시간없이 날짜로 java.util.Date를 위장 (안 매우 성공적으로) 시도하는 해킹입니다. 나는 당신이 클래스를 사용하지 않는 것이 좋습니다.

    문서에서 :

    "관련"는 모호한 수 있습니다. 이것이 의미하는 것은, 내가 믿는, 밀리 초 값이 JVM의 기본 시간대에 하루의 시작 나타내는해야한다는 것입니다 (귀하의 경우 유럽 / 취리히, 나는 가정한다). GMT 1970-01-01 00:00:00 같을 때 새로운 java.sql.Date (0) 작성이 시간이 시간대에 1시 0분 0초 때문에 그래서, 그것은 정말 잘못된 날짜 객체를 생성합니다. JDBC는 하루의 시간을 무시 (우리는 에러 메시지를 예상했을 수도 있지만, 어떤을하지 않습니다). 그래서 SQL은 점점 1970-01-01의 날짜를 반환합니다. JDBC 제대로 CET 1970-01-01 0시 0분 0초 포함하는 일이 다시 변환합니다. 또는 -3 (600) 000의 밀리 초 값은 예, 혼란. 내가 말했듯이, 그 클래스를 사용하지 마십시오.

    음수에 있었다면 GMT이 날짜했을 것이다, 그래서 당신은 통과했다, 1969년 12월 31일으로 해석되었을 것이다 (미주 예를 들어, 대부분의 시간대), 새로운 java.sql.Date (0) 오프셋 (offset) 및 SQL에서 얻었습니다.

    설상가상으로, JVM의 시간대 설정이 변경 될 때 발생하는 것 같아요. 프로그램의 모든 부분과 같은 JVM에서 다른 프로그램 실행 언제든지이 작업을 수행 할 수 있습니다. 이는 일반적으로 모든 java.sql.Date가 무효가되기 위해 변경 전에 생성 된 객체 때로는 (이일에 의해 드문 경우) 일일하여 날짜를 이동할 수 있습니다됩니다.

  7. ==============================

    7.나는 시간대 문제가 지나치게 복잡하고있다 생각합니다. 당신이 준비된 명령문에 날짜 / 시간을 통과 할 때, 그것은 단지 데이터베이스 서버에 날짜 및 / 또는 시간 정보를 전송한다. 사실상의 표준에 따라, 그것은 데이터베이스 서버와 JVM 사이의 시간대 차이를 조작하지 않습니다. 그리고 나중에 결과 집합에서 날짜 / 시간을 읽을 때. 그냥 데이터베이스에서 날짜 시간 정보를 읽고, JVM 시간대에 같은 날짜 / 시간을 만듭니다.

    나는 시간대 문제가 지나치게 복잡하고있다 생각합니다. 당신이 준비된 명령문에 날짜 / 시간을 통과 할 때, 그것은 단지 데이터베이스 서버에 날짜 및 / 또는 시간 정보를 전송한다. 사실상의 표준에 따라, 그것은 데이터베이스 서버와 JVM 사이의 시간대 차이를 조작하지 않습니다. 그리고 나중에 결과 집합에서 날짜 / 시간을 읽을 때. 그냥 데이터베이스에서 날짜 시간 정보를 읽고, JVM 시간대에 같은 날짜 / 시간을 만듭니다.

  8. from https://stackoverflow.com/questions/9202857/timezones-in-sql-date-vs-java-sql-date by cc-by-sa and MIT license