2020 년 3 월 30 일과 2020 년 3 월 1 일의 차이로 인해 29 일 대신 28 일이 잘못되는 이유는 무엇입니까?


124
TimeUnit.DAYS.convert(
   Math.abs(
      new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("30-03-2020 00:00:00").getTime() - 
      new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").parse("1-03-2020 00:00:00").getTime()
   ),
   TimeUnit.MILLISECONDS)

결과는 28이지만 29 여야합니다.

시간대 / 위치가 문제가 될 수 있습니까?


17
참고 : SimpleDateFormat더 이상 사용하지 마십시오. 더 이상 사용 되지 않습니다. java.time대신 패키지를 사용하십시오 . 에서 SimpleDateFormat의 경우, 사용 DateTimeFormatter. Java 7의 경우 아래 Andy Turner의 의견을 참조하십시오.
MC 황제

28
시간에 수학을 하지 마십시오 . 적절한 시간 라이브러리를 사용하십시오 ( java.time[여러분이 Java 7을 사용하고 있음에도 불구하고], ThreeTenBp , Joda).
Andy Turner

14
나는 누군가가가는 회의에 참석하고 싶었다. "좋아요. 이제 우리는 시간대가 생겼습니다. 100 % arse-mode로 가서 일광 절약이라고 불리는 것을 구현했습니다. 내 산 여행 후 꿈에서 나에게 왔습니다. 지난 밤."
MonkeyZeus

5
@gmauch TimeUnit는 DST에 대해 아는 척하지 않습니다. javadoc이 말한 것처럼 : 나노초는 1/100 분의 1 초, 1 분의 1 분의 1 밀리 초, 1 분의 1 분의 1 초, 1 분의 60 초, 1 시간 60 분, 1 일로 정의됩니다. 이십사 시간 . --- DST는 연중 2 일이 정확히 24 시간이되지 않기 때문에 TimeUnitDST가 관련 될 때 잘못됩니다.
Andreas

1
이 문제는 일광 절약 시간제 표준 시간대에있는 컴퓨터에서만 발생합니다. 일광 절약 시간제를 사용하지 않는 시간대의 정확한 일수 (29)를 제공합니다!
Gopinath

답변:


207

문제는 2020 년 3 월 8 일 일요일의 일광 절약 시간제 변경으로 인해 해당 날짜 사이에 28 일 및 23 시간 이 있다는 것입니다. TimeUnit.DAYS.convert(...) 깎다결과를 28 일로 .

문제를 보려면 (미국 동부 표준시입니다) :

SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
            fmt.parse("1-03-2020 00:00:00").getTime();

System.out.println(diff);
System.out.println("Days: " + TimeUnit.DAYS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Hours: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS));
System.out.println("Days: " + TimeUnit.HOURS.convert(Math.abs(diff), TimeUnit.MILLISECONDS) / 24.0);

산출

2502000000
Days: 28
Hours: 695
Days: 28.958333333333332

수정하려면 DST가없는 시간대 (예 : UTC)를 사용하십시오 .

SimpleDateFormat fmt = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
long diff = fmt.parse("30-03-2020 00:00:00").getTime() -
            fmt.parse("1-03-2020 00:00:00").getTime();

산출

2505600000
Days: 29
Hours: 696
Days: 29.0

121
"수정하기"는 시간과 함께 수학을하지 않습니다. 적절한 날짜 / 시간 라이브러리를 사용하십시오.
Andy Turner

62
@AndyTurner 내장 Java 7 API를 사용하여 수정합니다. 수정 될 있기 때문에 올바른 답변이 어떻게 표시되는지 보여줍니다. 누군가이 계산을 수행하기 위해 전체 라이브러리 (Joda-Time, ThreeTen 등)를 포함하도록 강요하는 것은 과잉입니다. 물론 라이브러리를 사용하는 것이 좋지만 필수 는 아닙니다 .
Andreas

38
나는 정중하게 동의하지 않습니다. 계산을하려면 올바르게 수행하십시오. 제대로하기 위해 비용을 지불하십시오.
Andy Turner

16
내 € 0.02 : 외부 라이브러리없이 Java 7에서 수행하는 "올바른"방법은 GregorianCalendar객체 를 사용 하고 종료 날짜에 도달 할 때까지 한 번에 하루를 추가하는 것입니다. 나는 그것을 피하기 위해 높은 가격을 지불합니다. 그리고 이미 Java 8, 9, 10, 11, 12, 13의 일부인 라이브러리의 백 포트를 추가하는 것은 비용이 많이 들지 않습니다. 반대로, 다음에 날짜 나 시간으로 무엇이든해야 할 때는 이미 이익이 될 것입니다.
Ole VV

27
UTC에서도 6 월 마지막 날은 실제 경고 나 예측 가능성없이 1 초가 너무 짧습니다. 항상 날짜-시간 라이브러리를 사용하십시오.
Affe

41

이 문제의 원인은 Andreas의 답변 에 이미 언급되어 있습니다.

문제는 무엇을 정확하게 계산합니다. 실제 차이가 28이 아니라 29 여야한다고 말하고 "위치 / 지역 시간이 문제가 될 수 있는지 " 묻는 것은 실제로 계산하고자하는 내용을 나타냅니다. 분명히 시간대 차이를 없애고 싶습니다.

시간과 시간대없이 요일 만 계산하려고한다고 가정합니다.

자바 8

아래에서 사이의 일 수를 올바르게 계산하는 방법의 예에서 나는 시간과 시간대가없는 날짜 –를 정확하게 나타내는 클래스를 사용하고 LocalDate있습니다.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d-MM-yyyy HH:mm:ss");
LocalDate start = LocalDate.parse("1-03-2020 00:00:00", formatter);
LocalDate end = LocalDate.parse("30-03-2020 00:00:00", formatter);

long daysBetween = ChronoUnit.DAYS.between(start, end);

그 주 ChronoUnit, DateTimeFormatter그리고 LocalDate받는 사람에 따라, 당신에게 사용할 수없는 자바 8, 적어도이 필요합니다 태그를. 그러나 아마도 미래의 독자들에게 해당 될 것입니다.

Ole VV에서 언급했듯이 Java 8 Date and Time API 기능을 Java 6 및 7로 백 포트 하는 ThreeTen Backport 도 있습니다 .


2
@ OleV.V. ThreeTen이 있다는 것을 알고 있습니다. 일부 사용자는 몇 번 언급했을 수도 있습니다. (텍스트를 포함하는 사용자 5772882의 모든 게시물과 댓글을 반환하는 SEDE 쿼리에 링크하려고 ThreeTen했지만 ;-) 불행히도 글을 쓰는 시점에는 오프라인 상태입니다. 게시물을 업데이트합니다.
MC 황제

2
@OleVV 전혀 나쁘지 않습니다. 나는 java.time학교에서 아직도 구식 수업을 사용하기 때문에 대부분의 사람들은 단순히 무지하다고 생각 합니다. 그러나 Java 8 Date and Time API는 매우 잘 설계 되어 있으므로 사용 하지 않는 것이 좋습니다.
MC 황제
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.