ZonedDateTime을 날짜로 변환하는 방법?


101

내 데이터베이스에 서버에 구애받지 않는 날짜 시간을 설정하려고하는데 UTC DateTime을 설정하는 것이 가장 좋은 방법이라고 생각합니다. 내 db 서버는 Cassandra이고 Java 용 db 드라이버는 날짜 유형 만 이해합니다.

내 코드에서 새로운 Java 8 ZonedDateTime을 사용하여 지금 UTC ( ZonedDateTime.now(ZoneOffset.UTC))를 얻는다고 가정하면 어떻게이 ZonedDateTime 인스턴스를 "레거시"Date 클래스로 변환 할 수 있습니까?


Java에는 두 개의 "Date"클래스가 내장되어 java.util.Date있으며 java.sql.Date.
Basil Bourque

답변:


161

ZonedDateTime을 Date와 직접 사용할 수있는 인스턴트로 변환 할 수 있습니다.

Date.from(java.time.ZonedDateTime.now().toInstant());

27
아니요, 영역 시스템 기본값의 현재 날짜가됩니다.
Slim Soltani Dridi

6
@MilenKovachev 귀하의 질문은 의미가 없습니다. 날짜에는 시간대가 없습니다. 단지 시간의 순간을 나타냅니다.
assylias

1
@assylias 사실, 당신의 진술은 의미가 없습니다. 시간이 저장되는 형식은 시간대를 의미합니다. 날짜는 UTC를 기반으로하지만, 불행히도 Java는 어리석은 일을 수행하고 그렇게 취급하지 않으며, 그 위에 시간을 UTC 대신 로컬 TZ로 간주합니다. Data, LocalDateTime, ZonedDateTime이 데이터를 저장하는 방식은 시간대를 의미합니다. 그들이 사용되는 방식은 마치 TZ가 존재하지 않는 것처럼, 단순히 잘못되었습니다. java.util.Date에는 암시 적으로 JVM 기본값의 TZ가 있습니다. 사람들이 그것을 다른 것으로 취급한다는 사실 (Java 문서 포함!)은 단지 나쁜 사람들입니다.
David

5
@David uh no-a Date는 epoch 이후 밀리 초 수이므로 UTC와 관련이 있습니다. 당신이 경우 인쇄 를 기본 시간대가 사용되지만, Date 클래스에서 ... 참조는 예를 들어 "매핑"섹션은 사용자의 시간대에 대한 지식이없는 docs.oracle.com/javase/tutorial/datetime/iso/legacy을 .html 중에서 ... 혼란으로 볼 수있다 - 그리고 LocalDateTime은 시간대에 관계없이 명시 적이다
assylias

1
귀하의 답변은 불필요하게 ZonedDateTime. 이 java.time.Instant클래스는을 직접 대체하며 java.util.Date둘 다 UTC로 순간을 나타내지 만 Instant밀리 초 대신 나노초의 더 미세한 해상도를 사용합니다. Date.from( Instant.now() )당신의 해결책이어야했습니다. 또는 그 문제 new Date()에 대해 UTC로 현재 순간을 캡처하여 동일한 효과를 발휘합니다.
Basil Bourque

64

tl; dr

java.util.Date.from(  // Transfer the moment in UTC, truncating any microseconds or nanoseconds to milliseconds.
    Instant.now() ;   // Capture current moment in UTC, with resolution as fine as nanoseconds.
)

위의 코드에는 의미가 없지만. 모두 java.util.DateInstant항상 UTC에, UTC의 순간을 나타냅니다. 위의 코드는 다음과 같은 효과가 있습니다.

new java.util.Date()  // Capture current moment in UTC.

여기서 ZonedDateTime. 이미있는 경우 ZonedDateTime,을 추출하여 UTC로 조정합니다 Instant.

java.util.Date.from(             // Truncates any micros/nanos.
    myZonedDateTime.toInstant()  // Adjust to UTC. Same moment, same point on the timeline, different wall-clock time.
)

다른 답변 정답

그만큼 응답 하여 ssoltanid은 올바르게 새로운 학교 java.time 객체를 (변환하는 방법, 특정 질문을 해결 ZonedDateTime오래된 학교에) java.util.Date객체입니다. InstantZonedDateTime에서를 추출하고 java.util.Date.from().

데이터 손실

이 점에 유의 데이터 손실을 고통 으로, Instant트랙 나노초 이후 시대 동안 java.util.Date트랙 (밀리 초) 시대입니다.

밀리 초, 마이크로 초 및 나노초의 해상도를 비교하는 다이어그램

귀하의 질문과 의견은 다른 문제를 제기합니다.

