Laboratory Training 2

Working with Dates and Text. Localization

1 Training Tasks

1.1 Individual Task

Design and implement classes to represent the entities of the third laboratory training of the course "Fundamentals of Java Programming". The solution should be based on the previously created class hierarchy.

You should create a derived class that represents the first entity. How to basically use the class created in the previous laboratory training of the course "Fundamentals of Java Programming". The class should implement the support of various localizations, in particular, Ukrainian and American. It is necessary to provide for the translation of text, the output of numbers, as well as dates and times, taking into account different localizations. Add (modify) search for words in comments (or other text) using regular expressions. Sort the entities alphabetically using the Collator class.

Create a derived class from the class that represents the second entity. Add the time and date when a certain event occurs as a field. To represent the time and date, use classes of java.time package(taking into account the time zone). Calculate the time intervals between events and find and output the smallest of the intervals. If the class that represented the second entity of the individual task did not contain a String type field, you should add a field, e.g. a comment to the event. For a new (or existing) text field, provide for the possibility of output in Ukrainian or English, depending on localization.

The program must demonstrate:

  • presentation of the implementation of tasks of laboratory trainings # 3 and # 4 of the course "Fundamentals of Java Programming";
  • formatting numerical data in various ways, as well as taking into account localization;
  • output of data about dates and times of events taking into account localization;
  • output of comments in Ukrainian and English;
  • the ability to sort alphabetically using a class Collator ;
  • calculation and output of time intervals between events related to the second essence of the task; finding and output the smallest of the intervals;
  • variants of complex search in the text (using regular expressions), in particular, searching for a fragment of text at the beginning (at the end) of a word.

1.2 Date Entering

Implement a program in which the user enters a string. The program checks whether the string corresponds to the date representation accepted in Ukraine. The regular expressions must be used for checking. If the string does not meet the requirements, an error message is displayed. Otherwise, objects of types Date, GregorianCalendar and LocalDate are created and output to the console.

1.3 Phone Number Verification

Develop a program to verify the correctness of the fact that the string is the telephone number of the Kyivstar operator. Use regular expressions.

1.4 Checking the Password String

Develop a program for checking password compliance with the requirements:

  • the password can contain letters of the Latin alphabet, numbers and special symbols: _ - *;
  • there must be at least one lowercase letter;
  • there must be at least one capital letter;
  • must be at least one digit;
  • there must be at least one special character.

Use regular expressions.

1.5 Obtaining an Array of Substrings (Advanced Task)

A string longer than 20 characters contains letters and numbers. Get from this string an array of substrings that contain letters between numbers (groups of numbers). Define numbers as separators.

2 Instructions

2.1 Working with Dates and Times

2.1.1 Overview

Working with dates and times is of great importance in information systems. Date and time are usually important information stored in databases as well as data warehouses.

Almost all software platforms provide facilities for working with dates and times. In particular, in the Java standard, work with dates and times is implemented at three levels:

  • class Date;
  • class Calendar and its descendants;
  • package java.time.

2.1.2 Using the Date Class

Classes Calendar and Date from the java.util package provide methods for working with date and time. The class Date object stores the number of milliseconds that have passed since January 1, 1970 00:00:00 GMT. This is the "birthday" of Unix, it is called "Epoch".

The Date class implements two constructors. The Date() constructor puts the current date and time into the object in the format of the current regional settings:

Date date1 = new Date();

The Date(long millisec) constructor creates the object using the specified number of milliseconds. For example, you can get the number of milliseconds that have passed since the Epoch using a static method System.currentTimeMillis():

Date date2 = new  Date(System.currentTimeMillis());         

Class Date methods:

  • long getTime() returns the value stored in the object;
  • void setTime(long newTime) sets a new value;
  • boolean after(Date when) returns true if the time when is less than the value stored in the object;
  • boolean before(Date when) returns true if the time when is greater than the value stored in the object.

Many of constructors and methods of Date class are deprecated because the class does not provide application of internationalization capabilities.

2.1.3 Using the Calendar Class

You should use the Calendar class to define dates.

Conversion of milliseconds stored in objects of the Date class to the current time and date is carried out by the methods of the Calendar class. This is an abstract class that provides methods for converting a moment of time to a set of calendar fields (year, month, day of the month, hour, etc.) and for working with calendar fields.

The Calendar class defines integer constants MONDAY ... SUNDAY (days of the week), as well as methods to read or set the first day of the week, time, time zone, etc.

The constants in the range 0..11 are used to determine the months. To avoid confusion, it is advisable to use constants defined in the Calendar class:

public static final int JANUARY = 0;
public static final int FEBRUARY = 1;
public static final int MARCH = 2;
public static final int APRIL = 3;
public static final int MAY = 4;
public static final int JUNE = 5;
public static final int JULY = 6;
public static final int AUGUST = 7;
public static final int SEPTEMBER = 8;
public static final int OCTOBER = 9;
public static final int NOVEMBER = 10;
public static final int DECEMBER = 11

Java provides the only implementation of Calendar class: GregorianCalendar class derived from Calendar. You can create a GregorianCalendar object using a static method getInstance():

Calendar gregorianCalendar = Calendar.getInstance(); 
// or Calendar gregorianCalendar = GregorianCalendar.getInstance();

The class GregorianCalendar provides constructors that create a calendar object by specifying the date and time as integer values:

GregorianCalendar()
GregorianCalendar(int year, int month, int date) 
GregorianCalendar(int year, int month, int date, int hour, int minute) 
GregorianCalendar(int year, int month, int date, int hour, int minute, int second)

This is an example of working with classes Date and Calendar:

package ua.inf.iwanoff.java.advanced.second;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Date;

public class DateTest {
    public static void main(String[] args) {
        // Setting the time using a Date instance:
        Date date = new Date(System.currentTimeMillis());
        Calendar calendar = GregorianCalendar.getInstance();
        calendar.setTime(date);
        System.out.println(calendar.getTime());

        // Call the getter and setter methods of the Calendar object:
        calendar.set(Calendar.MONTH, Calendar.AUGUST);
        calendar.set(Calendar.DAY_OF_MONTH, 24);
        calendar.set(Calendar.YEAR, 1991);
        calendar.set(Calendar.HOUR, 21);
        calendar.set(Calendar.MINUTE, 00);
        calendar.set(Calendar.SECOND, 01);
        System.out.println(calendar.getTime());
        System.out.println("The YEAR is: " + calendar.get(Calendar.YEAR));
        System.out.println("The MONTH is: " + calendar.get(Calendar.MONTH));
        System.out.println("The DAY is: " + calendar.get(Calendar.DATE));
        System.out.println("The HOUR is: " + calendar.get(Calendar.HOUR));
        System.out.println("The MINUTE is: " + calendar.get(Calendar.MINUTE));
        System.out.println("The SECOND is: " + calendar.get(Calendar.SECOND));
        System.out.println("The AM_PM indicator is: " + calendar.get(Calendar.AM_PM));
    }
}

