En guide till SimpleDateFormat

1. Introduktion

I den här guiden kommer vi att ta en djupgående rundtur i Simple klassen .

Vi tar en titt på enkla instantierings- och formateringsstilar samt användbara metoder som klassen exponerar för hantering av lokaliteter och tidszoner .

2. Enkel Instantiering

Låt oss först titta på hur man skapar ett nytt SimpleDateFormat- objekt.

Det finns fyra möjliga konstruktörer - men i enlighet med namnet, låt oss hålla sakerna enkla. Allt vi behöver för att komma igång är en strängrepresentation av ett datummönster vi vill ha .

Låt oss börja med ett dash-separerat datummönster som så:

"dd-MM-yyyy"

Detta formaterar korrekt ett datum som börjar med den aktuella dagen i månaden, den aktuella månaden och slutligen det aktuella året. Vi kan testa vår nya formaterare med ett enkelt enhetstest. Vi skapar ett nytt SimpleDateFormat- objekt och skickar in ett känt datum:

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

I ovanstående kod, de formateraren konverterar millisekunder som l ong i en läsbar datum - den 24 maj 1977.

2.1. Fabriksmetoder

Även om SimpleDateFormat är en praktisk klass för att snabbt skapa en datumformatör, uppmuntras vi att använda fabriksmetoderna på DateFormat- klassen getDateFormat () , getDateTimeFormat () , getTimeFormat () .

Ovanstående exempel ser lite annorlunda ut när du använder dessa fabriksmetoder:

DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT); assertEquals("5/24/77", formatter.format(new Date(233345223232L)));

Som vi kan se ovan, bestäms antalet formateringsalternativ i förväg av fälten i DateFormat- klassen. Detta begränsar till stor del våra tillgängliga alternativ för formatering, varför vi kommer att hålla oss till SimpleDateFormat i den här artikeln.

2.2. Trådsäkerhet

JavaDoc för SimpleDateFormat säger uttryckligen:

Datumformat synkroniseras inte. Vi rekommenderar att du skapar separata formatinstanser för varje tråd. Om flera trådar har åtkomst till ett format samtidigt måste det synkroniseras externt.

SimpleDateFormat- instanser är inte trådsäkra , och vi bör använda dem noggrant i samtidiga miljöer.

Det bästa sättet att lösa problemetär att använda dem i kombination med en ThreadLocal . På så sätt slutar varje tråd med sin egen SimpleDateFormat- instans, och bristen på delning gör programmet trådsäkert:

private final ThreadLocal formatter = ThreadLocal .withInitial(() -> new SimpleDateFormat("dd-MM-yyyy"));

Argumentet för metInitial- metoden är en leverantör av SimpleDateFormat- instanser. Varje gång ThreadLocal behöver skapa en instans använder den den här leverantören.

Sedan kan vi använda formateraren via ThreadLocal- instansen:

formatter.get().format(date)

Den ThreadLocal.get () metoden initierar Simple för den aktuella tråden först och sedan återanvänder den instansen.

Vi kallar denna teknik trådbegränsning eftersom vi begränsar användningen av varje instans till en specifik tråd.

Det finns två andra metoder för att ta itu med samma problem:

  • Använda synkroniserade block eller ReentrantLock s
  • Skapa kasta förekomster av SimpleDateFormat på begäran

Båda dessa tillvägagångssätt rekommenderas inte: Den förstnämnda drabbas av en betydande prestationshit när striden är hög, och den senare skapar många föremål, vilket sätter press på sopuppsamlingen.

Det är värt att nämna att sedan Java 8 har en ny DateTimeFormatter- klass introducerats . Den nya DateTimeFormatter- klassen är oföränderlig och trådsäker. Om vi ​​arbetar med Java 8 eller senare rekommenderas det att använda den nya DateTimeFormatter- klassen.

3. Tolkningsdatum

SimpleDateFormat och DateFormat tillåter oss inte bara att formatera datum utan vi kan också vända om operationen. Med användning av parse metoden, kan vi mata in String representation av ett datum och returnera Datum objektet likvärdiga:

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

Det är viktigt att notera här att mönstret levereras i konstruktören ska vara i samma format som datum tolkas med hjälp av parse metoden.

4. Mönster för datum och tid

SimpleDateFormat levererar en mängd olika alternativ vid formatering av datum. Medan hela listan finns i JavaDocs, låt oss utforska några av de vanligaste alternativen:

Brev Datumkomponent Exempel
M Månad 12; Dec
y år 94
d dag 23; Mån
H timme 03
m minut 57

Den utgång som returneras av datum komponenten beror också i hög grad på hur många tecken som används i String . Låt oss till exempel ta juni månad. Om vi ​​definierar datumsträngen som:

"MM"

Då kommer vårt resultat att visas som nummerkoden - 06. Om vi ​​dock lägger till ytterligare M i vår datumsträng:

"MMM"

Då visas vårt resulterade formaterade datum som ordet jun .

5. Tillämpa lokaler

Den Simple klassen också stöder ett brett spektrum av områden , som är inställd när konstruktören anropas.

Låt oss genomföra detta genom att formatera ett datum på franska. Vi skapar ett SimpleDateFormat- objekt medan vi levererar Locale.FRANCE till konstruktören.

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

Genom att ange ett visst datum, en onsdag eftermiddag, kan vi hävda att vår franceDateFormatter har formaterat datumet korrekt. Det nya datumet börjar korrekt med Vendredi -French för onsdag!

Det är värt att notera lite gotcha i den lokala versionen av konstruktören - medan många språk stöds, garanteras inte full täckning . Oracle rekommenderar att man använder fabriksmetoderna i DateFormat- klassen för att säkerställa lokal täckning.

6. Ändra tidszoner

Eftersom SimpleDateFormat utökar DateFormat- klassen kan vi också manipulera tidszonen med metoden setTimeZone . Låt oss ta en titt på detta i aktion:

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.

Vi lekte med att ändra lokaliteten för utmatningssträngen innan vi äntligen experimenterade med att använda tidszoner .

Som alltid kan hela källkoden hittas på GitHub.