Java 8 Datum – LocalDate, LocalDateTime, Instant

Datumtijd API is een van de grootste functies van de Java 8-release. Java miste vanaf het begin een consistente aanpak voor Datum en Tijd en de Java 8 Datumtijd API is een welkome toevoeging aan de kern-Java-API’s.

Waarom hebben we een nieuwe Java Datumtijd API nodig?

Voordat we naar de Java 8 Datumtijd API gaan kijken, laten we eens kijken waarom we een nieuwe API hiervoor nodig hebben. Er zijn verschillende problemen geweest met de bestaande datum- en tijdgerelateerde klassen in Java, enkele daarvan zijn:

  • Java Datumtijd-klassen zijn niet consistent gedefinieerd, we hebben de Datumklasse zowel in de java.util- als in de java.sql-pakketten. Opnieuw zijn opmaak- en analyseklassen gedefinieerd in het java.text-pakket.
  • java.util.Date bevat zowel datum- als tijdwaarden, terwijl java.sql.Date alleen een datumwaarde bevat. Het feit dat dit in het java.sql-pakket staat, heeft geen zin. Bovendien hebben beide klassen dezelfde naam, wat op zich al een zeer slecht ontwerp is.
  • Er zijn geen duidelijk gedefinieerde klassen voor tijd, tijdstempel, opmaak en analyse. We hebben de abstracte klasse java.text.DateFormat voor de analyse- en opmaakbehoeften. Gewoonlijk wordt de klasse SimpleDateFormat gebruikt voor analyse en opmaak.
  • Alle Datumklassen zijn veranderlijk, dus ze zijn niet thread-safe. Het is een van de grootste problemen met de Java Datum- en Kalenderklassen.
  • De Date-klasse biedt geen internationalisatie, er is geen ondersteuning voor tijdzones. Dus de klassen java.util.Calendar en java.util.TimeZone werden geïntroduceerd, maar ze hebben ook alle bovenstaande problemen.

Er zijn nog enkele andere problemen met de methoden gedefinieerd in de Date- en Calendar-klassen, maar bovenstaande problemen maken duidelijk dat er een robuuste Date Time API nodig was in Java. Daarom speelde Joda Time een sleutelrol als kwalitatieve vervanging voor Java Date Time vereisten.

Ontwerpprincipes Java 8 Date Time

De Java 8 Date Time API is een implementatie van JSR-310. Het is ontworpen om alle gebreken in de verouderde datumtijd-implementaties te overwinnen. Enkele van de ontwerpprincipes van de nieuwe Date Time API zijn:

  1. Onveranderlijkheid: Alle klassen in de nieuwe Date-Time API zijn onveranderlijk en goed voor multithreaded omgevingen.

  2. Scheiding van belangen: De nieuwe API scheidt duidelijk tussen menselijk leesbare datumtijd en machinetijd (Unix-tijdstempel). Het definieert afzonderlijke klassen voor Datum, Tijd, DatumTijd, Tijdstip, Tijdzone, enz.

  3. Duidelijkheid: De methoden zijn duidelijk gedefinieerd en voeren dezelfde actie uit in alle klassen. Bijvoorbeeld, om de huidige instantie te krijgen hebben we de methode nu(). Er zijn format() en parse() methoden gedefinieerd in al deze klassen in plaats van een aparte klasse ervoor te hebben.

    Alle klassen gebruiken het Factory Patroon en het Strategie Patroon voor betere afhandeling. Eenmaal de methoden in een van de klassen te hebben gebruikt, zal het werken met andere klassen niet moeilijk zijn.

  4. Nuttigheidsoperaties: Alle nieuwe klassen van de Date-Time API worden geleverd met methoden om veelvoorkomende taken uit te voeren, zoals optellen, aftrekken, opmaken, parsen, het verkrijgen van het afzonderlijke deel in datum/tijd, enz.

  5. Uitbreidbaar: De nieuwe Date Time API werkt op het ISO-8601-kalendersysteem, maar we kunnen het ook gebruiken met andere niet-ISO-kalenders.

Datumtijd-API-pakketten

