Лучший способ извлечь объект TimeZone из строки? - PullRequest
8 голосов
/ 17 сентября 2008

У меня есть поле базы данных, которое содержит необработанное поле даты (хранится как символьные данные), например,

Пятница, 26 сентября 2008 г. 20:30 по восточному поясному времени

Я могу легко разобрать это как дату, с помощью SimpleDateFormat

DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
Date scheduledDate = dbFormatter.parse(rawDate);

Что я хотел бы сделать, это извлечь объект TimeZone из этой строки. TimeZone по умолчанию в JVM, в которой запускается это приложение, - GMT, поэтому я не могу использовать .getTimezoneOffset() из Date, проанализированного выше (потому что он вернет TimeZone по умолчанию).

Помимо токенизации необработанной строки и нахождения начальной позиции строки часового пояса (поскольку я знаю, что формат всегда будет EEEE, MMMM dd, yyyy hh:mm aa zzzz), есть способ использовать API DateFormat / SimpleDateFormat / Date / Calendar для извлечения объекта TimeZone - который будет иметь тот же часовой пояс, что и строка, которую я проанализировал, с DateFormat.parse()?

Одна вещь, которая мешает мне Date против Calendar в Java API, заключается в том, что Calendar должен заменить Date во всех местах ... но затем они решили, о, эй, давайте все еще использовать Date в DateFormat классах.

Ответы [ 7 ]

2 голосов
/ 17 сентября 2008

Я обнаружил, что следующее:

        DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
        dbFormatter.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
        Date scheduledDate = dbFormatter.parse("Friday, September 26, 2008 8:30 PM Eastern Daylight Time");
        System.out.println(scheduledDate);
        System.out.println(dbFormatter.format(scheduledDate));
        TimeZone tz = dbFormatter.getTimeZone();
        System.out.println(tz.getDisplayName());
        dbFormatter.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
        System.out.println(dbFormatter.format(scheduledDate));

Производит следующее:

Fri Sep 26 20:30:00 CDT 2008
Friday, September 26, 2008 08:30 PM Eastern Standard Time
Eastern Standard Time
Friday, September 26, 2008 08:30 PM Central Daylight Time

Я действительно обнаружил, что это несколько удивительно. Но, думаю, это показывает, что ответом на ваш вопрос является простой вызов getTimeZone в форматере после того, как вы проанализировали.

Edit: Выше был запущен с Sun JDK 1.6.

1 голос
/ 26 октября 2016

ТЛ; др

ZonedDateTime.parse( 
    "Friday, September 26, 2008 8:30 PM Eastern Daylight Time" , 
    DateTimeFormatter.ofPattern( "EEEE, MMMM d, uuuu h:m a zzzz" ) 
).getZone()

java.time

Современный способ - классы java.time. В Вопросе и других ответах используются проблемные старые унаследованные классы даты и времени или проект Joda-Time, которые теперь заменены классами java.time.

Определите DateTimeFormatter объект с шаблоном форматирования для соответствия вашим данным.

DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEEE, MMMM d, uuuu h:m a zzzz" );

Назначьте Locale, чтобы указать на человеческом языке название дня и названия месяца, а также культурные нормы для других вопросов форматирования.

f = f.withLocale( Locale.US );

Наконец, выполните синтаксический анализ, чтобы получить объект ZonedDateTime.

String input = "Friday, September 26, 2008 8:30 PM Eastern Daylight Time" ;
ZonedDateTime zdt = ZonedDateTime.parse( input , f );

zdt.toString (): 2008-09-26T20: 30-04: 00 [America / New_York]

Вы можете запросить часовой пояс у ZonedDateTime, представленного как ZoneId объект. Затем вы можете запросить ZoneId, если вам нужна дополнительная информация о часовом поясе.

ZoneId z = zdt.getZone();

Убедитесь сами в IdeOne.com .

ISO 8601

Избегайте обмена данными даты и времени в таком формате ужасный . Не принимайте английский язык, не добавляйте в выходную информацию такие вещи, как имя дня, и никогда не используйте псевдо-часовые пояса, такие как Eastern Daylight Time.

Для часовых поясов: Укажите правильное имя часового пояса в формате continent/region, например America/Montreal, Africa/Casablanca или Pacific/Auckland. Никогда не используйте 3-4-буквенное сокращение, такое как EST или IST, поскольку они не являются истинными часовыми поясами, не стандартизированы и даже не уникальны (!).

Для сериализации значений даты и времени в тексте используйте только форматы ISO 8601 . Классы java.time по умолчанию используют эти форматы при разборе / генерации строк для представления их значения.


О java.time

