Java Videotraining Werbung

1. Raum und Zeit

In fast allen Aufgaben haben wir Bildschirmausgaben, und wer im Alltag Deutsch schreibt, wird die Ausgaben vermutlich auf Deutsch ausgeben. Doch an einigen Stellen taucht dann immer eine „falsche“ Ausgabe aus, etwa wenn Nachkommastellen bei Fließkommazahlen nicht mit Komma, sondern mit einem Punkt getrennt sind. Das Dezimaltrennzeichen ist nur eines von vielen Beispielen, wie unterschiedlich die Normen in den Ländern ist: Währungen stehen manches mal vor und dann wieder hinter der Zahl, bei Datumsangaben ist bei einigen Ländern das Format Jahr-Monat-Tag, bei anderen Tag-Monat-Jahr, dann wieder Monat-Tag-Jahr.

Dieses Kapitel rückt Aufgaben um Internationalisierung (wie man Programm prinzipiell sprachunabhängig macht) und Lokalisierung (Anpassung auf eine konkrete Sprache) in dem Mittelpunkt. Denn wenn unsere Software erfolgreich sein soll, muss wie natürlich weltweit zu jeder Tageszeit überall auf unseren Planeten laufen. Java kann problemlos viele sprachliche Besonderheiten berücksichtigen und das wollen wir uns in den Aufgaben anschauen, so dass auch Bonny Brain und Captain CiaoCiao ihre Geschäfte überall machen kann und jeder ihre „Sprache“ versteht.

Voraussetzungen

  • Locale Klasse kennen und Konstanten, wie Locale.GERMANY

  • Notwendigkeit für landestypische Formatierung erkennen können

  • Datentypen LocalDate, LocalDateTime, Duration kennen

Verwendete Datentypen in diesem Kapitel:

1.1. Sprachen und Länder

Damit die Java-Bibliothek Fließkommazahlen und Datum parsen und formatieren sowie Texte übersetzen kann, existiert der Datentyp Locale, der eine Sprache mit einer optionalen Region repräsentiert. Wir wollen mit diesem Datentyp einige Aufgaben lösen, die gut zeigen, an welcher Stelle Locale überall vorkommt.

1.1.1. Landes-/sprachtypische Formatierungen für Zufallszahl anwenden ⭐

Bonny Brain bereitet einen neuen E-Mail Scam vor: Bitcoins sollen deutlich unter dem Preis ›verkauft‹ werden. Sie bereitet dafür die Betreffzeilen vor, die zum Beispiel so aussehen:

Buy 𝑩𝒊𝒕𝒄𝒐𝒊𝒏 for just $11,937.70 💰

Natürlich plant die Crew einen weltweiten Betrug, und da ist es wichtig, die Zahl nach den Regeln der verschiedenen Länder zu formatieren.

Die printf(…​)-Methode ist überladen, so wie es auch String.format(…​) ist:

  • Mit Locale als ersten Parameter.

  • Ohne Locale. Wird kein Locale-Objekt übergeben, gilt die Default-Locale. Das führt dazu, dass unter einem deutschsprachigen Betriebssystem, die Java Virtual Machine diese Sprache übernimmt und bei der Ausgabe mit System.out.printf(…​) und einer Fließkommazahl standardmäßig ein Komma als Dezimaltrenner verwendet. Hat man ein englischsprachiges Betriebssystem, wird standardmäßig der Punkt als Trennzeichen verwendet, weil eben im englischsprachigen Raum Nachkommastelle mit einem Punkt abgetrennt werden.

Aufgabe:

  • Erzeuge eine Zufallszahl vom Typ double zwischen 10 000 (inklusiv) und 12 000 (exklusiv); Nachkommastellen sind erwünscht.

  • Nutze die Methode String.format(String format, Object... args), um eine Fließkommazahl mit zwei Nachkommastellen zu formatieren. Es soll ein Tausendertrennzeichen geben.

  • Erfrage alle Locale-Objekte des Systems und nutze sie als Argument für String.format(Locale l, String format, Object... args)-Methode, sodass die Fließkommazahl jeweils ›lokal‹ formatiert wird. Gib den String aus.

Im Allgemeinen kann man festhalten, dass alle Methoden, die eine sprachabhängige Formatierung realisieren, oder Strings parsen, üblicherweise ein Locale-Objekt als Parameter akzeptieren. Es kann sein, dass es sprachabhängige Methoden ohne Parameter zusätzlich gibt, doch das sind häufig überladene Methoden, die intern die Standardsprache erfragen, und dann an die Methode mit dem expliziten Locale-Parameter weiterleiten.

1.2. Datum und Zeit-Klassen

Auf den ersten Blick sieht es so aus, als ob das Datum nur aus Jahr, Monat und Tag besteht. Allerdings erwartet man von einer API, dass sie noch mehr Fragen beantworten kann: Ist ein Tag ein Mittwoch? Wenn am 27. Februar eine Party 3 Tage geht, bis wann geht sie? Wann fängt die KW 12 an? Wenn ich am 31. Dezember 2021 um 09:30 in Deutschland losfliege und nach 11 Stunden in Miami ankomme, wie spät ist es dann dort?

