java.time.LocalDateTime과 java.util.Date 간 변환


484

Java 8에는 날짜 및 시간에 대한 완전히 새로운 API가 있습니다. 이 API에서 가장 유용한 클래스 중 하나는 LocalDateTime시간대 독립적 날짜-시간 값을 보유하기위한입니다.

java.util.Date이 목적으로 레거시 클래스 를 사용하는 수백만 줄의 코드가있을 것입니다 . 따라서 이전 코드와 새 코드를 인터페이스 할 때 두 코드 사이를 변환해야합니다. 이를 달성하기위한 직접적인 방법이없는 것처럼 보이면 어떻게 할 수 있습니까?




답변:


706

짧은 답변:

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

설명 : (에 따라 이 문제 에 대한 LocalDate)

이름에도 불구하고 java.util.Date"날짜"가 아니라 타임 라인의 순간을 나타냅니다. 객체 내에 저장된 실제 데이터는 long1970-01-01T00 : 00Z (1970 GMT / UTC 시작시 자정) 이후 밀리 초 수입니다.

java.util.DateJSR-310 과 동등한 클래스 는 다음 Instant과 같이 변환을 제공하는 편리한 방법이 있습니다.

Date input = new Date();
Instant instant = input.toInstant();
Date output = Date.from(instant);

java.util.Date인스턴스는 시간대의 개념이 없습니다. 당신이 호출하면이 이상하게 보일 수도 toString()A의 java.util.Date(가) 때문에, toString시간 영역에 상대적입니다. 그러나이 메소드는 실제로 Java의 기본 시간대를 사용하여 문자열을 제공합니다. 시간대는의 실제 상태의 일부가 아닙니다 java.util.Date.

Instant또한 시간대에 대한 정보가 포함되어 있지 않습니다. 따라서에서 Instant날짜를 현지 날짜 로 변환하려면 시간대를 지정해야합니다. 기본 영역 ZoneId.systemDefault()일 수도 있고 사용자 기본 설정의 시간대와 같이 응용 프로그램이 제어하는 ​​시간대 일 수도 있습니다. LocalDateTime인스턴트 및 시간대를 모두 사용하는 편리한 팩토리 방법이 있습니다.

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());

반대로 LocalDateTime시간대는 atZone(ZoneId)메소드 를 호출하여 지정됩니다 . 그런 ZonedDateTime다음를 다음으로 직접 변환 할 수 있습니다 Instant.

LocalDateTime ldt = ...
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date output = Date.from(zdt.toInstant());

에서 LocalDateTime로 변환 ZonedDateTime하면 예기치 않은 동작이 발생할 가능성이 있습니다. 일광 절약 시간 제로 인해 모든 현지 날짜-시간이 존재하지 않기 때문입니다. 가을 / 가을에는 동일한 현지 날짜-시간이 두 번 발생하는 현지 타임 라인이 겹칩니다. 봄에는 한 시간이 사라지는 간격이 있습니다. atZone(ZoneId)변환이 수행 할 작업 에 대한 자세한 정의 는 Javadoc을 참조하십시오 .

요약하면 a java.util.Date를 a 로 왕복 LocalDateTime하고 다시 a 로 왕복하면 java.util.Date일광 절약 시간 제로 인해 다른 순간으로 끝날 수 있습니다.

추가 정보 : 아주 오래된 날짜에 영향을 줄 또 다른 차이점이 있습니다. java.util.Date1582 년 10 월 15 일에 변경되는 달력을 사용하며, 그 이전의 날짜는 그레고리력 대신 Julian 달력을 사용합니다. 반대로 java.time.*ISO 캘린더 시스템 (Gregorian과 동일)을 항상 사용합니다. 대부분의 경우 ISO 캘린더 시스템이 원하는 것이지만 1582 년 이전의 날짜를 비교할 때 이상한 효과가 나타날 수 있습니다.


5
명확한 설명을 주셔서 감사합니다. 특히 이유를 java.util.Date하지 않습니다 시간대가 포함되어 있지만 중에 인쇄 toString(). 이벤트 공식 문서 는 게시 할 때이를 명확하게 알려주지 않습니다.
Cherry

경고 : LocalDateTime.ofInstant(date.toInstant()... 순진하게 기대하는 것처럼 행동하지 않습니다. 예를 들어이 방법을 사용 new Date(1111-1900,11-1,11,0,0,0);하게됩니다 1111-11-17 23:53:28. 이전 예제에서 java.sql.Timestamp#toLocalDateTime()결과가 필요한 경우 의 구현을 살펴보십시오 1111-11-11 00:00:00.
dog

2
아주 오래된 날짜 (1582 년 이전)에 관한 섹션을 추가했습니다. FWIW, java.util.Date의 1111-11-11은 캘린더 시스템의 차이로 인해 java.time의 1111-11-18과 실제 날짜가 같기 때문에 제안 된 수정 사항이 잘못되었을 수 있습니다 (6.5 분 차이). ) 1,900하기 전에 많은 시간이 시간대에 발생
JodaStephen

2
또한 가치가 그주의해야 할 java.sql.Date#toInstant가 발생합니다 UnsupportedOperationException. 따라서 toInstantRowMapper on에 사용하지 마십시오 java.sql.ResultSet#getDate.
LazerBass

132

여기에 내가 생각해 낸 것이 있습니다 (그리고 모든 Date Time 수수께끼와 마찬가지로 이상한 시간대-윤년-일광 조정에 따라 반박 될 것입니다 : D)

