Как разобрать дату без использования местного часового пояса? - PullRequest
0 голосов
/ 29 апреля 2019

С сервера я получаю следующие значения:

epochMillis=1556532279322
iso8601=2019-04-29T10:04:39.322Z

Когда я делаю serverTimeDateFormat.parse(iso8601), я получаю в результате Mon Apr 29 10:04:39 GMT+02:00 2019

и для serverTimeDateFormat.parse(iso8601).time результат равен 1556525079322, что отличается от того, что я получаю с сервера (на 2 часа меньше, чем во время UNIX), в то время как я нахожусь в часовом поясе + 2 часа.

Когда я форматирую его обратно с serverTimeDatFormat.format(1556525079322), результат2019-04-29T10:04:39.322Z Я понимаю, что SimpleDateFormat использует местный часовой пояс, но почему результат отстает на 2 часа и как я могу проанализировать Date без учета часового пояса?Я не понимаю логику всего этого.

Мой код:

private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z" 
val epochMillis = 1556532279322
serverTimeDateFormat.parse(iso8601).time

Ответы [ 2 ]

1 голос
/ 29 апреля 2019

Избегайте устаревших классов даты и времени

Вы используете ужасные классы даты и времени, которые были вытеснены несколько лет назад современными java.time классами, определенными в JSR 310.

Date::toString ложь

почему результат на 2 часа позже

Это на самом деле не два часа позади.

Проблема в том, что, хотя java.util.Date представляет момент в UTC, его метод toString динамически применяет текущий часовой пояс JVM по умолчанию при генерации текста, представляющего значение объекта даты и времени. Несмотря на благие намерения, эта анти-функция сбивает с толку и создает иллюзию объекта Date, имеющего этот часовой пояс.

Другими словами, Date::toString ложь. Одно из многих плохих дизайнерских решений, найденных в этих классах наследства. И одна из многих причин никогда не использовать эти устаревшие классы.

java.time

Instant

Проанализируйте ваш счет в миллисекундах с начала отсчета эпохи первого момента 1970 года в UTC как Instant.

Instant instant = Instant.ofEpochMilli( 1556532279322 );

Ваш другой ввод, стандартная строка ISO 8601, также может быть проанализирован как мгновенный.

Instant instant = Instant.parse( "2019-04-29T10:04:39.322Z" ) ;

ZonedDateTime

Чтобы увидеть тот же момент через часы настенного времени, используемые людьми определенного региона (часового пояса), примените ZoneId, чтобы получить ZonedDateTime объект.

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ) ;

Оба объекта instant и zdt представляют один и тот же момент времени. Два способа чтения одного и того же момента, когда два человека, разговаривающих по телефону в Исландии и Квебеке, каждый видел разное время на часах на стене при одновременном взгляде.

1 голос
/ 29 апреля 2019

Проблема заключается в шаблоне для вашего SimpleDateFormat. В конце у вас есть 'Z', что указывает на то, что в строке даты должен быть указан буквенный символ "Z" для анализа. Однако «Z» в конце даты имеет особое значение, а именно означает часовой пояс UTC . Следовательно, вы должны проанализировать его как указатель часового пояса, чтобы получить правильное значение даты. Вы можете сделать это с помощью шаблона XXX (см. JavaDocs ).

private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z" 
print( serverTimeDateFormat.parse(iso8601).time ) // 1556532279322

Пример выполнения на pl.kotl.in


Приложение : хотя приведенный выше код должен работать для вас, если это вообще возможно, вам следует рассмотреть возможность добавления ThreeTen Android Backport в ваш проект. Это даст вам доступ к новым классам времени, добавленным JSR310 в Java / Kotlin (также доступно по умолчанию в Android API> = 26 ). Классы обычно имеют более простой API и по умолчанию используют ISO8601, поэтому вам вообще не понадобится никакой форматировщик:

print( ZonedDateTime.parse(iso8601).toInstant().toEpochMilli() )
...