왜 1927 년에이 두 번 빼는 것이 이상한 결과를 낳는가?

java date timezone


다음 프로그램을 실행하면 시간을 1 초 간격으로 참조하는 두 개의 날짜 문자열을 구문 분석하고 비교합니다.

public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

출력은 다음과 같습니다.

353

ld4-ld31 이 아닌 (시간의 1 초 차이에서 예상되는 것처럼) 353 입니까?

날짜를 1 초 후에 시간으로 변경하면 :

String str3 = "1927-12-31 23:54:08";  
String str4 = "1927-12-31 23:54:09";  

그러면 ld4-ld31 이 됩니다.


자바 버전 :

java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)
Timezone(`TimeZone.getDefault()`):

sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]

Locale(Locale.getDefault()): zh_CN



Answer 1 Jon Skeet


12 월 31 일 상하이에서 시간대가 변경되었습니다.

1927 년 상하이에 대한 자세한 내용은 이 페이지 를 참조하십시오 . 기본적으로 1927 년 말 자정에 시계는 5 분 52 초로 되돌아갔습니다. 따라서 "1927-12-31 23:54:08"은 실제로 두 번 발생했으며 Java가 현지 날짜 / 시간에 대해 나중에 가능한 순간 으로 구문 분석하는 것처럼 보이 므로 차이가 있습니다.

종종 이상하고 멋진 시간대의 세계에서 또 다른 에피소드.

편집 : 보도 중지! 역사 변경 ...

TZDB 버전 2013a로 다시 빌드하면 원래 질문은 더 이상 똑같은 동작을 나타내지 않습니다 . 2013a에서 결과는 358 초이며 전환 시간은 23:54:08 대신 23:54:03입니다.

노다 타임 (Noda Time)에서 단위 테스트 형태로 이와 같은 질문을 수집하기 때문에이 사실을 알게 되었습니다. 이제 테스트가 변경되었지만 과거 데이터만으로는 안전하지 않습니다.

편집 : 역사가 다시 변경되었습니다 ...

TZDB 2014f에서 변경 시간이 1900-12-31로 이동했으며 이제는 343 초에 불과합니다 (따라서 tt+1 사이의 시간 은 344 초입니다.

편집 : 1900에서 전환에 관한 질문에 대답하기 위해 ... Java 시간대 구현은 모든 시간대를 단순히 1900 UTC가 시작되기 전에 모든 표준 시간대로 처리하는 것처럼 보입니다 .

import java.util.TimeZone;

public class Test {
    public static void main(String[] args) throws Exception {
        long startOf1900Utc = -2208988800000L;
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone zone = TimeZone.getTimeZone(id);
            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {
                System.out.println(id);
            }
        }
    }
}

위의 코드는 Windows 컴퓨터에서 출력을 생성하지 않습니다. 따라서 1900 년 시작시 표준 시간대 이외의 오프셋이있는 시간대는 전환으로 간주합니다. TZDB 자체는 이전보다 약간의 데이터를 가지고 있으며 "고정 된"표준 시간 ( getRawOffset 이 유효한 개념이라고 가정) 에 대한 아이디어에 의존하지 않으므로 다른 라이브러리는이 인공적인 전환을 도입 할 필요가 없습니다.




Answer 2 Michael Borgwardt


현지 시간 불연속 이 발생했습니다 .

1928 년 1 월 1 일 일요일 오전 10시 00 분 00 초에 도달하려고했을 때, 시계는 0:05:52 시간에서 0:05:52 시간으로 되돌아갔습니다. 1927 년 12 월 31 일 23:54:08

이것은 특히 이상하지 않으며 정치 또는 행정적 조치로 시간대가 바뀌거나 바뀌면서 한 번에 거의 모든 곳에서 발생했습니다.




Answer 3 Raedwald


이 이상의 도덕은 다음과 같습니다.

  • 가능하면 UTC로 날짜와 시간을 사용하십시오.
  • 날짜 나 시간을 UTC로 표시 할 수없는 경우 항상 시간대를 표시하십시오.
  • UTC로 입력 날짜 / 시간을 요구할 수없는 경우 명시 적으로 표시된 시간대를 요구하십시오.



Answer 4 PatrickO


시간이 증가하면 UTC로 다시 변환 한 다음 더하거나 빼야합니다. 현지 시간 만 표시하십시오.

이 방법으로 몇 시간 또는 몇 분이 두 번 발생하는 기간을 걸을 수 있습니다.

UTC로 변환 한 경우 1 초마다 추가하고 표시 할 현지 시간으로 변환하십시오. LMT 오후 11:54:08 (오후 LMT -11:59:59), LST 오후 11:54:08 ( CST -11:59:59) CST 를 거칩니다 .




Answer 5 Rajshri


각 날짜를 변환하는 대신 다음 코드를 사용할 수 있습니다.

long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

그리고 결과는 다음과 같습니다.

1