Подтвердить что ты не робот

Android parse String to Date - неизвестный символ шаблона 'X'

У меня есть Service строка даты получения из сети, а затем я хочу привязать ее к объекту Date. Но почему-то приложение падает. Это моя строка, которую я обрабатываю: 2015-02-05T05:20:02+00:00

onStartCommand()

String datetime = "2015-02-05T05:20:02+00:00";
Date new_date = stringToDate(datetime);

stringToDate()

private Date stringToDate(String s){
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
    try{
        return df.parse(s);
    }catch(ParseException e){
        e.printStackTrace();
    }
    return null;
}

LogCat:

02-06 20:37:02.008: E/AndroidRuntime(28565): FATAL EXCEPTION: main
02-06 20:37:02.008: E/AndroidRuntime(28565): Process: com.dotmav.runescapenotifier, PID: 28565
02-06 20:37:02.008: E/AndroidRuntime(28565): java.lang.RuntimeException: Unable to start service [email protected] with Intent { cmp=com.dotmav.runescapenotifier/.GEService }: java.lang.IllegalArgumentException: Unknown pattern character 'X'
02-06 20:37:02.008: E/AndroidRuntime(28565):    at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2881)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at android.app.ActivityThread.access$2100(ActivityThread.java:144)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1376)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at android.os.Handler.dispatchMessage(Handler.java:102)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at android.os.Looper.loop(Looper.java:135)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at android.app.ActivityThread.main(ActivityThread.java:5221)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at java.lang.reflect.Method.invoke(Native Method)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at java.lang.reflect.Method.invoke(Method.java:372)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
02-06 20:37:02.008: E/AndroidRuntime(28565): Caused by: java.lang.IllegalArgumentException: Unknown pattern character 'X'
02-06 20:37:02.008: E/AndroidRuntime(28565):    at java.text.SimpleDateFormat.validatePatternCharacter(SimpleDateFormat.java:314)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at java.text.SimpleDateFormat.validatePattern(SimpleDateFormat.java:303)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:356)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:249)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at com.dotmav.runescapenotifier.GEService.stringToDate(GEService.java:68)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at com.dotmav.runescapenotifier.GEService.onStartCommand(GEService.java:44)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2864)
02-06 20:37:02.008: E/AndroidRuntime(28565):    ... 9 more

EDIT: onDestroy() установить будильник для периодического обновления...

AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.set(
    AlarmManager.RTC_WAKEUP,
    System.currentTimeMillis() + (1000 * 60),
    PendingIntent.getService(this, 0, new Intent(this, GEService.class), 0)
);
4b9b3361

Ответ 1

Удалить "XXX" из

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");

и все будет нормально работать.

Пройдите список символов, который можно использовать внутри конструктора SimpleDateFormat. Это документация

Возможно, вы ищете "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

Измените свой код на

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); 

или

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); // if timezone is required

Ответ 2

Версия Android SimpleDateFormat не поддерживает шаблон X, поэтому XXX не будет работать, но вместо этого вы можете использовать ZZZZZ, который делает то же самое, и выводит часовой пояс в формате +02:00 (или -02:00 в зависимости от локального часового пояса).

Ответ 3

Используется неверный формат даты.

Используйте это вместо: DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

Я думаю, что андроид в отличие от Java 7 использует Z (как в Java 6) для часовых поясов, а не X. Таким образом, используйте этот для ваших форматов дат.

Ответ 4

Так как Z и XXX разные, я применил следующее обходное решение:

// This is a workaround from Z to XXX timezone format
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") {

    @Override
    public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
        StringBuffer rfcFormat = super.format(date, toAppendTo, pos);
        return rfcFormat.insert(rfcFormat.length() - 2, ":");
    }

    @Override
    public Date parse(String text, ParsePosition pos) {
        if (text.length() > 3) {
            text = text.substring(0, text.length() - 3) + text.substring(text.length() - 2);
        }
        return super.parse(text, pos);
    }
}

Ответ 5

Android SimpleDateFormat отличается от Java 7 SDK и не поддерживает "X" для анализа ISO 8601. Вы можете использовать" Z 'или' ZZZZZ 'для форматирования и программной настройки часового пояса в формате UTC. Вот класс util:

public class DateUtil {

    public static final String iso8601DatePattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
    public static final DateFormat iso8601DateFormat = new SimpleDateFormat(iso8601DatePattern);
    public static final TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");

    static {
        iso8601DateFormat.setTimeZone(utcTimeZone);
    }

    public static String formatAsIso8601(Date date) {

        return iso8601DateFormat.format(date);
    }
}

Ответ 6

