Преобразование строки в дату генерирует разные результаты на разных устройствах - PullRequest
0 голосов
/ 20 сентября 2018

Я, например, извлекаю строку с именем date в форме 2018-09-20T17:00:00Z и преобразовываю ее в дату в формате Thu Oct 20 17:00:00 GMT+01:00 2018, используя

SimpleDateFormat dateConvert = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US);

convertedDate = new Date();
try {
    convertedDate = dateConvert.parse(date);
} catch (ParseException e) {
    e.printStackTrace();
}

На разных устройствах, однако получаю по-разномуРезультаты.Один дает Thu Oct 20 17:00:00 BST 2018 (британское летнее время, эквивалентное GMT ​​+ 01: 00), но это оказывается проблематичным позже.Есть ли способ убедиться, что даты отформатированы с учетом смещения по Гринвичу, т.е. GMT + 01: 00 вместо BST?

Ответы [ 3 ]

0 голосов
/ 20 сентября 2018

java.time

    Instant convertInstant = Instant.parse(date);

Instant (точно так же, как Date) представляет момент времени независимо от часового пояса.Так что ты в порядке.В качестве дополнительного бонуса ваша строка 2018-09-20T17:00:00Z на мгновение находится в формате ISO 8601, поэтому класс Instant анализирует ее без необходимости указывать формат.

РЕДАКТИРОВАТЬ: Чтобы отформатировать ее в человекенапример, читаемая строка в британском летнем времени с однозначным смещением UTC:

    DateTimeFormatter formatter 
            = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss Z yyyy", Locale.UK);
    ZonedDateTime dateTime = convertInstant.atZone(ZoneId.of("Europe/London"));
    String formatted = dateTime.format(formatter);
    System.out.println(formatted);

Этот фрагмент кода напечатан:

Чт 20 сентября 18:00:00 +0100 2018

18: 00 - правильное время по смещению +01: 00.Z в конце исходной строки означает нулевое смещение, AKA «часовой пояс Зулу» и 17 в нулевом смещении - это тот же момент времени, что и 18:00 со смещением +01: 00.Я взял строку шаблона формата из вашего собственного ответа.

РЕДАКТИРОВАТЬ 2

Я хотел бы представить вам мое предложение переписать класс Fixture из вашего собственногоответ:

public class Fixture implements Comparable<Fixture> {

    private static DateTimeFormatter formatter 
            = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss Z yyyy", Locale.UK);

    public Instant date;

    /** @param date Date string from either web service or persistence */
    public Fixture(String date) {
        this.date = Instant.parse(date);
    }

    /** @return a string for persistence, e.g., Firebase */
    public String getDateForPersistence() {
        return date.toString();
    }

    /** @return a string for the user in the default time zone of the device */
    public String getFormattedDate() {
        return date.atZone(ZoneId.systemDefault()).format(formatter);
    }

    @Override
    public int compareTo(Fixture other) {
        return date.compareTo(other.date);
    }

    @Override
    public String toString() {
        return "Fixture [date=" + date + "]";
    }

}

Этот класс имеет естественный порядок (а именно по дате и времени) в том смысле, что он реализует Comparable, что означает, что вам больше не нужен ваш класс DateSorter.Несколько строк кода, демонстрирующих использование новых методов getXx:

    String date = "2018-09-24T11:30:00Z";
    Fixture fixture = new Fixture(date);
    System.out.println("Date for user:     " + fixture.getFormattedDate());
    System.out.println("Date for Firebase: " + fixture.getDateForPersistence());

Когда я запускал этот фрагмент в часовом поясе Европы / Лондона, я получил:

Date for user:     Mon Sep 24 12:30:00 +0100 2018
Date for Firebase: 2018-09-24T11:30:00Z

Итакпользователь получает дату и время со своим смещением от UTC, как я думаю, вы просили.Пробуем тот же фрагмент в часовом поясе Европы / Берлина:

Date for user:     Mon Sep 24 13:30:00 +0200 2018
Date for Firebase: 2018-09-24T11:30:00Z

Мы видим, что пользователю в Германии говорят, что матч в 13:30, а не в 12:30, что согласуется с его или ее часами,Дата, которая будет сохранена в Firebase, не изменилась, и это то, что вы хотите.

Что пошло не так в вашем коде

В строке шаблона формата есть две ошибки, yyyy-MM-dd'T'hh:mm:ss'Z':

  • Строчные hh - для часов в пределах AM или PM с 01 по 12 и имеют значение только для маркера AM / PM.На практике вы получите правильный результат за исключением парсинга часа 12 , который будет восприниматься как 00.
  • Анализируя Z как литерал, вы не получаете смещение UTCинформация из строки.Вместо этого SimpleDateFormat будет использовать настройку часового пояса JVM.Это очевидно отличается от одного устройства к другому и объясняет, почему вы получили разные и противоречивые результаты на разных устройствах.

Еще одна вещь, происходящая в вашем коде, это своеобразное поведение Date.toString: этот методзахватывает настройку часового пояса JVM и использует ее для генерации строки.Поэтому, если для одного устройства установлено значение «Европа / Лондон», а для другого - «GMT + 01: 00», на этих устройствах объекты с одинаковыми Date будут отображаться по-разному.Такое поведение многих смутило.

Вопрос: Могу ли я использовать java.time на Android?