De Java 8 Date Time API bestaat uit de volgende pakketten.

  1. java.time: Dit is het basispakket van de nieuwe Java Date Time API. Alle belangrijke basisklassen maken deel uit van dit pakket, zoals LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration, enz. al deze klassen zijn onveranderlijk en thread-safe. Meestal zullen deze klassen voldoende zijn voor het afhandelen van gemeenschappelijke vereisten.
  2. java.time.chrono: Dit pakket definieert generieke API’s voor niet-ISO-kalendersystemen. We kunnen de klasse AbstractChronology uitbreiden om ons eigen kalendersysteem te maken.
  3. java.time.format: Deze package bevat klassen die worden gebruikt voor het formatteren en parseren van datum-tijdobjecten. Meestal gebruiken we ze niet direct omdat de hoofdklassen in het java.time-pakket methoden voor formatteren en parseren bieden.
  4. java.time.temporal: Deze package bevat temporale objecten en we kunnen deze gebruiken om specifieke data of tijden met betrekking tot datum/tijdobjecten te vinden. Bijvoorbeeld, we kunnen deze gebruiken om de eerste of laatste dag van de maand te vinden. Je kunt deze methoden gemakkelijk herkennen omdat ze altijd de indeling “withXXX” hebben.
  5. java.time.zone Package: Deze package bevat klassen voor het ondersteunen van verschillende tijdzones en hun regels.

Java 8 Date Time API-klassen Voorbeelden

We hebben de belangrijkste onderdelen van de Java Date Time API bekeken. Het is nu tijd om de belangrijkste klassen van de Date Time API te bekijken met voorbeelden.

1. LocalDate

LocalDate is een onveranderlijke klasse die een datum vertegenwoordigt met het standaard formaat yyyy-MM-dd. We kunnen de methode now() gebruiken om de huidige datum te krijgen. We kunnen ook invoerargumenten voor jaar, maand en datum verstrekken om een LocalDate-instantie te maken.

Deze klasse biedt een overbelaste methode voor now() waarbij we ZoneId kunnen doorgeven om datums te krijgen in een specifieke tijdzone. Deze klasse biedt dezelfde functionaliteit als java.sql.Date.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;

/**
 * LocalDate Voorbeelden
 * @author pankaj
 *
 */
public class LocalDateExample {

	public static void main(String[] args) {
		
		//Huidige Datum
		LocalDate today = LocalDate.now();
		System.out.println("Current Date="+today);
		
		//LocalDate maken door invoerargumenten te verstrekken
		LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
		System.out.println("Specific Date="+firstDay_2014);
		
		
		//Probeer datum te maken door ongeldige invoer te verstrekken
		//LocalDate feb29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
		//Uitzondering in thread "main" java.time.DateTimeException: 
		//Ongeldige datum '29 februari' omdat '2014' geen schrikkeljaar is
		
		//Huidige datum in "Asia/Kolkata", u kunt deze krijgen uit de ZoneId javadoc
		LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: Onbekende tijdzone-ID: IST
		//LocalDate vandaagIST = LocalDate.now(ZoneId.of("IST"));
		
		//Datum krijgen vanaf de basisdatum d.w.z. 01/01/1970
		LocalDate dateFromBase = LocalDate.ofEpochDay(365);
		System.out.println("365th day from base date= "+dateFromBase);
		
		LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);
		System.out.println("100th day of 2014="+hundredDay2014);
	}

}

Er wordt uitleg gegeven over de methoden van LocalDate in de opmerkingen. Wanneer we dit programma uitvoeren, krijgen we de volgende uitvoer.

Current Date=2014-04-28
Specific Date=2014-01-01
Current Date in IST=2014-04-29
365th day from base date= 1971-01-01
100th day of 2014=2014-04-10

2. LocalTime

LocalTime is een onveranderlijke klasse waarvan een instantie een tijd in het leesbare formaat van mensen vertegenwoordigt. De standaardindeling is hh:mm:ss.zzz. Net als LocalDate biedt deze klasse ondersteuning voor tijdzones en het maken van een instantie door uur, minuut en seconde als invoerargumenten door te geven.

package com.journaldev.java8.time;

import java.time.LocalTime;
import java.time.ZoneId;

/**
 * Voorbeelden van LocalTime
 * @author pankaj
 *
 */
public class LocalTimeExample {

	public static void main(String[] args) {
		
		//Huidige Tijd
		LocalTime time = LocalTime.now();
		System.out.println("Current Time="+time);
		
		//LocalTime maken door invoerargumenten te geven
		LocalTime specificTime = LocalTime.of(12,20,25,40);
		System.out.println("Specific Time of Day="+specificTime);
		
		
		//Probeer tijd te maken door ongeldige invoer te geven
		//LocalTime ongeldigeTijd = LocalTime.of(25,20);
		//Uitzondering in thread "main" java.time.DateTimeException: 
		//Ongeldige waarde voor UurVanDeDag (geldige waarden 0 - 23): 25
		
		//Huidige datum in "Asia/Kolkata", je kunt het krijgen uit de ZoneId javadoc
		LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Time in IST="+timeKolkata);

		//java.time.zone.ZoneRulesException: Onbekende tijdzone-ID: IST
		//LocalTime vandaagIST = LocalTime.now(ZoneId.of("IST"));
		
		//Datum krijgen vanaf de basisdatum, dwz 01/01/1970
		LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
		System.out.println("10000th second time= "+specificSecondTime);

	}

}