2.1.4 Using java.time Tools

Standard Java tools (up to JDK 7 inclusive) for working with dates and times have a number of disadvantages related to the inconvenience of their use, which led to the appearance of alternative (non-standard) libraries, such as the Joda-Time library that was very popular among programmers. In order to correct this situation, Java 8 added new classes and tools to support working with dates and calendar. The new date and time library is very similar to Joda-Time.

The package java.time defines classes for date and time representation:

  • LocalDate (day, month, year);
  • LocalTime (time of day only);
  • LocalDateTime ((date and time).

These three classes are used in cases where the explained time is not taken into account. You can create objects using static now() functions that return the current time:

LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dateTime = LocalDateTime.now();

Other ways to create an object are to use an of() static function that accepts hours, minutes, and seconds as parameters:

time = LocalTime.of(11, 15, 0);
date = LocalDate.of(2023, 3, 8);
dateTime = LocalDateTime.of(2023, Month.MARCH, 8, 11, 15, 0);
dateTime = LocalDateTime.of(date, time); // another option

Note: unlike a Calendar class, classes of java.time package use month numbers in a range 1..12.

As can be seen from the example, both integer values and constants of java.time.Month enumeration can be used to define months. You can create an object of type LocalDateTime from an existing object of type LocalDate using the atTime() method:

dateTime = date.atTime(time);

Using the plus() and minus() methods, you can change the date and time values of existing objects. You can set the unit of measurement using the java.time.temporal.ChronoUnit enumeration. The following example sets the time two hours later than the current time:

LocalTime now = LocalTime.now();
LocalTime later = now.plus(2, ChronoUnit.HOURS);
System.out.println(later);

There are also addition and subtraction methods plusHours(), minusHours(), plusMinutes(), minusMinutes(), plusSeconds(), minusSeconds(), plusNanos() and minusNanos() with integer parameters. Similarly, the LocalDate class provides methods plusDays(), plusMonths(), minusDays() and minusMonths(). Example:

LocalDate today = LocalDate.now();
LocalDate thirtyDaysFromNow = today.plusDays(30);
LocalDate nextMonth = today.plusMonths(1);
LocalDate aMonthAgo = today.minusMonths(1);

Identification of time zones is carried out by means of the ZoneId class. The easiest way to get the identifier of the default time zone is to use a static function systemDefault():

ZoneId myZone = ZoneId.systemDefault();

You can get the time zone identifier using a string constant. The set of possible constants can be obtained using the ZoneId.getAvailableZoneIds() static method. The following code shows the constants for European zones:

import java.time.ZoneId;

public class AvailableZoneIds {
    public static void main(String[] args) {
        for (String zone: ZoneId.getAvailableZoneIds()) {
            if (zone.startsWith("Europe")) {
                System.out.println(zone);
            }
        }
    }
}

The listed constants can be used in the of() method, for example:

ZoneId germanZone = ZoneId.of("Europe/Berlin");

To work with time, taking into account the time zone, the ZonedDateTime class is used. Its use is similar to LocalTime, but if you define an object of type ZoneId as a parameter of the method now(), you can work with the time of the corresponding time zone:

ZonedDateTime germanTime = ZonedDateTime.now(germanZone); 
System.out.println(germanTime);

You can also use the Clock class to work with the zoned time.

Clock clock = Clock.system(ZoneId.of("Europe/Kiev"));
LocalTime kyivTime = LocalTime.now(clock);
System.out.println(kyivTime);

Java 8 defines two classes, Period and Duration, that allow defining intervals between dates and times, respectively. For this, the following between() methods are used:

Period p = Period.between(date1, date2);
Duration d = Duration.between(time1, time2);

Objects of these classes can also be created using static methods, for example:

Duration twoHours = Duration.ofHours(2);
Duration tenMinutes = Duration.ofMinutes(10);
Duration thirtySecs = Duration.ofSeconds(30);

The class java.time.temporal.TemporalAdjusters contains useful methods such asfirstDayOfMonth(), firstDayOfNextMonth(), firstInMonth(DayOfWeek), lastDayOfMont(), next(DayOfWeek), nextOrSame(DayOfWeek), previous(DayOfWeek), previousOrSame(DayOfWeek), etc.

The Instant class represents a moment in time measured in nanoseconds. Using a nanosecond representation, the new library provides backward compatibility . The Date and Calendar classes previous versions of Java provide a method toInstant(), the result of which can be used to create objects of LocalDateTime or ZonedDateTime classes. The following example demonstrates the possibilities of converting different date and time formats::

package ua.inf.iwanoff.java.advanced.second;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class DateConversion {
    static Date fromLocalDateTimeDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }
    static LocalDateTime fromDateToLocalDateTime(Date date) {
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    public static void main(String[] args) {
        LocalDateTime dateTime = LocalDateTime.now();
        System.out.println(dateTime);
        Date date = fromLocalDateTimeDate(dateTime);
        System.out.println(date);
        Calendar calendar = GregorianCalendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.YEAR, 1);
        date.setTime(calendar.getTimeInMillis());
        System.out.println(date);
        dateTime = fromDateToLocalDateTime(date);
        System.out.println(dateTime);
    }
}

2.2 Working with Text in Java

2.2.1 Common Principles of Working with String Class

The course "Fundamentals of Java Programming" covered the basic concepts of working with the String. In particular, the following main features of working with strings were determined:

  • the string contains a sequence of Unicode characters;
  • strings actually appear in all programs in the Java language;
  • it is possible to create a string both with the help of a constructor and by directly assigning a string literal (a sequence of characters in quotes) to a reference of type String;
  • you can get a representation of each object (or variable of a primitive type) by appending a string using the + operator to the left or to the right;
  • arrays of strings can be sorted alphabetically by default;
  • an object of the String type cannot be changed after it was created.

To effectively replace individual characters of strings and add new characters, you should use special classes StringBuffer and StringBuilder.

Next, we will consider additional options for working with text data - text blocks, localization, sorting, formatting, iteration and regular expressions.

2.2.2 Working with Text Blocks

Working with strings causes certain inconveniences when one object of String type contains many lines, as well as other symbols for which special escape sequences must be used. For greater convenience when working with such strings, Java 15 version added a special syntactic construct: a text block.