Да, java.time прекрасно работает на старых и новых устройствах Android.Для этого требуется как минимум Java 6 .

  • В Java 8 и более поздних версиях и на более новых устройствах Android (от уровня API 26, как мне сказали), современный API поставляется встроенным.
  • В Java 6 и 7 получите ThreeTen Backport, бэкпорт новых классов (ThreeTen для JSR 310; см. ссылки внизу).Приведенный выше код был разработан и запущен с org.threeten.bp.Duration из бэкпорта.
  • На более старых версиях Android используется версия ThreeTen Backport для Android.Это называется ThreeTenABP.И убедитесь, что вы импортируете классы даты и времени из org.threeten.bp с подпакетами.

Ссылки

0 голосов
/ 22 сентября 2018

Так что просто добавить немного обновления здесь.Ответы, которые дали люди, очень помогли, и теперь у меня есть код, который делает именно то, что я хочу, но термин «наследие» в одном из комментариев дает мне понять, что может быть лучший и более продолжительный путь.Вот что в настоящее время происходит в коде.

1) Я получаю футбольное приспособление, которое поставляется с датой String utc в форме 2018-09-22T11:30:00Z

2) Затем я анализирую дату, используя SimpleDateFormat convertUtcDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); и convertUtcDate.setTimeZone(TimeZone.getTimeZone("GMT"));

3) Затем я получаю текущее время, используя currentTime = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime();, и сравниваю их, используя if(convertedDate.after(currentTime)), чтобы найти следующий отряд команды.В этот момент я обнаружил, что устройство будет иметь эти две даты в одной и той же форме, либо с BST, либо с GMT + 01: 00, но в любом случае эти даты можно точно сравнить.

4) Затем я форматируюдата, поэтому она выражается в виде смещения по Гринвичу с использованием SimpleDateFormat convertToGmt = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US); и String dateString = convertToGmt.format(convertedDate);

5) Для даты utc в 1) возвращается Sat Sep 22 12:30:00 GMT+01:00 2018 независимо от устройства.Обратите внимание, что время отличается от даты utc.Не совсем уверен, почему это так (может быть потому, что парень, работающий с API, находится в Германии, что на час впереди меня здесь, в Англии), но важно то, что это время правильное (это относится к Фулхэму - Уотфордузавтрашняя игра, которая действительно в 12:30 BST / GMT + 01: 00).

6) Затем я отправляю эту строку вместе с несколькими другими частями информации о приборе в Firebase.На этом этапе важно, чтобы дата была в форме GMT ​​+ 01: 00, а не в BST, потому что другие устройства могут не распознавать форму BST, когда они читают эту информацию.

7) Когда речь идет о вызове,Возвращая информацию из Firebase, я преобразовываю ее обратно в дату, анализируя ее с помощью SimpleDateFormat String2Date = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US);, а затем я могу расположить приборы в хронологическом порядке, сравнивая их даты.

Я просто хотел бы повторить, что этот метод работает,Я проверил время, когда часовой пояс в Англии меняется на GMT + 00: 00, и он все еще работает нормально.Я постарался убедиться, что все сделано в терминах GMT, чтобы оно работало где угодно.Хотя я не могу быть уверен, что это будет так.Кто-нибудь видит какие-либо недостатки в этом методе?Можно ли это улучшить?

Редактировать: Вот фрагмент кода, который, я надеюсь, просто и точно отражает то, что я делаю.

public class FragmentFixture extends Fragment {

SimpleDateFormat convertUtcDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
SimpleDateFormat String2Date = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.US);
SimpleDateFormat convertToGmt = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
private List<Fixture> fixtureList;
Date date1;
Date date2;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    convertUtcDate.setTimeZone(TimeZone.getTimeZone("GMT"));
    fixtureList = new ArrayList<>();

    // retrieve fixtures from API and get date for a certain fixture. I will provide an example

    String date = "2018-09-22T11:30:00Z";

    Date convertedDate = new Date();
    try {
        convertedDate = convertUtcDate.parse(date);
    } catch (ParseException e) {
        e.printStackTrace();
    }

    Date currentTime = Calendar.getInstance(TimeZone.getTimeZone("GMT")).getTime();

    if (convertedDate.after(currentTime)) {
        String dateString = convertToGmt.format(convertedDate);
        Fixture fixture = new Fixture(dateString);
        fixtureList.add(fixture);
        Collections.sort(fixtureList, new DateSorter());
    }
}

public class DateSorter implements Comparator<Fixture> {

    @Override
    public int compare(Fixture fixture, Fixture t1) {
        try {
            date1 = String2Date.parse(fixture.getDate());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        try {
            date2 = String2Date.parse(t1.getDate());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date1.compareTo(date2);
    }
}

public class Fixture {

    public String date;

    public Fixture() {

    }

    public Fixture(String date) {
        this.date = date;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

}

}

0 голосов
/ 20 сентября 2018

Вы просто выполняете шаг 1 двухэтапного процесса:

  1. Анализируете дату, чтобы преобразовать ее в Date объект
  2. Возьмите этот разобранный Date объект иformat это снова с использованием SimpleDateFormat.

Итак, вы правильно сделали первый шаг, вот что вы должны сделать с шагом 2, попробуйте это:

final String formattedDateString = new SimpleDateFormat("EEE MMM dd HH:mm:ss 'GMT'XXX yyyy").format(convertedDate);

Источник

...