2017-02-01 3 views
3

Я немного наткнулся на это. Я сделал небольшое примерное приложение, чтобы быть уверенным, что я не вижу вещей, или значения не обрабатывались в более позднем моменте, который я не ловил.SimpleDateFormat.parse приводит к совершенно другим значениям на основе версии Android

Проблема в том, что когда я пытаюсь проанализировать дату String с использованием формата даты, я получаю два очень разных результата в зависимости от уровня API-устройства.

DateFormat utcFormat; 
String utcDate = "2017-01-31 18:58:12.2334924Z"; 
utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSS'Z'"); 
utcFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 
try{ 
    conversion.setText(utcFormat.parse(utcDate).toString()); 
} 
catch (ParseException e){ 
    conversion.setText("exception encountered: " + e.getMessage()); 
} 

Вот мои результаты:

On Android 4.4.2 device: Tue Jan 31 14:37:06 EST 2017 
On Android 6.0.1 device: Tue Jan 31 13:58:12 EST 2017 

Как вы можете видеть, есть разница около 40 минут на основе точно такой же вход. Локалы устройств одинаковы (хотя я также пробовал жестко кодировать локаль, чтобы убедиться), а установленное время на устройствах одинаково.

Я делаю что-то неправильно здесь?

+0

Попробуйте использовать «SSS» только и подстригать вклад миллисекундной точности. Вы все еще видите разные результаты в этом случае? –

+0

Я только заметил, что интерпретация «2334924» в виде миллисекунд дает количество минут около 40 минут (почти). Возможно, Android 6 изменил интерпретацию таким образом, что ввод теперь корректно интерпретируется, но пока не нашел намека на официальную документацию (и ваш пример кода действительно использует неправильный шаблон и, следовательно, неправильный расчет - 14:37:06 EST ошибочен !). –

+0

Есть ли простой способ обрезать миллисекунды в этом случае? –

ответ

2

Общая проблема с символом S: Точность java.util.Date и java.text.SimpleDateFormat ограничена в миллисекундах в лучшем случае. Более высокие требования не поддерживаются. Поэтому не имеет смысла указывать более трех шаблонных символов SSS.

Кроме того, я пытался сделать некоторые исследования и нашел это:

pattern symbol table of official javadoc определяет «S» как «миллисекунда». Это означает, что значение «2334924» за точкой в ​​вашем вводе буквально анализируется как абсолютное количество миллисекунд, что составляет около 39 минут. Это объясняет разобранное время вашего первого примера, протестированного на Android v4.4.

Однако опубликованный javadoc, кажется, немного устарел в этой детали. Если Google для source code, то вы найдете в строке 71:

<tr> <td>{@code S}</td> 
<td>fractional seconds</td>  
<td>(Number)</td>  
<td>978</td> </tr> 

Но доли секунды приводит к совершенно иной интерпретации текста «2334924», а именно в 233 миллисекунд (плюс игнорировании часть более высокой точности) , Это объясняет ваш второй пример кода на Android v6. Я подозреваю, что где-то между версиями Android Google решил изменить поведение без какой-либо документации. Лучшим, что я мог найти, является issue 78859, который может быть связан с этим изменением в поведении.

ICU4J, который, кажется, все более интегрирован в новые версии Android, также объявляет символ «S» как дробный.

Временное решение и решение:

Избегайте использования более 3-х символов шаблонов S. Вместо того, чтобы использовать (не обращая внимания на более высокую точность часть):

String input = "2017-01-31 18:58:12.2334924Z"; 
int dot = input.indexOf("."); 
String trimmed = input.substring(0, dot + 4) + "Z"; // 2017-01-31 18:58:12.233Z 
DateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS'Z'"); 
utcFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 
java.util.Date d = utcFormat.parse(trimmed); 
Смежные вопросы