Die Java-Bibliothek hat sich im Laufe der Jahre immer weiterentwickelt, und so gibt es auch für Datum-/Zeitrechnung diverse Typen:

  • java.util.Date seit Java 1.0.

  • java.util.Calendar, java.util.GregorianCalendar, java.util.TimeZone seit Java 1.1

  • Paket java.time seit Java 8 mit Klassen wie LocalDate, LocalTime, LocalDateTime, Duration

Für ein Datum mit Zeitanteil gibt es gleich drei Möglichkeiten; allerdings sind Date und Calendar seit Java 8 nicht mehr angesagt, denn sie bringen eine Reihe von Problemen mit sich. Die Datentypen finden sich jedoch immer noch in vielen Beispielen, insbesondere online. Wir sollten von diesen „alten“ Typen Abstand davon nehmen und daher trainiert dieser Abschnitt ganz gezielt die aktuellen Datentypen aus java.time.

1.2.1. Datumsausgabe in verschiedenen Sprachen formatieren ⭐

Am 19. September wird wieder der Sprich-wie-ein-Pirat-Tag (International Talk Like a Pirate Day) gefeiert. Captain CiaoCiao plant ein Fest und bereitet Einladungen vor und das Datum soll für die Sprachen Locale.CHINESE, Locale.ITALIAN und new Locale("th") formatiert werden; Deutsche schreiben zum Beispiel Tag.Monat.Jahr, aber wie ist das in den anderen Sprachen?

Aufgabe:

  • Erzeuge ein LocalDate-Objekt für den 19. September:

    LocalDate now = LocalDate.of( Year.now().getValue(), Month.SEPTEMBER, 19 );
  • Rufe die toString()-Methode aus, wie ist die Ausgabe?

  • Rufe format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)) auf dem LocalDate auf, wie die Ausgabe?

  • Es gibt insgesamt vier FormatStyle-Stile, probiere alle aus. Welches Muster wird verwendet?

  • Auf dem DateTimeFormatter-Objekt kann man withLocale(Locale) aufrufen und die Sprache ändern, probiere das für verschiedene Sprachen aus.

1.2.2. An welchem Tag feiert Sir Francis Beaufort dieses Jahr Geburtstag? ⭐

Captain CiaoCiao feiert jedes Jahr den Geburtstag von Sir Francis Beaufort, der am 27. Mai 1774 geboren wurde.

Aufgabe:

  • Gegeben ist ein LocalDate mit Francis Geburtstag:

    LocalDate beaufortBday = LocalDate.of( 1774, Month.MAY, 27 );
  • Entwickle ausgehend von beaufortBday ein neues LocalDate-Objekt mit dem aktuellen Jahr, wobei das jetzige Jahr nicht hart einkodiert, sondern dynamisch vom System stammen soll.

  • Erzeuge eine Ausgabe, an welchem Wochentag in diesem Jahr Francis Geburtstag feiert. In welcher Form der Wochentag ausgegeben wird, also Zahl oder String, oder welche Sprache, ist egal.

Beispiel:

  • Für das Jahr 2020 könnte die Ausgabe lauten:

    WEDNESDAY
    3
    Mittwoch

1.2.3. Durchschnittliche Dauer der Karaoke-Nächte ermitteln ⭐

Karaoke-Abende mit Rum, Tanz und Gesang sind bei der Crew beliebt. Oft gehen die Feiern bis zum Morgengrauen, und das stört Bonny Brain, weil die Crew am nächsten Tag dösig ist.

Um herauszufinden, wie viele Stunden die Exzesse im Schnitt gehen, will Bonny Brain eine Statistik aufstellen. Sie schreibt die Start- und Endzeiten auf, und kann später den Durchschnitt berechnen. Auf den Notizblättern steht zum Beispiel: 2022-03-12, 20:20 - 2022-03-12, 23:50.

Aufgabe:

  • Schreibe ein Programm, das einen String im oberen Format auswertet und die durchschnittliche Partydauern ermittelt und ausgibt.

  • Bei dem Programm müssen keine Zeitzonen, Schaltsekunden oder sonstige Sonderfälle berücksichtigt werden — ein Tag kann exakt 24 Stunden lang sein.

Beispiel:

  • Für den String

    2022-03-12, 20:20 - 2022-03-12, 23:50
    2022-04-01, 21:30 - 2022-04-02, 01:20

    soll die Ausgabe sein:

    3 h 40 m

Bei Zeitdifferenzen hilft die Klasse Duration.

1.2.4. Verschiedene Datumsformate parsen ⭐⭐⭐

Ein Datum kann absolut oder relativ spezifiziert werden und es gibt mehrere Möglichkeiten zur Datumsangabe. Ein paar Beispiele:

2020-10-10
2020-12-2
1/3/1976
1/3/20
tomorrow
today
yesterday
1 day ago
2234 days ago

Aufgabe:

  • Schreibe eine Methode Optional<LocalDate> parseDate(String string), die oben genannte Formate erkennt.

  • Wenn der String in einem von den Formaten ist, soll die Methode den String parsen, in ein LocalDate konvertieren und im Optional zurückgeben.

  • Konnte kein Format geparst werden, ist die Rückgabe Optional.empty().