SimpleDateFormat игнорирует TimeZone, когда также присутствует в String - PullRequest
1 голос
/ 10 июня 2019

Java SimpleDateFormat позволяет указать TimeZone , который будет использоваться при разборе строки на Date .

Это работает, как и следовало ожидать, когда строка не содержит часовой пояс, но когда часовой пояс присутствует, кажется, ничего не делает.

Документация, похоже, действительно не объясняет, как используется TimeZone.

Пример кода:

public class DateFormatTest {
    public static void main(final String[] args) throws ParseException {
         testBoth("HH:mm", "13:40");
         testBoth("HH:mm z", "13:40 UTC");
    }

    private static void testBoth(final String dateFormatString, final String dateString) throws ParseException {
        // First, work with the "raw" date format
        DateFormat dateFormat = new SimpleDateFormat(dateFormatString);
        parse(dateFormat, dateString);

        // Now, set the timezone to something else and try again
        dateFormat = new SimpleDateFormat(dateFormatString);
        dateFormat.setTimeZone(TimeZone.getTimeZone("PST"));
        parse(dateFormat, dateString);
    }

    private static void parse(final DateFormat dateFormat, final String dateString) throws ParseException {
        System.out.println(MessageFormat.format("Parsed \"{0}\" with timezone {1} to {2}", dateString,
        dateFormat.getTimeZone().getDisplayName(), dateFormat.parse(dateString)));
    }
}

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

Parsed "13:40" with timezone Greenwich Mean Time to 01/01/70 13:40
Parsed "13:40" with timezone Pacific Standard Time to 01/01/70 22:40
Parsed "13:40 UTC" with timezone Greenwich Mean Time to 01/01/70 14:40
Parsed "13:40 UTC" with timezone Pacific Standard Time to 01/01/70 14:40

Обратите внимание, что в первом примере Дата изменяется, а во втором - нет.

Ответы [ 2 ]

1 голос
/ 10 июня 2019

Неправильные типы

Вы используете неправильные типы данных, пытаясь вписать значение времени дня в тип, который содержит время суток, дату и смещение от UTC (ноль). Квадратный колышек, круглое отверстие .

Кроме того, этот класс java.util.Date ужасно спроектирован и реализован. Это было вытеснено несколько лет назад современными java.time классами с принятием JSR 310.

Время суток: LocalTime

"13:40"

Просто проанализируйте как LocalTime объект.

LocalTime lt = LocalTime.parse( "13:40" ) ;

Если вы хотите объединить дату и часовой пояс для определения момента, примените LocalDate и ZoneId, чтобы получить объект ZonedDateTime.

ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
LocalDate today = LocalDate.now( z ) ;
ZonedDateTime zdt = ZonedDateTime.of( today , lt , z ) ;

Чтобы увидеть тот же момент в UTC, извлеките Instant.

Instant instant = zdt.toInstant() ; 

Время суток со смещением от UTC: OffsetTime

"13:40 UTC"

Время дня с часовым поясом или смещение от UTC на самом деле не имеет смысла. Без даты нет смысла думать о времени, так как оно относится к определенному часовому поясу. Никто не смог объяснить мне пример того, как это может иметь логическое значение. Каждая попытка, которую я слышал, на самом деле подразумевает предполагаемую дату.

Тем не менее, комитет по стандартизации SQL решил по своему усмотрению определить тип данных TIME WITH TIME ZONE. И поэтому, чтобы поддержать это, классы java.time включают соответствующий класс OffsetTime.

К сожалению, я не могу найти шаблон форматирования, который работает для анализа пробела и UTC в конце ввода. В качестве обходного пути я предлагаю заменить эти символы одним Z символом. Так что "13:40 UTC" становится "13:40Z". Z означает UTC и произносится как «зулу». Этот формат обрабатывается по умолчанию, поэтому нет необходимости указывать шаблон форматирования.

String input = "13:40 UTC".replace( " UTC" , "Z" ) ;  // "13:40 UTC" becomes "13:40Z".
OffsetTime ot = OffsetTime.parse( input ) ;

Table of date-time types in Java (both modern and legacy) and in standard SQL.

0 голосов
/ 13 июня 2019

В 2019 году никого не должно волновать, почему классы SimpleDateFormat и TimeZone ведут себя так же, как и они, поскольку мы должны были отказаться от использования этих классов много лет назад.Василий Бурк дал вам ответ, который вы должны хотеть.Этот ответ попытается удовлетворить ваше любопытство, но, пожалуйста, не используйте его, чтобы заставить SimpleDateFormat вести себя.Вам будет намного лучше не с использованием этого класса.

Я предполагаю, что ваш часовой пояс JVM - Европа / Лондон (мы увидим, что это имеет значение).Когда я установлю свой часовой пояс таким образом и свою локализацию в Великобритании, я смогу точно воспроизвести ваши результаты.

Проанализировал "13:40" со средним временем по Гринвичу для часового пояса 01.01.1970, 13:40

Разбор 13:40 в вашем часовом поясе по умолчанию дает 13:40 в вашем часовом поясе по умолчанию, никаких сюрпризов.Поскольку Великобритания была со смещением UTC +01: 00 зимой 1970 года, это время совпадает с 12:40 UTC (когда дата не указана, SimpleDateFormat использует значение по умолчанию 1 января 1970 года).Когда на выходе выдается Greenwich Mean Time, это ошибка.

Проанализировано "13:40" с часовым поясом по тихоокеанскому стандартному времени на 01.01.1970, 22: 40

В 1970 году западное побережье США отстало от UTC на 8 часов, то есть от Великобритании на 9 часов.Поэтому, когда вы говорите SimpleDateFormat, что предполагается, что 13:40 находится в часовом поясе Америки / Лос-Анджелеса, оно разбирается во время 13:40 по тихоокеанскому времени, то есть в 21:40 UTC или в 22:40 по британскому времени (Америка / Лос-Анджелескак TimeZone интерпретирует PST, но он устарел, не полагайтесь на него.) MessageFormat использует часовой пояс по умолчанию для печати времени, поэтому печатается 22:40.

Parsed "13:40 UTC "с часовым поясом по Гринвичу Среднее время до 01.01.1970, 14:40

Поскольку (как уже упоминалось) Лондон находился в смещении +01: 00 в это время, а с MessageFormat использует часовой пояс по умолчанию, 14:40 - правильный и ожидаемый результат.dateFormat.parse(dateString) разбирается на Date, еще один плохо спроектированный и давно устаревший класс.Date является моментом времени и не может содержать смещение UTC (в отличие от класса OffsetTime, который правильно использует Базилик Бурк).И ваши наблюдения верны: SimpleDateFormat использует UTC из строки для интерпретации 13:40 в момент времени (а не в настройке часового пояса).MessageFormat не может знать, что время было ранее проанализировано с 13:40 UTC.

Выполнено синтаксический анализ "13:40 UTC" с часовым поясом Тихоокеанского стандартного времени на 01.01.1970, 14: 40

Поскольку SimpleDateFormat использует UTC из строки для интерпретации 13:40 в момент времени, мы получаем то же время, что и выше.

...