Einführung in die Joda-Zeit

1. Einleitung

Joda-Time ist vor der Veröffentlichung von Java 8 die am häufigsten verwendete Bibliothek für die Verarbeitung von Datum und Uhrzeit. Ziel war es, eine intuitive API für die Verarbeitung von Datum und Uhrzeit anzubieten und auch die in der Java-API für Datum und Uhrzeit auftretenden Entwurfsprobleme zu beheben.

Die in dieser Bibliothek implementierten zentralen Konzepte wurden mit der Veröffentlichung der Java 8-Version im JDK-Kern eingeführt. Die neue Datums- und Uhrzeit-API befindet sich im Paket java.time (JSR-310). Eine Übersicht über diese Funktionen finden Sie in diesem Artikel.

Nach der Veröffentlichung von Java 8 betrachten die Autoren das Projekt als weitgehend abgeschlossen und empfehlen, wenn möglich die Java 8-API zu verwenden.

2. Warum Joda-Time verwenden?

Die Datums- / Uhrzeit-API vor Java 8 hatte mehrere Entwurfsprobleme.

Zu den Problemen gehört die Tatsache, dass die Klassen Date und SimpleDateFormatter nicht threadsicher sind. Um dieses Problem zu beheben , verwendet Joda-Time unveränderliche Klassen für die Behandlung von Datum und Uhrzeit.

Die Date- Klasse stellt kein tatsächliches Datum dar, sondern gibt einen Zeitpunkt mit Millisekundengenauigkeit an. Das Jahr in einem Datum beginnt um 1900, während die meisten Datumsoperationen normalerweise die Epochenzeit verwenden, die am 1. Januar 1970 beginnt.

Außerdem ist der Versatz von Tag, Monat und Jahr eines Datums nicht intuitiv. Tage beginnen bei 0, während der Monat bei 1 beginnt. Um auf einen von ihnen zugreifen zu können, müssen wir die Calendar- Klasse verwenden. Joda-Time bietet eine saubere und flüssige API für den Umgang mit Datum und Uhrzeit.

Joda-Time bietet auch Unterstützung für acht Kalendersysteme , während Java nur zwei bietet: Gregorian - java.util.GregorianCalendar und Japanisch - java.util.JapaneseImperialCalendar .

3. Setup

Um die Funktionalität der Joda-Time-Bibliothek einzuschließen, müssen wir die folgende Abhängigkeit von Maven Central hinzufügen:

 joda-time joda-time 2.10 

4. Bibliotheksübersicht

Joda-Time modelliert das Konzept von Datum und Uhrzeit mithilfe der Klassen im Paket org.joda.time .

Unter diesen Klassen werden am häufigsten verwendet:

  • LocalDate - repräsentiert ein Datum ohne Uhrzeit
  • LocalTime - repräsentiert die Zeit ohne Zeitzone
  • LocalDateTime - repräsentiert sowohl das Datum als auch die Uhrzeit ohne Zeitzone
  • Sofort - repräsentiert einen genauen Zeitpunkt in Millisekunden ab der Java-Epoche von 1970-01-01T00: 00: 00Z
  • Dauer - Gibt die Dauer in Millisekunden zwischen zwei Zeitpunkten an
  • Zeitraum - ähnlich wie Dauer , ermöglicht jedoch den Zugriff auf einzelne Komponenten des Datums- und Zeitobjekts wie Jahre, Monat, Tage usw.
  • Intervall - repräsentiert das Zeitintervall zwischen 2 Zeitpunkten

Weitere wichtige Funktionen sind die Datums-Parser und Formatierer . Diese finden Sie im Paket org.joda.time.format .

Das Kalendersystem und die zeitzonenspezifischen Klassen finden Sie in den Paketen org.joda.time.chrono und org.joda.time.tz.

Schauen wir uns einige Beispiele an, in denen wir die wichtigsten Funktionen von Joda-Time verwenden, um Datum und Uhrzeit zu verarbeiten.

5. Darstellung von Datum und Uhrzeit

5.1. Aktuelles Datum und Uhrzeit

Das aktuelle Datum ohne Zeitangaben kann mithilfe der now () -Methode aus der LocalDate- Klasse abgerufen werden :

