2014-04-01 3 views
17

Java 8에서 새로운 java.time 패키지를 사용하고 있습니다. Instant으로 변환되는 java.util.Date을 제공하는 기존 데이터베이스가 있습니다.Java 8 java.time : InstantDesign LocalDateTime에 TemporalUnit 추가

다른 데이터베이스 플래그를 기반으로하는 기간을 추가하려고합니다. 일, 주, 달 또는 년을 추가 할 수 있습니다. 나는 내가 추가하고있는 것을 신경 쓰고 싶지 않고 앞으로 더 많은 옵션을 추가 할 수 있기를 바란다.

내 첫 번째 생각은 Instant.plus() 이었지만 하루보다 큰 값은 UnsupportedTemporalTypeException이되었습니다. 인스턴트 분명히 큰 단위 시간에 작업을 지원하지 않습니다. 좋아, 뭐든간에, LocalDateTime. 이 새로운 시간 API를 사용하여 내 첫 번째 시간입니다, 이제

private Date adjustDate(Date myDate, TemporalUnit unit){ 
    Instant instant = myDate.toInstant(); 
    LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 
    dateTime = dateTime.plus(1, unit); 
    Instant updatedInstant = dateTime.atZone(ZoneId.systemDefault()).toInstant(); 
    return new Date(dueInstant.toEpochMilli()); 
} 

, 그래서 내가 여기서 뭔가를 놓친 수 있습니다

는 그래서 나에게이 코드를 제공합니다. 내가 날짜 부분을 사용하지 않은

Date --> Instant --> LocalDateTime --> do stuff--> Instant --> Date. 

하더라도, 나는 아직도 조금 어색 생각 :하지만 내가 가야 나에게 거추장스러운 것 같다. 그래서 제 질문은 이것입니다, 저는 이것을 완전히 잘못하고 있으며, 이것을하는 가장 좋은 방법은 무엇입니까?


편집 : 코멘트에 토론에 확장.

LocalDateTime 및 Instant가 java.util.Date 및 java.sql.Timestamp를 사용하여 재생되는 방식에 대해 더 나은 아이디어가 있다고 생각합니다. 모두에게 감사드립니다.

이제 더 실제적인 고려 사항입니다. 사용자가 세계 어느 곳에서든지 임의의 시간대로 데이트를 보냈다고 가정 해 봅시다. 그들은 내가 LocalDateTime으로 구문 분석 할 수있는 2014-04-16T13:00:00을 보낸다. 그런 다음이를 java.sql.Timestamp로 직접 변환하고 내 데이터베이스에서 유지합니다.

이제 다른 작업을 수행하지 않고 내 데이터베이스에서 java.sql.timestamp를 가져오고 LocalDateTime으로 변환하여 timestamp.toLocalDateTime()을 사용합니다. 문제 없다. 그런 다음 ISO_DATE_TIME 형식을 사용하여이 값을 내 사용자에게 반환합니다. 결과는 2014-04-16T09:00:00입니다.

이 차이는 UTC와의 암시 적 변환으로 인해 발생한다고 가정합니다. 내 기본 시간대가 왜 숫자가 4 시간 떨어져 있는지 설명하는 값 (EDT, UTC-4)에 적용될 수 있다고 생각합니다.

새로운 질문. 여기서 현지 시간에서 UTC로 암시 적으로 변환하는 것은 어디에서 발생합니까? 시간대를 보존하는 더 좋은 방법은 무엇입니까? 현지 시간을 문자열 (2014-04-16T13 : 00 : 00)에서 LocalDateTime으로 직접 이동하지 않아야합니까? 사용자 입력에서 시간대가 필요합니까?

+3

여기서 어떤 가치를 나타낼 예정입니까? 인스턴트 (Instant)는 * 논리적으로 * 달력 시스템에 대해 알지 못합니다 - 단지 시점 일뿐입니다 - 그래서 한 달 추가하는 것은 의미가 없습니다. 시스템 시간대를 실제로 사용할지 여부를 신중하게 고려해야합니다. 실행중인 위치에 따라 동일한 값에 대해 다른 결과를 얻고 싶습니까? –

+0

