java.time

Wer die Zeitzone nicht kennt hat schnell Weihnachten verpennt

Es war einmal…​

vor einigen Java Releases…​

in einer längst vergangenen Zeit…​

oder etwa doch nicht?

java.util.Date

Zeitpunkt in Millisekunden seit 1.1.1970

var date = new Date(System.currentTimeMillis());
date.toString(); // Tue Dec 03 22:08:57 CET 2019
// uses -Duser.timezone
date.setTime(1231211251L); // Mutable!
// + round about 350 @Deprecated Methods

🤮

java.util.Calendar

Ortsgebundene Zeit, über java.util.Date gestülpt

var cal = Calendar.getInstance(TimeZone.getDefault());
/* Interface that uses many -Duser.* properties for
 * selecting an implementation out of e.g.:
 * GregorianCalendar,
 * BuddhistCalendar or
 * JapaneseImperialCalendar */
cal.toString();
// java.util.GregorianCalendar[time=1575409375587,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Berlin",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Berlin,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2019,MONTH=11,WEEK_OF_YEAR=49,WEEK_OF_MONTH=1,DAY_OF_MONTH=3,DAY_OF_YEAR=337,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=42,SECOND=55,MILLISECOND=587,ZONE_OFFSET=3600000,DST_OFFSET=0]
cal.set(2019, 10, 12, 18, 30, 0); // Yeah!
cal.setTimeInMillis(1231211251L); // What?

🤮🤮

java.text.SimpleDateFormat

Formatieren und parsen von java.util Zeiten

var sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
sdf.setTimeZone(TimeZone.getTimeZone("CET"));
Date toFormat = Calendar.getInstance().getTime();
String text = sdf.format(toFormat);
// 2019-12-03T22:50:42.562+01:00

Date parsed = sdf.parse(text);
var cal = Calendar.getInstance();
cal.setTime(parsed);

🤮🤮🤮

Arbeiten mit Calendar

Ohne (viele) Worte…​

var today = new Date();
var tomorrow = new Date(today.getTime() + 1000 * 60 * 60 * 24);

var now = Calendar.getInstance();
now.add(Calendar.DAY_OF_WEEK_IN_MONTH, 1); // What?
now.add(Calendar.HOUR, -2); // All right
now.add(Calendar.HOUR_OF_DAY, 3); // ehhh?
// BUT: now isn't now anymore!
var sometime = (Calendar) now.clone();
// ...

🤮🤮🤮🤮🤮🤮🤮🤮🤮🤮🤮🤮

Zeitzonen

Gibt’s zum Download im Internet: https://www.iana.org/time-zones

IANA TZ Database

# Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
Rule	Germany	1947	only	-	Apr	 6	3:00s	1:00	S
Rule	Germany	1947	only	-	May	11	2:00s	2:00	M
Rule	Germany	1947	only	-	Jun	29	3:00	1:00	S
Rule	Germany	1948	only	-	Apr	18	2:00s	1:00	S
Rule	Germany	1949	only	-	Apr	10	2:00s	1:00	S

Rule SovietZone	1945	only	-	May	24	2:00	2:00	M # Midsummer
Rule SovietZone	1945	only	-	Sep	24	3:00	1:00	S
Rule SovietZone	1945	only	-	Nov	18	2:00s	0	-

# Zone	NAME		STDOFF	RULES	FORMAT	[UNTIL]
Zone	Europe/Berlin	0:53:28 -	LMT	1893 Apr
			1:00	C-Eur	CE%sT	1945 May 24  2:00
			1:00 SovietZone	CE%sT	1946
			1:00	Germany	CE%sT	1980
			1:00	EU	CE%sT

Zeitzonen in Deutschland

# Büsingen <http://www.buesingen.de>, surrounded by the Swiss canton
# Schaffhausen, did not start observing DST in 1980 as the rest of DE
# (West Germany at that time) and DD (East Germany at that time) did.
# DD merged into DE, the area is currently covered by code DE in ISO 3166-1,
# which in turn is covered by the zone Europe/Berlin.
#
# Source for the time in Büsingen 1980:
# http://www.srf.ch/player/video?id=c012c029-03b7-4c2b-9164-aa5902cd58d3

Link	Europe/Zurich	Europe/Busingen

Java Zeitzonen

In der JRE enthalten:

$JAVA_HOME/lib/tzdb.dat
$JAVA_HOME/lib/tzmappings

Java Laufzeit Umgebung

System Umgebung (System.getProperties())

user.variant:
user.timezone:
user.country: DE
user.language: de

Benutzer Eigenschaften (System.getenv())

TZ: Europe/Berlin
LANG: de_DE.UTF-8

Java 8: JSR 310

