yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z' преобразовать в формат даты

Я получаю дату от API в этом формате:

гггг-ММ-дд'T'ЧЧ:мм:сс.СССССССС'Z'

и преобразовать его таким образом:

String rawDate = "2017-05-11T15:46:48.2226756Z";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'", Locale.getDefault());
Date date = simpleDateFormat.parse(rawDate);
System.out.println(date); //Thu May 11 16:23:54 PDT 2017

Однако вывод даты такой:

Чт, 11 мая, 16:23:54 по тихоокеанскому времени 2017 г.

Выход должен быть:

Чт, 11 мая, 15:46:48 PDT 2017

Как правильно преобразовать необработанную дату?


person bkm    schedule 26.05.2017    source источник
comment
Вы должны использовать DateFormat. Вы можете найти образцы здесь: stackoverflow.com/questions/454315/   -  person EtherPaul    schedule 26.05.2017
comment
Попробуйте установить для параметра rawData только значение yyyy-MM-dd'T'HH:mm:ss.   -  person    schedule 26.05.2017
comment
Почему у вас есть 'Z' как литерал?   -  person bradimus    schedule 26.05.2017
comment
Тут кто-то что-то не так понял. Z означает часовой пояс Зулу, также известный как UTC. Таким образом, выход определенно должен быть не в четверг, 11 мая, в 15:46:48 по тихоокеанскому времени 2017 года, а в 15:46:48 по Гринвичу, что равно 8:46:48 по тихоокеанскому летнему времени. Была ли ошибка в написании строки или в ее интерпретации, я не осмеливаюсь сказать.   -  person Ole V.V.    schedule 26.05.2017
comment
Возможный дубликат Разбор ISO_INSTANT и аналогичных строк даты и времени   -  person Ole V.V.    schedule 26.05.2017
comment
(1) Вы игнорируете важную информацию: Z. (2) Вы пытаетесь поместить наносекунды в устаревший класс, который поддерживает только миллисекунды. Оба вопроса уже обсуждались много раз. Искать перед публикацией в Stack Overflow.   -  person Basil Bourque    schedule 27.05.2017


Ответы (3)


Вы можете просто использовать ниже для анализа.

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());
person yogidilip    schedule 26.05.2017
comment
Обратите внимание, что это округляет миллисекунды от вашей метки времени до 0, если это имеет значение... - person Markus A.; 26.05.2017
comment
Неверно (а) Вы игнорируете часовой пояс. (b) Вы игнорируете дробную секунду, как уже упоминалось. (c) Вы столкнетесь с исключением, поскольку этот шаблон форматирования не соответствует входной строке. - person Basil Bourque; 27.05.2017
comment
@BasilBourque, тогда как это сделать без библиотеки и в Java 7? @ Оле В.В. ответ использует библиотеку или в Java 8 - person bkm; 27.05.2017
comment
@bkm Мудрое решение состоит в том, чтобы добавить в ваш проект обратный порт java.time, проект ThreeTen-Backport, который дополнительно адаптирован для Android в проекте ThreeTenABP. Да, см. Ответ Оле В.В.. Устаревшие классы даты и времени представляют собой ужасный беспорядок, теперь полностью вытесненный java.time. Никогда больше не вводите «SimpleDateFormat»! - person Basil Bourque; 27.05.2017
comment
Я видел тег Android, прежде чем писать свой ответ. Я знаю, что новые классы еще не встроены в Android Java. Я все еще думаю, что посоветовал хорошее решение с ThreeTenABP. И перспективный. - person Ole V.V.; 27.05.2017

SimpleDateFormat не может обрабатывать любое другое количество десятичных знаков в секундах, кроме трех (миллисекунд), поэтому нет никакого способа правильно проанализировать вашу строку. Кроме того, более новые классы даты и времени Java, как правило, гораздо более удобны и понятны для программиста. И они имеют наносекундную точность (9 знаков после запятой в секундах). Поэтому я предлагаю вам подумать о том, чтобы перейти к ним.

Как уже отмечалось, Z означает часовой пояс Зулу, также известный как UTC. Таким образом, 2017-05-11T15:46:48.2226756Z означает 15:46:48 UTC, что равно 8:46:48 по тихоокеанскому летнему времени. Ваш формат на мгновение является форматом ISO 8601, который класс Instant понимает как его значение по умолчанию, поэтому синтаксический анализ прост:

    Instant instant = Instant.parse(rawDate);

Результат

2017-05-11T15:46:48.222675600Z

Единственное, что следует отметить, это два добавленных нуля. Метод toString печатает десятичные дроби группами по три, достаточно групп для отображения полной точности. Таким образом, с 7 десятичными знаками будет напечатано 9.

Чтобы получить дату в тихоокеанском часовом поясе:

    ZonedDateTime dateTime = instant.atZone(ZoneId.of("America/Los_Angeles"));