Output:

Current Time=15:51:45.240
Specific Time of Day=12:20:25.000000040
Current Time in IST=04:21:45.276
10000th second time= 02:46:40

3. LocalDateTime

LocalDateTime is een onveranderlijk datum-tijd object dat een datum-tijd voorstelt met standaard formaat als yyyy-MM-dd-HH-mm-ss.zzz. Het biedt een fabrieksmethode die LocalDate en LocalTime invoerargumenten gebruikt om een LocalDateTime-instantie te maken.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZoneOffset;

public class LocalDateTimeExample {

	public static void main(String[] args) {
		
		//Huidige Datum
		LocalDateTime today = LocalDateTime.now();
		System.out.println("Current DateTime="+today);
		
		//Huidige Datum met behulp van LocalDate en LocalTime
		today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
		System.out.println("Current DateTime="+today);
		
		//LocalDateTime maken door invoerargumenten te verstrekken
		LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
		System.out.println("Specific Date="+specificDate);
		
		
		//Probeer een datum te maken door ongeldige invoer te verstrekken
		//LocalDateTime feb29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
		//Exception in thread "main" java.time.DateTimeException: 
		//Ongeldige waarde voor UurVanDeDag (geldige waarden 0 - 23): 25

		
		//Huidige datum in "Asia/Kolkata", je kunt deze vinden in de ZoneId javadoc
		LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: Onbekende tijdzone-ID: IST
		//LocalDateTime vandaagIST = LocalDateTime.now(ZoneId.of("IST"));
		
		//Datum krijgen vanaf de basisdatum d.w.z. 01/01/1970
		LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
		System.out.println("10000th second time from 01/01/1970= "+dateFromBase);

	}

}

In alle drie voorbeelden hebben we gezien dat als we ongeldige argumenten verstrekken voor het maken van Datum/Tijd, het een java.time.DateTimeException genereert, wat een RuntimeException is, dus we hoeven het niet expliciet op te vangen.

We hebben ook gezien dat we Datum/Tijd-gegevens kunnen krijgen door ZoneId door te geven, je kunt de lijst met ondersteunde ZoneId-waarden krijgen vanuit zijn JavaDoc. Wanneer we de bovenstaande klasse uitvoeren, krijgen we de volgende uitvoer.

Current DateTime=2014-04-28T16:00:49.455
Current DateTime=2014-04-28T16:00:49.493
Specific Date=2014-01-01T10:10:30
Current Date in IST=2014-04-29T04:30:49.493
10000th second time from 01/01/1970= 1970-01-01T02:46:40

4. Instant

Instant-klasse wordt gebruikt om te werken met machinaal leesbaar tijdsformaat. Instant slaat datumtijd op in Unix-timestamp.

package com.journaldev.java8.time;

import java.time.Duration;
import java.time.Instant;

public class InstantExample {

	public static void main(String[] args) {
		// Huidige timestamp
		Instant timestamp = Instant.now();
		System.out.println("Current Timestamp = "+timestamp);
		
		// Instant vanaf timestamp
		Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
		System.out.println("Specific Time = "+specificTime);
		
		// Voorbeeld van duur
		Duration thirtyDay = Duration.ofDays(30);
		System.out.println(thirtyDay);
	}

}

Output:

Current Timestamp = 2014-04-28T23:20:08.489Z
Specific Time = 2014-04-28T23:20:08.489Z
PT720H

Java 8 Datum API Hulpprogramma’s

De meeste principeklassen van Datum Tijd bieden verschillende hulpprogramma-methoden zoals plus/min dagen, weken, maanden, enz. Er zijn enkele andere hulpprogramma-methoden voor het aanpassen van de datum met behulp van TemporalAdjuster en om de periode tussen twee data te berekenen.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;

public class DateAPIUtilities {

	public static void main(String[] args) {
		
		LocalDate today = LocalDate.now();
		
		//Haal het jaar op, controleer of het een schrikkeljaar is
		System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
		
		//Vergelijk twee LocalDate voor en na
		System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1)));
		
		//Maak LocalDateTime van LocalDate
		System.out.println("Current Time="+today.atTime(LocalTime.now()));
		
		//plus en min operaties
		System.out.println("10 days after today will be "+today.plusDays(10));
		System.out.println("3 weeks after today will be "+today.plusWeeks(3));
		System.out.println("20 months after today will be "+today.plusMonths(20));

		System.out.println("10 days before today will be "+today.minusDays(10));
		System.out.println("3 weeks before today will be "+today.minusWeeks(3));
		System.out.println("20 months before today will be "+today.minusMonths(20));
		
		//Temporale aanpassers voor het aanpassen van de data
		System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth()));
		LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
		System.out.println("Last date of this year= "+lastDayOfYear);
		
		Period period = today.until(lastDayOfYear);
		System.out.println("Period Format= "+period);
		System.out.println("Months remaining in the year= "+period.getMonths());		
	}
}