Платформа java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют проблемные старые устаревшие классы даты и времени, такие как java.util.Date, Calendar и & SimpleDateFormat.

Проект Joda-Time , который теперь находится в режиме обслуживания , рекомендует выполнить переход на java.time.

Чтобы узнать больше, см. Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Где взять классы java.time?

  • Java SE 8 и SE 9 и более поздние
    • Встроенный.
    • Часть стандартного Java API со встроенной реализацией.
    • Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7
    • Большая часть функциональности java.time перенесена на Java 6 и 7 в ThreeTen-Backport .
  • Android

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Здесь вы можете найти несколько полезных классов, таких как Interval, YearWeek, YearQuarter и more .

1 голос
/ 17 сентября 2008

Я рекомендую проверить API даты и времени Joda Time . Недавно я стал верующим в это, поскольку он значительно превосходит встроенную поддержку даты и времени в Java. В частности, вы должны проверить класс DateTimeZone . Надеюсь это поможет.

http://joda -time.sourceforge.net /

http://joda -time.sourceforge.net / апи-релиз / index.html

1 голос
/ 17 сентября 2008

@ Эд Томас:

Я пробовал что-то очень похожее на ваш пример, и я получаю совсем другие результаты:

String testString = "Friday, September 26, 2008 8:30 PM Pacific Standard Time";
DateFormat df = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");

System.out.println("The default TimeZone is: " + TimeZone.getDefault().getDisplayName());

System.out.println("DateFormat timezone before parse: " + df.getTimeZone().getDisplayName());

Date date = df.parse(testString);

System.out.println("Parsed [" + testString + "] to Date: " + date);

System.out.println("DateFormat timezone after parse: " + df.getTimeZone().getDisplayName());

Выход:

Часовой пояс по умолчанию: Восточное стандартное время

Часовой пояс DateFormat перед анализом: восточное стандартное время

Анализ [пятница, 26 сентября 2008 г., 20:30 по тихоокеанскому стандартному времени]: дата: суббота, 27 сентября, 00:30:00 ПО ВОСТОЧНОМУ ВРЕМЕНИ 2008

Часовой пояс DateFormat после разбора: восточное стандартное время

Похоже, DateFormat.getTimeZone() возвращает один и тот же часовой пояс до и после parse() ... даже если я добавлю явный setTimeZone() перед вызовом parse().

Глядя на источник для DateFormat и SimpleDateFormat, кажется, что getTimeZone() просто возвращает TimeZone базового Календаря ... который по умолчанию будет Календаром локали / TimeZone по умолчанию, если вы не укажете определенный для использования. 1025 *

0 голосов
/ 17 сентября 2008

Эд правильно понял. вы хотите timeZone для объекта DateFormat после анализа времени.

 String rawDate = "Friday, September 26, 2008 8:30 PM Eastern Daylight Time";
 DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
 Date scheduledDate = dbFormatter.parse(rawDate);

 System.out.println(rawDate); 
 System.out.println(scheduledDate); 
 System.out.println(dbFormatter.getTimeZone().getDisplayName());

производит

Friday, September 26, 2008 8:30 PM Eastern Daylight Time
Fri Sep 26 20:30:00 CDT 2008
Eastern Standard Time
0 голосов
/ 17 сентября 2008

Основное различие между датой и календарем состоит в том, что дата - это просто объект-значение без методов для его изменения. Так что он предназначен для хранения информации о дате / времени где-то. Если вы используете объект Calendar, вы можете изменить его после того, как он будет установлен на постоянный объект, который выполняет некоторую бизнес-логику с информацией о дате / времени. Это очень опасно, потому что у сущности нет возможности распознать это изменение. Класс Calendar предназначен для операций с датой / временем, например, добавления дней или чего-то в этом роде.

Играя с вашим примером, я получаю следующее:

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

public class TimeZoneExtracter {

    public static final void main(String[] args) throws ParseException {
        DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
        System.out.println(dbFormatter.getTimeZone());
        dbFormatter.parse("Fr, September 26, 2008 8:30 PM Eastern Daylight Time");
        System.out.println(dbFormatter.getTimeZone());
    }

}

Выход:

sun.util.calendar.ZoneInfo [ID = "Europe / Berlin" ... sun.util.calendar.ZoneInfo [ID = "Африка / Addis_Ababa" ...

Это результат, который вы хотели?

0 голосов
/ 17 сентября 2008

В качестве частичного решения вы можете использовать совпадение RegEx для получения часового пояса, поскольку перед ним всегда будет один и тот же текст. AM или PM.

Я не знаю достаточно о часовых поясах Java, чтобы получить последнюю часть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...