UTC로 서버 유지

일반적으로 서버는 호스트 OS를 UTC로 설정해야합니다. JVM은 내가 알고있는 Java 구현에서이 호스트 OS 설정을 기본 시간대로 선택합니다.

시간대 지정

그러나 JVM의 현재 기본 시간대에 의존해서는 안됩니다. 호스트 설정을 선택하는 대신 JVM을 시작할 때 전달 된 플래그가 다른 시간대를 설정할 수 있습니다. 더 나쁜 점은 앱의 모든 스레드에있는 모든 코드가 java.util.TimeZone::setDefault런타임에 기본값을 변경하도록 호출 할 수 있다는 것입니다.

카산드라 Timestamp 유형

모든 괜찮은 데이터베이스 및 드라이버가 자동으로 저장을 위해 UTC에 전달 된 날짜와 시간을 조정 처리해야합니다. 나는 Cassandra를 사용하지 않지만 날짜 시간에 대한 기본적인 지원이있는 것 같습니다. 문서에 따르면Timestamp 유형은 동일한 시대 (UTC에서 1970 년의 첫 순간)의 밀리 초 수입니다.

ISO 8601

또한 Cassandra는 ISO 8601 표준 형식 의 문자열 입력을 허용합니다 . 다행히 java.time은 문자열 구문 분석 / 생성을위한 기본값으로 ISO 8601 형식을 사용합니다. Instant클래스 'toString 구현은 잘 할 것입니다.

정밀도 : 밀리 초 대 Nanosecord

그러나 먼저 ZonedDateTime의 나노초 정밀도를 밀리 초로 줄여야합니다. 한 가지 방법은 밀리 초를 사용하여 새로운 인스턴트를 만드는 것입니다. 다행히 java.time에는 밀리 초 단위로 변환하는 몇 가지 편리한 방법이 있습니다.

예제 코드

다음은 Java 8 업데이트 60의 몇 가지 예제 코드입니다.

ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );

Instant instant = zdt.toInstant();
Instant instantTruncatedToMilliseconds = Instant.ofEpochMilli( instant.toEpochMilli() );
String fodderForCassandra = instantTruncatedToMilliseconds.toString();  // Example: 2015-08-18T06:36:40.321Z

또는이 Cassandra Java 드라이버 문서 에 따라 java.util.Date인스턴스를 전달할 수 있습니다 (와 혼동하지 마십시오 java.sqlDate). 따라서 instantTruncatedToMilliseconds위 코드에서 juDate를 만들 수 있습니다.

java.util.Date dateForCassandra = java.util.Date.from( instantTruncatedToMilliseconds );

이렇게 자주하면 한 줄로 만들 수 있습니다.

java.util.Date dateForCassandra = java.util.Date.from( zdt.toInstant() );

그러나 약간의 유틸리티 방법을 만드는 것이 더 깔끔 할 것입니다.

static public java.util.Date toJavaUtilDateFromZonedDateTime ( ZonedDateTime zdt ) {
    Instant instant = zdt.toInstant();
    // Data-loss, going from nanosecond resolution to milliseconds.
    java.util.Date utilDate = java.util.Date.from( instant ) ;
    return utilDate;
}

이 모든 코드가 질문과 다른 점을 주목하십시오. 질문의 코드는 ZonedDateTime 인스턴스의 시간대를 UTC로 조정하려고했습니다. 그러나 그것은 필요하지 않습니다. 개념적으로 :

ZonedDateTime = 인스턴트 + ZoneId

이미 UTC로되어있는 Instant 부분을 추출하기 만하면됩니다 (기본적으로 UTC로되어 있습니다. 정확한 세부 사항은 클래스 문서를 참조하십시오).


Java의 최신 및 레거시 날짜-시간 유형 표


java.time 정보

java.time의 프레임 워크는 나중에 자바 8에 내장되어 있습니다. 이 클래스는 까다로운 기존에 대신 기존 과 같은 날짜 - 시간의 수업을 java.util.Date, Calendar, SimpleDateFormat.

Joda 타임 지금 프로젝트, 유지 관리 모드는 의에 마이그레이션을 조언 java.time 클래스.

자세한 내용은 Oracle Tutorial을 참조하십시오 . 그리고 많은 예제와 설명을 위해 Stack Overflow를 검색하십시오. 사양은 JSR 310 입니다.

java.time 객체를 데이터베이스와 직접 교환 할 수 있습니다 . JDBC 4.2 이상을 준수 하는 JDBC 드라이버를 사용하십시오 . 문자열이나 클래스 가 필요하지 않습니다 .java.sql.*

java.time 클래스는 어디서 구할 수 있습니까?