Ошибка говорит о том, что simpleDateFormat не распознает символ X. Если вы ищете миллисекунды, он представлен символом S.

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

Ответ 7

Следующий класс может использоваться для преобразования строки на дату, когда шаблон не знает об этом.

    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;

/**
 * StringToDateFormater is a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows for
 * formatting (date → text), parsing (text → date), and normalization.
 * 
 * This class is mainly used for convert the date from string. It should be used only when date pattern doesn't aware of
 * it. 
 *
 */
public class StringToDateFormater extends SimpleDateFormat {

    private static final List<String> DATE_SUPPORTED_FORMAT_LIST = Arrays.asList("yyyyMMddHHmmss", "yyyyMMddHHmm",
            "yyyyMMddHHmm", "yyyyMMddHH", "yyyyMMdd", "yyyyMMddHHmmssSS");

    /**
     * 
     */
    private static final long serialVersionUID = -1732857502488169225L;

    /**
     * @param pattern
     */
    public StringToDateFormater() {
    }

    @Override
    public Date parse(String source) {
        Date date = null;

        SimpleDateFormat dateFormat = null;
        for (String format : DATE_SUPPORTED_FORMAT_LIST) {
            dateFormat = new SimpleDateFormat(format);
            try {
                return dateFormat.parse(source);
            } catch (Exception exception) {

            }

        }

        return date;
    }
}

Ответ 8

TL;DR

long millisecondsSinceEpoch = OffsetDateTime.parse( "2015-02-05T05:20:02+00:00" ).plusHour( 1 ).toInstant().toEpochMilli()  // Warning: Possible loss of data in truncating nanoseconds to milliseconds. But not in this particular case.

Подробнее

Другие ответы верны, но теперь устарели. Старые классы даты и времени теперь являются устаревшими. Вместо этого используйте классы java.time.

ISO 8601

Строка ввода находится в стандартном формате ISO 8601. Парсинг напрямую, нет необходимости определять шаблон форматирования, поскольку классы java.time по умолчанию используют ISO 8601.

OffsetDateTime

Ввод включает в себя смещение-от-UTC с помощью +00:00, поэтому мы можем проанализировать как объект OffsetDateTime.

String input = "2015-02-05T05:20:02+00:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input );

Math

Если вы хотите добавить час или минуту позже, чтобы установить будильник, вызовите методы plus.

OffsetDateTime minuteLater = odt.plusMinutes( 1 );
OffsetDateTime hourLater = odt.plusHours( 1 );

Чтобы получить количество миллисекунд, перейдите в класс Instant. Класс Instant представляет момент на временной шкале в UTC с разрешением наносекунды. Запрос миллисекунд означает возможную потерю данных, поскольку девять цифр десятичной дроби усекаются до трех цифр десятичной дроби.

long millisecondsSinceEpoch = odt.toInstant().toEpochMilli();  // Warning: Possible loss of data in truncating nanoseconds to milliseconds.

О java.time

Структура java.time встроена в Java 8 и более поздних версий. Эти классы вытесняют старые неудобные классы времени, такие как java.util.Date, .Calendar и java.text.SimpleDateFormat.

Проект Joda-Time, теперь в режиме обслуживания, советует перейти на java.time.

Чтобы узнать больше, см. Учебник Oracle. И поиск Qaru для многих примеров и объяснений.

Большая часть функциональных возможностей java.time обратно переносится на Java 6 и 7 в ThreeTen-Backport и далее адаптируется к Android в ThreeTenABP.

Проект ThreeTen-Extra расширяет java.time с дополнительными классами. Этот проект является доказательством возможных будущих дополнений к java.time.

Ответ 9

Простое решение:

Используйте этот yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ

Вместо yyyy-MM-dd'T'HH:mm:ss.SSSXXX

Готово.

Ответ 10

Используйте SimpleDateFormat для создания правильно отформатированного вывода String:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

String formattedNow = simpleDateFormat.format(new Date(System.currentTimeMillis()));

Выход: 2018-02-27T07:36:47.686Z

Ответ 11

Никто не упомянул об этой ошибке, возникающей на устройствах с преднугой, поэтому я решил поделиться своим ответом и, возможно, это полезно для тех, кто достиг этой темы из-за этого.

В этом ответе справедливо упоминается, что "X" поддерживается только для устройств Nougat+. Я по-прежнему вижу, что в документации предлагается использовать "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" и не уверен, почему они не делают эту точку явной.

Для меня yyyy-MM-dd'T'HH:mm:ssXXX работал нормально, пока я не попытался протестировать его на устройстве 6.0, и он начал сбой, что привело меня к исследованиям по этой теме. Замена его на yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ разрешила проблему и работает на всех устройствах 0+.