The syntax of the text block is as follows: the block begins with three characters """, then you must jump to a new line, then place the necessary text in one or more lines and close the block with """. The compiler automatically creates an object of the String type from such a block:

String block = """
        Hello, block!""";

In the previous example, it would be easier to use a plain literal. The real need arises when the text must occupy several lines, or if you want to place quotation marks inside, etc. Example:

String block = """
        Hello, block!
        This is the same string.
        We can add use "quotes" without backslash.
            We can indent.""";

Very often, fragments of HTML text or program code in other programming languages are added to the Java code in this way.

2.3 Localization

2.3.1 Overview

Internationalization (internationalization, i18n) is the principle of designing and implementing software in such a way as to ensure the possibility of further adaptation to different languages and regions without constructive changes. Compliance with internationalization requirements greatly simplifies the future process of adapting the software product to regional and national requirements.

Localization (localization, l10n) is the process of adapting software to national standards and regulations, such as, for example:

  • language;
  • alphabetical sorting rules;
  • format for representing numbers, date and time;
  • time zone;
  • currency format;
  • unit of measurement;
  • the direction of writing the text;
  • paper format.

Java uses class objects to set and store localization information java.util.Locale. There are several options for creating an object of type Locale. The easiest option is to get the locale that is already defined for the Java virtual machine:

Locale locale = Locale.getDefault();

You can define localization using constructors, for example:

Locale locale1 = new Locale("en", "US");
Locale locale2 = new Locale("en", "GB");
Locale locale3 = new Locale("uk"); // language is set, region is not set

