Ръководство за SimpleDateFormat

1. Въведение

В този урок ще направим задълбочена обиколка на класа SimpleDateFormat .

Ще разгледаме прости стилове за инстанциране и форматиране, както и полезните методи, които класът предлага за обработка на локали и часови зони .

2. Просто инстанциране

Първо, нека разгледаме как да създадем екземпляр на нов обект SimpleDateFormat .

Има 4 възможни конструктора - но в съответствие с името, нека улесним нещата. Всичко, от което се нуждаем, за да започнем, е String представяне на датен модел, който искаме .

Нека започнем с модел на дата, разделен с тире, така:

"dd-MM-yyyy"

Това правилно ще форматира дата, започваща с текущия ден на месеца, текущия месец от годината и накрая текущата година. Можем да тестваме новия си форматиращ апарат с прост единичен тест. Ще създадем екземпляр на нов обект SimpleDateFormat и ще предадем известна дата:

SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); assertEquals("24-05-1977", formatter.format(new Date(233345223232L))); 

В горния код, за Formatter конвертира милисекунди като л 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 ThreadLocal formatter = ThreadLocal .withInitial(() -> new SimpleDateFormat("dd-MM-yyyy"));

Аргументът за метода withInitial е доставчик на екземпляри SimpleDateFormat . Всеки път, когато ThreadLocal трябва да създаде екземпляр, той ще използва този доставчик.

След това можем да използваме форматиращия чрез екземпляра ThreadLocal :

formatter.get().format(date)

Методът ThreadLocal.get () първоначално инициализира SimpleDateFormat за текущата нишка и след това използва повторно този екземпляр.

Ние наричаме тази техника ограничение на нишките, тъй като ограничаваме използването на всеки екземпляр до една конкретна нишка.

Има два други подхода за справяне със същия проблем:

  • Използване на синхронизирани блокове или ReentrantLock s
  • Създаване на изхвърлящи копия на SimpleDateFormat при поискване

И двата подхода не се препоръчват: първият има значителен удар в производителността, когато оспорването е високо, а вторият създава много обекти, оказвайки натиск върху събирането на боклука.

Струва си да се спомене, че от Java 8 е въведен нов клас DateTimeFormatter . Новият клас DateTimeFormatter е неизменим и безопасен за нишки. Ако работим с Java 8 или по-нова версия, се препоръчва използването на новия клас DateTimeFormatter .

3. Дати за анализиране

SimpleDateFormat и DateFormat не само ни позволява да дати формат - но ние също може да се обърне на операцията. Използвайки метода на синтактичния анализ , можем да въведем String представяне на дата и да върнем еквивалента на обекта Date :

SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); Date myDate = new Date(233276400000L); Date parsedDate = formatter.parse("24-05-1977"); assertEquals(myDate.getTime(), parsedDate.getTime());

Тук е важно да се отбележи, че моделът, предоставен в конструктора, трябва да бъде в същия формат като датата, анализирана чрез метода на синтактичен анализ .

4. Модели за дата и час

SimpleDateFormat предоставя широк спектър от различни опции при форматиране на дати. Въпреки че пълният списък е наличен в JavaDocs, нека разгледаме някои от най-често използваните опции:

Писмо Компонент на датата Пример
М Месец 12; Дек
у година 94
д ден 23; Понеделник
З. час 03
м минута 57

На изхода върнат от дата компонент също е силно зависима от броя на символите, използвани в рамките на String . Да вземем например месец юни. Ако дефинираме низа на датата като:

"MM"

Тогава нашият резултат ще се появи като цифров код - 06. Ако обаче добавим още М към нашия низ за дата:

"MMM"

Тогава получената ни форматирана дата се появява като думата юни .

5. Прилагане на локали

Класът SimpleDateFormat също поддържа широк набор от локали, които се задават при извикване на конструктора.

Нека приложим това на практика, като форматираме дата на френски. Ще създадем екземпляр на обект SimpleDateFormat, докато доставяме Locale.FRANCE на конструктора.

SimpleDateFormat franceDateFormatter = new SimpleDateFormat("EEEEE dd-MMMMMMM-yyyy", Locale.FRANCE); Date myWednesday = new Date(1539341312904L); assertTrue(franceDateFormatter.format(myWednesday).startsWith("vendredi"));

Като предоставим дадена дата, сряда следобед, можем да твърдим, че franceDateFormatter е форматирал правилно датата. Новата дата правилно започва с Vendredi -French за сряда!

It's worth noting a little gotcha in the Locale version of the constructor – whilst many locales are supported, full coverage is not guaranteed. Oracle recommends using the factory methods on DateFormat class to ensure locale coverage.

6. Changing Time Zones

Since SimpleDateFormat extends the DateFormat class, we can also manipulate the time zone using the setTimeZone method. Let's take a look at this in action:

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));

In the above example, we supply the same Date to two different time zones on the same SimpleDateFormat object. We've also added the ‘Z' character to the end of the pattern String to indicate the time zone differences. The output from the format method is then logged for the user.

Hitting run, we can see the current times relative to the two time zones:

INFO: Friday 12-Oct-18 12:46:14+0100 INFO: Friday 12-Oct-18 07:46:14-0400

7. Summary

In this tutorial, we've taken a deep dive into the intricacies of SimpleDateFormat.

We've looked at how to instantiate SimpleDateFormat as well as how the pattern String impacts how the date is formatted.

Поиграхме си с промяна на локалите на изходния низ, преди накрая да експериментираме с използването на часови зони .

Както винаги пълният изходен код може да бъде намерен в GitHub.