Java DateFormat parse() не соблюдает часовой пояс

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("America/New_York"));
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
df.setTimeZone(TimeZone.getTimeZone("America/New_York"));

try {
    System.out.println(df.format(cal.getTime()));
    System.out.println(df.parse(df.format(cal.getTime())));
} catch (ParseException e) {
    e.printStackTrace();
}

Вот результат:

2011-09-24 14:10:51 -0400

Сб Сен 24 20:10:51 CEST 2011

Почему при анализе даты, которую я получаю из формата(), она не соблюдает часовой пояс?

Ответы

Ответ 1

Вы печатаете результат вызова Date.toString(), который всегда использует часовой пояс по умолчанию. В принципе, вы не должны использовать Date.toString() для чего угодно, кроме отладки.

Не забывайте, что a Date не имеет часового пояса - он представляет собой момент времени, измеренный как миллисекунды с эпохи Unix (полночь 1 января 1970 UTC).

Если вы форматируете дату с помощью своего форматирования, это должно совпадать с тем же ответом, что и раньше.

В стороне, я бы рекомендовал использовать Joda Time вместо Date/Calendar, если вы делаете какие-либо значительные время работы на Java; это гораздо более удобный API.

Ответ 2

DateFormat.parse() НЕ является запросом (то, что возвращает значение и не изменяет состояние системы). Это команда, которая имеет побочный эффект обновления внутреннего объекта Calendar. После вызова parse() вам нужно получить доступ к часовому поясу либо путем доступа к DateFormat Calendar, либо вызова DateFormat.getTimeZone(). Если вы не хотите выбросить исходный часовой пояс и использовать локальное время, не используйте возвращаемое значение Date из parse(). Вместо этого используйте объект календаря после разбора. И то же самое верно для метода формата. Если вы собираетесь отформатировать дату, передайте календарь с информацией о часовом поясе в объект DateFormat перед вызовом format(). Вот как вы можете конвертировать один формат в другой формат, сохраняя исходный часовой пояс:

    DateFormat originalDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
    DateFormat targetDateFormat = new SimpleDateFormat("EEE., MMM. dd, yyyy");

    originalDateFormat.parse(origDateString);
    targetDateFormat.setCalendar(originalDateFormat.getCalendar());
    return targetDateFormat.format(targetDateFormat.getCalendar().getTime());

Это беспорядочно, но необходимо, так как parse() не возвращает значение, которое сохраняет часовой пояс, а format() не принимает значение, определяющее часовой пояс (класс Date).

Ответ 3

DateFormat - это абстрактный класс для подклассов форматирования даты и времени который форматирует и анализирует даты или время в независимом от языка манера. Подкласс даты/времени, например SimpleDateFormat, позволяет форматировать (т.е. дата → текст), разбор (текст → дата), и нормализация. Дата представляется как объект Date или как миллисекунд с 1 января 1970 года, 00:00:00 GMT.

Из spec возвращается EPOCH время