В результате получилось то, что я предсказывал:

2017-05-11T08:46:48.222675600-07:00[America/Los_Angeles]

Теперь предположим, что вы получили необработанную строку даты и времени от кого-то, кто неправильно понял и действительно имел в виду четверг, 11 мая, 15:46:48 по тихоокеанскому времени 2017 года (это будет не первый случай в истории). Затем вам нужно преобразовать его в это. Опять же, хотя это было бы громоздко со старомодными классами, с новыми все идет гладко:

    ZonedDateTime dateTime = instant.atOffset(ZoneOffset.UTC)
            .atZoneSimilarLocal(ZoneId.of("America/Los_Angeles"));

Результат тот, который вы просили (за исключением того, что я также даю вам все десятичные знаки):

2017-05-11T15:46:48.222675600-07:00[America/Los_Angeles]

Для Android вы получаете более новые классы даты и времени из библиотеки ThreeTenABP.

Ссылки

person Ole V.V.    schedule 26.05.2017
comment
Отличный ответ. Это для Java 8, не так ли? - person bkm; 26.05.2017
comment
Спасибо. Это для Java 8 или 9 или более поздней версии, или для Android Java 7 с использованием ThreeTenABP, о котором я упоминал, или для Java 6 и 7 с использованием ThreeTen Backport. Вы вроде правы. :-) - person Ole V.V.; 26.05.2017
comment
@bkm, в самом ответе используются классы, добавленные в Java8 и выше. Однако весь пакет java.time был получен из другой библиотеки, Joda-Time (автор которой был в экспертной группе для JSR-310, который стал пакетом java.time.). Короче говоря, вы можете использовать этот ответ изначально в Java 8 и выше или с помощью аналогичных классов из библиотеки Joda-Time в Java 7 или ниже. - person M. Prokhorov; 29.05.2017

Проблема в том, что «S» означает миллисекунды. Итак, в вашем случае вы сообщаете, что время составляет 15 часов, 46 минут, 48 секунд и 2226756 миллисекунд. Если вы прибавите 2226756 миллисекунд, то есть 2226 секунд и 756 миллисекунд к 15:46:48, вы действительно получите 16:23:54.

Самое простое решение, вероятно, состоит в том, чтобы просто найти точку в вашей строке и обрезать строку в трех местах позже, т.е. преобразовать ее в:

2017-05-11T15:46:48.222

Вы можете добиться этого с помощью следующей строки:

rawDate = rawDate.substring(0, rawDate.indexOf('.') + 4);

А затем проанализируйте его с помощью

SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault());

Обратите внимание, что это неправильно округляет микросекунды. В вашем случае, например, 222,6756 мс следует округлить до 223 мс, а не до 222 мс. Если это имеет значение, вы можете сделать это вручную, проверив первую пропущенную цифру, чтобы увидеть, является ли она 5 или выше, и добавив миллисекунду к date.

Обновление (относительно Бэзила Бурка):

Если вы действительно хотите учитывать идентификатор часового пояса в своей строке времени (который указывает UTC, как объяснено ниже Оле В.В.), вы можете просто добавить «UTC» в конец строки и проанализировать его с этим часовым поясом в более старых версии Java без использования дополнительных библиотек:

rawDate = rawDate.substring(0, rawDate.indexOf('.') + 4) + "UTC";
SimpleDateFormat sDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz",
                                            Locale.getDefault());
Date date = sDF.parse(rawDate);
person Markus A.    schedule 26.05.2017
comment
Неверно Вы игнорируете часовой пояс, Z. Текущий часовой пояс вашей JVM по умолчанию будет применяться неявно, изменяя значение исходных данных. См. правильный ответ Оле В.В.. - person Basil Bourque; 27.05.2017
comment
@BasilBourque Спасибо! Сегодня кое-что узнал! Я даже не думал, что буква «Z» является индикатором часового пояса. Я почему-то предположил, что они всегда трехбуквенные (PDT, EST, CET, GMT, UTC, ...). Тем более, что ОП специально заявил, что хочет, чтобы часовой пояс понимался как PDT (см. ожидаемый образец вывода). - person Markus A.; 27.05.2017
comment
На самом деле эти аббревиатуры из 3-4 букв не являются часовыми поясами не. Они не стандартизированы. Они даже не уникальны! CST — это центральное время в стандартном времени США/Канады и Китая. IST – это стандартное время Индии и стандартное время Ирландии и т. д. Избегайте этих кодов. Используйте названия часовых поясов в формате continent/region, например America/Montreal, Asia/Kolkata и Pacific/Auckland. Это одна из многих причин избегать ужасных устаревших классов даты и времени. Используйте только классы java.time. - person Basil Bourque; 27.05.2017