2017-02-10 5 views
0

자바 캘린더를 사용하여 자정 (GMT)에 날짜를 만든 다음, 현지 시간대로 변경하여 요청한 시간이 1:00 (GMT + 1). 다음 코드는 작동합니다. 2 개의 성공적인 주장.자바 캘린더 시간대 변경이 예상대로 작동하지 않습니다.

TimeZone TIMEZONE_GMT = TimeZone.getTimeZone("GMT"); 
TimeZone TIMEZONE_LOCAL = TimeZone.getDefault(); // GMT + 1 

private Calendar getMidnightGmtCalendarWithLocalTimezone() { 
    Calendar calendar = Calendar.getInstance(Locale.GERMAN); 
    calendar.set(2017, Calendar.JANUARY, 1, 0, 0, 0); 

    calendar.setTimeZone(TIMEZONE_GMT); // Probably not required 
    assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY)); // Assert 1 

    // Log.i("TAG", "" + calendar.get(Calendar.HOUR_OF_DAY)); 

    calendar.setTimeZone(TIMEZONE_LOCAL); 
    assertEquals(1, calendar.get(Calendar.HOUR_OF_DAY)); // Assert 2 
    return calendar; 
} 

이제 첫 번째 어설 션을 삭제하고 변경 사항을 기대하지 않습니다. 현실 : 2 단언은 이제 실패합니다! 저는 .get()의 구현을 이해하려고 노력했으며 분명히 시간도 계산하기 때문에 값을 수집하는 것이 아닙니다. 아직도 제 2 주장이 실패하는 이유를 모르겠습니다. 왜 이런 일이 않습니다

제 2 어설 션 (! 어설) 단지 문제가 아니라, Calendar.get (에서 발생해야하고,하지)

그래서 첫 번째 질문에 나는 로그 행의 주석을 해제하면 다시 성공? 두 번째 질문 : Timezone GMT의 자정에 시간을 설정 한 경우 로컬 표준 시간대가있는 Calendar 인스턴스가 있는지 어떻게 확인할 수 있습니까?

EDIT (? 즉, 어떻게 제대로 시간대를 변환 할 수 있습니다) : 나는 안드로이드에 사용하고, 그래서 Java8을 사용할 수 없습니다. Joda 시간 (아래에 제안 된대로)은 훌륭한 대안이며, 나는 그것에 대해 확실히 살펴볼 것입니다. 그러나, 내 질문에 대한 대답으로 나는 Java7 Calendar와 어떻게 작동하는지 알고 싶다. 왜냐하면 나는 그 코드에 잠시 묶여 있기 때문이다.

+3

JDK 8을 사용하는 경우 캘린더를 포기하고 새로운 java.time 패키지를 사용하는 것이 좋습니다. 지원 수명이 끝나지 않은 유일한 JDK 일 것입니다. – duffymo

+0

@duffymo 나는 왜'Calendar' 클래스의 사용을 포기해야하는지에 대한 권위있는 설명을 제공 할 수 있습니까? – Andremoniy

+0

신뢰할 수 있습니까? java.util.Calendar는 JDK 1.0의 빈티지입니다. 그것은 처음부터 그것이 뒤죽박죽이고 사용하기가 어렵다는 것을 인정 받았다. (IBM이 작성, 올바르게 회신 한 경우) JODA는 이러한 오류를 해결하기 위해 개발되었습니다. 오라클이 성공적으로 JDK 8을 java.time 패키지로 접어 넣었습니다. 캘린더는 20 년 넘게 실패한 것입니다. 그것은 거대한 개선으로 인정 된 것으로 대체되었습니다. 나는 역사에 근거한 권위를 주장한다. 그게 당신에게 충분합니까? – duffymo

답변

0

그래서, 나는 다음과 같은 코드를 실행 한 :

public static void main(String[] args) throws Exception { 
    TimeZone TIMEZONE_GMT = TimeZone.getTimeZone("GMT"); 

    Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("Europe/Berlin")); 
    calendar.set(2017, Calendar.JANUARY, 1, 0, 0, 0); 

    System.out.println(calendar.get(Calendar.HOUR_OF_DAY)); 
    calendar.setTimeZone(TIMEZONE_GMT); // Probably not required 
    System.out.println(calendar.get(Calendar.HOUR_OF_DAY)); 
} 

그것을 실행에, 나는 올바른 콘솔에서 023을 얻었다.

그런 다음 첫 번째 Sysout 문을 주석 처리하고 0 (나는 23을 인쇄 할 것으로 예상 됨)을 인쇄했습니다.

