Неожиданные побочные эффекты при разборе дат в Android

В различных проектах Android я использую следующую статическую функцию для анализа дат, таких как 1900-12-31. Конечно, эта функция должна быть детерминированной — но оказывается, что это не так. Почему?

Обычно он разбирает дату 2010-10-30, например, на правильный экземпляр Date, содержащий это значение. Но я заметил, что когда у меня одновременно работает IntentService и анализируются некоторые даты, эта функция анализирует ту же дату, что и выше, до 1983-01-20, которая является одной из дат, проанализированных в IntentService. Как это может произойти?

public static Date dateFromString(String dateStr) {
    SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
    SimpleDateFormat mDateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());
    Date dateOut = null;
    try {
        if (dateStr != null) {
            if (dateStr.length() == 7) {
                if (dateStr.startsWith("--")) {
                    dateStr = "0000"+dateStr.substring(1);
                }
            }
            else if (dateStr.length() == 6) {
                if (dateStr.startsWith("-")) {
                    dateStr = "0000"+dateStr;
                }
            }
            else if (dateStr.length() == 5) {
                dateStr = "0000-"+dateStr;
            }
            else if (dateStr.matches("[0-9]{2}\\.[0-9]{2}\\.[0-9]{4}")) {
                dateStr = dateStr.substring(6, 10)+"-"+dateStr.substring(3, 5)+"-"+dateStr.substring(0, 2);
            }
            else if (dateStr.matches("[0-9]{2}\\/[0-9]{2}\\/[0-9]{4}")) {
                dateStr = dateStr.substring(6, 10)+"-"+dateStr.substring(3, 5)+"-"+dateStr.substring(0, 2);
            }
            else if (dateStr.matches("[0-9]{8}")) {
                dateStr = dateStr.substring(0, 4)+"-"+dateStr.substring(4, 6)+"-"+dateStr.substring(6, 8);
            }
            if (dateStr.length() >= 20) {
                String dateTimeStr = dateStr.trim();
                if (dateTimeStr.endsWith("Z")) {
                    dateTimeStr = dateStr.substring(0, dateTimeStr.length()-1)+"+0000";
                }
                if (dateStr.charAt(10) == ' ') {
                    dateTimeStr = dateStr.substring(0, 10)+"T"+dateStr.substring(11);
                }
                try {
                    dateOut = mDateTimeFormat.parse(dateTimeStr);
                }
                catch (Exception e2) {
                    dateOut = mDateFormat.parse(dateStr);
                }
            }
            else {
                dateOut = mDateFormat.parse(dateStr);
            }
        }
    }
    catch (Exception e) {
        dateOut = null;
    }
    return dateOut;
}

Редактировать: я выполняю синтаксический анализ в onCreate() моего Activity, где я запускаю AsyncTask, который выполняет эту работу. В onStop() Activity запускается фоновая служба, которая выполняет ту же работу. Когда я закрываю приложение (onStop()) и быстро перезапускаю его (onCreate()), кажется, что оба работают одновременно, и возникает ошибка.


person caw    schedule 13.01.2013    source источник
comment
Просто в качестве эксперимента, что произойдет, если вы сделаете dateFromString синхронизированным?   -  person Diego Basch    schedule 14.01.2013
comment
Во-первых, почему метод static, а во-вторых, в каком он классе?   -  person Squonk    schedule 14.01.2013
comment
@DiegoBasch Спасибо, попробую! Но на самом деле synchronized ни в коем случае не должен быть нужен, так как он только предотвращает помехи на объектах - которые здесь не используются (статические).   -  person caw    schedule 14.01.2013
comment
@Squonk На самом деле раньше он был нестатичным. Но так как он не работает ни с какими объектами, я сделал его статическим. Проблема также возникла для нестатического метода. Он расположен в MyApp.java, который расширяет Application.   -  person caw    schedule 14.01.2013
comment
@МаркоВ. : Я думаю, что ответ Дираджа В.С. наверное это объясняет. Тот факт, что SimpleDateFormat не является потокобезопасным, скорее всего, является причиной проблемы.   -  person Squonk    schedule 14.01.2013
comment
Я подозреваю, что код, который вы показали, не является источником проблемы. где-то вы берете результаты этой функции и сохраняете их в структуре данных. Вот где ваше состояние гонки, скорее всего, происходит. Взгляните на код, где вы храните результаты — синхронизирована ли эта структура данных?   -  person iagreen    schedule 14.01.2013
comment
@iagreen Я проверил, что проблема не вызвана внешними структурами данных или методами, именно в этом методе я регистрировал все входные и выходные данные. Как видите, проблема была в потокобезопасности SimpleDateFormat.   -  person caw    schedule 14.01.2013


Ответы (1)


В документации SimpleDateFormat говорится:

SimpleDateFormat не является потокобезопасным. Пользователи должны создавать отдельный экземпляр для каждого потока.

Ну вот. Просто создайте объект SimpleDateFormat отдельно в каждом потоке и передайте его методу.

person Dheeraj Vepakomma    schedule 14.01.2013
comment
Он создает отдельный экземпляр в каждом потоке, фактически он создает два новых экземпляра при каждом вызове. - person iagreen; 14.01.2013
comment
Спасибо! Я не ожидал, что метод parse() будет иметь побочные эффекты и изменять некоторые поля класса, но на самом деле метод parse() (унаследованный от класса Date) записывает данные в поле calendar класса Date. Таким образом, логично, что одновременное использование parse() вызывает проблемы. - person caw; 14.01.2013