다음 프로그램을 실행하면 시간을 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-ld3
하지 1
(나는 시간에 1 초 차이에서 기대하는 것처럼), 그러나 353
?
1 초 후에 날짜를 시간으로 변경하면 :
String str3 = "1927-12-31 23:54:08";
String str4 = "1927-12-31 23:54:09";
그러면 ld4-ld3
됩니다 1
.
자바 버전 :
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
답변
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 2014 년, 변화의 시간이 1900년 12월 31일로 이동하고 있으며, (사이의 시간, 그래서 지금은 단순한 343초 변화의 t
및 t+1
3백44초 당신이 무슨 뜻인지 볼 경우).
편집 : 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
유효한 개념이라고 가정) 에 대한 아이디어에 의존하지 않으므로 다른 라이브러리는이 인공적인 전환을 도입 할 필요가 없습니다.
답변
현지 시간 불연속 이 발생했습니다 .
1928 년 1 월 1 일 일요일 오전 10시 00 분 00 초에 도달하려고했을 때, 시계는 0:05:52 시간에서 0:05:52 시간으로 되돌아갔습니다. 1927 년 12 월 31 일 23:54:08
이것은 특별히 이상하지는 않으며 정치적 또는 행정적 조치로 시간대가 바뀌거나 바뀌면서 한 번에 거의 모든 곳에서 발생했습니다.
답변
이 이상의 도덕은 다음과 같습니다.
- 가능하면 UTC로 날짜와 시간을 사용하십시오.
- 날짜 또는 시간을 UTC로 표시 할 수없는 경우 항상 시간대를 표시하십시오.
- UTC로 입력 날짜 / 시간을 요구할 수없는 경우 명시 적으로 표시된 시간대를 요구하십시오.
답변
시간이 증가하면 UTC로 다시 변환 한 다음 더하거나 빼야합니다. 현지 시간 만 표시하십시오.
이렇게하면 몇 시간 또는 몇 분이 두 번 발생하는 기간을 걸을 수 있습니다.
UTC로 변환 한 경우 1 초마다 추가하고 표시 할 현지 시간으로 변환하십시오. LMT 오후 11:54:08 (오후 LMT -11:59:59), LST 오후 11:54:08 ( CST -11:59:59) CST 를 거칩니다 .
답변
각 날짜를 변환하는 대신 다음 코드를 사용할 수 있습니다.
long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);
그리고 결과는 다음과 같습니다.
1
답변
죄송 합니다만 시간 불연속이 조금 바뀌 었습니다.
JDK 6 2 년 전, JDK 7 에서 최근 업데이트 25 .
배우는 교훈 : 디스플레이를 제외하고 모든 비용으로 비 UTC 시간을 피하십시오.
답변
다른 사람들이 설명했듯이 시간 불연속이 있습니다. 1927-12-31 23:54:08
at Asia/Shanghai
에 대해 두 개의 가능한 시간대 오프셋이 있지만에 대한 하나의 오프셋 만 1927-12-31 23:54:07
있습니다. 따라서 어떤 오프셋이 사용되는지에 따라 1 초 차이 또는 5 분 53 초 차이가 있습니다.
우리가 익숙한 일반적인 1 시간 일광 절약 시간 (서머 타임) 대신이 약간의 오프셋 이동은 문제를 조금 가려줍니다.
시간대 데이터베이스의 2013a 업데이트는 몇 초 전에이 불연속성을 옮겼지만 그 효과는 여전히 관찰 가능합니다.
java.time
Java 8 의 새로운 패키지를 사용하면이를보다 명확하게보고 처리 할 수있는 도구를 제공 할 수 있습니다. 주어진:
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:59
으로 1928-01-01 00:00:00
하지만,이 경우는 이전에 더 이상 차이를 생산하는 오프셋, 그것은 두 가지 오프셋을 가지고 이전 날짜입니다.
이에 접근하는 또 다른 방법은 전환이 진행 중인지 확인하는 것입니다. 우리는 이렇게 할 수 있습니다 :
// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);
에서 isOverlap()
및 isGap()
메소드를 사용하여 전환이 해당 날짜 / 시간에 대해 유효한 오프셋이 둘 이상인 경우 겹침인지 또는 해당 날짜 / 시간이 해당 영역 ID에 대해 유효하지 않은 간격인지 확인할 수 있습니다 zot4
.
Java 8이 널리 사용 가능해 지거나 JSR 310 백 포트를 채택한 Java 7을 사용하는 사람들이 이런 종류의 문제를 처리하는 데 도움이되기를 바랍니다.