To identify languages and countries, the class Locale uses identifiers according to the IETF BCP 47 standard (https://en.wikipedia.org/wiki/IETF_language_tag).

You can also use a static method forLanguageTag(), for example:

Locale locale4 = Locale.forLanguageTag("en-US");

You can "build" an object using a nested class Locale.Builder:

Locale Locale5 = new Locale.Builder().setLanguage("en").setRegion("US").build();    

The Java platform does not require just one localization for the entire application. This flexibility allows you to develop multilingual applications. For some countries, regional parameters are set using constants, for example: Locale.US, Locale.FRANCE, Locale.CANADA. For other countries, the Locale object must be created using a constructor, for example, new Locale("ua", "UA").

You can get information about the language and region for localization:

Locale locale = new Locale("uk", "UA");
System.out.println(locale.getCountry()); //region code
System.out.println(locale.getDisplayCountry()); //region name
System.out.println(locale.getLanguage()); //region language code
System.out.println(locale.getDisplayLanguage()); //name of the language of the region 

It is possible to set the required default localization for an application, e.g.

Locale.setDefault(Locale.US);

The SimpleTimeZone class that implements the abstract class TimeZone, allows you to work with time zones in the Gregorian calendar. This class takes daylight saving into account.

The class GregorianCalendar has constructors that allow you to define a calendar by time zone and localization:

GregorianCalendar(Locale locale) 
GregorianCalendar(TimeZone timeZone) 
GregorianCalendar(TimeZone timeZone, Locale locale)

2.3.2 Creation of Applications with Support for Several Languages

The most important use of internationalization and localization is to create applications that support the output of texts (messages) in different languages without significant reworking of the code, preferably without recompilation. In this case, it is important to keep strings in different languages separate from the source code. The standard solution for Java applications is to use capabilities of java.util.ResourceBundle class. This class is responsible for "bundle of resources" – a logically connected set of data, in particular, files.

The ResourceBundle class is abstract. At the standard level, two derived classes are implemented: PropertyResourceBundle and ListResourceBundle. The PropertyResourceBundle class provides work with the properties file. A properties file is a plain text file that contains the names and values of the properties (strings). Properties files are not part of the Java source code. The ListResourceBundle class manages resources put into list; it receives data from the .class file. This allows you to store any locale-specific objects, not just strings.

To get the corresponding resource file, you should call the ResourceBundle.getBundle() method. This is a factory method that attempts to create an object of ListResourceBundle type and, if no matching resources are available, creates an object of PropertyResourceBundle class. If the resource bundle is not found, an exception MissingResourceException is thrown.

Properties files that belong to the same bundle have a common base name and different "suffixes". For example, "strings_en.properties", "strings_uk.properties", etc. You can also use more complex suffixes: "strings_en_US.properties", "strings_uk_UA.properties", etc.

The file format is very simple. These are text files that consist of the following lines:

property=value

For example, you can create the following files with strings for the English and German localizations, respectively words_en.properties

word1=one
word2=two
word3=three

and words_de.properties:

word1=eins
word2=zwei
word3=drei

The tools of the IntelliJ IDEA environment allow you to create a whole bundle of properties files at once. In the IntelliJ IDEA project, the resource bundle should be located in the out | production | <project name>.

Note: in Maven projects, property files are located in the resources project folder; Maven projects will be discussed later.

In order to more conveniently edit property files in IntelliJ IDEA projects, you can (but not necessarily) additionally download the Resource Bundle Editor plugin.

There is a problem related to the use of Cyrillic in properties files. Such files must use the ISO 8859-1 code table, which does not support Cyrillic characters. Therefore, instead of using Cyrillic characters directly, it is necessary to specify their Unicode table codes. For example, in a words_uk.properties file instead of text

word1=один
word2=два
word3=три

the following text must be placed:

word1=\u043E\u0434\u0438\u043D
word2=\u0434\u0432\u0430
word3=\u0442\u0440\u0438

Such text is very inconvenient to encode and enter manually. There are special utilities for transcoding files .properties that contain Cyrillic characters, for example, native2ascii. But the easiest way is to use the built-in capabilities of IntelliJ IDEA. First, in Settings... | Editors| File Encodings, after the line Default encoding for properties files, select the option Transparent native-to-ascii conversion. After that, the Cyrillic text that we enter or edit in the properties files will be automatically encoded into the required representation.

The program that uses prepared resources, contains creation of an object of type ResourceBundle with loading of a bundle using the getBundle() function, specifying as parameters the base name of the bundle and the required locale. Now you can get the necessary strings by calling the getString() method with necessary property as parameter.

Suppose, the program should display the string "Hello, world!" or "Привіт, світ!", depending on the localization. We create a bundle of resources from two files. The hello_en.properties file:

message=Hello, world!

The text of the hello_uk.properties file that we type in the IntelliJ IDEA environment will be as follows :

message=Привіт, світ!

In fact, the contents of the hello_uk.properties file will be:

message=\u041F\u0440\u0438\u0432\u0456\u0442, \u0441\u0432\u0456\u0442!

The program, which sequentially displays messages using different localizations, will look like this :

package ua.inf.iwanoff.java.advanced.second;

import java.util.Locale;
import java.util.ResourceBundle;

public class HelloResourceBundle {
    public static void main(String[] args) {
        ResourceBundle bundle = ResourceBundle.getBundle("hello", new Locale("en"));
        String msg = bundle.getString("message");
        System.out.println(msg);
        bundle = ResourceBundle.getBundle("hello", new Locale("uk"));
        msg = bundle.getString("message");
        System.out.println(msg);
    }
}

As can be seen from the given example, unlike keys (property names), property values can contain spaces and any characters.

2.4 Using the java.text Package

2.4.1 Strings Sorting

The package java.text provides classes and interfaces for handling text, dates, and numeric values independently of language and other national characteristics. This means that a program can be written for language-independent operation, and locale-specific resources can be connected dynamically. This allows flexibility to add new app localizations later.

The classes in this package are designed for formatting dates, numbers, and messages, parsing, searching, and sorting strings, and iterating through characters, words, sentences, and strings. This package contains three main groups of classes and interfaces, which respectively solve the following groups of problems:

  • strings sorting;
  • formatting and parsing into tokens;
  • iteration over text.

Sorting is provided by the class Collator. With the help of an instance of the Collator class, you can sort strings with dynamic localization. For example, if you sort Ukrainian texts by conventional means, there is a problem with specific Ukrainian letters, such as ґ, є, і, and ї, because their codes are located separately from the codes of other symbols of the Ukrainian alphabet. To correctly sort arrays of lines with Ukrainian text, it is necessary to create an instance of the Collator class using the function getInstance() whose parameter is the required localization.

The example below demonstrates sorting an array of strings first without using Collator, and then with the creation of an object of Collator type that takes into account the Ukrainian localization. To determine the sort order, we create an anonymous inner class.

import java.text.Collator;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;

public class SortDemo {

    public static void main(String[] args) {
        String[] words = { "воля", "воїн", "возити" };
        // Perform default sorting:
        Arrays.sort(words);
        System.out.println(Arrays.asList(words)); // [возити, воля, воїн]
        // Sort based on localization:
        Arrays.sort(words, new Comparator<String>() {
            Collator collator = Collator.getInstance(new Locale("uk"));
            
            @Override
            public int compare(String s1, String s2) {
                return collator.compare(s1, s2);
            }
        });
        System.out.println(Arrays.asList(words)); // [возити, воїн, воля]
    }

}

2.4.2 Formatting

Localization-aware data formatting is provided by the abstract class java.text.Format. Derived classes NumberFormat, DateFormat and MessageFormat allow formatting of numbers, dates, and messages, respectively. The Format class declares a format() method that converts the parameter into a string in the specified format.

The following example demonstrates output of a value of type double given the specified locale:

import java.text.NumberFormat;
import java.util.Locale;
import java.util.Scanner;

public class NumberPrinter {

    public static void main(String[] args) {
        NumberFormat form = NumberFormat.getInstance(new Locale("uk"));
        Scanner scan = new Scanner(System.in);
        double x = scan.nextDouble();
        System.out.println(form.format(x));
    }

}

To format integers, you can get an object using the getIntegerInstance() method. Similarly, you can obtain getCurrencyInstance() for the output of monetary amounts, getPercentInstance() for the output of percents, etc.

In order to convert information into regional standards, you should create an object of NumberFormat class with a constructor that accepts as a parameter an object of Locale class, or with a constructor without parameters (for the default localization):

NumberFormat nf = NumberFormat.getInstance(new Locale("uk"));
NumberFormat nf = NumberFormat.getInstance();    

The code below demonstrates converting a string containing a number to different regional standards.

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

public class DemoNumberFormat {

    public static void main(String[] args) {
        NumberFormat nfGe = NumberFormat.getInstance(Locale.GERMAN);
        NumberFormat nfUs = NumberFormat.getInstance(Locale.US);
        NumberFormat nfFr = NumberFormat.getInstance(Locale.FRANCE);
        NumberFormat nfUa = NumberFormat.getInstance(new Locale("ua", "UA"));
        double iGe = 0, iUs = 0, iFr = 0, iUa = 0;
        String str = "1.245,999";
        try {
            // convert string to German standard:
            System.out.println(iGe = nfGe.parse(str).doubleValue());
            // convert string to US standard:
            System.out.println(iUs = nfUs.parse(str).doubleValue());
            // convert string to French standard:
            System.out.println(iFr = nfFr.parse(str).doubleValue());
            // convert string to the Ukrainian standard: 
            System.out.println(iUa = nfUa.parse(str).doubleValue());  
        }
        catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println();
        String sUs = nfUs.format(iGe);//number conversion from German to American standard    
        String sFr = nfFr.format(iGe);//number conversion from German to French standard
        String sUa = nfUa.format(iGe);//number conversion from German to Ukrainian standard
        System.out.println(sUs + "\n" + sFr + "\n" + sUa);
    }

}

Here, the NumberFormat parse(String source) and String format(double number) methods and are used to convert strings into numbers and back.

The java.text.DateFormat class helps solve the task of supporting national specifics in displaying the date and time in different countries and regions of the world. When creating an object of this class, a specific locale can be specified or the default locale for this environment can be used:

DateFormat df = DateFormat.getDateInstance( DateFormat.MEDIUM, new Locale("Ua"));
DateFormat df = DateFormat.getDateInstance();    

The abstract DateFormat class and its subclass SimpleDateFormat of the java.text package contain methods that allow different ways to format date and time representations. The DateFormat class offers the following date and time representation styles:

  • SHORT style represents the date and time in short numeric form: 27.04.01 17:32;
  • MEDIUM style sets the year in four digits and shows the seconds: 27.04.2001 17:32:45;
  • LONG style represents the month as a word and adds the time zone: 27 квітень 2001 р. 17:32:45 GMT+03.-00;
  • FULL style is the same as LONG;
  • DEFAULT style is the same as MEDIUM.

For example, the following code creates a formatted date string with a default format for the current region:

DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT);
Date today = new Date();
String formattedDate = dateFormatter.format(today);
System.out.println(formattedDate);    

In addition to the DateFormat.getDateInstance() method, DateFormat.getTimeInstance() and DateFormat.getDateTimeInstance() functons can be used for time formatting, as well as for formatting both date and time.

Similarly, you can format the dates and time of the java.time package.

A child class SimpleDateFormat of an abstract DateFormat class can be used to define custom user formats. When creating an object of SimpleDateFormat you can specify a template in the constructor that defines any other format, for example:

SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy hh mm");
System.out.println(sdf.format(new Date()));

In the template, the letter d means the number of the day of the month, M is the number of the month, y is the number of the year, h is the number of the hour, m is the number of minutes.

For example, the following code will output a date in the format "04/29/2013":

Date today = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
String formattedDate = formatter.format(today);
System.out.println(formattedDate);

To set the symbols for any date or time component, use the DateFormatSymbols class:

DateFormatSymbols symbols = new DateFormatSymbols();
String[] oddMonthAbbreviations = new String[] {"Ja","Fe","Mh","Ap","My","Jn","Jy","Au","Se","Oc","No","De" };
symbols.setShortMonths(oddMonthAbbreviations);
formatter = new SimpleDateFormat("MMM dd, yyyy", symbols);
formattedDate = formatter.format(today);
System.out.println(formattedDate);    

The constructor of SimpleDateFormat takes a template string and a DateFormatSymbols object.

You can get a presentation of the current date in all possible regional standards as follows:

Date d = new Date();
Locale[] locales = DateFormat.getAvailableLocales();
for (Locale loc : locales) {
    DateFormat df = DateFormat.getDateInstance(DateFormat.FULL, loc);
    System.out.println(loc.toString() + "-> " + df.format(d));
}

The java.util.Formatter class is responsible for presentation-level formatting in terms of output field width, alignment, presence of a + sign in front of numbers, etc.

A format() static method format of the String class allows you to get a string that represents the data according to a format, for example:

    double d = 3.5;
    int i = 12;
    String result = String.format("%f %d%n", d, i);
    System.out.print(result);

A similar method is declared in classes PrintStream and PrintWriter. In addition, these classes declare a printf() method with parameters identical to the parameters of the format() method that performs the formatted output of the stream. This way you can get direct output formatting using the System.out.printf() function:

    double d = 3.5;
    int i = 12;
    System.out.printf("%f %d%n", d, i);    

The following format specifiers are used during formatting:

Format specifier Formatting
%a

A floating-point hexadecimal value

%b

Logical (Boolean) value of the argument

%c

Symbolic presentation of the argument

%d

The decimal integer value of the argument

%h

The hash code of the argument

%e

Exponential presentation of the argument

%f

Floating point decimal value

%g

A shorter option of the two: %e or %f

%o

The octal integer value of the argument

%n

Insert a newline character

%t

Time and date

%x

The hexadecimal integer value of the argument

%%

Insert symbol %

Specifiers with capital letters are also possible: %A (equivalent to %a). Formatting with capital letters ensures that characters are converted to upper case.

import java.util.Formatter;

public class SimpleFormatString {

    public static void main(String[] args) {
        Formatter f = new Formatter(); 
        f.format("%s %c %nBasics of Java Application Development %S ", "Module",'2',"se");
        System.out.print(f); 
    }

}    

A precision specifier can be applied to floating-point data (specifiers %f, %e, %g) and strings (specifier %s). It specifies the number of characters to be output. For example, the specifier %10.3f outputs a number with a field width of 10 characters and three decimal places (fixed precision is equal to six decimal places). The precision specifier applied to strings determines the maximum length of the output field. For example, %.15s outputs a string with a length of 15 characters, a specifier %3.7s outputs a string with a length of at least three and more than seven characters. If the string is longer, the trailing characters are discarded. It is possible to set flags that allow you to implement additional formatting options:

import java.util.Formatter;

public class SimpleFormatString {

    public static void main(String[] args) {
        Formatter f = new Formatter(); 
        f.format("|%10.2f|", 123.123);  // align right 
        System.out.println(f);
        f = new Formatter();            // align left
        f.format("|%-10.2f|", 123.123); // apply the '-' flag 
        System.out.println(f);
        f = new Formatter();
        f.format("%,.2f", 123456789.34);// apply the ',' flag
        System.out.println(f);
        f = new Formatter();
        f.format("%.4f", 1111.1111111); // set representation precision for numbers 
        System.out.println(f);
        f = new Formatter();
        f.format("%.6s", "Working with text data."); // set representation precision for strings
        System.out.println(f);
    }

}

There are also specifiers for date and time formatting that can only be used for types long, Long, Calendar, Date, for example:

import java.util.*;

public class Time {

  public static void main(String args[]) {
    Formatter f = new Formatter();
    Calendar calendar = Calendar.getInstance(); 
    f.format("%tr", calendar);// output in 12-hour time format 
    System.out.println(f); 
    f = new Formatter();
    f.format("%tc", calendar);// full-format output of time and date 
    System.out.println(f); 
    f = new Formatter();
    f.format("%tl:%tM", calendar, calendar);// display the current hour and minute 
    System.out.println(f); 
    f = new Formatter();
    f.format("%tB %tb %tm", calendar, calendar, calendar);// various month output options 
    System.out.println(f);
  }

}

2.4.3 Iteration over String Characters

The java.text.CharacterIterator interface provides means for bidirectional traversal of a string using an iterator. The StringCharacterIterator class implements the java.text.CharacterIterator interface. Methods getBeginIndex() and getEndIndex() allow you to return the indices of the first and last characters of the line. The index of the current character can be obtained using the getIndex() method. Calling the setIndex(int index) method moves the iterator to a new position. The previous() and next() methods move the iterator to the previous and next position. If the limits of the range are reached, these methods will return DONE. The first() and last() and methods set the iterator to the first and last positions, respectively, by returning the character at that position. In the following example, we use a CharacterIterator iterator of StringCharacterIterator class to traverse the string from the starting index to the end of the string.

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;

public class StringCharacterExample {
    private static final String text = "Jackdaws love my big sphinx of quartz";

    public static void main(String[] args) {
        CharacterIterator it = new StringCharacterIterator(text);
        for (char ch = it.first(); ch != CharacterIterator.DONE; ch = it.next()) {
            System.out.print(ch);
        }
    }

}

Since the Character class takes localization into account, it is advisable to use it instead of char applications designed for internationalization and localization.

2.6 Regular Expressions

Regular expressions are a way to describe many strings based on common characteristics. Regular expressions can be used to search, edit, or manipulate text and data. The syntax of regular expressions is the same in different programming languages.

The work with regular expressions in Java is implemented in the package java.util.regex, in particular, by classes Pattern and Matcher. A special exception class PatternSyntaxException is also defined.

A Pattern object is a compiled representation of regular expressions. This class does not provide public constructors. To create an object of Pattern type, you must first call one of the static compile() methods compile(), each of which creates a Pattern object and returns a reference to it. These methods take a regular expression as the first argument. Example:

