Nutzungsvorgaben Datum & Zeit
Diese Seite ist ein Teil der IsyFact-Standards. Alle Inhalte der Seite, insbesondere Texte und Grafiken, sind urheberrechtlich geschützt. Alle urheberrechtlichen Nutzungs- und Verwertungsrechte liegen beim Bundesverwaltungsamt.
Die Nutzung ist unter den Lizenzbedingungen der Creative Commons Namensnennung 4.0 International gestattet.
1. Einleitung
Im Konzept Datum & Zeit werden die Anforderungen und Vorgaben zu Datum und Zeit der IsyFact definiert, um eine einheitliche Umsetzung in der gesamten Anwendungslandschaft zu gewährleisten.
Dieses Dokument beschreibt alle Aspekte, die bei der Entwicklung einer Anwendung zu berücksichtigen sind, um die definierten Anforderungen zu erfüllen.
Die IsyFact stellt eine eigene Bibliothek (isy-datetime
) bereit, um die Anforderungen einheitlich umsetzen zu können.
Kern dieses Dokuments sind Vorgaben zur Nutzung der Java 8 Date and Time API des JSR 310 und die Verwendung der zuvor genannten Bibliothek.
Das Dokument richtet sich daher vorrangig an Entwickler, Konstrukteure und technische Chefdesigner von Anwendungssystemen, die gemäß den Vorgaben der IsyFact umgesetzt werden.
2. Aufbau und Zweck des Dokuments
Der Zweck dieses Dokuments ist Vorgaben zur Nutzung der Java 8 Date and Time API zu machen und die Verwendung der Bibliothek isy-datetime
zu beschreiben.
Das Dokument ist entsprechend dieser Zielsetzungen in die folgenden Kapitel untergliedert:
Das Kapitel Dauern und Zeitzonen enthält grundlegende Informationen zum Umgang mit Dauern und Zeitzonen.
Im Kapitel Verwendung der Bibliothek wird der Inhalt und die Verwendung der Bibliothek isy-datetime
beschrieben.
3. Dauern und Zeitzonen
Dieses Kapitel enthält einige grundlegende Informationen zum Umgang Dauern und Zeitzonen.
3.1. Zeitdauer mit Period und Duration
Zur Darstellung einer Dauer sind die Java 8 Typen Period
und Duration
zu verwenden.
Eine Period
ist eine datumsbasierte Menge von Zeit, wie z.B. „2 Jahre, 3 Monate und 4 Tage“.
Eine Dauer wird also auf der Basis von Jahren, Monaten und Tagen modelliert.
Eine Duration
ist eine zeitbasierte Menge von Zeit, wie z.B. „34,5 Sekunden“.
Eine Dauer wird hier auf der Basis von Sekunden und Nanosekunden modelliert.
Der Zugriff ist auch über andere Zeiteinheiten, wie z.B. Minute, Stunde oder Tag, möglich.
Period
und Duration
unterscheiden sich bei Berechnungen mit einer ZonedDateTime
im Zusammenhang mit der Sommerzeit.
Mit einer Duration
wird eine exakte Anzahl von Sekunden hinzuaddiert und die Dauer eines Tages ist somit immer 24 Stunden.
Im Gegensatz dazu wird bei einer Period
ein „konzeptioneller“ Tag hinzuaddiert.
Wenn also am Tag vor der Umstellung auf die Sommerzeit um 18:00 eine Duration
von einem Tag addiert wird, wird die resultierende ZonedDateTime
am nächsten Tag auf 19:00 stehen.
Bei der Addition einer Period
wird dagegen die Uhrzeit auf 18:00 stehen, obwohl das nur 23 Stunden sind.
Das Mischen der beiden Darstellungen (2 Monaten, 3 Stunden) ist nicht möglich.
3.2. Zeitzonen und Offsets
Zum allgemeinen Verständnis folgen ein paar Erläuterungen zum Thema Offsets (Zeitverschiebung) und Zeitzonen.
Eine Zeitzone kann nicht nur durch einen Offset von UTC repräsentiert werden. Viele Zeitzonen haben mehr als einen Offset aufgrund der Sommerzeitumstellung. Das Datum, an dem die Umstellung von Normal- auf Sommerzeit und wieder zurück erfolgt, ist ebenfalls Teil der Zeitzonenregeln. Sollten sich diese Datumsangaben im Laufe der Geschichte geändert haben, sind auch diese Daten in den Regeln für die jeweilige Zeitzone enthalten.
-
Ein Offset / Zeitverschiebung ist nur eine Zahl die besagt, wie weit bestimmte Datums- und Zeitangabe hinter oder vor der UTC-Zeit ist.
-
Die meisten Offsets sind in ganzen Stunden angegeben.
-
Es existieren aber auch Offsets mit einem 30-minütigen Offset.
-
Einige wenige verwenden 45-minütigen Offset.
-
-
Eine Zeitzone enthält deutlich mehr:
-
Ein Name oder ID, mit der die Zone eindeutig identifiziert werden kann.
-
Einer oder mehrere Offsets von UTC
-
Die Daten und Zeiten, an denen zwischen den Offsets gewechselt wird.
-
Optional ein sprachspezifischer Anzeigename für die Anzeige beim Nutzer.
-
3.3. Abkürzungen für Zeitzonen / Offsets
Häufig werden Zeitzonen oder Offsets mit drei oder mehr Buchstaben abgekürzt, wie z.B. MEZ für „Mitteleuropäische Zeit“ oder PST für "Pacific Standard Time". Die Verwendung solcher Abkürzungen kann aber zu Problemen führen:
-
Häufig sind Abkürzungen nicht eindeutig. Es gibt fünf Interpretationen für “CST”:
-
Central Standard Time (Nord Amerika)
-
Central Standard Time (Australien)
-
Central Summer Time (Australien)
-
China Standard Time
-
Cuba Standard Time
-
-
Bestenfalls wird durch eine Abkürzung ein Offset, aber keine Zeitzone repräsentiert. In Deutschland wird beispielsweise die Mitteleuropäische Zeit (MEZ) zur Normalzeit, aber die Mitteleuropäische Sommerzeit (MESZ) während der Sommerzeit.
-
Ein großer Teil der Abkürzungen kommt aus dem Englischen. Manchmal existiert eine gültige Abkürzung auf Deutsch (z.B. MEZ für CET) und es kann zu Verwirrung kommen, welche Abkürzung genutzt werden soll.
Wenn solche Abkürzungen genutzt werden, sollte diese nur zur Anzeige für den Nutzer verwendet werden. Abkürzungen sollten nicht geparst oder anderweitig technisch verarbeitet werden. Die Abkürzungen "UTC" und "GMT" sind die einzigen, die davon explizit ausgenommen sind.
4. Verwendung der Bibliothek isy-datetime
In diesem Abschnitt werden Inhalt und Einsatz der Bibliothek isy-datetime beschrieben.
4.1. Inhalt von isy-datetime
4.1.1. Formatierung
Zur formatierten Ein- und Ausgabe stellt isy-datetime die Klassen InFormat
und OutFormat
bereit.
Diese beinhalten vorkonfigurierte DateTimeFormatter
für die im Konzept beschriebene Formate.
Die Verarbeitung in der ISO-8601-konformen Darstellung wird direkt durch die Klassen der Java 8 Date and Time API unterstützt.
4.1.1.1. Ausgabeformate für Datum- und Zeitangaben
Folgende Formate und deren zugehörige DateTimeFormatter
werden von isy-datetime in der Klasse InFormat
bereitgestellt:
Format (Beispiel) | Formatter in OutFormat |
---|---|
Montag, 17. Juli 2017 14:35:19 MESZ |
DATUM_ZEIT_LANG_TAG_ZONE |
17. Juli 2017 14:35:19 MESZ |
DATUM_ZEIT_LANG_ZONE |
Montag, 17.Juli 2017 14:35:19 |
DATUM_ZEIT_LANG_TAG |
17.07.2017 14:35:19 +02:00 |
DATUM_ZEIT_ZONE |
17.07.2017 14:35:19 |
DATUM_ZEIT |
17.07.2017 |
DATUM |
14:35:19 |
ZEIT |
14:35 |
ZEIT_KURZ |
4.1.1.2. Eingabeformate für Datum- und Zeitangaben
Für die Eingabe werden DateTimeFormatter
bereitgestellt.
Diese sind nach Datum und Zeit getrennt.
Zur direkten Verwendung dieser Formate stellt die Klasse InFormat
verschiedene Methoden zum Parsen in verschiedene Typen bereit.
Methode | Mögliche Eingabe (Beispiel) |
---|---|
parseToLocalTime() |
“15:24“ |
parseToLocalDate() |
“30.08.2017“ |
parseToLocalDateTime() |
“17.07.2017 14:35:19“ |
parseToOffsetTime() |
“14:35:19 +02:00“ |
parseToOffsetDateTime() |
“17.07.2017 14:35:19 +02:00“ |
parseToZonedDateTime() |
“17.07.2017 14:35:19 Europe/Berlin“ |
4.1.1.3. Formate für Dauer
Zur Eingabe von Dauern werden folgende Methoden von InFormat
bereitgestellt:
Methode | Mögliche Eingabe (Beispiel) |
---|---|
parseToDuration() |
4h 3min 2s 1ms |
parseToPeriod() |
7a 6M 5d |
Hierbei ist zu beachten, dass nicht alle Zeiteinheiten von Period
bzw. Duration
unterstützt werden.
Enthält die Zeichenkette Zeiteinheiten, die nicht unterstützt werden, wird eine Exception geworfen.
4.1.2. Zeitraum
Die Repräsentation eines Zeitraums im Sinne des Konzepts wird nicht durch die Java 8 Date and Time API bereitgestellt.
Für diesen Zweck stellt isy-datetime die Klasse Zeitraum
bereit.
Ein Zeitraum kann aus einem zwei Datumsangaben, zwei Datums- und Zeitangaben oder nur aus Zeitangaben erstellt werden. Das angegebene Ende ist immer exklusive und nicht Teil des Zeitraums. Ein Zeitraum, der nur aus Zeiten besteht, kann nicht länger als 24 Stunden sein, aber über einen Tageswechsel (22:00 – 06:00) gehen. Bei Zeiträumen die nur aus Datumsangaben bestehen, ist die Anfangszeit 00:00 des Anfangstages und die Endzeit 00:00 des Endtages.
Intern werden Anfang und Ende mit Angabe der Zeitzone gespeichert, um die Dauer bei Zeitumstellungen korrekt berechnen zu können. Wird bei der Erstellung keine Zeitzone angegeben, wird Standard-Zeitzone der JVM verwendet.
4.2. Berechnungen
Wenn möglich, sollten Berechnungen mit Datums- und Zeitangaben mit den Bordmitteln der Java 8 Date and Time API umgesetzt werden. Für die im Konzept geforderten Berechnungen gibt es folgende Möglichkeiten zur Umsetzung:
-
Chronologische Sortierung einer Menge von Datums- und Zeitwerten und chronologische Vergleiche zwischen Datums- und Zeitwerten („früher als“, „später als“, „gleichzeitig“).
Die Klassen LocalDate
, LocalTime
, LocalDateTime
, OffsetTime
, OffsetDateTime
, ZonedDateTime
und Duration
implementieren das Interface Comparable
, daher ist ein Sortieren mit Bordmitteln möglich (bspw. mit Collections.sort()
).
Für chronologische Vergleiche werden die Methoden isBefore()
, isAfter()
, isEqual()
bereitgestellt.
Bei LocalTime
ist die Methode equals()
zu verwenden.
Der Unterschied zwischen equals()
und isEqual()
besteht darin, dass isEqual()
einen chronologischen Vergleich und equals()
einen „technischen“ Vergleich durchführt.
Dazu ein Beispiel:
OffsetTime offsetTime1 = OffsetTime.of(15, 0, 0, 0, ZoneOffset.ofHours(2));
OffsetTime offsetTime2 = OffsetTime.of(13, 0, 0, 0, ZoneOffset.UTC);
offsetTime1.isEqual(offsetTime2); // true
offsetTime1.equals(offsetTime2); // false
JapaneseDate todayJapanese = JapaneseDate.now();
LocalDate todayLocal = LocalDate.now();
todayJapanese.equals(todayLocal); // false
todayJapanese.isEqual(todayLocal); // true
-
Ermittlung des „Tagesdatums“ (der zeitliche Anteil eines Datumswerts wird auf 0, d.h. auf Mitternacht, gestellt)
Dafür sollte die Methode
toLocalDate()
verwendet werden. Wenn das Ergebnis keinLocalDate
sein soll, sondern einer Objekt des gleichen Typs mit dem Zeitwert auf 0 gesetzt, ist die MethodetruncatedTo(ChronoUnit.DAYS)
zu verwenden. -
Berechnung des Abstands („Dauer“) zwischen zwei Datums- oder Zeitwerten
Es ist die Methode
until()
der Datum- und Zeitklassen oder die Methodebetween()
des InterfacesTemporalUnit
zu verwenden. Beispiele:
LocalTime time1 = LocalTime.of(15, 0);
LocalTime time2 = LocalTime.of(16, 30);
time1.until(time2, ChronoUnit.MINUTES); // 90
ChronoUnit.MINUTES.between(time1, time2); // 90
LocalDate date1 = LocalDate.of(2017, 8, 1);
LocalDate date2 = LocalDate.of(2017, 9, 1);
date1.until(date2); // Period von 1 Monat
ChronoUnit.DAYS.between(date1, date2); // 31
ZonedDateTime zonedDateTime1 = ZonedDateTime.of(
LocalDateTime.of(2017, 3, 25, 18, 0),
ZoneId.of("Europe/Berlin"));
ZonedDateTime zonedDateTime2 = ZonedDateTime.of(
LocalDateTime.of(2017, 3, 26, 18, 0),
ZoneId.of("Europe/Berlin"));
zonedDateTime1.until(zonedDateTime2, ChronoUnit.DAYS); // 1
ChronoUnit.HOURS.between(zonedDateTime1, zonedDateTime2); // 23
-
Addition und Subtraktion von Datums- und Zeitwerten mit einer Dauer.
Die Datums- und Zeitklassen von Java 8 stellen umfangreicheplus*()
undminus*()
-Methoden zur Addition/Subtraktion von Datums- und Zeitwerten bereit. -
Prüfung, ob zwei Datumswerte für zwei direkt aufeinanderfolgende Tage stehen ("Liegt der 28.2.2016 direkt vor dem 1.3.2016?“).
LocalDate date1 = LocalDate.of(2016, 2, 28);
LocalDate date2 = LocalDate.of(2016, 3, 1);
date1.plusDays(1).isEqual(date2)); // false
-
Berechnung des nächsten Werktags nach einem übergebenen Datumswert.
Die Klasse
DateTimeUtil
stellt hierfür die MethodegetWerkTag(LocalDate datum)
bereit.Liegt das übergebene Datum auf einem Sonntag, wird das Datum des folgenden Montags zurückgegeben, sonst das gleiche Datum. Feiertage werden nicht berücksichtigt.
-
Prüfung, ob ein Datum in einem Zeitraum liegt.
Die Klasse
Zeitraum
aus isy-datetime stellt dazu die MethodeisInZeitraum()
bereit. -
Prüfung, ob sich zwei Zeiträume überlappen.
Die Klasse
Zeitraum
aus isy-datetime stellt dazu die MethodeueberschneidetSichMit()
bereit.
4.3. Testunterstützung
Für die Umsetzung der Testunterstützung stellt isy-datetime die Klasse TestClock
bereit.
Sie erweitert die Klasse java.time.Clock
und wird an Stelle dieser verwendet.
Damit diese Klasse bei Tests verwendet kann, dürfen in der zu testenden Anwendung keine Aufrufe der now()
-Methoden Java 8 Date and Time API Klassen benutzt werden.
Stattdessen müssen die Methoden *now()
aus der Klasse DateTimeUtil
zum Erzeugen von Datums- und Zeitobjekten verwendet werden.
Diese rufen intern die Methode now(Clock)
der Java 8 Klassen auf.
Als Clock
wird standardmäßig Clock.systemDefaultZone()
verwendet.
Zum Test wird als Clock
eine TestClock
gesetzt.
Der Ablauf eines Tests mit der TestClock
sieht dann schematisch folgendermaßen aus:
@Test
public void someTest() {
TestClock testClock = TestClock.at(LocalDateTime.of(2017, 9, 1, 15, 0));
DateTimeUtil.setClock(testClock);
// Test der Anwendung zu bestimmer Zeit
// Zeit um eine Stunde nach vorne Stellen
DateTimeUtil.getClock().advanceBy(Duration.ofHours(1));
// Erneuter Test der Anwendung eine Stunde später...