Что пошло не так в вашем коде?
Ваша ошибка в этой строке:
calendar.set(Calendar.HOUR_OF_DAY, timestamp.charAt(1) + 1);
Символы представлены в компьютерах цифрами.Это сбивает с толку тот факт, что во многих случаях символы и числа могут использоваться взаимозаменяемо.Если timestamp.charAt(1)
- это символ '9'
, как в вашем примере, он представлен как число 57. Когда вы добавляете 1, вы получаете 58. Когда вы устанавливаете час дня на 58 - если вы ожидали, что Calendar
класс с настройками по умолчанию для сообщения об ошибке, вы были не правы, это запутанная вещь об этом классе и только одна из многих причин, почему мы рекомендуем избегать его использования.Он просто продолжает считать часы в следующие дни и по совпадению заканчивается в нужный час, 10, только через два дня (два дня - 48 часов и 48 + 10 = 58).
Другая рекомендация:
- Не разбирайте вручную строку времени.Оставьте разбор в классе библиотеки.
- Не переводите в центральноевропейское время, добавив час.Это подвержено ошибкам.В летнее время (DST) это даст неверный результат.Это не переносимо на другие часовые пояса.Вместо этого снова оставьте преобразование в библиотечные классы.
Как исправить?
Базиль Бурк уже показал хороший способ решения вашей проблемы с помощью java.time, современной Java-даты ивремя API.Я хотел бы показать вам, что с java.time вы также можете включить долю секунды без особых проблем, если хотите.
static DateTimeFormatter timeFormatter = new DateTimeFormatterBuilder()
.appendPattern("HHmmss")
.appendFraction(ChronoField.NANO_OF_SECOND, 3, 4, true)
.toFormatter(Locale.ROOT);
static ZoneId zone = ZoneId.of("Europe/Paris");
public static ZonedDateTime utcToLocalTimeFromLock(String timestamp) {
return LocalDate.now(ZoneOffset.UTC)
.atTime(LocalTime.parse(timestamp, timeFormatter))
.atOffset(ZoneOffset.UTC)
.atZoneSameInstant(zone);
}
Давайте попробуем:
System.out.println(utcToLocalTimeFromLock(timestamp));
Вывод при запуске только сейчас:
2018-12-11T10: 12: 15 + 01: 00 [Европа / Париж]
Метод appendFraction
требует минимумаи максимальное количество знаков после запятой, поэтому я указал 3 и 4 соответственно.В зависимости от ваших потребностей вы можете указать минимум до 0 и максимум до 9 цифр.
Конечно, замените свой часовой пояс, если это не произошло в Европе / Париже.
Если вам необходим старомодный объект Date
для устаревшего API, который вы не хотите обновлять прямо сейчас:
public static Date utcToLocalTimeFromLock(String timestamp) {
Instant inst= LocalDate.now(ZoneOffset.UTC)
.atTime(LocalTime.parse(timestamp, timeFormatter))
.atOffset(ZoneOffset.UTC)
.toInstant();
return Date.from(inst);
}
Вт 11 дек. 10:12:15 CET 2018
Если используется обратный порт (ThreeTen Backport и / или ThreeTenABP, см. Ниже) для преобразования из Instant
в Date
вместо Date.from(inst)
, используйте это:
return DateTimeUtils.toDate(inst);
Поскольку Date
не имеет часового пояса, в этом случае преобразование часового пояса не требуется.Только потому, что я тоже нахожусь в часовом поясе Центральной Европы, вывод совпадает с тем, что вы ожидаете - это будет время в часовом поясе JVM.
Вопрос: Могу ли я использовать java.time на Android?
Да, java.time прекрасно работает на старых и новых устройствах Android.Для этого требуется как минимум Java 6 .
- В Java 8 и более поздних версиях и на более новых устройствах Android (от уровня API 26) современный API поставляется встроенным.
- В Java 6 и 7 получают ThreeTen Backport, бэкпорт новых классов (ThreeTen для JSR 310; см. Ссылки внизу).
- На (более старых) Android используется версия ThreeTen для AndroidBackport.Это называется ThreeTenABP.И убедитесь, что вы импортируете классы даты и времени из
org.threeten.bp
с подпакетами.
Ссылки