LocalDate currentDate = LocalDate.now();

Wenn wir nur die aktuelle Uhrzeit ohne Datumsangaben benötigen, können wir die LocalTime- Klasse verwenden:

LocalTime currentTime = LocalTime.now();

Um eine Darstellung des aktuellen Datums und der aktuellen Uhrzeit ohne Berücksichtigung der Zeitzone zu erhalten, können wir LocalDateTime verwenden :

LocalDateTime currentDateAndTime = LocalDateTime.now();

Mit currentDateAndTime können wir es jetzt in die anderen Objekttypen konvertieren, die Datum und Uhrzeit modellieren.

Mit der Methode toDateTime () können wir ein DateTime- Objekt (das die Zeitzone berücksichtigt ) erhalten . Wenn keine Zeit erforderlich ist, können wir sie mit der Methode toLocalDate () in ein LocalDate konvertieren. Wenn wir nur die Zeit benötigen, können wir toLocalTime () verwenden , um ein LocalTime- Objekt abzurufen :

DateTime dateTime = currentDateAndTime.toDateTime(); LocalDate localDate = currentDateAndTime.toLocalDate(); LocalTime localTime = currentDateAndTime.toLocalTime();

Alle oben genannten Methoden verfügen über eine überladene Methode, die ein DateTimeZone- Objekt akzeptiert , um das Datum oder die Uhrzeit in der angegebenen Zeitzone darzustellen:

LocalDate currentDate = LocalDate.now(DateTimeZone.forID("America/Chicago"));

Außerdem bietet Joda-Time eine hervorragende Integration in die Java-API für Datum und Uhrzeit. Die Konstruktoren akzeptieren ein java.util.Date- Objekt und wir können auch die toDate () -Methode verwenden, um ein java.util.Date- Objekt zurückzugeben:

LocalDateTime currentDateTimeFromJavaDate = new LocalDateTime(new Date()); Date currentJavaDate = currentDateTimeFromJavaDate.toDate();

5.2. Benutzerdefiniertes Datum und Uhrzeit

Zur Darstellung von benutzerdefiniertem Datum und Uhrzeit stellt uns Joda-Time mehrere Konstruktoren zur Verfügung. Wir können die folgenden Objekte angeben:

  • ein Augenblick
  • ein Java Date- Objekt
  • Eine Zeichenfolgendarstellung von Datum und Uhrzeit im ISO-Format
  • parts of the date and time: year, month, day, hour, minute, second, millisecond
Date oneMinuteAgoDate = new Date(System.currentTimeMillis() - (60 * 1000)); Instant oneMinutesAgoInstant = new Instant(oneMinuteAgoDate); DateTime customDateTimeFromInstant = new DateTime(oneMinutesAgoInstant); DateTime customDateTimeFromJavaDate = new DateTime(oneMinuteAgoDate); DateTime customDateTimeFromString = new DateTime("2018-05-05T10:11:12.123"); DateTime customDateTimeFromParts = new DateTime(2018, 5, 5, 10, 11, 12, 123); 

Another way we can define a custom date and time is by parsing a given String representation of a date and time in the ISO format:

DateTime parsedDateTime = DateTime.parse("2018-05-05T10:11:12.123");

We can also parse custom representations of a date and time by defining a custom DateTimeFormatter:

DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("MM/dd/yyyy HH:mm:ss"); DateTime parsedDateTimeUsingFormatter = DateTime.parse("05/05/2018 10:11:12", dateTimeFormatter);

6. Working with Date and Time

6.1. Using Instant

An Instant represents the number of milliseconds from 1970-01-01T00:00:00Z until a given moment in time. For example, the current moment in time can be obtained using the default constructor or the method now():

Instant instant = new Instant(); Instant.now();

To create an Instant for a custom moment in time we can use either one of the constructors or use the methods ofEpochMilli() and ofEpochSecond():

Instant instantFromEpochMilli = Instant.ofEpochMilli(milliesFromEpochTime); Instant instantFromEpocSeconds = Instant.ofEpochSecond(secondsFromEpochTime);