Pattern pattern = Pattern.compile(ex); // ex is a string that defines a regular expression

After the Pattern object is created, it is used to initialize the Matcher object:

Matcher matcher = pattern.matcher(s); // s is the line to be checked

You can then use functions of Matcher class to compare strings (matches()), find substrings (find()), and so on.

In the simplest case, a regular expression is a string containing a sequence of characters. This means that when comparing strings, their equivalence is checked, and when searching, the full text of the regular expression is found. The following program prints true to the console window:

package ua.inf.iwanoff.java.advanced.second;

import java.util.regex.*;

public class FirstRegex {

    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("text");
        Matcher matcher = pattern.matcher("text");
        System.out.println(matcher.matches());
    }

}

If the second string differs in length, or at least one character, the result will be false.

Instead of comparing, you can search for the first occurrence of the pattern in the string. Example:

    Pattern pattern = Pattern.compile("text");
    Matcher matcher = pattern.matcher("textual");
    System.out.println(matcher.find());    

Now the result will be true. The result will also be true, if we check strings like "the text", "context" etc.

In order to sequentially find all occurrences of a substring, you can use the following loop:

    while (matcher.find()) {
        System.out.printf("Text found" + " \"%s\" starting at position"
                  + "%d and ending with position %d.%n", matcher.group(),
                  matcher.start(), matcher.end());
    }

In the given example, the group() function returns a sequence of characters that satisfy the search conditions. The start() and end() function return the start and end positions of the found sequence in the string.

Escape sequences that begin with a backslash (\) can be added to regular expressions. For example, you can define characters by their code::

Representation Explanation Coding
\0n n is an octal number from 0 to 377 8 bit
\xdd d s a hexadecimal number
\udddd 16 bit (Unicode)

You can also use escape sequences:

Representation Symbol
\t Tabulation
\v Vertical tab
\r Cursor return
\n New line
\f End of page
\a Bell
\e Escape symbol
\b Backspace
Note: Backspace must be placed inside square brackets (otherwise interpreted as a word boundary).
\cA ... \cZ Ctrl+A ... Ctrl+Z
Equivalent to \x01 ... \x1A (\cA = \001, \cZ = \032)

To create complex regular expressions, so-called metacharacters are used. These are special characters that can be used to create character classes, quantifiers, control sequences, etc. These are the following symbols:

[ ] \ ^ $ . | ? * + ( ) { }

The meaning of characters depends on their position in the expression. To display a metacharacter as a regular text character, it must be preceded by a backslash (\). To get the character directly \, you need to use two backslash characters (\\).

The set of characters in square brackets [ ] is the so-called character class. It allows you to specify that one of the listed characters can be placed at this position. For example, [abc] specifies the possibility of one of the three specified symbols appearing in the text, [1234567890] determines the correspondence of one of the numbers. You can specify a range of characters: for example, [A-Za-z] matches all letters of the Latin alphabet. If you want to specify characters that are not included in the specified set, you can use the character ^ inside the square brackets. Example, [^0-9] is any character other than numbers.

An example of using the [ ] expression: "[bcr]at" match the lines ""bat", "cat" and "rat"; these lines do not match the expression "[^bcr]at", but a string "hat" does.

To create a character class that combines two or more different character classes, union is used. To create a union, one class is nested inside another: [0-3[7-9]], which corresponds to the numbers 0, 1, 2, 3, 7, 8, 9.

It is possible to create an intersection – a single character class that defines strings that match both nested classes. For example, the expression [0-9&&[234]] matches the numbers 2, 3, and 4.

Subtraction is used to negate one or more symbols of a class, for example, the expression [0-9&&[^345]] creates a class that matches the digits 0 through 9 except for the digits 3, 4, and 5.

Escape sequences are also used to denote some character classes:

Representation Equivalent Value
\d [0-9] Digit
\D [^\d] Any character except a number
\w [A-Za-zа-Яа-Я0-9_] Characters that make up a "word" (letters, numbers, and an underscore)
\W [^\w] Symbols that do not form "words"
\s [ \t\v\r\n\f] Separator
\S [^\s] Not a separator

You can specify the number of repetitions of characters in braces, for example:

  • {2} (exactly two characters),
  • {2, 4} (from two to four),
  • {1,} (one or more).
  • a dot is exactly one (any) character,
  • * zero or more
  • + one or more
  • ? zero or one.

For example, an expression "A*" matches any number of consecutive characters A. An empty line will also match this pattern. The expression "A+" matches a string with at least one character A. The expression "A {1,4}" corresponds to the lines "A", "AA", "AAA", "AAAA". The expression "AB?" will correspond to the strings "A" and "AB". An expression "." matches any string consisting of one character. The expression ".+" will match any text (one or more arbitrary characters). The expression "A.+" matches any string that begins with a letter "A".

There are special characters that do not match any character being checked in a string, but some place in that string.The beginning of the line is determined by the ^ symbol, and the end of the line by the symbol $. For example, the expression "^this" matches a string that begins with "this".

The sequence \b indicates the word boundary, that is, the space between a word and a space. The expression "\bis" corresponds to the second "is" in the string "this is". The string "\b\w\w\w\w\b" corresponds to a four-letter word. The sequence "\B" matches all places except word boundaries. The expression "\Bis" corresponds to the first "is" in "this is".

You can use the character "|" ("OR"), when it is necessary to specify several options that a string can correspond to:

Pattern pattern = Pattern.compile("Ivanov|Petrov");
Matcher matcher = pattern.matcher("These are Ivanov and Petrov and another Ivanov");
while (matcher.find()) {
    System.out.printf( "\"%s\"", matcher.group());
}

In the above example, each of the options will be matched ("Ivanov" "Petrov" "Ivanov").

If the character "|" is not used separately, but in combination with other elements, then the expression with this symbol must be enclosed in parentheses.

Some class functions of String class use regular expressions. For example, functions replaceFirst() and replaceAll() return strings for which search and replacement are implemented by regular expressions.

For example, you need to remove spaces at the beginning and end of a string:

Pattern p = Pattern.compile("^\\s+"); // all spaces at the beginning of the string
String s = p.matcher("  These are Ivanov and Petrov and another Ivanov  ").replaceFirst("");
p = Pattern.compile("\\s+$");         // all spaces at the end of the string
s = p.matcher(s).replaceAll("");
System.out.println(s);

3 Sample Programs

3.1 View Available Localizations

The getAvailableLocales() method allows us to view the available localizations. The toString() method returns a short representation of the localization. With the help of the function getDisplayName() we can get more detailed information:

package ua.inf.iwanoff.java.advanced.second;