Java 또는 Android 버전과 함께 사용할 java.time 라이브러리 표

ThreeTen - 추가 프로젝트 추가 클래스와 java.time를 확장합니다. 이 프로젝트는 java.time에 향후 추가 될 수있는 가능성을 입증하는 곳입니다. 당신은 여기에 몇 가지 유용한 클래스와 같은 찾을 수 있습니다 Interval, YearWeek, YearQuarter, 그리고 .


멋진 설명, 잘 설명되어 있습니다. 정말 고맙습니다!
Gianmarco F.

4

다음은 현재 시스템 시간을 UTC로 변환하는 예입니다. ZonedDateTime을 문자열로 형식화 한 다음 문자열 개체는 java.text DateFormat을 사용하여 날짜 개체로 구문 분석됩니다.

    ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
    final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
    final DateFormat FORMATTER_YYYYMMDD_HH_MM_SS = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
    String dateStr = zdt.format(DATETIME_FORMATTER);

    Date utcDate = null;
    try {
        utcDate = FORMATTER_YYYYMMDD_HH_MM_SS.parse(dateStr);
    }catch (ParseException ex){
        ex.printStackTrace();
    }

4

Android 용 ThreeTen 백 포트 를 사용 중이고 최신 버전 Date.from(Instant instant)(최소 API 26 필요)을 사용할 수없는 경우 다음을 사용할 수 있습니다.

ZonedDateTime zdt = ZonedDateTime.now();
Date date = new Date(zdt.toInstant().toEpochMilli());

또는:

Date date = DateTimeUtils.toDate(zdt.toInstant());

Basil Bourque의 답변 에서 조언을 읽어보십시오.