The constructors accept a String representing a date and time in the ISO format, a Java Date or a long value representing the number of milliseconds from 1970-01-01T00:00:00Z:

Instant instantFromString = new Instant("2018-05-05T10:11:12"); Instant instantFromDate = new Instant(oneMinuteAgoDate); Instant instantFromTimestamp = new Instant(System.currentTimeMillis() - (60 * 1000));

When date and time are represented as a String we have the option to parse the String using our desired format:

Instant parsedInstant = Instant.parse("05/05/2018 10:11:12", dateTimeFormatter);

Now that we know what Instant represents and how we can create one, let's see how it can be used.

To compare to Instant objects we can use compareTo() because it implements the Comparable interface, but also we can use the Joda-Time API methods provided in the ReadableInstant interface which Instant also implements:

assertTrue(instantNow.compareTo(oneMinuteAgoInstant) > 0); assertTrue(instantNow.isAfter(oneMinuteAgoInstant)); assertTrue(oneMinuteAgoInstant.isBefore(instantNow)); assertTrue(oneMinuteAgoInstant.isBeforeNow()); assertFalse(oneMinuteAgoInstant.isEqual(instantNow));

Another helpful feature is that Instant can be converted to a DateTime object or event a Java Date:

DateTime dateTimeFromInstant = instant.toDateTime(); Date javaDateFromInstant = instant.toDate();

When we need to access parts of a date and time, like the year, the hour and so on, we can use the get() method and specify a DateTimeField:

int year = instant.get(DateTimeFieldType.year()); int month = instant.get(DateTimeFieldType.monthOfYear()); int day = instant.get(DateTimeFieldType.dayOfMonth()); int hour = instant.get(DateTimeFieldType.hourOfDay());

Now that we covered the Instant class let's see some examples of how we can use Duration, Period and Interval.

6.2. Using Duration, Period and Interval

A Duration represents the time in milliseconds between two points in time or in this case it could be two Instants. We'll use this when we need to add or subtract a specific amount of time to or from another Instant without considering chronology and time zones:

long currentTimestamp = System.currentTimeMillis(); long oneHourAgo = currentTimestamp - 24*60*1000; Duration duration = new Duration(oneHourAgo, currentTimestamp); Instant.now().plus(duration);

Also, we can determine how many days, hours, minutes, seconds or milliseconds the duration represents:

long durationInDays = duration.getStandardDays(); long durationInHours = duration.getStandardHours(); long durationInMinutes = duration.getStandardMinutes(); long durationInSeconds = duration.getStandardSeconds(); long durationInMilli = duration.getMillis();

The main difference between Period and Duration is that Period is defined in terms of its date and time components (years, months, hours, etc.) and doesn't represent an exact number of milliseconds. When using Period date and time calculations will consider the time zone and daylight saving.

For example, adding a Period of 1 month to the February 1st will result in the date representation of March 1st. By using Period the library will take into account leap years.

If we're to use a Duration we the result wouldn't be correct, because the Duration represents a fixed amount of time that doesn't take into account chronology or time zones:

Period period = new Period().withMonths(1); LocalDateTime datePlusPeriod = localDateTime.plus(period);

An Interval, as the name states, represents the date and time interval between two fixed points in time represented by two Instant objects:

Interval interval = new Interval(oneMinuteAgoInstant, instantNow);

The class is useful when we need to check whether two intervals overlap or calculate the gap between them. The overlap() method will return the overlapping Interval or null when they don't overlap:

Instant startInterval1 = new Instant("2018-05-05T09:00:00.000"); Instant endInterval1 = new Instant("2018-05-05T11:00:00.000"); Interval interval1 = new Interval(startInterval1, endInterval1); Instant startInterval2 = new Instant("2018-05-05T10:00:00.000"); Instant endInterval2 = new Instant("2018-05-05T11:00:00.000"); Interval interval2 = new Interval(startInterval2, endInterval2); Interval overlappingInterval = interval1.overlap(interval2);

The difference between intervals can be calculated using the gap() method, and when we want to know whether the end of an interval is equal to the start of another interval we can use the abuts() method:

assertTrue(interval1.abuts(new Interval( new Instant("2018-05-05T11:00:00.000"), new Instant("2018-05-05T13:00:00.000"))));

