java - 변경 - 자바 date gettime




왜이 두 번을(1927 년에) 뺀 것이 이상한 결과를 낳았습니까? (6)

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

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-ld31ld4-ld3 (나는 1 ld4-ld3 가 예상 됨) 353 입니까?

날짜를 1 초 후에 변경하면 :

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

그러면 ld4-ld31 됩니다.

Java 버전 :

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

IMHO Java에서 널리 퍼져있는 암시 적 로컬라이제이션은 최대의 설계 결함입니다. 그것은 사용자 인터페이스를위한 것일 수도 있지만 솔직히 말해서 프로그래머가 목표 대상이 아니기 때문에 현지화를 기본적으로 무시할 수있는 일부 IDE를 제외하고는 현재 사용자 인터페이스 용으로 Java를 사용하고 있습니다. 다음을 통해 (특히 Linux 서버에서)이를 수정할 수 있습니다.

  • 수출 LC_ALL = C TZ = UTC
  • 시스템 시계를 UTC로 설정하십시오.
  • 절대적으로 필요한 경우가 아니면 (예 : 표시 전용) 현지화 된 구현을 사용하지 마십시오.

Java Community Process 멤버에게는 다음을 권장합니다.

  • 지역화 된 메소드를 기본값이 아닌 사용자가 명시 적으로 현지화를 요청하도록합니다.
  • 대신 UTF-8 / UTC를 FIXED 기본값으로 사용하십시오. 이와 같은 스레드를 생성하려는 경우를 제외하고는 다른 것을 할 이유가 없습니다.

내 말은, 전역 정적 변수가 반 OO 패턴이 아닌가? 어떤 초보적 인 환경 변수에 의해 주어진 퍼베이시브 디폴트 값은 없다.


각 날짜를 변환하는 대신 다음 코드를 사용합니다.

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

결과가 다음과 같습니다.

1

다른 사람들이 설명했듯이, 거기에는 시간 불연속이 있습니다. Asia/Shanghai 에는 1927-12-31 23:54:08 에 두 가지 가능한 시간대 오프셋이 1927-12-31 23:54:08 에는 하나의 오프셋 만 1927-12-31 23:54:07 합니다. 따라서 어느 오프셋을 사용 하느냐에 따라 1 초의 차이나 5 분과 53 초의 차이가 있습니다.

우리가 익숙했던 평소의 1 시간 일광 절약 (여름철) 대신에 약간의 오프셋 이동이 문제를 다소가립니다.

시간대 데이터베이스의 2013a 업데이트가이 불연속성을 몇 초 일찍 이동 시켰지만 효과는 여전히 관찰 가능합니다.

Java 8의 새로운 java.time 패키지를 사용하면이를보다 명확하게 이해하고이를 처리 할 수있는 도구를 제공합니다. 주어진:

DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

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

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());

그러면 durationAtEarlierOffset 은 1 초가되고, durationAtLaterOffset 은 5 분 53 초가됩니다.

또한이 두 오프셋은 동일합니다.

// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();

그러나이 두 가지는 다릅니다 :

// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();

1927-12-31 23:59:591928-01-01 00:00:00 비교하는 것과 같은 문제를 볼 수 있습니다. 1928-01-01 00:00:00 경우에는 더 긴 분기를 생성하는 초기 오프셋이며, 두 가지 가능한 오프셋이있는 이전 날짜.

이것에 접근하는 또 다른 방법은 진행중인 전환이 있는지 확인하는 것입니다. 우리는 이렇게 할 수 있습니다 :

// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

해당 날짜 / 시간에 유효한 오프셋이 두 개 이상있는 경우 또는 해당 날짜 / 시간이 해당 영역 ID에 유효하지 않은 경우 갭이 isOverlap() 를 사용하여 전환이 겹침인지 여부를 확인할 수 있습니다. isGap() 메소드.

Java 8이 광범위하게 사용 가능 해지면 JSR 310 백 포트를 채택하는 Java 7을 사용하는 사람들에게 이러한 문제를 처리하는 데 도움이되기를 바랍니다.


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

상해에서 1927 년의 세부 사항을 위해이 페이지 를보십시오. 기본적으로 1927 년 말 자정에 시계는 5 분 52 초가되었습니다. 그래서 "1927-12-31 23:54:08"실제로 두 번 일어났습니다. 자바가 그 지역 날짜 / 시간에 대해 나중에 가능한 순간으로 파싱하는 것처럼 보입니다. 따라서 차이점이 있습니다.

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

편집 : 그만 눌러! 역사 변화 ...

원래 질문은 TZDB 버전 2013a로 다시 작성된 경우 더 이상 똑같은 동작을 보여주지 않습니다. 2013a에서 결과는 전환 시간이 23:54:08 대신 23:54:03 인 358 초가됩니다.

나는 Noda Time에서 이와 같은 질문을 TZDB 의 형태로 수집하기 때문에 이것을 알아 차렸다. 테스트는 변경되었지만 단지 보여 주기만하면된다. 역사적인 데이터조차도 안전하지 않다.

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

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 이 유효한 개념으로 간주하는 것)에 대한 아이디어에 의존하지 않으므로 다른 라이브러리에서이 인공 전환을 도입 할 필요가 없습니다.


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

  • 가능하면 UTC의 날짜와 시간을 사용하십시오.
  • UTC로 날짜 또는 시간을 표시 할 수 없으면 항상 시간대를 지정하십시오.
  • UTC로 입력 날짜 / 시간을 요구할 수 없으면 명시 적으로 표시된 시간대가 필요합니다.

현지 시간 불일치 가 발생했습니다.

현지 표준 시간이 일요일에 이르렀을 때, 1 1. 1928 년 1 월, 00:00:00 시계가 0:05:52 시간으로 토요일 31. 12 월 1927, 23:54:08 현지 표준 시간으로 되돌아갔습니다.

이것은 특히 이상한 것은 아니며 정치적 또는 행정적 조치로 인해 시간대가 변경되거나 변경 될 때 한 번에 또는 다른 곳에서 거의 모든 곳에서 발생했습니다.







timezone