1
ThreeTen Backport (및 ThreeTenABP)에는 DateTimeUtils변환 메서드가 있는 클래스가 포함 되어 있으므로 Date date = DateTimeUtils.toDate (zdt.toInstant ());`를 사용합니다. 그렇게 낮은 수준이 아닙니다.
Ole VV

1

Java 8 이상에 내장 된 java.time 클래스를 사용하여 이를 수행 할 수 있습니다 .

ZonedDateTime temporal = ...
long epochSecond = temporal.getLong(INSTANT_SECONDS);
int nanoOfSecond = temporal.get(NANO_OF_SECOND);
Date date = new Date(epochSecond * 1000 + nanoOfSecond / 1000000);

죄송합니다. 시간과이 모든 상수는 어디에서 왔습니까?
Milen Kovachev 2015.08.28

@MilenKovachev ZonedDateTime은 임시의 인스턴스
피터 Lawrey

감사합니다.하지만 내 의견이 어리석은 것처럼 보이지 않도록 답변을 편집했다고 언급 했어야합니다.
Milen Kovachev 2015 년

2
@MilenKovachev는 댓글을 삭제할 수 있지만 어리석은 질문은 아닙니다.
Peter Lawrey 2015-08-28

1
이 답변이 반대 투표 된 이유를 이해할 수 없습니다. Instants는 초와 나노초를 추적합니다. Dates 트랙 밀리 초. 이 변환은 하나에서 다른 것으로 올 바릅니다.
scottb

1

나는 이것을 사용한다.

public class TimeTools {

    public static Date getTaipeiNowDate() {
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Asia/Taipei");
        ZonedDateTime dateAndTimeInTai = ZonedDateTime.ofInstant(now, zoneId);
        try {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInTai.toString().substring(0, 19).replace("T", " "));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant()); 작동하지 않기 때문에 !!! 컴퓨터에서 응용 프로그램을 실행하면 문제가되지 않습니다. 그러나 AWS, Docker 또는 GCP의 모든 지역에서 실행하면 문제가 발생합니다. 컴퓨터는 클라우드의 시간대가 아니기 때문입니다. 코드에서 올바른 시간대를 설정해야합니다. 예 : 아시아 / 타이페이. 그런 다음 AWS, Docker 또는 GCP에서 수정됩니다.

public class App {
    public static void main(String[] args) {
        Instant now = Instant.now();
        ZoneId zoneId = ZoneId.of("Australia/Sydney");
        ZonedDateTime dateAndTimeInLA = ZonedDateTime.ofInstant(now, zoneId);
        try {
            Date ans = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateAndTimeInLA.toString().substring(0, 19).replace("T", " "));
            System.out.println("ans="+ans);
        } catch (ParseException e) {
        }
        Date wrongAns = Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant());
        System.out.println("wrongAns="+wrongAns);
    }
}

1
이것은 7 개의 답변 중 가장 복잡한 방법 중 하나 인 것 같습니다. 장점이 있습니까? 나는 그렇게 생각하지 않을 것이다. 형식 지정 및 구문 분석을 거칠 필요가 없습니다.
Ole VV

물어봐 주셔서 감사합니다. Date wrongAns = Date.from (java.time.ZonedDateTime.ofInstant (now, zoneId) .toInstant ()) ;; 작동하지 않습니다 !!!!!
beehuang

내 시간대에서 17:08 직전에 초 스 니펫을 실행했는데 ans=Mon Jun 18 01:07:56 CEST 2018, 이는 정확하지 않은 다음 wrongAns=Sun Jun 17 17:07:56 CEST 2018, 맞습니다.
Ole VV

내 컴퓨터에서 앱을 실행할 때 문제가 없습니다. 그러나 도커를 사용하면 문제가 있습니다. Docker의 시간대는 컴퓨터의 시간대가 아니기 때문입니다. 코드에서 올바른 시간대를 설정해야합니다. 예 : 아시아 / 타이페이. 그런 다음 AWS, Docker, GCP 또는 모든 컴퓨터에서 수정됩니다.
beehuang

@ OleV.V. 이해할 수 있습니까 ?? 또는 질문이 있습니까?
beehuang

1

받아 들인 대답이 저에게 효과가 없었습니다. 반환 된 날짜는 항상 원래 시간대의 날짜가 아니라 현지 날짜입니다. 저는 UTC + 2에 살고 있습니다.

//This did not work for me
Date.from(java.time.ZonedDateTime.now().toInstant()); 

ZonedDateTime에서 올바른 날짜를 가져 오는 두 가지 다른 방법을 생각해 냈습니다.

이 ZonedDateTime for Hawaii가 있다고 가정합니다.

LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneId.of("US/Hawaii"); // UTC-10

또는 원래 요청한 UTC의 경우

Instant zulu = Instant.now(); // GMT, UTC+0
ZonedDateTime zdt = zulu.atZone(ZoneId.of("UTC"));

대안 1

java.sql.Timestamp를 사용할 수 있습니다. 간단하지만 프로그래밍 무결성에도 영향을 미칠 것입니다.

Date date1 = Timestamp.valueOf(zdt.toLocalDateTime());

대안 2

millis에서 Date를 만듭니다 ( 앞서 여기 에서 답변 함 ). 로컬 ZoneOffset은 필수입니다.

ZoneOffset localOffset = ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now());
long zonedMillis = 1000L * zdt.toLocalDateTime().toEpochSecond(localOffset) + zdt.toLocalDateTime().getNano() / 1000000L;
Date date2 = new Date(zonedMillis);

0

beehuang과 같은 도커 애플리케이션의 경우 시간대를 설정해야합니다.

또는 withZoneSameLocal 을 사용할 수 있습니다 . 예를 들면 :

2014-07-01T00 : 00 + 02 : 00 [GMT + 02 : 00]는

Date.from(zonedDateTime.withZoneSameLocal(ZoneId.systemDefault()).toInstant())

7월 1일 중부 유럽 표준시 00시 00분 0초 2014 화 및로

Date.from(zonedDateTime.toInstant())

~ 2014 년 6 월 30 일 월요일 22:00:00 UTC


-1

지금 만 관심이 있다면 다음을 사용하십시오.

Date d = new Date();

지금은 관심이 있지만 지금은 UTC입니다.
Milen Kovachev 2015 년

2
예, 이제 UTC가 될 것입니다. Date는 더 잘 알지 못합니다.
Jacob Eckel

3
아니, 그렇지 않습니다. 이제 로컬 시스템 시간대가됩니다. 날짜는 시간대 정보를 저장하지 않지만 현재 시스템 시간을 사용하고 현재 시스템 시간대를 무시하므로 UTC로 다시 변환하지 않습니다
David

2
@David Date는 UTC epoch를 기준으로 시간을 유지하므로 미국의 시스템에서 new Date ()를 가져오고 일본의 컴퓨터에서 다른 객체를 가져 오면 동일합니다 (둘 다 내부적으로 보관하는 기간을 살펴보십시오).
Jacob Eckel 2015 년

1
@JacobEckel-예,하지만 tz가 US tz 인 동안 오전 9시를 날짜에 입력 한 다음 JP tz로 변경하고 오전 9시에 새 날짜를 생성하면 Date의 내부 값이 달라집니다. DST를 믹스에 넣 자마자 앱이 항상 UTC로 실행되지 않는 한 Date를 안정적으로 사용할 수 없습니다. 이는 대부분의 이유가 잘못된 코드를 중심으로 변경 될 수 있습니다.
David
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.