Невозможно преобразовать местную дату в объект даты в Лондоне - PullRequest
1 голос
/ 16 июня 2020

Android Studio 4.0, Java 6. Мой GMT: GMT + 03: 00

 defaultConfig {
        minSdkVersion 17
        targetSdkVersion 28


 const val LONDON_TIME_ZONE_ID = "Europe/London"
const val DEFAULT_DATE_JSON_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.000'Z'"

    fun fromDateToLondonDate(localDate : Date) : Date? {
        val dateLondonAsString = fromDate2LondonDateAsString(localDate)
        val timeZoneLondon : TimeZone = TimeZone.getTimeZone(LONDON_TIME_ZONE_ID)
        val dateLondon = getDateFromString(dateLondonAsString, timeZoneLondon)
        if (BuildConfig.DEBUG)
            Log.d("", "fromDateToLondonDate:" +
                    "\nlocalDate = $localDate"+
                    "\ndateLondonAsString = $dateLondonAsString"+
                    "\ndateLondon = $dateLondon")
        return dateLondon
    }

    fun fromDate2LondonDateAsString(date : Date) : String? {
        val timeZoneLondon : TimeZone = TimeZone.getTimeZone(LONDON_TIME_ZONE_ID)
        val formatter: DateFormat = SimpleDateFormat(DEFAULT_DATE_JSON_FORMAT)
        formatter.setTimeZone(timeZoneLondon)
        val dateAsString : String = formatter.format(date)
        return dateAsString
    }

    fun getDateFromString(str: String?, tz: TimeZone?): Date? {
        return try {
            val sdf = SimpleDateFormat(DEFAULT_DATE_JSON_FORMAT);
            sdf.setTimeZone(tz)
            val date = sdf.parse(str)
            return date
        } catch (e: ParseException) {
            //e.printStackTrace();
            null
        }
    }

и вот результат:

fromDateToLondonDate:
 localDate = Tue Jun 16 13:14:59 GMT+03:00 2020
 dateLondonAsString = 2020-06-16T11:14:59.000Z
 dateLondon = Tue Jun 16 13:14:59 GMT+03:00 2020

как вы можете видеть, местная дата вторник, 16 июня 13:14:59 GMT + 03: 00 2020 и успешное преобразование в лондонскую дату как String -> dateLondonAsString = 2020-06-16T11: 14 : 59.000Z

Красиво. Но мне нужно преобразовать String в London Date. И я использую метод getDateFromString , и результат неверен:

dateLondon = Tue 16 Jun 13:14:59 GMT + 03: 00 2020

Правильная дата в Лондоне должна быть: Вт 16 июня 11:14:59 GMT + 03: 00 2020

Почему getDateFromString неверно преобразовать из строки в дата?

1 Ответ

3 голосов
/ 16 июня 2020

java .time и ThreeTenABP

Чтобы получить объект даты и времени с указанным c часовым поясом, откройте java .time, современный Java API даты и времени. Я пишу код Java, это то, что я могу, и надеюсь, что вы переведете:

    ZoneId london = ZoneId.of("Europe/London");

    Date date = getOldfashionedDateFromSomewhere();
    System.out.println("Original Date: " + date);

    ZonedDateTime dateTimeLondon = date.toInstant().atZone(london);
    System.out.println("Date-time in London: " + dateTimeLondon);

Пример вывода:

Original Date: Tue Jun 16 13:14:59 GMT+03:00 2020
Date-time in London: 2020-06-16T11:14:59+01:00[Europe/London]

A Date не получил, так как не может иметь часовой пояс. Да, я знаю, когда вы его распечатываете, неявно вызывая его метод toString, он печатает часовой пояс. Что происходит, так это то, что он захватывает часовой пояс по умолчанию вашей JVM и использует его для рендеринга строки. Итак, пока настройка часового пояса вашей JVM равна GMT + 03: 00, все ваши Date объекты всегда будут печатать этот часовой пояс.

Итак, если (в каком-то другом проекте) мне нужна дата с часовым поясом лучше использовать java .time.ZonedDateTime (java 8)?

По крайней мере, так я бы рекомендовал. Как я уже сказал, использование Date - это не способ. Вот несколько способов:

  1. Плохой и устаревший способ: использовать GregorianCalendar.
  2. Лучший способ: использовать DateTime от Joda-Time. Если вы принимаете внешнюю зависимость, я бы предпочел ThreeTenABP.
  3. Хороший способ: ZonedDateTime из java .time.
  4. Продвинутый способ: использование Time4J. У меня нет опыта, поэтому я бы не стал рекомендовать за или против. Если у вашего проекта есть требования, которые go превосходят то, что предлагает java .time, я бы обязательно исследовал этот вариант.

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

Во-первых, вы вероятно, не следует устанавливать часовой пояс на GMT + 03: 00. Хотя в вашем часовом поясе сейчас используется это смещение по Гринвичу, это не означает, что оно всегда использовалось и будет всегда. Для правильных результатов для исторических c и будущих дат используйте идентификатор реального часового пояса, такой как Африка / Найроби или Европа / Москва, то есть в формате регион / город . Так же, как вы использовали Europe/London для времени Briti sh (к счастью, не GMT+00:00).

Я уже упоминал второй момент: вы не можете использовать Date для даты и времени со временем пояс, потому что Date не имеет часового пояса. A Date - это момент времени, не больше и не меньше.

Далее, 2020-06-16T11:14:59.000Z неверно для времени в Лондоне. 11:14:59 - правильное время суток. Z означает UT C или смещение 0 от UT C и неверно, так как Великобритания использует летнее время (DST) и, следовательно, находится со смещением +01: 00 в июне (как говорится в выводе моего кода выше) . Время по смещению Z было бы 10:14:59. Другими словами, у вас выходной на 1 час. Это определение Z является частью стандарта ISO 8601. Ваш формат JSON - это формат ISO 8601. Я включаю ссылку внизу. Поскольку Z является смещением, вы всегда должны форматировать и анализировать его как таковое, а никогда жестко кодировать его как литерал в строке шаблона формата.

Ваше преобразование обратно из строки в Date показывает ту же ошибку, поэтому она уравновешивается ошибкой преобразования в String, и вам удается вернуть эквивалентный объект Date.

Это старый проект Android. И я не могу использовать java .time. Я могу использовать только java .util.Date

Конечно, вы можете использовать java .time в старых Android проектах и ​​для старых Android версий.

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

Ссылки

...