Output:

Year 2014 is Leap Year? false
Today is before 01/01/2015? true
Current Time=2014-04-28T16:23:53.154
10 days after today will be 2014-05-08
3 weeks after today will be 2014-05-19
20 months after today will be 2015-12-28
10 days before today will be 2014-04-18
3 weeks before today will be 2014-04-07
20 months before today will be 2012-08-28
First date of this month= 2014-04-01
Last date of this year= 2014-12-31
Period Format= P8M3D
Months remaining in the year= 8

Java 8 Datumanalyse en -formattering

Het is zeer gebruikelijk om een datum in verschillende formaten weer te geven en vervolgens een String te parsen om de Datum Tijd objecten te verkrijgen.

package com.journaldev.java8.time;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateParseFormatExample {

	public static void main(String[] args) {
		
		//Voorbeelden van formatteren
		LocalDate date = LocalDate.now();
		//standaardformaat
		System.out.println("Default format of LocalDate="+date);
		//specifiek formaat
		System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
		System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		
		LocalDateTime dateTime = LocalDateTime.now();
		//standaardformaat
		System.out.println("Default format of LocalDateTime="+dateTime);
		//specifiek formaat
		System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
		System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		Instant timestamp = Instant.now();
		//standaardformaat
		System.out.println("Default format of Instant="+timestamp);
		
		//Voorbeelden van parsen
		LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48",
				DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
		System.out.println("Default format after parsing = "+dt);
	}

}

Output:

Default format of LocalDate=2014-04-28
28::Apr::2014
20140428
Default format of LocalDateTime=2014-04-28T16:25:49.341
28::Apr::2014 16::25::49
20140428
Default format of Instant=2014-04-28T23:25:49.342Z
Default format after parsing = 2014-04-27T21:39:48

Java Date API Legacy Date Time Ondersteuning

Legacy Datum/Tijd klassen worden in bijna alle applicaties gebruikt, dus achterwaartse compatibiliteit is een must. Daarom zijn er verschillende hulpprogramma-methoden waarmee we Legacy klassen naar nieuwe klassen en vice versa kunnen converteren.

package com.journaldev.java8.time;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class DateAPILegacySupport {

	public static void main(String[] args) {
		
		//Datum naar Instant
		Instant timestamp = new Date().toInstant();
		//Nu kunnen we Instant naar LocalDateTime of andere vergelijkbare klassen converteren
		LocalDateTime date = LocalDateTime.ofInstant(timestamp, 
						ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
		System.out.println("Date = "+date);
		
		//Kalender naar Instant
		Instant time = Calendar.getInstance().toInstant();
		System.out.println(time);
		//TimeZone naar ZoneId
		ZoneId defaultZone = TimeZone.getDefault().toZoneId();
		System.out.println(defaultZone);
		
		//ZonedDateTime van specifieke Kalender
		ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
		System.out.println(gregorianCalendarDateTime);
		
		//Datum API naar Legacy klassen
		Date dt = Date.from(Instant.now());
		System.out.println(dt);
		
		TimeZone tz = TimeZone.getTimeZone(defaultZone);
		System.out.println(tz);
		
		GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
		System.out.println(gc);
		
	}

}

Output:

Date = 2014-04-28T16:28:54.340
2014-04-28T23:28:54.395Z
America/Los_Angeles
2014-04-28T16:28:54.404-07:00[America/Los_Angeles]
Mon Apr 28 16:28:54 PDT 2014
sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
java.util.GregorianCalendar[time=1398727734404,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2014,MONTH=3,WEEK_OF_YEAR=18,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=118,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=28,SECOND=54,MILLISECOND=404,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]

Zoals je kunt zien zijn de toString() methoden van de legacy TimeZone en GregorianCalendar klassen te uitgebreid en niet gebruiksvriendelijk.

Conclusie

I like this new Date Time API a lot. Some of the most used classes will be LocalDate and LocalDateTime. It’s very easy to work with the new classes. And, having similar methods that does a particular job makes it easy to find. It will take some time from moving legacy classes to new Date Time classes, but I believe it will be worth the time and effort.

Source:
https://www.digitalocean.com/community/tutorials/java-8-date-localdate-localdatetime-instant