라운드 트립 : Date<<->>LocalDateTime

주어진: Date date = [some date]

(1) LocalDateTime<< Instant<<Date

    Instant instant = Instant.ofEpochMilli(date.getTime());
    LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

(2) Date<< Instant<<LocalDateTime

    Instant instant = ldt.toInstant(ZoneOffset.UTC);
    Date date = Date.from(instant);

예:

주어진:

Date date = new Date();
System.out.println(date + " long: " + date.getTime());

(1) LocalDateTime<< Instant<< Date:

만들기 Instant에서 Date:

Instant instant = Instant.ofEpochMilli(date.getTime());
System.out.println("Instant from Date:\n" + instant);

만들기 Date에서 Instant(필요는 없습니다 만, 그림) :

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

만들기 LocalDateTime에서Instant

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
System.out.println("LocalDateTime from Instant:\n" + ldt);

(2) Date<< Instant<<LocalDateTime

만들기 Instant에서 LocalDateTime:

instant = ldt.toInstant(ZoneOffset.UTC);
System.out.println("Instant from LocalDateTime:\n" + instant);

만들기 Date에서 Instant:

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

출력은 다음과 같습니다.

Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

Instant from Date:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

LocalDateTime from Instant:
2013-11-01T14:13:04.574

Instant from LocalDateTime:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

2
@ scottb 당신은 나 또는 질문을 제기 한 사람에게 그 문제를 다루고 있습니까? 내 생각에 관해서 는, jdk 8 API에 명시 적으로 변환을 명시하는 것이 좋을 것입니다. 적어도 그렇게 할 수 있습니다. 어쨌든 새로운 Java-8 기능을 포함하고이를 수행하는 방법을 알기 위해 리팩토링 될 라이브러리가 많이 있습니다.
코디네이터

4
@scottb 왜 타사 사례가 드문 일입니까? 지독한 공통점. 한 가지 예 : JDBC 4 이하 (5가 아님).
Raman

5
일반적인 JSR-310 규칙으로서 epoch-millis를 사용하여 유형간에 변환 할 필요가 없습니다. 더 나은 대안은 객체를 사용하여 존재합니다. 아래의 전체 답변을 참조하십시오. 위의 답변은 UTC와 같은 영역 오프셋을 사용하는 경우에만 완전히 유효합니다. 답변의 일부는 America / New_York와 같은 표준 시간대에서 작동하지 않습니다.
JodaStephen

2
대신에 Instant.ofEpochMilli(date.getTime())할 일date.toInstant()
염소

1
@goat toInstant()은 arggggh에 실패한다는 것을 제외하고는 멋지게 보입니다 java.sql.Date! 결국 사용하기가 더 쉽습니다 Instant.ofEpochMilli(date.getTime()).
vadipp

22

기본 시간대가 필요한 경우 훨씬 편리한 방법입니다.

Date d = java.sql.Timestamp.valueOf( myLocalDateTime );

25
물론, 더 쉽지만 적어도 IMHO와 간단한 날짜 처리와 jdbc 관련 항목을 혼합하는 것을 좋아하지 않습니다.
Enrico Giurin

9
죄송합니다. 간단한 문제에 대한 끔찍한 해결책입니다. 5를 올림픽 로고의 고리 수와 동일하게 정의하는 것과 같습니다.
Madbreaks

7

다음은 새 API LocalDateTime에서 java.util.date로 변환 할 때 작동하는 것 같습니다.

Date.from(ZonedDateTime.of({time as LocalDateTime}, ZoneId.systemDefault()).toInstant());

역변환은 (희망적으로) 비슷한 방식으로 달성 될 수 있습니다 ...

그것이 도움이되기를 바랍니다 ...


5

모든 것이 여기에 있습니다 : http://blog.progs.be/542/date-to-java-time

"라운드 트리핑"의 대답은 정확하지 않습니다.

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

시스템 시간대가 UTC / GMT가 아닌 경우 시간을 변경하십시오!


LocalDateTime에서 두 번 겹치는 가을에 1 년에 1 시간 동안 만 수행하는 경우에만 스프링 포워드 이동은 문제를 일으키지 않습니다. 대부분의 경우 양방향을 올바르게 변환합니다.
David


3

이것이 가장 간단한 방법인지 또는 가장 좋은 방법인지 또는 함정이 있는지 확실하지 않지만 작동합니다.

static public LocalDateTime toLdt(Date date) {
    GregorianCalendar cal = new GregorianCalendar();
    cal.setTime(date);
    ZonedDateTime zdt = cal.toZonedDateTime();
    return zdt.toLocalDateTime();
}

static public Date fromLdt(LocalDateTime ldt) {
    ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
    GregorianCalendar cal = GregorianCalendar.from(zdt);
    return cal.getTime();
}

3
에서가는 함정 확실히 거기 LocalDateTime에가 Date. 일광 절약 전환시 a LocalDateTime가 없거나 두 번 발생할 수 있습니다. 각각의 경우에 일어나고 싶은 일을 해결해야합니다.
Jon Skeet

1
BTW, GregorianCalendar새로운 java.timeAPI를 대체하려는 오래된 어색한 API에 속함
Vadzim

3

당신이 안드로이드에 있고 threetenbp 를 사용 한다면DateTimeUtils 하는 대신 .

전의:

Date date = DateTimeUtils.toDate(localDateTime.atZone(ZoneId.systemDefault()).toInstant());

Date.fromapi 26 이상에서만 지원되므로 사용할 수 없습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.