2017-02-10 1 views
0

Я использую календарь Java для создания какой-либо даты в полночь (GMT), а затем измените часовой пояс на мой местный часовой пояс, чтобы удостовериться, что запрошенное время будет 1:00 (GMT + 1). Код ниже работает, вкл. 2 успешных утверждения.Изменение часового пояса Java не работает должным образом

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

Теперь я удаляю первое утверждение, и я ожидаю, что ничего не изменится. Реальность: второе утверждение теперь терпит неудачу! Я попытался понять реализацию .get(), и, по-видимому, он также вычисляет некоторое время, поэтому он не просто собирает значение. Тем не менее, я не понимаю, почему мое второе утверждение терпит неудачу.

2-й утверждают преуспевает снова, когда я раскомментировать строки лога (только, чтобы быть уверенным, что проблема возникает из-за calendar.get(), а не Assert!)

Итак первый вопрос: почему это происходит? Второй вопрос: как я могу убедиться, что у меня есть экземпляр календаря с локальным часовым поясом, когда я установил время в полночь для Timezone GMT? (Другими словами, как я правильно конвертировать часовых поясов?)

EDIT: Я использую это на Android, поэтому не может использовать Java8. Время Джоды (как предлагается ниже) - отличная альтернатива, и я обязательно буду в этом разбираться. Однако, как ответ на мой вопрос, я хотел бы знать, КАК это работает с календарем Java7, потому что я привязан к этому коду на данный момент.

+3

Я бы рекомендовал отказаться от Календаря и попробовать новый пакет java.time, если вы на JDK 8. Вы должны быть - это единственный JDK, который не прошел свой срок службы поддержки. – duffymo

+0

@duffymo Я полностью согласен с вами, просто интересно, не могли бы вы дать какое-либо авторитетное описание о том, почему * использование класса 'Calendar' должно быть отменено? – Andremoniy

+0

Авторитетный? java.util.Calendar - это JDK 1.0 vintage. С самого начала было признано, что оно запутано и сложно использовать. (Написано IBM, если я правильно помню.) JODA была разработана для устранения этих недостатков. Это было настолько успешным, что Oracle свернула его в 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)); 
} 

Исполняя его, я получил 0 и 23 в консоли, которая является правильным.

Я затем прокомментировал первый оператор Sysout и напечатал 0 (я ожидал, что он напечатает 23).

При изучении немного больше, я нашел this SO ответ, который правильно объясняет, что объект календаря хранит миллисекунды, а TimeZone не принимается во внимание.

Так что, если бы я тебя, я бы не слишком беспокоиться о 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

Т.Л., д-р

Использование 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.time package, встроенными в Java 8 и более поздние версии.

Instant

Instant класс представляет собой момент на временной шкале в UTC с разрешением наносекунд.

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

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

ZonedDateTime

Вы попросили, чтобы увидеть, что в часовом поясе, который один час вперед УНИВЕРСАЛЬНОЕ ГЛОБАЛЬНОЕ ВРЕМЯ. Итак, давайте попробуем применить часовой пояс Africa/Algiers, чтобы получить ZonedDateTime.

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

zdt.toString(): 2017-01-01T01: 00 + 01: 00 [Африка/Алжир]

OffsetDateTime

Если вы хотите построить свой UTC дату -time через аргументы, а не разбор строки (как показано выше), передать аргументы фабричному методу OffsetDateTime. Укажите удобную константу, ZoneOffset.UTC. Месяцы пронумерованы, 1-12 за январь-декабрь, в отличие от Calendar.

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 каркас встроен в Java 8 и более поздних версий. Эти классы вытесняют неприятные старые legacy классы времени, такие как java.util.Date, Calendar, & SimpleDateFormat.

Проект Joda-Time, в настоящее время в maintenance mode, советует перейти на классы java.time.

Чтобы узнать больше, см. Oracle Tutorial. И поиск Stack Overflow для многих примеров и объяснений.Спецификация: JSR 310.

Где получить классы java.time?

  • Java SE 8 и SE 9, а затем
    • Встроенный.
    • Часть стандартного Java API с объединенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функциональности java.time будет обратно портирован на Java 6 & 7 в ThreeTen-Backport.
  • Android

Проект ThreeTen-Extra расширяет java.time с дополнительными классами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные классы, такие как Interval, YearWeek, YearQuarter и more.

Смежные вопросы