aber Rettung naht…​

java.time.LocalDate

var localDate = LocalDate.now();
localDate.toString(); // 2019-12-04

Ein Tag. Irgendwo. Irgendwann.

java.time.LocalTime

var localTime = LocalTime.now();
localTime.toString(); // 20:47:16.232363100

Ortszeit. Zeitanzeige der Uhr.

java.time.LocalDateTime

var localDateTime = LocalDateTime.now();
localDateTime.toString();
// 2019-12-04T20:47:16.231363900

Datum und Zeit zusammen.

java.time.OffsetTime

var offsetTime = OffsetTime.now();
offsetTime.toString();
// 20:47:16.234362400+01:00

Ortszeit inklusive Stundenversatz von Greenwich.

java.time.OffsetDateTime

var offsetDateTime = OffsetDateTime.now();
offsetDateTime.toString();
// 2019-12-04T20:47:16.233362900+01:00

VersatzDatumZeit.

java.time.ZonedDateTime

var zonedDateTime = ZonedDateTime.now();
zonedDateTime.toString();
// 2019-12-04T20:47:16.236364500+01:00[Europe/Berlin]

Volles Programm, Datum und Zeit, Offset und Zeitzone.

java.time.Instant

var instant = Instant.now();
instant.toString();
// 2019-12-04T19:47:16.090017700Z

Internetzeit. UTC. GMT.

Rausgeben und Einlesen

Standards

TemporalAccessor ta = Instant.now();
var text = DateTimeFormatter.ISO_INSTANT.format(ta);
// 2019-12-05T20:28:37.257047300Z
ta = DateTimeFormatter.ISO_INSTANT.parse(text);

// DateTimeFormatter.BASIC_ISO_DATE
// DateTimeFormatter.ISO_WEEK_DATE
// DateTimeFormatter.RFC_1123_DATE_TIME

Benutzerdefiniert

// import static java.time.temporal.ChronoField.*;
var dtf = new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .appendValue(HOUR_OF_DAY, 2).appendLiteral(":")
        .appendValue(MINUTE_OF_HOUR, 2)
        .appendLiteral(" Uhr am ")
        .appendValue(DAY_OF_MONTH, 2).appendLiteral('.')
        .appendValue(MONTH_OF_YEAR, 2).appendLiteral('.')
        .appendValue(YEAR, 4).toFormatter();
dtf.format(LocalDateTime.now());
// 22:05 Uhr am 05.12.2019

dtf.parse("18:30 uhr am 10.12.2019");

JSON Mapping

Jackson

new ObjectMapper()
    .registerModule(new JavaTimeModule());

Gson

new GsonBuilder()
    .registerTypeAdapterFactory(
		new GsonJava8TypeAdapterFactory()
	).create()

Zeitreisen / -rechnen

Grundrechenarten

var now = LocalDateTime.now();
var nextWeek = now.plusDays(7); // Immutable
var yesterday = now.minusHours(24);

Dauer und Perioden

var duration = Duration.ofDays(5).plus(Duration.ofHours(2));
var sometimes = Instant.now().plus(duration);
sometimes.isAfter(Instant.now());
Duration.between(Instant.now(), sometimes);
// PT121H59M59.9990012S

Period.between(LocalDate.now(), LocalDate.of(2019, 12, 24));
// P19D

Zeitzonentreue

var sixMonth = Period.ofMonths(6);

var odtNow = OffsetDateTime.now();
// 2019-12-06T20:39:54.717348800+01:00

odtNow.plus(sixMonth).toString();
// 2020-06-06T20:39:54.717348800+01:00

💩

und jetzt richtig

var sixMonth = Period.ofMonths(6);

var zdt = ZonedDateTime.now();
zdt.toOffsetDateTime().toString();
// 2019-12-06T20:50:36.461772200+01:00

zdt.plus(sixMonth).toOffsetDateTime();
// 2020-06-06T20:50:36.461772200+02:00

📅

Konvertieren

var instant = Instant.now();
ZonedDateTime zdt = instant.atZone(ZoneId.of("Europe/Berlin"));
LocalDateTime ldt = zdt.toLocalDateTime();
OffsetDateTime odt = ldt.atOffset(ZoneOffset.ofHours(4));
zdt = odt.atZoneSameInstant(ZoneId.systemDefault());
zdt = odt.atZoneSimilarLocal(ZoneId.systemDefault());
zdt.toInstant();

🧮

Resümee

  • Konstanter Ort: Local[Date]Time

  • Ortsübergreifende Zeitpunktbetrachtung (API):

    • Instant

    • Offset[Date]Time

    • ZonedDateTime

  • Speichern / Rechnen:

    • Instant

    • ZonedDateTime