import java.util.Locale;
import java.text.NumberFormat;

public class AvailableLocales {

    public static void main(String[] args) {
        Locale list[] = NumberFormat.getAvailableLocales();
        for (Locale locale : list) {
            System.out.printf("%-25s %s%n", locale.toString(), locale.getDisplayName());
        }
    }

}

3.2 The First Substring that Matches the Pattern

The following program finds the first substring that matches the pattern.

package ua.inf.iwanoff.java.advanced.second;

import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class RegexTest {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.printf("%nEnter the regular expression: ");
        String ex = scan.nextLine();
        Pattern pattern = Pattern.compile(ex);
        System.out.printf("Enter string to search: ");
        Matcher matcher = pattern.matcher(scan.nextLine());
        if (matcher.find()) {
            System.out.printf("Text found " + "\"%s\", starting at position "
                       + "%d and ending with position %d.%n", matcher.group(),
                       matcher.start(), matcher.end());
        }
    }

}

3.3 Checking the Phone Number Format

Suppose we need to write a program that checks the entered string for compliance with the format of a landline phone number of the Kharkiv telephone network. Assume that numbers should be written as groups of numbers separated by hyphens. A phone number without a country or city code can be seven or six digits long. Only the last six or seven digits are mandatory for input. Note that the number may contain the country code (three digits), with a "+" sign at the beginning and the city code (57- for seven-digit numbers or 572- for six-digit numbers).

package ua.inf.iwanoff.java.advanced.second;

import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class RegexTest {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.printf("%nEnter a Phone Number: ");
        String phone = scan.nextLine();
        Pattern p = Pattern.compile("\\+?(380-)?((57-)?\\d{3}|(572-)?\\d{2})-\\d{2}-\\d{2}$");
        Matcher m = p.matcher(phone);
        if (m.matches()) {
            System.out.println("\"" + phone + "\" - correct number format");
        }
        else {
            System.out.println("\"" + phone + "\" - wrong number format");
        }
    }
 
}

3.4 Output Words of Sentence

Suppose it is necessary to enter a sentence from the keyboard and output its words in separate lines. We will use the split() method of the String class:

package ua.inf.iwanoff.java.advanced.first;

import java.util.Scanner;

public class UsingSplit {

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String sentence = scan.nextLine();
        String[] words = sentence.split("\\s");
        for (String word : words) {
            System.out.println(word);
        }
    }

}

A regular expression is specified in the split() method call, which defines the delimiter between words.

3.5 "Country" and "Census" Classes

In the examples from the "Fundamentals of Java Programming" course, class hierarchies to represent country and population censuses were considered. In laboratory training # 2 of this course, an abstract class AbstractCountry was created, as well as concrete classes Census and CountryWithArray. Next, in laboratory training # 3, classes CountryWithList and CountryWithSet were created. And finally, in laboratory training # 4, an abstract class CountryWithFile was created, as well as concrete classes CountryWithTextFile and CountryWithDataFile.

Now, using the classes CountryWithList and Census, we will create an application that

  • reproduces the sorting defined in work example laboratory training # 3 of the "Fundamentals of Java Programming" course;
  • performs formatting of numerical data in various ways, as well as taking into account localization;
  • outputs data about dates and times of events taking into account localization;
  • outputs text in English and Ukrainian;
  • implements the ability to sort alphabetically using the Collator class;

Before starting the implementation of the program, it is advisable to determine the bundle of resources with all necessary strings. File censuses_en.properties:

name=Ukraine
area=Area
unit=sq.km.
density=Population density
census=Census
year=Year
population=Population
comments=Comments
commentsDate=Date and time of adding comments
firstPostwarCensus=The first postwar census
populationIncreases=Population increases
noComments=No comments
lastSovietCensus=The last soviet census
firstCensusInIndependentUkraine=The first census in the independent Ukraine
yearWithMaximumPopulation=The year with the maximum population
SortingByPopulation=Sorting by population
SortingByComments=Sorting by comments

The code of the censuses_uk.properties file typed in the IntelliJ IDEA environment:

name=Україна
area=Територія
unit=кв. км.
density=Густота населення
census=Перепис
year=Рік
population=Населення
comments=Коментар
commentsDate=Дата і час додавання коментаря
firstPostwarCensus=Перший післявоєнний перепис
populationIncreases=Нас побільшало
noComments=Просто перепис
lastSovietCensus=Останній радянський перепис
firstCensusInIndependentUkraine=Перший перепис у незалежній Україні
yearWithMaximumPopulation=Рік з найбільшим населенням
SortingByPopulation=Сортування за кількістю населення
SortingByComments=Сортування за алфавітом коментарів

The actual content of the censuses_uk.properties file will be different and will contain the codes of Ukrainian letters.

In the new CensusWithDates class, we add a field timeOfEditing of type ZonedDateTime for storing the date and time of comment editing. In order to be able to work with different languages, it is advisable to override the getComments() method in the CensusWithDates class, in which the comments field will not be used directly, but as a key to find another string in the resource bundle files. In addition, we override methods toString() and testCensus() so that the output of data is carried out taking into account the current localization. In the main() function, we successively change the current localization and display the results. The source code of the CensusWithDates class will be as follows:

package ua.inf.iwanoff.java.advanced.second;

import ua.inf.iwanoff.java.advanced.first.CensusWithStreams;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
import java.util.ResourceBundle;

/**
 * The class is responsible for presenting the census.
 * Localization is taken into account
 */
public class CensusWithDates extends CensusWithStreams {
    private ZonedDateTime timeOfEditing;

    /**
     * Constructor initializes the object with default values
     */
    public CensusWithDates() {
    }

    /**
     * Constructor initializes the object with given values
     *
     * @param year year of census
     * @param population population in the specified year
     * @param comments the text of the comment
     * @param timeOfEditing time of comment editing
     */
    public CensusWithDates(int year, int population, String comments, ZonedDateTime timeOfEditing) {
        super(year, population, comments);
        this.timeOfEditing = timeOfEditing;
    }

    /**
     * Returns time of comment editing
     * @return time of comment editing of a ZonedDateTime object
     */
    public ZonedDateTime getTimeOfEditing() {
        return timeOfEditing;
    }

    /**
     * Sets the time of comment editing
     * @param timeOfEditing the time of comment editing in the form of a ZonedDateTime object
     */
    public void setTimeOfEditing(ZonedDateTime timeOfEditing) {
        this.timeOfEditing = timeOfEditing;
    }

    /**
     * Returns the comment string using resource bundle
     * @return a census comment in the form of a string
     */
    @Override
    public String getComments() {
        ResourceBundle bundle = ResourceBundle.getBundle("censuses");
        return bundle.getString(super.getComments());
    }

