Автор оригинала: Sam Millington.
1. введение
В этом уроке мы проведем углубленную экскурсию по классу SimpleDateFormat .
Мы рассмотрим простые экземпляры | и стили форматирования , а также полезные методы, которые класс предоставляет для обработки локалей и часовых поясов .
2. Простое Создание Экземпляра
Во-первых, давайте рассмотрим, как создать экземпляр нового объекта SimpleDateFormat .
Есть 4 возможных конструктора – но в соответствии с названием давайте все упростим. Все, что нам нужно для начала, – это Строка представление шаблона даты , который мы хотим .
Давайте начнем с шаблона даты, разделенного черточками, вот так:
"dd-MM-yyyy"
Это позволит правильно отформатировать дату, начиная с текущего дня месяца, текущего месяца года и, наконец, текущего года. Мы можем протестировать наш новый форматер с помощью простого модульного теста. Мы создадим новый объект SimpleDateFormat и передадим известную дату:
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); assertEquals("24-05-1977", formatter.format(new Date(233345223232L)));
В приведенном выше коде форматер преобразует миллисекунды как длинные ong в читаемую человеком дату-24 мая 1977 года.
2.1. Заводские методы
Хотя SimpleDateFormat является удобным классом для быстрого создания средства форматирования данных, нам рекомендуется использовать заводские методы в DateFormat классе | getDateFormat () , getDateTimeFormat () , getTimeFormat() .
Приведенный выше пример выглядит немного иначе при использовании этих заводских методов:
DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT); assertEquals("5/24/77", formatter.format(new Date(233345223232L)));
Как видно из приведенного выше, количество параметров форматирования предопределено полями в классе DateFormat . Это в значительной степени ограничивает наши доступные варианты форматирования , поэтому в этой статье мы будем придерживаться SimpleDateFormat .
2.2. Безопасность резьбы
В JavaDoc для SimpleDateFormat явно указано:
Форматы дат не синхронизируются. Рекомендуется создавать отдельные экземпляры формата для каждого потока. Если несколько потоков одновременно обращаются к формату, он должен быть синхронизирован извне.
Таким образом , экземпляры SimpleDateFormat не являются потокобезопасными , и мы должны осторожно использовать их в параллельных средах.
Лучший подход к решению этой проблемы состоит в том, чтобы использовать их в сочетании с ThreadLocal . Таким образом, каждый поток получает свой собственный экземпляр SimpleDateFormat , а отсутствие общего доступа делает программу потокобезопасной:
private final ThreadLocalformatter = ThreadLocal .withInitial(() -> new SimpleDateFormat("dd-MM-yyyy"));
Аргумент для метода with Initial является поставщиком экземпляров SimpleDateFormat . Каждый раз, когда ThreadLocal необходимо создать экземпляр, он будет использовать этого поставщика.
Затем мы можем использовать форматер через экземпляр ThreadLocal :
formatter.get().format(date)
Метод ThreadLocal.get() сначала инициализирует SimpleDateFormat для текущего потока, а затем повторно использует этот экземпляр.
Мы называем эту технику ограничение потока поскольку мы ограничиваем использование каждого экземпляра одним конкретным потоком.
Есть два других подхода к решению той же проблемы:
- Использование синхронизированных блоков или ReentrantLock s
- Создание ненужных экземпляров SimpleDateFormat по требованию
Оба этих подхода не рекомендуются: первый приводит к значительному снижению производительности, когда конкуренция высока, а второй создает много объектов, оказывая давление на сборку мусора.
Стоит отметить, что начиная с Java 8, был введен новый DateTimeFormatter класс . Новый класс DateTimeFormatter является неизменяемым и потокобезопасным. Если мы работаем с Java 8 или более поздней версией, рекомендуется использовать новый класс DateTimeFormatter .
3. Анализ Дат
SimpleDateFormat и DateFormat не только позволяют нам форматировать даты, но мы также можем отменить операцию. Используя метод parse , мы можем ввести строковое представление даты и вернуть эквивалент даты объекта:
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); Date myDate = new Date(233276400000L); Date parsedDate = formatter.parse("24-05-1977"); assertEquals(myDate.getTime(), parsedDate.getTime());
Здесь важно отметить, что шаблон , предоставленный в конструкторе, должен быть в том же формате, что и дата, проанализированная с помощью метода parse .
4. Шаблоны Даты и времени
SimpleDateFormat предоставляет широкий спектр различных опций при форматировании дат. Хотя полный список доступен в JavaDocs , давайте рассмотрим некоторые из наиболее часто используемых опций:
M | 12; Дек | Месяц |
y | 94 | год |
d | 23; Пн | день |
H | 03 | час |
m | 57 | минута |
Вывод , возвращаемый компонентом date, также сильно зависит от количества символов, используемых в строке . Например, возьмем июнь месяц. Если мы определим строку даты как:
"MM"
Тогда наш результат будет выглядеть как код номера – 06. Однако, если мы добавим еще один M в нашу строку даты:
"MMM"
Затем наши результирующие отформатированные данные отображаются в виде слова Jun .
5. Применение локализаций
Класс SimpleDateFormat также поддерживает широкий диапазон локализаций , который устанавливается при вызове конструктора.
Давайте применим это на практике, отформатировав дату на французском языке. Мы создадим экземпляр объекта SimpleDateFormat , предоставив Locale.ФРАНЦИЯ к конструктору.
SimpleDateFormat franceDateFormatter = new SimpleDateFormat("EEEEE dd-MMMMMMM-yyyy", Locale.FRANCE); Date myWednesday = new Date(1539341312904L); assertTrue(franceDateFormatter.format(myWednesday).startsWith("vendredi"));
Указав заданную дату, в среду днем, мы можем утверждать, что наш france DateFormatter правильно отформатировал дату. Новая дата правильно начинается с Vendredi -Французский для среды!
Стоит отметить небольшую ошибку в версии Locale конструктора – хотя поддерживаются многие локали, полное покрытие не гарантируется . Oracle рекомендует использовать заводские методы в классе DateFormat для обеспечения локального покрытия.
6. Смена часовых Поясов
Поскольку SimpleDateFormat расширяет класс DateFormat , мы также можем манипулировать часовым поясом с помощью метода setTimeZone . Давайте посмотрим на это в действии:
Date now = new Date(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEEE dd-MMM-yy HH:mm:ssZ"); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Europe/London")); logger.info(simpleDateFormat.format(now)); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York")); logger.info(simpleDateFormat.format(now));
В приведенном выше примере мы поставляем одну и ту же Дату в два разных часовых пояса одного и того же объекта SimpleDateFormat . Мы также добавили символ ‘Z’ в конец шаблона String , чтобы указать различия в часовых поясах . Вывод из метода format затем регистрируется для пользователя.
Нажав run, мы можем увидеть текущее время относительно двух часовых поясов:
INFO: Friday 12-Oct-18 12:46:14+0100 INFO: Friday 12-Oct-18 07:46:14-0400
7. Резюме
В этом уроке мы глубоко погрузились в тонкости SimpleDateFormat .
Мы рассмотрели, как создать экземпляр SimpleDateFormat , а также как шаблон Строка влияет на форматирование даты .
Мы поиграли с изменением локалей выходной строки, прежде чем, наконец, поэкспериментировать с использованием часовых поясов .
Как всегда, полный исходный код можно найти на GitHub .