1. Locale, Date and Time

In almost all exercises we have screen outputs, and whoever writes in everyday life, for example, Chinese, Spanish or Arabic, will probably prefer program outputs in this language as well. However, in some places, a wrong format might appear, e.g., when decimal places in floating-point numbers are not separated correctly. The decimal separator is only one of many examples of how different the standards are in the countries: Currencies sometimes precede and then follow the number; for dates, the format is year-month-day in some countries, day-month-year in others, and then month-day-year again.

This chapter focuses on exercises around internationalization (how to make programs language-independent in principle) and localization (adaptation to a specific language). After all, if our software is to be successful, it must, of course, run anywhere on the planet at any time of day. Java can easily accommodate many language specifics, and we’ll look at that in the exercises, so that Captain CiaoCiao and Bonny Brain can also do their business anywhere and everyone understands their "language".

Prerequisites

  • know Locale class and constants, like Locale.US

  • be able to recognize the need for country-specific formatting

  • know temporal data types LocalDate, LocalDateTime, `Duration

Data types used in this chapter:

1.1. Languages and countries

For the Java library to parse and format floating-point numbers and dates, as well as translate text, there is a data type called Locale that represents a language with an optional region. We want to use this data type to solve some exercises that show well where Locale occurs everywhere.

1.1.1. Apply country/language specific formatting for random number. ⭐

Bonny Brain is preparing a new email scam: Bitcoins are to be "sold" well below their price. It prepares subject lines for this, which look like this, for example

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

Of course, the crew is planning a worldwide scam, and that’s where it’s important to format the number according to the rules of the different countries.

The printf(…​) method is overloaded, as is String.format(…​):

  • With Locale as the first parameter.

  • Without Locale. If no Locale object is passed, the default locale applies. This leads to the fact that the Java Virtual Machine under let’s say an operating system language Spanish, this language adopts and with the output with System.out.printf(…​) and a floating-point number by default a comma as a decimal separator is used. If you have an English-language operating system, the point is used as a separator by default because decimal places are separated with a point in English-speaking countries.

Apply country language specific formatting for random number

Exercise:

  • Create a random number of type double between 10 000 (inclusive) and 12 000 (exclusive); decimal places are desired.

  • To format a floating-point number with two decimal places and a thousand separator, employ the String.format(String format, Object... args) method.

  • Get all Locale objects of the system, and use them as arguments to the String.format(Locale l, String format, Object... args) method, so that the floating-point number is formatted "locally" in each case. Output the string.

As a general rule, it can be stated that all methods implementing language-dependent formatting, or parsing strings, usually accept a Locale object as a parameter. There may be language-dependent methods without parameters in addition, but these are often overloaded methods that internally query the default language, and then pass to the method with the explicit Locale parameter.

1.2. Date and time classes

At first glance, it looks like the date consists only of year, month, and day. However, you would expect an API to be able to answer more questions: Is a day a Wednesday? If a party goes for three days on February 27, when does it end? When does week 12 start? If I leave Beijing at 09:30 on December 31, 2021, and arrive in Miami after 11 hours, what time is it when I get there?

The Java library has evolved over the years, and so there are various types for date and time calculation:

  • java.util.Date since Java 1.0

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

  • package java.time since Java 8 with classes like LocalDate, LocalTime, LocalDateTime, Duration.

For a date with time part, there are three possibilities at once; however, Date and Calendar are no longer popular since Java 8 because they are causing several problems. However, these data types can still be found in many written examples, especially online. We should stay away from these "old" types, and therefore this section specifically trains how to use the current data types from java.time.

1.2.1. Formatting date output in different languages ⭐

September 19 marks the return of International Talk Like a Pirate Day. Bonny Brain is planning a party and preparing invitations, and the date is to be formatted for the languages Locale.CHINESE, Locale.ITALIAN and new Locale("th"); Germans, for example, write Day.Month.Year, but what about in the other languages?

Exercise:

  • Create a LocalDate object for September 19:

    LocalDate now = LocalDate.of( Year.now().getValue(), Month.SEPTEMBER, 19 );
  • Call the toString() method; what is the output?

  • Call format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)) on the LocalDate; what is the output?

  • There are four FormatStyle styles in total — try them all. Which pattern is shown?

  • On the DateTimeFormatter object, you can call withLocale(Locale) and change the language; try this for different languages.

1.2.2. On which day does Sir Francis Beaufort celebrate his birthday? ⭐

Captain CiaoCiao celebrates the birthday of Sir Francis Beaufort every year, who was born on May 27, 1774.

SirFrancisBeaufortBirthday

Exercise:

  • Given a LocalDate with Francis` birthday:

    LocalDate beaufortBday = LocalDate.of( 1774, Month.MAY, 27 );
  • Starting from beaufortBday, develop a new LocalDate object with the current year, where the current year should not be hard-coded but dynamically obtained from the system.

  • Create an output on which day of the week Francis celebrates his birthday this year. In which form the weekday is output, i.e., number or string or which language, is not relevant.

Example:

  • For the year 2020 the output could be:

    WEDNESDAY
    3
    Wednesday

1.2.3. Find all Friday the 13th. ⭐

Every Friday the 13th, Captain CiaoCiao feels uncomfortable sailing. On such days, he does not set sail and sends Bravius Gritty instead.

Task:

  • Write a program that lists all Fridays that fall on the 13th for a given year.

  • Bonus: For this, write a TemporalAdjuster that returns the next Friday the 13th for a Temporal object.

Examples:

  • For the year 1925, the output may look like this:

    1925-02-13
    1925-03-13
    1925-11-13
  • For 2024:

    2024-09-13
    2024-12-13

1.2.4. Get average duration of karaoke nights ⭐

Karaoke nights with rum, dancing, and singing are popular among the crew. Often the parties go on until dawn, and this disturbs Bonny Brain because the crew is dozy the next day.

To find out how many hours the excesses go on average, Bonny Brain wants to keep a record of statistics. She writes down the start and end times and can calculate the average later. For example, the note sheets say "2022-03-12, 20:20 - 2022-03-12, 23:50".

Exercise:

  • Write a program that evaluates a string in the format above, finds the average party duration, and prints it out.

  • The program does not need to consider time zones, leap seconds, or other special cases — a day can be exactly 24 hours long.

Example:

  • For the string

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

    the output should look like this:

    3 h 40 m

For time differences, the class Duration helps.

1.2.5. Parse different date formats ⭐⭐⭐

A date can be specified as absolute or relative, and there are several ways to specify dates. A few examples:

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

Exercise:

  • Write a method Optional<LocalDate> parseDate(String string) that recognizes the above formats.

  • If the string is in one of the formats, the method should parse the string, convert it to a LocalDate and return it in Optional.

  • If no format could be parsed, the return is Optional.empty().