@JonSkeet ZoneId를 임의로 선택하는 것이 더 적절합니까? 항상 그리니치 표준시인가? 그것은 그 자체의 이슈들로 나타날지도 모릅니다. 아마도 문제는 java.util.date를 나타내는 방법을 모른다는 것입니다. 나는 시간을 보낸다. 특정 조건이 충족되는 경우 해당 시점을 향후 1 개월 (또는 일, 년, 기타)으로 변경하고 싶습니다. – jacobhyphenated

+0

UTC를 사용하는 것이 가장 좋은 방법 일 수 있지만 요구 사항이 무엇인지 생각해야합니다. 특정 시점 (시간대 또는 달력 없음)을 한 달씩 변경한다는 것은 무엇입니까? –

답변

16

필자는 최종 해결책과 매우 긴 코멘트 체인에 대한 요약을 토대로 답변을 게시 할 것입니다.

Date --> Instant --> LocalDateTime --> Do stuff --> Instant --> Date 

은 시간 영역 정보를 보존하고 여전히 일정 및 내부의 상황을 모두 알고있는 객체와 같은 날짜에 작업을 할 필요가있다 :

은 전체 변환 체인을 시작합니다.그렇지 않으면 우리는 암시 적으로 현지 표준 시간대로 변환 할 위험을 감수하고, 사람이 읽을 수있는 날짜 형식으로 변환하려고 시도하면 시간이 변경 될 수 있습니다.

예를 들어 java.sql.Timestamp 클래스의 toLocalDateTime() 메서드는 암시 적으로 기본 표준 시간대로 변환됩니다. 이는 제 목적에는 바람직하지 않지만 반드시 나쁜 행동은 아닙니다. 그러나 그것을 인식하는 것이 중요합니다. 이는 레거시 Java 날짜 객체에서 LocalDateTime 객체로 직접 변환 할 때의 문제입니다. 레거시 개체는 일반적으로 UTC로 간주되므로 변환에서는 로컬 시간대 오프셋을 사용합니다.

이제 우리 프로그램이 2014-04-16T13:00:00의 입력을 받아 java.sql.Timestamp으로 데이터베이스에 저장한다고 가정 해 보겠습니다.

//Parse string into local date. LocalDateTime has no timezone component 
LocalDateTime time = LocalDateTime.parse("2014-04-16T13:00:00"); 

//Convert to Instant with no time zone offset 
Instant instant = time.atZone(ZoneOffset.ofHours(0)).toInstant(); 

//Easy conversion from Instant to the java.sql.Timestamp object 
Timestamp timestamp = Timestamp.from(instant); 

이제 우리는 타임 스탬프를 가지고 그것에 일의 일부 번호를 추가 :

Timestamp timestamp = ... 

//Convert to LocalDateTime. Use no offset for timezone 
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); 

//Add time. In this case, add one day. 
time = time.plus(1, ChronoUnit.DAYS); 

//Convert back to instant, again, no time zone offset. 
Instant output = time.atZone(ZoneOffset.ofHours(0)).toInstant(); 

Timestamp savedTimestamp = Timestamp.from(output); 

지금 우리는 단지 ISO_LOCAL_DATE_TIME의 형식으로 사람이 읽을 수있는 문자열로 출력해야합니다.

Timestamp timestamp = .... 
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); 
String formatted = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(time); 
+6

ZoneOffset.ofHours (0)은 ZoneOffset.UTC로 표현하는 것이 좋습니다. 이 클래스는 LocalDateTime의 모든 더하기/빼기 동작을 지원하지만 표준 시간대 정보는 유지하므로 ZonedDateTime을 사용하는 것이 더 쉽습니다. – JodaStephen

+0

일의 특정 유즈 케이스의 경우, (Local/Zoned) 날짜 시간이 필요하지 않습니다. 일의 지속 시간을 즉석에서 직접 추가 할 수 있기 때문에 :'Timestamp.from (timestamp.toInstant(). ofDays (1)))' –

+1

'Duration.ofDays'를'Instant'에 ​​추가하면 일광 절약 시간제 등을 고려하지 않으므로 권장하지 않습니다. 대신에'ZonedDateTime'으로 변환하십시오. –