Я добавил информацию об этом в своем ответе.

естве входных данных у меня есть строка, которая представляет собой строку в ISO 8601 для представления даты. Например:

"2017-04-04T09: 00: 00-08: 00"

Последняя частьString, который"-08: 00" обозначает смещение часового пояса. Я превращаю эту строку вCalendar экземпляр, как показано ниже:

Calendar calendar = GregorianCalendar.getInstance();
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).parse(iso8601Date);
calendar.setTime(date);

iso8601Дата "2017-04-04T09: 00: 00-08: 00"

Но это не выбирает часовой пояс, и если я получу часовой пояс изCalendar Например, он дает текущий установленный экземпляр ноутбука и не получает метку времени из строки ISO 8601. Я проверяю часовой пояс с помощью экземпляра календаря как:

calendar.getTimeZone().getDisplayName()

Может кто-то показать, как выбрать часовой пояс также вCalendar экземпляр?

Ответы на вопрос(3)

Решение Вопроса

Calendar, он принимает часовой пояс по умолчанию JVM. И когда вы анализируетеString кDate, он просто устанавливает одно значение: количество миллисекунд с начала эпохи (1970-01-01T00:00Z).Date не имеет никакой информации о часовом поясе, только это значение в миллисекундах. Так что вам нужно установить часовой пояс в календаре.

В вашем форматере вы лечитеZ как буквальный, потому что это внутри кавычек ('Z'). Это игнорирует смещение и получает дату в часовом поясе JVM по умолчанию (который будет иметь другое значение, если соответствующее смещение не равно -08: 00).

В JDK> = 7 вы можете использоватьX шаблон для разбора смещения:

Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.US).parse(iso8601Date);

Но это не устанавливает часовой пояс в календаре (он все равно будет использовать JVM по умолчанию). Итак, «лучший» способ - убрать смещение из входных данных и обработать их отдельно:

Calendar calendar = GregorianCalendar.getInstance();
String iso8601Date = "2017-04-04T09:00:00-08:00";
// get the offset (-08:00)
String offset = iso8601Date.substring(19);
TimeZone tz = TimeZone.getTimeZone("GMT" + offset);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
// set the offset in the formatter
sdf.setTimeZone(tz);
// parse just date and time (without the offset)
Date date = sdf.parse(iso8601Date.substring(0, 19));
// set the offset in the calendar
calendar.setTimeZone(tz);
calendar.setTime(date);

При этом календарь будет иметь смещение-08:00 установлен. Как@ Ответ BasilBourque уже сказал,-08:00 это смещение, а не часовой пояс (TimeZone класс обрабатывает смещения так же, как они были часовыми поясами, что является обходным / плохим выбором дизайна).

Java новый API даты / времени

Старые занятия (Date, Calendar а такжеSimpleDateFormat) имеютмного проблем а такжевопросы дизайнаи они заменяются новыми API.

В Android вы можете использоватьThreeTen Backportотличный бэкпорт для новых классов даты / времени в Java 8. Вам также понадобитсяThreeTenABP заставить его работать (подробнее о том, как его использоватьВот).

@ Ответ BasilBourque уже говорит вам оOffsetDateTime, Но чтобы преобразовать вCalendarВы можете использоватьorg.threeten.bp.ZonedDateTime и преобразовать его, используяorg.threeten.bp.DateTimeUtils класс:

String iso8601Date = "2017-04-04T09:00:00-08:00";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601Date);
Calendar cal = DateTimeUtils.toGregorianCalendar(zdt);

Календарь уже будет установлен с-08:00 смещение.

Если вы хотите получить часовой пояс со смещением, боюсь, это не так просто. Более одного часового поясаможет использовать то же смещение, так что вы не можете точно знать, какой часовой пояс использовать (лучшее, что вы можете сделать, это получить список возможных кандидатов).

java.util.Date

Просто более подробное примечание оjava.util.Date. Эта ссылка объясняет многое об этом, поэтому я очень рекомендую вам прочитать это.

Как уже говорилось выше,Date не имеет информации о часовом поясе. Он просто сохраняет количество миллисекунд с начала эпохи (1970-01-01T00:00Z, или же1 январяулица 1970 в полночь в UTC).

Это значение одинаково во всем мире. Пример: в данный момент я пишу это, значение миллис для текущего времени1504632865935, Это число одинаково для всех в мире, кто получает текущее время в то же время, что и я, независимо от того, какой часовой пояс они используют.

Чем отличается этоместная дата и время что соответствует этому значению миллис. В UTC это соответствует2017-09-05T17:34:25.935Zв Нью-Йорке дата та же (5 сентябряго 2017) но время другое (13:34), а в Токио сентябрь6го 2017 в 02:34.

ХотяDate объект тот же (потому что его значение в миллис1504632865935 для всех)соответствующий дата и время меняются в зависимости от используемого часового пояса.

Люди склонны думать, чтоDate имеет часовой пояс, потому что при его печати (сSystem.out.println или при входе в систему), или при проверке в отладчике, это простота используетtoString() метод, и это преобразует дату в часовой пояс по умолчанию JVM (и он также печатает имя зоны). Это создает впечатление, чтоDate имеет формат и часовой пояс, но это не так.

 Tarun05 сент. 2017 г., 19:51
Я бы хотел Плюс еще один раз.

и мой дальнейший поиск заключается в следующем. Пожалуйста, поправьте меня, если я ошибаюсь:

Дата не заботится о часовом поясе. Это миллисекунды, прошедшие с эпохи.

Что касается нахождения часового пояса из предоставленного формата ISO 8061, класс Date не может сказать об этом, и мы должны использовать некоторые альтернативные методы, указанные в @Hugo и @Basil Bourque.

 user760532505 сент. 2017 г., 19:26
Не совсем.Date имеет количество миллисекунд с1970-01-01T00:00Z (также называется «эпоха Unix», или1 января 1970 полночь в UTC). Предположим, это количество миллисекунд составляет 1491325200000. Это число представляет разные дату и время в каждом часовом поясе. Может быть2017-04-04 at 5 PM in UTCили 13:00 в Нью-Йорке или на следующий день (2017-04-05) в 2 часа ночи в Токио.Date не знает полей даты / времени (не имеет ни дня / месяца / года, ни часа / минуты / секунды - эти поля будут иметь разные значения в разных часовых поясах - так как дата не имеет часового пояса, она не имеет поля).
 user760532505 сент. 2017 г., 19:32
Когда ты сказал"миллисекунды представляют собой, например: 1 января 2017 года 02:00"Вы также должны спросить: "где?" (в каком часовом поясе?). Эта дата и время могут представлять различное количество миллисекунд с начала эпохи, в зависимости от используемого часового пояса. Если вы считаете, что 1 января 2017 года в 2:00 по UTC, значение в миллис1483236000000, Но та же дата / время в Нью-Йорке соответствует миллису1483254000000и в Токио есть1483203600000, Значение миллис является единственным «абсолютным» значением (оно везде одинаково), но соответствующая ему местная дата / время зависит от часового пояса.
 user760532505 сент. 2017 г., 19:45
Я добавил информацию об этом в своем ответе.
ТЛ; др
OffsetDateTime.parse( "2017-04-04T09:00:00-08:00" ) 
подробности

-08: 00" обозначает смещение часового пояса.

Не путайте смещение с часовым поясом.

-08:00 представляет собойофсетным из-UTCнечасовой пояс, Часовой пояс - это история различных смещений, используемых в прошлом, настоящем и будущем людьми определенного региона. Часовой пояс называется с континентом, косой чертой и регионом, таким какAmerica/Los_Angeles или жеPacific/Auckland или жеAsia/Kolkata.

Вы используете проблемные старые классы даты и времени, которые теперь заменены классами java.time. Для Android см.ThreeTen-Backport а такжеThreeTenABP проекты.

Ваш ввод указывает только смещение, но не зону. Таким образом, мы разбираем какOffsetDateTime.

OffsetDateTime odt = OffsetDateTime.parse( "2017-04-04T09:00:00-08:00" ) ;

Если вы абсолютно уверены в предполагаемом часовом поясе, назначьте его.

ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant() ;
О java.time

java.time фреймворк встроен в Java 8 и выше. Эти классы вытесняют хлопотное староенаследие классы даты и времени, такие какjava.util.Date, Calendar&SimpleDateFormat.

Joda времени проект, сейчас вРежим обслуживанияПосоветует миграцию наjava.time классы.

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

Где взять классы java.time?

Java SE 8, Java SE 9, и позжеВстроенный.Часть стандартного Java API с комплексной реализацией.Java 9 добавляет некоторые незначительные функции и исправления.Java SE 6 а такжеJava SE 7Большая часть функциональности java.time перенесена на Java 6 и 7 вThreeTen-Backport.AndroidThreeTenABP проект адаптируетсяThreeTen-Backport (упомянуто выше) специально для Android.ВидетьКак использовать ThreeTenABP ....
 Eugen Pechanec05 сент. 2017 г., 19:01
@BasilBourque Как часть возврата отдельных полей (getHour(), getDay()и т. д.) календарь «нормализуется» по текущему часовому поясу. Таким образом, если вы сконструировали объект Date с помощью конструктора millis (то есть в UTC), вы получите мусор, если ваш локальный TZ не является UTC. Если вы сконструировали объект Date с использованием отдельных полей, вы получите мусор, если ваш локальный TZ отличается. В заключение, это не UTC и даже не местный TZ. Это неопределенный кровавый беспорядок.
 Eugen Pechanec05 сент. 2017 г., 18:03
Я согласен, держись подальше отjava.util.Date (если это не требуется каким-либо другим APIа также вы работаете в местном часовом поясе).
 Basil Bourque05 сент. 2017 г., 18:16
@Terri Смотрите проекты бэк-порта, связанные в моем ответе. Стоит потрудиться добавить библиотеку в ваш проект.
 Basil Bourque05 сент. 2017 г., 18:06
@EugenPechanec На самом деле,java.util.Date всегда в UTC, поэтому ваш часовой пояс не имеет значения. ОбеDate а такжеCalendar классы такой жалкий беспорядок, что я советуювсегда держаться подальше от них; всегда используйте java.time или его бэк-порт. Когда унаследованные классы требуются другим API, выполните свою собственную работу в java.time, а затем конвертируйте результаты. Чтобы выполнить преобразование в Java 8 и более поздних версиях, обратите внимание на новые методы, добавленные к старым классам, в обратном порту посмотрите на служебный класс с методами преобразования.
 Tarun05 сент. 2017 г., 18:07
Я читал о java.time.OffsetDateTime, но он не доступен для Android.

Ваш ответ на вопрос