6.3. Date and Time Operations

Some of the most common operations are to add, subtract and convert date and time. The library provides specific methods for each of the classes LocalDate, LocalTime, LocalDateTime, and DateTime. It's important to note that these classes are immutable so that every method invocation will create a new object of its type.

Let's take LocalDateTime for the current moment and try to change its value:

LocalDateTime currentLocalDateTime = LocalDateTime.now();

To add an extra day to currentLocalDateTime we use the plusDays() method:

LocalDateTime nextDayDateTime = currentLocalDateTime.plusDays(1);

We can also use plus() method to add a Period or Duration to our currentLocalDateTime:

Period oneMonth = new Period().withMonths(1); LocalDateTime nextMonthDateTime = currentLocalDateTime.plus(oneMonth);

The methods are similar for the other date and time components, for example, plusYears() for adding extra years, plusSeconds() for adding more seconds and so on.

To subtract a day from our currentLocalDateTime we can use the minusDays() method:

LocalDateTime previousDayLocalDateTime = currentLocalDateTime.minusDays(1);

Besides, doing calculations with date and time, we can also, set individual parts of the date or time. For example, setting the hour to 10 can be achieved using the withHourOfDay() method. Other methods that start with the prefix “with” can be used to set components of that date or time:

LocalDateTime currentDateAtHour10 = currentLocalDateTime .withHourOfDay(0) .withMinuteOfHour(0) .withSecondOfMinute(0) .withMillisOfSecond(0);

Another important aspect is that we can convert from a date and time class type to another. To do this, we can use specific methods provided by the library:

  • toDateTime() – converts LocalDateTime to a DateTime object
  • toLocalDate() – converts LocalDateTime to a LocalDate object
  • toLocalTime() – converts LocalDateTime to a LocalTime object
  • toDate() – converts LocalDateTime to a Java Date object

7. Working with Time Zones

Joda-Time makes it easy for us to work with different time zones and changed between them. We have the DateTimeZone abstract class which is used to represent all aspects regarding a time zone.

The default time zone used by Joda-Time is selected from the user.timezone Java system property. The library API lets us specify, individually for each class or calculation what timezone should be used. For example, we can create a LocalDateTime object

When we know that we'll use a specific time zone across the whole application, we can set the default time zone:

DateTimeZone.setDefault(DateTimeZone.UTC);

From now one all the date and time operations, if not otherwise specified, will be represented in the UTC time zone.

To see all the available time zones we can use the method getAvailableIDs():

DateTimeZone.getAvailableIDs()

When we need to represent the date or time in a specific time zone we can use any of the classes LocalTime, LocalDate, LocalDateTime, DateTime and specify in the constructor the DateTimeZone object:

DateTime dateTimeInChicago = new DateTime(DateTimeZone.forID("America/Chicago")); DateTime dateTimeInBucharest = new DateTime(DateTimeZone.forID("Europe/Bucharest")); LocalDateTime localDateTimeInChicago = new LocalDateTime(DateTimeZone.forID("America/Chicago"));

Also, when converting between those classes we can specify the desired time zone. The method toDateTime() accepts a DateTimeZone object and toDate() accepts java.util.TimeZone object:

DateTime convertedDateTime = localDateTimeInChicago.toDateTime(DateTimeZone.forID("Europe/Bucharest")); Date convertedDate = localDateTimeInChicago.toDate(TimeZone.getTimeZone("Europe/Bucharest"));

8. Conclusion

Joda-Time ist eine fantastische Bibliothek, die mit dem Hauptziel begann, die Probleme im JDK in Bezug auf Datums- und Zeitoperationen zu beheben. Es wurde bald zur De-facto- Bibliothek für die Handhabung von Datum und Uhrzeit, und kürzlich wurden die wichtigsten Konzepte daraus in Java 8 eingeführt.

Es ist wichtig zu beachten, dass der Autor es als „weitgehend abgeschlossenes Projekt“ betrachtet und empfiehlt, den vorhandenen Code zu migrieren, um die Java 8-Implementierung zu verwenden.

Der Quellcode für den Artikel ist auf GitHub verfügbar.