    /**
     * Provides a census data in the form of a string
     *
     * @return string representation of a census data
     */
    @Override
    public String toString() {
        ResourceBundle bundle = ResourceBundle.getBundle("censuses");
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).
                withLocale(Locale.getDefault());
        return String.format(Locale.getDefault(), bundle.getString("census") + ". "
                + bundle.getString("year") + ": %d, "
                + bundle.getString("population") + ": %d, "
                + bundle.getString("comments") + ": %s, "
                + bundle.getString("commentsDate") + ": %s ",
                getYear(), getPopulation(), getComments(), getTimeOfEditing().format(dateFormatter));
    }
    /**
     * Performs testing of creation and output of object data
     */
    @Override
    protected void testCensus() {
        setYear(2001);
        setPopulation(48475100);
        setComments("firstCensusInIndependentUkraine");
        setTimeOfEditing(ZonedDateTime.of(2023, Month.MARCH.getValue(), 1, 10, 0, 0, 0, ZoneId.of("Europe/Kiev")));
        System.out.println(this);
    }
    
    /**
     * Output testing program
     * taking into account different localizations
     * @param args command line arguments (not used)
     */
    public static void main(String[] args) {
        Locale.setDefault(Locale.US);
        new CensusWithDates().testCensus();
        Locale.setDefault(new Locale("uk"));
        new CensusWithDates().testCensus();
    }
}

In the new CountryWithLocalization class we override methods toString(), sortByComments(), createCountry() and testCountry(). In the main() function, we successively change the current localization and display the results. The source code of the CountryWithLocalization class will be as follows:

package ua.inf.iwanoff.java.advanced.second;

import ua.inf.iwanoff.java.third.AbstractCountry;
import ua.inf.iwanoff.java.advanced.first.CountryWithStreams;
import ua.inf.iwanoff.java.third.Census;

import java.text.Collator;
import java.text.NumberFormat;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.ResourceBundle;

/**
 * Class for presenting the country in which the censuses are carried out.
 * Localization is taken into account
 */
public class CountryWithLocalization extends CountryWithStreams {
    /**
     * Returns a string representation of the country
     * @return a string representation of the country
     */
    @Override
    public String toString() {
        ResourceBundle bundle = ResourceBundle.getBundle("censuses");
        StringBuilder result = new StringBuilder(getName() + ". " + bundle.getString("area")
                + ": " + getArea() + " " + bundle.getString("unit"));
        for (int i = 0; i < censusesCount(); i++) {
            result.append("\n").append(getCensus(i));
        }
        return result + "";
    }

    /**
     * Sorts the sequence of censuses in the alphabetic order of comments
     */
    @Override
    public void sortByComments() {
        Census[] arr = getCensuses();
        Arrays.sort(arr, new Comparator<Census>() {
            Collator collator = Collator.getInstance(Locale.getDefault());

            @Override
            public int compare(Census c1, Census c2) {
                return collator.compare(c1.getComments(), c2.getComments());
            }
        });
        setCensuses(arr);
    }

    /**
     * Auxiliary function of creating a new country object
     * @return reference to the new country object
     */
    @Override
    public AbstractCountry createCountry() {
        ResourceBundle bundle = ResourceBundle.getBundle("censuses");
        setName(bundle.getString("name"));
        setArea(603628);
        System.out.println(addCensus(new CensusWithDates(1959, 41869000, "firstPostwarCensus",
                ZonedDateTime.of(2023, Month.MARCH.getValue(), 1, 10, 0, 0, 0, ZoneId.of("Europe/Kiev")))));
        System.out.println(addCensus(new CensusWithDates(1970, 47126500, "populationIncreases",
                ZonedDateTime.of(2023, Month.MARCH.getValue(), 3, 10, 30, 0, 0, ZoneId.of("Europe/Kiev")))));
        System.out.println(addCensus(new CensusWithDates(1979, 49754600, "noComments",
                ZonedDateTime.of(2023, Month.MARCH.getValue(), 1, 11, 30, 0, 0, ZoneId.of("Europe/Kiev")))));
        System.out.println(addCensus(new CensusWithDates(1989, 51706700, "lastSovietCensus",
                ZonedDateTime.of(2023, Month.MARCH.getValue(), 2, 14, 0, 0, 0, ZoneId.of("Europe/Kiev")))));
        System.out.println(addCensus(new CensusWithDates(2001, 48475100, "firstCensusInIndependentUkraine",
                ZonedDateTime.of(2023, Month.MARCH.getValue(), 4, 10, 0, 0, 0, ZoneId.of("Europe/Kiev")))));
        return this;
    }

    /**
     * Performs testing class methods
     */
    @Override
    public void testCountry() {
        ResourceBundle bundle = ResourceBundle.getBundle("censuses");
        NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
        System.out.println(bundle.getString("yearWithMaximumPopulation") + ": " + maxYear() + "."
                + bundle.getString("density") + ": " + numberFormat.format(density(maxYear())) + "\n");

        sortByPopulation();
        System.out.println("\n" + bundle.getString("SortingByPopulation"));
        System.out.println(this);

        sortByComments();
        System.out.println("\n" + bundle.getString("SortingByComments"));
        System.out.println(this);
    }

    /**
     * Demonstration of functions' work
     * @param args command line arguments (not used)
     */
    public static void main(String[] args) {
        Locale.setDefault(Locale.US);
        new CountryWithLocalization().createCountry().testCountry();
        Locale.setDefault(new Locale("uk"));
        new CountryWithLocalization().createCountry().testCountry();

    }
}

4 Exercises

  1. Display information about all time zones.
  2. Read a string from the keyboard and check the correct representation of the monetary amount using regular expressions.
  3. Read a string from the keyboard and check the correctness of the time representation using regular expressions.

5 Quiz

  1. Name the main components of the Java SE platform.
  2. What is the difference between classes Calendar and Date ?
  3. How to create an instance of GregorianCalendar?
  4. What tools for working with the calendar does Java 8 provide?
  5. What facilities for working with time zones does Java 8 provide?
  6. What is the difference between internationalization and localization?
  7. How is a class ResourceBundle used to internationalize and localize applications?
  8. What is the general purpose of the java.text package?
  9. What are the advantages of sorting by Collator class?
  10. What is data formatting?
  11. How is localization-sensitive data formatting done?
  12. What is a regular expression?
  13. How to work with regular expressions in Java?
  14. What are the methods of dividing the text into tokens?
  15. What is the advantage of using the method split() compared to means of StringTokenizer?

 

up