좀 더 자세히 살펴보면, 은 캘린더 개체가 밀리 초 만 저장한다는 것을 정확하게 설명하고있는 this SO 대답을 발견했습니다.

asserts에 대해 걱정하지 않으려 고합니다. 필요한 시간대 (기본 시간대가 필요한 시간대로 설정되어있는 경우 기본 시간대)와 함께 Calendar 인스턴스를 가져옵니다. 예 :

Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("Europe/Berlin")); 
//OR 
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("Europe/Berlin")); 
//OR 
Calendar calendar = Calendar.getInstance(); //If default timezone is Europe/Berlin 
3

TL; DR

사용 java.time하지 java.util.Calendar.

Instant.parse("2017-01-01T00:00:00Z")   // UTC. 
     .atZone(ZoneId.of("Africa/Algiers")) // Time zone with offset +01:00. 

Calendar 포함 Calendar

오래된 날짜 - 시간의 수업이 잘못 설계, 번잡 한 혼란, 그리고 결함이 피하십시오. 그들을 피하십시오. API가 너무 많아서 이해하려고 노력할 필요가 없습니다.

레거시 날짜 - 시간 클래스는 Java 8 이상에 내장 된 클래스의 java.time package으로 대체되었습니다.

Instant

Instant 클래스는 나노초의 해상도를 가진 UTC의 타임 라인에 잠시 시간을 나타냅니다.

Instant instant = Instant.parse("2017-01-01T00:00:00Z"); 

instant.toString() : 2017-01-01T00 : 00 : 00Z

ZonedDateTime

당신이 한 시간 앞인지 시간대에 그보고 요청 UTC. 시간대 Africa/Algiers을 적용하여 ZonedDateTime을 사용해 보겠습니다.

ZoneId z = ZoneId.of("Africa/Algiers"); 
ZonedDateTime zdt = instant.atZone(z); 

zdt.toString() : 2017-01-01T01 : 00 + 01 : 00 [아프리카/알제] 당신이 당신의 UTC 날짜를 구성하려면

OffsetDateTime

문자열을 구문 분석하기보다는 인수를 통해 (위에서 볼 수 있듯이) 인수를 OffsetDateTime의 팩토리 메서드에 전달합니다. 편리한 상수 ZoneOffset.UTC을 지정하십시오. 개월은 Calendar과 달리 1 월 -12 월에 1-12 사이에 산뜻하게 번호가 매겨집니다.

OffsetDateTime odt = OffsetDateTime.of(2017 , 1 , 1 , 0 , 0 , 0 , 0 , ZoneOffset.UTC) ; 

odt.toString() : 2017-01-01T00 : 위

유사 00Z

, 우리는 시간 영역으로 조정할 수있다. Daylight Saving Time (DST)과 같은 비정상적인 경우 조정이 이루어집니다.

ZoneId z = ZoneId.of("Africa/Algiers"); 
ZonedDateTime zdt = odt.atZoneSameInstant(z); 

zdt.toString는() : 2017-01-01T01 : 00 + 01 : 00 [아프리카/알제]

code run line in IdeOne.com 참조. 질문의 코드에서 Locale의 사용에 관한

Locale

... Locale는 완전히 시간대에 수직 분리 된 별개의 것입니다. Locale 개체는 날짜 - 시간 값을 나타 내기 위해 생성되는 문자열의 서식 지정에만 영향을줍니다. java.time

소개


java.time 프레임 워크는 나중에 자바 8에 내장되어 있습니다. 이 클래스는 java.util.Date, Calendar, & SimpleDateFormat과 같이 문제가있는 이전 legacy 날짜 - 시간 클래스를 대체합니다.

maintenance mode에있는 Joda-Time 프로젝트는 java.time 클래스로의 마이그레이션을 권장합니다.

자세히 알아 보려면 Oracle Tutorial을 참조하십시오. 그리고 많은 예제와 설명을 위해 스택 오버플로를 검색하십시오.사양은 JSR 310입니다.

어디에서 java.time 클래스를 얻을 수 있습니까?

  • Java SE 8SE 9 이후
    • 내장한다.
    • 번들로 구현 된 표준 Java API의 일부입니다.
    • Java 9에는 몇 가지 사소한 기능과 수정 사항이 추가되었습니다.
  • Java SE 6 및 java.time 기능의 대부분
  • Android

ThreeTen-Extra 프로젝트는 추가 클래스와 java.time를 확장합니다. 이 프로젝트는 향후 java.time에 추가 될 수있는 가능성을 입증합니다. 여기에 Interval, YearWeek, YearQuartermore과 같은 유용한 클래스가 있습니다.