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

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

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



답변

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

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


답변

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, 그리고 .


답변

다음은 현재 시스템 시간을 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();
    }


답변

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의 답변 에서 조언을 읽어보십시오.


답변

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);


답변

나는 이것을 사용한다.

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);
    }
}


답변

받아 들인 대답이 저에게 효과가 없었습니다. 반환 된 날짜는 항상 원래 시간대의 날짜가 아니라 현지 날짜입니다. 저는 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);