Почему момент времени UTC не конвертируется в BST?
У меня есть следующий код:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.UK);
Instant inst = DateTimeUtils.toInstant(sdf.parse("2019-08-13T18:00:00Z"));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm").withLocale(Locale.UK).withZone(ZoneId.of("Europe/London"));
System.out.println(formatter.format(inst));
//prints 18:00
Это удивительно для меня, как я думал inst
будет время GMT/UTC и formatter
отформатировал бы его по лондонскому времени (которое является BST (UTC+1:00) для этой даты), производя 19:00
,
Что мне здесь не хватает?
Я предполагаю, что это общая проблема с моим кодом, но если это имеет значение, это использование org.threeten.bp.*
классы из проекта ThreeTen-Backport, дополнительно адаптированные для ранней версии Android в проекте ThreeTenABP.
1 ответ
ТЛ; др
Instant // Represent a moment in UTC.
.parse( // Generate a `Instant` object from the content of text input.
"2019-08-13T18:00:00Z" // String in standard ISO 8601 format.
) // Returns a `Instant` object.
.atZone( // Adjust from UTC to the wall-clock time used by the people of a particular region (a time zone).
ZoneId.of( "Europe/London" ) // Specify a time zone using name in proper `Continent/Region` format. Never use 2-4 letter pseudo-zones such as `BST`.
) // Returns a `ZonedDateTime` object.
.toLocalTime() // Extract the time-of-day, without a date and without a time zone or offset. Returns a `LocalTime` object.
.format( // Generate text representing the content of this `LocalTime` object.
DateTimeFormatter
.ofLocalizedTime ( FormatStyle.SHORT ) // Automatically localize while generating a `String`.
.withLocale ( Locale.UK ) // Locale determines the human language and cultural norms to use in localizing.
) // Returns a `String` object.
19:00
Избегайте устаревших классов даты и времени
Вы смешиваете ужасные классы наследия (SimpleDateFormat
, Date
) с современными классами java.time. Не делай этого. Используйте только java.time.
Instant
= момент в UTC
Пропустите первые две строки кода. Ваша входная строка "2019-08-13T18:00:00Z"
в стандартном формате ISO 8601. Эти стандартные форматы по умолчанию используются классами java.time при разборе / генерации строк. Поэтому нет необходимости указывать шаблон форматирования.
String input = "2019-08-13T18:00:00Z" ;
Instant instant = Instant.parse( input ) ;
instant.toString (): 2019-08-13T18: 00: 00Z
Instant
не гибкий
Я подозреваю, что ваша проблема была в вашей попытке отформатировать значение в Instant
, Instant
класс - это базовый класс строительных блоков в java.time. Это просто представляет момент в UTC. Он не предназначен для таких вещей, как гибкая генерация строк.
Более гибкие классы OffsetDateTime
& ZonedDateTime
классы.
ZonedDateTime
Применить ZoneId
на ваш Instant
настроить в часовой пояс, оказывая ZonedDateTime
объект.
ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString (): 2019-08-13T19: 00 + 01: 00 [Европа / Лондон]
Вы, кажется, хотите сосредоточиться только на времени суток. Извлечь LocalTime
объект.
LocalTime lt = zdt.toLocalTime ();
lt.toString (): 19:00
Для лондонского региона в летнее время (DST) на эту дату смещение от UTC на один час вперед. Итак, мы видим, что время дня 7 вечера против 6 вечера UTC.
Правильный часовой пояс
Кстати BST
это не часовой пояс. Я предлагаю вам избегать использования этих псевдо-зон.
Укажите правильное название часового пояса в формате Continent/Region
, такие как America/Montreal
, Africa/Casablanca
, или же Pacific/Auckland
, Никогда не используйте 2-4 буквенное сокращение, такое как BST
или же EST
или же IST
поскольку они не являются истинными часовыми поясами, не стандартизированы и даже не уникальны (!).
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
Используйте умные объекты, а не тупые строки
Ваш пример кода предполагает, что вы слишком сосредоточены на строках. Используйте смарт-объекты, а не тупые строки.
Получите ваши объекты прямо, используя соответствующие типы. Генерация строк должна быть последним шагом, дополнительным усилием, похожим на локализацию. Ваша бизнес-логика должна быть сделана с использованием правильных объектов, а не путем манипулирования строками.
локализация
И если говорить о локализации:
Locale locale = Locale.UK;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedTime ( FormatStyle.MEDIUM ).withLocale ( locale );
String output = lt.format ( f );
19:00:00
Переключите язык на Locale.US
для другого вида результата:
7:00:00 вечера
Весь приведенный выше код был запущен в раннем доступе Java 13 с библиотекой ThreeTen-Backport в соответствии с вашими потребностями, указанными в Вопросе.
import org.threeten.bp.* ;
import org.threeten.bp.format.* ;
Примечание для читателя: библиотека ThreeTen-Backport дополнительно адаптирована для ранних версий Android в библиотеке ThreeTenABP. См. Как использовать ThreeTenABP в Android. Если вы используете Android 26 и более поздние версии, классы java.time связаны, поэтому вам вообще не нужен бэк-порт.