Dates, Times, Locales, and Resource Bundles
Dates, Times, Locales, and Resource Bundles
CERTIFICATION OBJECTIVES
This chapter focuses on the exam objectives related to working with date- and time-
related events, formatting dates and times, and using resource bundles for localization
and internationalization tasks. Many of these topics could fill an entire book. Fortunately,
you won’t have to become a guru to do well on the exam. The intention of the exam team
was to include just the basic aspects of these technologies, and in this chapter, we cover
more than you’ll need to get through the related objectives on the exam.
CERTIFICATION OBJECTIVE
Dates, Times, and Locales (OCP Objectives 7.1, 7.2, 7.3, and
12.1)
7.1 Create and manage date-based and time-based events including a combination of date and
time into a single object using LocalDate, LocalTime, LocalDateTime, Instant, Period, and
308
Duration.
7.2 Work with dates and times across timezones and manage changes resulting from daylight
savings including Format date and times values.
7.3 Define, create and manage date-based and time-based events using Instant, Period,
Duration, and TemporalUnit.
12. 1 Read and set the locale by using the Locale object.
The Java API provides an extensive (perhaps a little too extensive) set of classes to help
you work with dates and times. The exam will test your knowledge of the basic classes and
methods you’ll use to work with dates and such. When you’ve finished this section, you
should have a solid foundation in tasks such as creating date and time objects, and creating,
manipulating, and formatting dates and times, and doing all of this for locations around
the globe. In fact, a large part of why this section was added to the exam was to test
whether you can do some basic internationalization (often shortened to “i18n“).
Note: In this section, we’ll introduce the Locale class. Later in the chapter, we’ll be
discussing resource bundles, and you’ll learn more about Locale then.
Local dates and times These dates and times are local to your time zone and so
don’t have time-zone information associated with them. These are represented by
classes java.time.LocalDate, java.time.LocalTime, and
java.time.LocalDateTime.
Zoned dates and times These dates and times include time-zone information.
They are represented by classes java.time.ZonedDateTime and
java.time.OffsetDateTime.
Formatters for dates and times With java.time.format
.DateTimeFormatter, you can parse and print dates and times with patterns and
in a variety of styles.
Adjustments to dates and times With java.time.temporal
.TemporalAdjusters and java.time.temporal.ChronoUnit, you can
adjust and manipulate dates and times by handy increments.
Periods, Durations, and Instants java.time.Periods and java
.time.Durations represent an amount of time, periods for days or longer and
309
durations for shorter periods like minutes or seconds. java.time
.Instants represent a specific instant in time, so you can, say, compute the
number of minutes between two instants.
310
Here we’re using the static method LocalDate.now() to get the current date. It has
no time zone, so think of it as a description of “the date,” whatever that date is for you
today, wherever you are, as you try this code. Similarly, we’re using the static method
LocalTime.now() to get the current time, so that will be whatever the time is for you
right now, wherever you are, as you try this code. We then use the date and time of “now”
to create a LocalDateTime object using the of() static method and then display it.
When we run this code we see
The string 2017-10-11T14:51:19.982 represents the date, October 11, 2017, and
time, 14:51:19.982, which is 2:51 PM and 19 seconds and 982 milliseconds. Notice that
Java displays a “T” between the date and the time when converting the LocalDateTime to
a string.
Of course, you’ll see a completely different date and time because you’re running this
code in your own date and time, wherever and whenever that is.
We could also write:
311
We’re creating the same date in two slightly different ways: first, by specifying a year,
month, and day as arguments to the LocalDate.of() static method; and second, by
using the LocalDate.parse() method to parse a string that matches the date.
LocalDate represents a date in the ISO-8601 calendar system (which specifies a format of
YYYY-MM-DD). You don’t need to know that, except to know the kinds of strings that
the parse() method can parse correctly. (For a full list of the formats of the dates and
times that can be parsed and represented, check out the documentation for
java.time.format.DateTimeFormatter, which we’ll talk more about in a little bit.
See https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html.)
When we run this code, we see the default display (using that ISO format) for both
dates:
An eclipse happens at a specific time of day. The eclipse begins awhile before totality
(when the moon almost completely obscures the sun), so let’s create LocalTime objects to
represent the time the eclipse begins and the time of totality:
As with LocalDate, a LocalTime has no time zone associated with it, so in this case,
we need to know that we’re creating times that are valid in Madras, OR (on U.S. Pacific
time). For these times, we again use the static of() and parse() methods to demonstrate
two different ways to create LocalTime objects.
When we print the times, we see:
312
Eclipse begins at 09:06:43 and totality is at 10:19:36
If you want to be precise about the format of the date and time you’re parsing into a
LocalDate or LocalTime or LocalDateTime, you can use DateTimeFormatter. You
can either use one of several predefined formats or create your own format for parsing using
a sequence of letters and symbols. In the following example, we create a date and time in a
string and then tell the LocalDateTime how to parse that using a DateTimeFormatter:
This creates a LocalDateTime object from the string, formatted using the pattern we
specified, and when we print out the LocalDateTime, we see the correct date and time,
printed in the standard ISO format we saw before:
Of course, you can also use DateTimeFormatter to change the format of the output
(again using letters and symbols):
313
which reveals that it will be 12:19 PM where she is in Tennessee when you’re watching the
eclipse at 10:19 AM in Oregon:
Then she asks, “When are you coming home?” You can tell her, “In three days” by
writing the following code:
Of course, there are loads of other handy methods too, like getDayOfWeek() to find
out what day of the week the eclipse occurs:
You don’t need to know too much about time zones for the exam, except, of course, to
understand what a time zone is and that different places in different parts of the world
are on different time zones. For instance, in Madras, Oregon, you are in the U.S.
Pacific time zone, whereas your Mom in Tennessee is in the U.S. Central time zone.
All time zones are based on Greenwich Mean Time (GMT), the time in Greenwich,
England. GMT is a time zone. The name of the time standard that uses GMT as the basis
for all other time zones is Coordinated Universal Time (UTC).
Your time zone will either be ahead of or behind GMT. For instance, in Madras, OR,
for the eclipse, you are GMT-7, meaning you are seven hours behind GMT. That’s for
314
summer; in winter, you’ll be GMT-8, or eight hours behind GMT because the United
States has daylight savings time in summer and standard time in winter. Yes, zoned dates
and times can get complicated fast, but rest assured, the exam is not about your depth of
understanding of time zones. As long as you know the basics and you can create and use
zoned dates and times, you’ll be fine.
Let’s create a zoned date and time for the date and time of the eclipse:
With this code, we’re displaying only the U.S. zoneIds. If you, say, want to display the
zoneIds for Great Britain, then use "GB" in place of "US". The list includes some
315
zoneIds by country, some by city, and some by other names.
Let’s get back to daylight savings time. Recall that the U.S. Pacific time zone is either
GMT-7 (in winter, standard time) or GMT-8 (in summer, daylight savings time). That
means when you’re creating a ZonedDateTime for Madras, Oregon (or any other place
that uses daylight savings), you’re going to need to know if you’re currently in daylight
savings time.
You can find out if you’re in daylight savings time by using ZoneRules. All you need
to know about ZoneRules is that the class captures the current rules about daylight
savings in various parts of the world. Unfortunately, this tends to change (as politicians
tend to change their minds—what a shocker!) so the rules are, as the documentation states,
only as accurate as the information provided. Let’s assume the rules are up to date (as you
should for the exam) and write some code to find out if the "US/Pacific" time zone is
currently in daylight savings time:
In this code, we first get the ZoneId for the "US/Pacific" time zone. We can then
use that ZoneId to get the ZoneRules with the getRules() method. A ZoneRules
object has a method isDaylightSavings(), which takes an Instant and determines
whether that Instant is currently in daylight savings. We’ll discuss Instants in more
detail shortly; for now, just know that we can convert the ZonedDateTime representing
the date and time of the eclipse into an Instant with the toInstant() method. The
result is
The result is true because the date of the eclipse, zTotalityDateTime (that is, August
21, 2017, in the U.S. Pacific zone), was in daylight savings time (summer time).
316
Let’s say you want to find the date of the Thursday following the eclipse. We can take
the ZonedDateTime for the eclipse we made above, zTotalityDateTime, and create a
new ZonedDateTime from it to represent “the following Thursday” like this:
We can see that the Thursday following the eclipse (which, remember, was on Monday,
August 21, 2017) is August 24.
The class TemporalAdjusters has a whole slew of handy methods to make a
TemporalAdjuster for a variety of scenarios, such as firstDayOfNextYear(),
lastDayOfMonth(), and more.
You’ve already seen how you can add days and hours from a datetime; many other
adjustments, including plusMinutes(), plusYears(), minusWeeks(),
minusSeconds(), and so on, are available as methods in LocalDateTime and
ZonedDateTime. You’ll also find a variety of adjustments like withHour() and
withYear(), which you can use to create a new datetime object from an existing one, but
with a different hour or year. Each of these methods creates a new adjusted datetime from
an existing one.
ZonedDateTimes are subject to ZoneRules when you adjust them. So, if you, say,
add a month to an existing ZonedDateTime, the ZoneRules will be used to determine if
the new ZonedDateTime is GMT-7 or GMT-8, for instance, (depending on daylight
savings time). If you want to create a datetime with a zone offset from GMT that does not
use the ZoneRules, then you can use an OffsetDateTime. An OffsetDateTime is a
fixed datetime and offset that doesn’t change even if the ZoneRules change.
317
Periods You’re looking forward to the next eclipse on April 8, 2024, which you’re going to
watch in Austin, Texas, and you want to set a reminder for yourself one month in advance,
so you don’t forget. You could just say, well, I’ll remind myself on March 8, 2024, but
what fun would that be? Let’s compute one month before the eclipse in code:
To create the reminder, we first create a ZonedDateTime for when totality begins in
Austin. We use the ZonedDateTIme.of() static method to create the datetime with the
arguments year, month, day, hours, minutes, seconds, nanoseconds, and zone id. We have
no idea what the nanoseconds are for the totality beginning, so we just put 0. Notice we
used 13 to specify 1 PM. Austin is in the U.S. Central time zone, so we get the zoneId
using that name (which we got earlier when we displayed all the U.S. time-zone names).
When we print this ZonedDateTime, we see
Now let’s create the reminder for one month before this date and time by creating a
Period that represents one month and subtract it from the date and time for the eclipse:
Period is P1M
DateTime of 1 month reminder: 2024-03-08T13:35:56-
318
06:00[US/Central]
Notice how the period is displayed, with “P” meaning period and “1M” meaning month.
While we’re here, let’s see how to create a LocalDateTime from the ZonedDateTime
for people who are in Austin:
And finally, let’s figure out when we’ll see the reminder in Madras, Oregon:
We’ll see the reminder at 11 AM, two hours earlier than the reminder in Austin, because
Madras is two hours behind.
One more thing to notice about this code. The eclipse is happening in April 2024. April
happens to be in summer time, or daylight savings time, so notice that during daylight
savings, U.S. Central time is five hours behind GMT:
When we subtracted the one-month period from this date and time to get the date and
time for our reminder, we compute that the reminder is on March 8, which is in winter
time, or standard time:
So, on March 8, 2024, Austin will be six hours behind GMT. Nice for us that Java
correctly computed the time using ZoneRules behind the scenes.
Durations How many minutes from the time the eclipse begins to the time totality begins?
We can compute the time in a couple of ways; we’re going to do it using ChronoUnit and
Duration. ChronoUnit is an enum in java.time
.temporal that provides a set of predefined units of time periods. For instance,
319
ChronoUnit.MINUTES represents the concept of a minute. ChronoUnit also supplies a
method between() that we can use to compute a ChronoUnit time period between two
times. Once we have the number of minutes between two times, we can use that to create a
Duration. Durations have all kinds of handy methods for computing things, like adding
and subtracting hours and minutes and seconds, or converting a Duration into a number
of seconds or milliseconds, and so on. Think of ChronoUnit as a unit of time and
Duration as specifying a period of time (like a Period, only for period lengths less than a
day).
First, let’s create two LocalTimes to represent the start of the eclipse (when the moon
first starts to cross the sun) and the time of totality (when the moon completely obscures
the sun):
Notice we’re just using LocalTime here, not ZonedDateTime, so we don’t have to
specify a time zone. The output looks like this:
Now, let’s use a ChronoUnit to compute the number of minutes between begins and
totality:
The minutes returned by the between() method is a long. When we look at the
output, we see we have 78 minutes between the beginning of the eclipse and the beginning
of totality. Notice that we lost the number of seconds in this computation because we asked
for the number of minutes between the two times.
Let’s turn this into a Duration. As you might expect, we can turn the number of
minutes into a Duration using Duration.ofMinutes():
320
Looking at the output we see:
Duration: PT1H18M
PT means “period of time,” meaning Duration (rather than Period), and then 1H18M
means “1 hour and 18 minutes” corresponding to our 78 minutes.
Just to double-check ourselves, let’s take the begin LocalTime we created before and
add back our Duration using the LocalTime.plus() method. We could also do this
with our betweenMins value, using LocalTime.plusMinutes(). The plus() method
takes a TemporalAmount (like a Duration or a Period), whereas plusMinutes()
takes minutes as a long. Either way will work.
The result is
This time is slightly different than our original begins time of 13:35:56 because we lost
the seconds when we created the Duration from the minutes between begins and
totality.
Instants An Instant represents an instant in time. Makes sense, right? But how is it
different from a DateTime? If you’re used to timestamps, then you’ll probably recognize
an Instant as the number of seconds (and nanoseconds) since January 1, 1970—the
standard Java epoch. Instants can’t be represented as just one long, like you might be
used to, because an Instant includes nanoseconds, so the seconds plus the nanoseconds is
too big for a long. However, once you’ve created an Instant, you can always get the
number of seconds as a long value from the Instant.
ZonedDateTimes can be converted to Instants using the toInstant() method:
321
Looking at the output we see
Even though we created a ZonedDateTime for Austin at 1:35 PM, in the US/Central
time zone, the instant displays as 6:35 PM and shows a Z at the end. That datetime
represents 6:35 PM GMT. The Z is how you know the time displayed is for the GMT zone,
rather than the U.S. Central zone. This format is the ISO_INSTANT format of displaying a
datetime.
Note that if you want to call the toInstant() method on a LocalDateTime, you’ll
need to supply a ZoneOffset as an argument. To create a unique instant in time that
works globally, a time zone is required when the Instant is created. If we don’t include a
time zone, then your instant and our instant may mean two different things.
Let’s once again compute the number of minutes between two times using
ChronoUnit.MINUTES. This time we’ll compute the minutes between now and the
Austin eclipse as represented by Instants and then use that to create a Duration. We’ll
use the totalityInstant we created above for the instant of the totality, and we’ll use
the Instant.now() method to create an instant representing right now:
The output is
As you can see (reading the Duration), between now and the next eclipse in Austin, we
have only 56,754 hours and 10 minutes to wait. That eclipse will be here in no time.
Lastly, if you want to get the number of seconds since January 1, 1970, from an
Instant, use the method getEpochSecond():
322
The number of seconds is
And we really should call our sister in Paris a couple of hours after the next eclipse to tell
her how it was:
From the output, we can see that the eclipse happens at 8:35 Paris time, and when we
call our sister two hours after the eclipse, it will be 3:35 PM Austin time and 10:35 PM Paris
time:
323
If you tend to lose track of time, but you really, really don’t want to miss the eclipse,
you can check to make sure the eclipse is still in the future with this code:
Since we’re writing this in 2017, the 2024 eclipse is still far in the future, so we see
And finally, we’d better do one more check about 2024. How about checking to see if
2024 is a leap year? You definitely don’t want to miss the eclipse by a day:
Hmm. It turns out isLeapYear() is defined only for LocalDate, not for
LocalDateTime or ZonedDateTime. We can fix the code by converting
totalityAustin to a LocalDate:
The output from both lines of code shows that 2024 is, indeed, a leap year.
324
Earlier we used DateTimeFormatter to specify a pattern when parsing a date string.
We can also use DateTimeFormatter when we display a datetime as a string.
You might know that in the United States, we tend to write month/day/year, and in the
European Union, they tend to write day/month/year. (Yes, that can get a bit confusing at
times!)
Let’s format and display the datetime of the eclipse in Austin using the European-
preferred format. There are a couple of different ways we can do that. First, we can specify
exactly the format we want using letters and symbols, as we described earlier:
You’ll learn more about Locales shortly; essentially, Locales are designed to tailor
data for a specific region. Here, we’re creating a DateTimeFormatter by specifying a
built-in style and then using that to create a new formatter for a specific locale, the UK
locale. Creating a formatter with a specific locale means the formatter is adjusted
appropriately for that locale. We see that the UK locale uses the day/month/year format for
the date and the 24-hour format for the time. As you can see, there is a lot to the
325
java.time.* package. There’s no way you can memorize everything in the package for
the exam, so we recommend you focus on the classes, properties, and methods in Tables 4-
1 and 4-2 (later in the chapter) and familiarize yourself with the rest of the package by
looking over 222the documentation to get a sense of what’s there (see
https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html).
You’ve probably noticed a pattern in the method names used in the java
.time.* package. For instance, of() methods create a new date from, typically, a
sequence of numbers specifying the year, month, day, and so on. parse() methods create
a new date by parsing a string that’s either in a standard ISO format already or by using a
formatter. with() methods allow you to adjust a date with a TemporalAdjuster to
make a new date. plusX() and minusX() methods create a new datetime object from an
existing one by adding and subtracting TemporalUnits or longs representing weeks,
minutes, and so on. Study the LocalDateTime and ZonedDateTime methods enough to
get the hang of this pattern so you can recognize the methods on the exam (without having
to memorize them all).
The language argument represents an ISO 639 Language code, so, for instance, if you
want to format your dates or numbers in Walloon (the language sometimes used in
southern Belgium), you’d use "wa" as your language string. There are over 500 ISO
Language codes, including one for Klingon ("tlh"), although, unfortunately, Java doesn’t
yet support the Klingon locale. We thought about telling you that you’d have to memorize
all these codes for the exam…but we didn’t want to cause any heart attacks. So rest assured,
you will not have to memorize any ISO Language codes or ISO Country codes (of which
there are about 240) for the exam.
Let’s get back to how you might use these codes. If you want to represent basic Italian in
your application, all you need is the Language code. If, on the other hand, you want to
represent the Italian used in Switzerland, you’d want to indicate that the country is
Switzerland (yes, the Country code for Switzerland is "CH"), but that the language is
Italian:
326
Using these two locales on a date could give us output like this:
Now let’s put this all together in some code that creates a ZonedDateTime object and
sets its date. We’ll then take that datetime object and print it using locales from around the
world:
327
328
This code, on our JVM, produces the output:
So you can see how a single ZonedDateTime object can be formatted to work for many
locales and varying amounts of detail. (Note that you’ll need Eclipse in UTF-8 format to
see the Indian output properly.)
There are a couple more methods in Locale (getDisplayCountry() and
getDisplayLanguage()) that you need to know for the exam. These methods let you
create strings that represent a given locale’s country and language in terms of both the
default locale and any other locale:
329
This code, on our JVM, produces the output:
330
Our JVM’s locale (the default for us, which we saw displayed earlier) is en_US, and
when we display the country name for Brazil, in our locale, we get Brazil. In Brazil,
however, the country is Brasil. Same with the language; for us the language of Brazil is
Portuguese; for people in Brasil, it’s português. Likewise, for Denmark, you can see
how we have different names for the country and the language than the Danish do.
Finally, just for fun, we discovered that in Italy, the Danish language is called danese.
331
332
TABLE 4-2 Adjustment Options for java.time Classes
CERTIFICATION OBJECTIVE
Property files are typically used to externally store configuration settings and operating
parameters for your applications. In the Java world, there are at least three variations on
property files:
1. There is a system-level properties file that holds system information like hardware
info, software versions, classpaths, and so on. The java.lang.System class has
methods that allow you to update this file and view its contents. This property file
333
is not on the exam.
2. There is a class called java.util.Properties that makes it easy for a
programmer to create and maintain property files for whatever applications the
programmer chooses. We’ll talk about the java.util.Properties class in this
section. In this section, when we say “property” files, we’re referring to files that are
compliant with the java.util.Properties class.
3. There is a class called java.util.ResourceBundle that can—optionally—use
java.util.Properties files to make it easier for a programmer to add
localization and/or internationalization features to applications. After we discuss
java.util.Properties, we’ll discuss java.util.ResourceBundle.
! comment
or
# comment
Property files can define key/value pairs in any of the following formats:
key=value
key:value
key value
Let’s refresh what we’ve learned about property files and take a closer look. Aside from
comments, a property file contains key/value pairs:
A key is the first string on a line. Keys and values are usually separated by an equal sign.
334
If you want to break up a single line into multiple lines, you use a backslash. Given an
entry in a property file:
hello1 = Hello \
World!
System.out.println(rb.getString("hello1"));
Hello World!
If you actually want a line break, you use the standard Java \n escape sequence. Given
an entry in a property file:
System.out.println(rb.getString("hello2"));
Hello
World !
You can mix and match these to your heart’s content. Java helpfully ignores any
whitespace before subsequent lines of a multiline property, so you can use indentation for
clarity:
hello3 = 123\
45
Given the above entry in a properties file, the code and output would be
System.out.println(rb.getString("hello3"));
12345
335
will produce output that contains entries like these:
336
which produces the following output:
337
Note that there are a couple of comments at the top of the file. Now let’s run a second
program that opens up the file we just created, adds a new key/value pair, then saves the
result to a second file on disk:
338
which produces
Next, let’s move on to the resource bundles to see how they work and how you can (if
339
you want to) use property files to support your resource bundles.
CERTIFICATION OBJECTIVE
Earlier, we used the Locale class to display dates for basic localization. For full-fledged
localization, we also need to provide language- and country-specific strings for display.
There are only two parts to building an application with resource bundles:
Locale You can use the same Locale we used for DateFormat and
NumberFormat to identify which resource bundle to choose.
ResourceBundle Think of a ResourceBundle as a map. You can use property
files or Java classes to specify the mappings.
Let’s build a simple application to be used in Canada. Since Canada has two official
languages, we want to let the user choose her favorite language. Designing our application,
we decided to have it just output “Hello Java” to show off how cool it is. We can always
add more text later.
We are going to externalize everything language specific to special property files. They’re
just property files that contain keys and string values to display, but they follow very
specific, ResourceBundle-required naming conventions. Here are two simple resource
bundle files:
A file named Labels_en.properties that contains a single line of data:
hello=Hello Java!
hello=Bonjour Java!
It’s critical to understand that when you use Properties files to support
ResourceBundle objects, the naming of the files MUST follow two rules:
340
MyApp_fr_CA.properties). ResourceBundle only knows how to find the
appropriate file via the filename. There is no requirement for the data in the file to
contain locale information.
Using a resource bundle requires three steps: obtaining the Locale, getting the
ResourceBundle, and looking up a value from the resource bundle. First, we create a
Locale object. To review, this means one of the following:
Next, we need to create the resource bundle. We need to know the bundle name of the
resource bundle and the locale. The bundle name of the resource bundle is that part of the
filename up to (but not including) the underscore that is the start of the locale info. For
example, if a Properties file is named MyApp_en.properties, then the bundle name
is “MyApp.” Then we pass those values to a factory, which creates the resource bundle. The
getBundle() method looks in the classpath for bundles that match the bundle name (in
the code below, the bundle name is "Labels") and the provided locale.
Finally, we use the resource bundle like a map and get a value based on the key:
341
Running the code twice, we get
The Java API for java.util.ResourceBundle lists three good reasons to use
resource bundles. Using resource bundles “allows you to write programs that can
If you encounter any questions on the exam that ask about the advantages of using
resource bundles, this quote from the API will serve you well.
The most common use of localization in Java is web applications. You can get the user’s
locale from information passed in the request rather than hard-coding it.
342
use resource bundles that are Java classes. We write Java classes that extend
ListResourceBundle. The class name is similar to the one for property files. Only the
extension is different.
Default Locale
What do you think happens if we call ResourceBundle.getBundle("Labels")
without any locale? It depends. Java will pick the resource bundle that matches the locale
the JVM is using. Typically, this matches the locale of the machine running the program,
but it doesn’t have to. You can even change the default locale at runtime, which might be
useful if you are working with people in different locales so you can get the same behavior
on all machines.
Let’s explore the API to get and set the default locale:
343
which on our computer prints:
For the first and last line, you may get different output depending on where you live. The
key is that the middle of the program executes as if it were in Germany, regardless of where
it is actually being run. It is good practice to restore the default unless your program is
ending right away. That way, the rest of your code works normally—it probably doesn’t
expect to be in Germany.
344
getBundle(), such as taking a ClassLoader. But don’t worry—these aren’t on the
exam.
Now on to the rules. How does Java choose the right resource bundle to use? In a
nutshell, Java chooses the most specific resource bundle it can while giving preference to
Java ListResourceBundle.
Going back to our Canadian application, we decide to request the Canadian French
resource bundle:
Java will look for the following files in the classpath in this order:
345
Outputs:
The common "ride.in" property comes from the parent noncountry-specific bundle
“RB_en.properties.” The "elevator" property is different by country and comes
from the UK version that we specifically requested.
The parent hierarchy is more specific than the search order. A bundle’s parent always
has a shorter name than the child bundle. If a parent is missing, Java just skips along that
hierarchy. ListResourceBundles and PropertyResourcesBundles do not share a
hierarchy. Similarly, the default locale’s resource bundles do not share a hierarchy with the
requested locale’s resource bundles. Table 4-3 shows examples of bundles that do share a
hierarchy.
346
Remember that searching for a property file uses a linear list. However, once a matching
resource bundle is found, keys can only come from that resource bundle’s hierarchy.
One more example to make this clear. Think about which resource bundles will be used
from the previous code if we use the following code to request a resource bundle:
CERTIFICATION SUMMARY
347
Dates and Times The Date and Calendar classes, as well as DateFormat, have all been
replaced by classes in the java.time package, so pay close attention if you’re transitioning
from the old classes to the new. The key datetime classes to know from java.time are
LocalDate, LocalTime, LocalDateTime, and ZonedDateTime. Each has a variety of
methods to create and adjust datetime objects. You also need to know about
TemporalAdjusters (like TemporalAdjuster.firstDayOfMonth()) and
TemporalUnits (like ChronoUnit.DAYS), both from the java.time.temporal
package, and Instants, Periods, and Durations, in the java.time package.
DataFormat has been replaced with DateTimeFormatter in the java.time.format
package, which is used to parse, format, and print datetime objects. The Locale class is
used with DateTimeFormatter to generate a variety of output styles that are language
and/or country specific.
Make sure you are clear on how to work with time zones and daylight savings time.
Fortunately, the ZonedDateTime and related classes handle most of the hard work for
you, but pay close attention to the format of the datetimes when they are represented as
strings so you can recognize a local datetime from a zoned datetime and so you know how
to create a ZonedDateTime using a ZoneId.
Locales, Properties Files, and Resource Bundles Resource bundles allow you to move
locale-specific information (usually strings) out of your code and into external files where
they can easily be amended. This provides an easy way for you to localize your applications
across many locales. Properties files allow you to create text files formatted as key/value
pairs to store application customization parameters and such external to your application.
The ResourceBundle class provides convenient ways to use files that are Properties
class–compatible to store internationalization and localization values.
TWO-MINUTE DRILL
Here are some of the key points from the certification objectives in this chapter.
348
UTC.
A ZoneId can be created from a string representing a time zone (e.g.
"US/Pacific").
When you adjust ZonedDateTimes, daylight savings time will be automatically
handled using the ZoneRules.
If you want a datetime object with a time zone that is independent of zone rules,
use an OffsetDateTime.
A Period is a period of time that is a day or longer.
A Duration is a period of time that is shorter than a day.
An Instant is an instant in time and represents the number of seconds and
nanoseconds since January 1, 1970. You can get the number of seconds as a long
value from an Instant and convert any ZonedDateTime object into an Instant.
There are several format “styles” available in the java.format class. You can use
format styles such as FormatStyle.SHORT with DateTimeFormatter to format
datetime objects.
The DateTimeFormatter class is used to parse and create strings containing
properly formatted dates.
The Locale class is used in conjunction with DateFormat and NumberFormat.
A DateTimeFormatter object can be constructed with a specific, immutable
Locale.
For the exam, you should understand creating Locales using either language or a
combination of language and country.
Locales, Properties Files, and Resource Bundles (OCP 12.1, 12.2, and 12.3)
The java.util.Properties class gives you a convenient way to create and
maintain text files that are external to your applications and can hold configuration
values.
A file that is java.util.Properties–compliant and has a name that ends with
a locale and a suffix of .properties can be used by
ResourceBundle.getBundle().
A ListResourceBundle comes from Java classes, and a
PropertyResourceBundle comes from .properties files.
ResourceBundle.getBundle(name)uses the default Locale.
Locale.getDefault()returns the JVM’s default Locale. Locale
.setDefault(locale) can change the JVM’s locale.
Java searches for resource bundles in this order: requested language/country,
requested language, default locale language/country, default locale language, default
bundle. Within each item, Java ListResourceBundle is favored over
349
PropertyResourceBundle.
Once a ResourceBundle is found, only parents of that bundle can be used to
look up keys.
SELF TEST
1. Given the code fragment:
ZonedDateTime zd = ZonedDateTime.parse("2020-05-
04T08:05:00");
System.out.println(zd.getMonth() + " " +
zd.getDayOfMonth());
Which of the following code fragment(s) will produce a new LocalTime t3 that
represents the same time as t2? (Choose all that apply.)
350
3. Given the code fragment:
351
5. The next total solar eclipse visible in South America is on July 2, 2019, at 16:55
UTC. Which code fragment will correctly compute and display the time in San Juan,
Argentina, for this solar eclipse?
352
353
6. Given:
Assume the default Locale is Italian. If each of the following is the only resource
bundle on the classpath and contains key=value, which will be used? (Choose all
that apply.)
A. Flag.java
354
B. Flag_CA.properties
C. Flag_en.java
D. Flag_en.properties
E. Flag_en_CA.properties
F. Flag_fr_CA.properties
7. Given three resource bundles and a Java class:
Which of the following, when made independently, will change the output to “ride
underground”? (Choose all that apply.)
A. Add train=underground to Train_en.properties
B. Change line 3 to Locale.setDefault(new Locale("en", "UK"));
C. Change line 5 to Locale.ENGLISH);
D. Change line 5 to new Locale("en", "UK"));
E. Delete file Train_en_US.properties
8. Let’s say you want to print the day of the week and the date of Halloween (October
31) 2018, at 5 PM in German, using the LONG style. Complete the code below using
the following fragments. Note: You can use each fragment either zero or more times,
and you might not need to fill all of the slots. You probably won’t encounter a fill-in-
355
the-blank question on the exam, but just in case, we put a few in the book, like this
one.
Code:
Fragments:
356
9. Given two files:
357
Which, inserted independently, will compile? (Choose all that apply.)
A. Object obj = rb.getInteger("123");
B. Object obj = rb.getInteger(123);
C. Object obj = rb.getObject("123");
D. Object obj = rb.getObject(123);
E. Object obj = rb.getString("123");
F. Object obj = rb.getString(123);
10. Given the following code fragment:
358
Which String inserted as an argument to DateTimeFormatter.ofPattern() at
// L1 will produce the output? (Choose all that apply.)
Formatted DateTime: 2017-10-27 14:22:54
A. "yyyy-MM-dd hh:mm:ss a"
B. "yyyy-MM-dd hh:mm:ss"
C "yyyy-mm-dd HH:MM:ss"
D. "yyyy-MM-dd HH:mm:ss"
E. "yyyy-MM-dd HH:mm:ss Z"
11. Given the following code fragment:
359
2017-11-23
B. 2017-11-28T00:00, 2017-12-31T00:00, 2017-12-01T00:00, 2017-
01-01T00:00, 2017-12-25T00:00, 2017-11-23T00:00
C. 2017-11-28, 2017-12-31, 2018-01-01, 2017-01-01, 2017-12-25,
2017-11-23
D.2017-11-28T00:00, 2017-12-31T00:00, 2018-01-01T00:00, 2017-
01-01T00:00, 2017-12-25T00:00, 2017-11-23T00:00
E.2017-11-28, 2017-12-31, 2018-01-01, 2018-01-01, 2017-12-25,
2017-11-23
12. If it is 19:12:53 on October 27, 2017, in the US/Pacific Zone (which is GMT-8:00,
summer time), then what does the following code fragment produce? (Choose all that
apply.)
A. 10:12:53 PM
B. 20:12:53
C. 19:12:53
D. 7:12:53 PM
E. 2017-10-27 10:12:53 PM
360
3. E is correct. Period is the correct type to measure a period of time in days.
A, B, C, D, and F are incorrect based on the above. (OCP Objective 7.1 and 7.3)
4. B and C are correct. In both cases, we’re creating an Instant from nowzdt and
then creating a new ZonedDateTime from that Instant, representing the same time
as nowzdt, in Berlin.
A, D, and E are incorrect. A is incorrect because, although you can create a new
ZonedDateTime from an existing ZonedDateTime with from(), you can’t change
the zone when you do. D is incorrect because withZoneId() is not a valid method.
E is almost correct, except that it is not precisely the same time as nowzdt because
you’re calling the now() method again, though it may only be slightly different
(perhaps only a few nanoseconds). (OCP Objective 7.2)
5. C is correct. We first create a ZonedDateTime for the UTC time with zone “Z”
(corresponding to GMT zone) and then create the equivalent ZonedDateTime for
the San Juan, Argentina, zone.
A, B, D, and E are incorrect. A is missing the time zone on the UTC time. B
includes incorrect arguments to the LocalDateTime.of() method. D has the
incorrect time zone on the UTC time. E has the incorrect type for
totalitySanJuan. (OCP Objective 7.2)
6. A, C, D, and E are correct. The default Locale is irrelevant here since none of
the choices use Italian. A is the default resource bundle. C and D use the language but
not the country from the requested locale. E uses the exact match of the requested
locale.
B is incorrect because the language code of CA does not match en. And CA isn’t a
valid language code. F is incorrect because the language code "fr" does not match
en. Even though the country code of CA does match, the language code is more
important. (OCP Objectives 12.2 and 12.3)
7. D is correct. As is, the code finds resource bundle Train_en_US.properties,
which uses Train_en.properties as a parent. Choice D finds resource bundle
Train_en_UK.properties, which uses Train_en.properties as a parent.
A, B, C, E, and F are incorrect. A is incorrect because both the parent and child
have the same property. In this scenario, the more specific one (child) gets used. B is
incorrect because the default locale only gets used if the requested resource bundle
can’t be found. C is incorrect because it finds the resource bundle
Train_en.properties, which does not have any “train” key. E is incorrect because
there is no “ride” key once we delete the parent. F is incorrect based on the above.
(OCP Objectives 12.2 and 12.3)
8. Answer:
361
Reminders: To create a ZonedDateTime with the of() method, you must include
all portions of the date and time (including nanoseconds) and a zone.
DateTimeFormatter
.ofLocalizedDateTime() returns a locale-specific date-time formatter, and
withLocale() returns a copy of this formatter with a new locale. (OCP Objectives
7.2 and 12.1)
9. C and E are correct. When getting a key from a resource bundle, the key must be
a string. The returned result must be a string or an object. While that object may
happen to be an integer, the API is still getObject(). E will throw a
ClassCastException since 456 is not a string, but it will compile.
A, B, D, and F are incorrect because of the above. (OCP Objectives 12.2 and
12.3)
10. D is correct; this string corresponds to the format shown in the output.
A, B, C, and E are incorrect. A uses hh for the hour, which will show 02 instead
14 (that is, a 12-hour format instead of a 24-hour format), and displays the AM/PM at
the end, which is great if we’re using 12-hour format, but that’s not what we’re
looking for. B results in 12-hour format instead of 24-hour format. C switches
months and minutes. E requires a ZonedDateTime instead of a LocalDateTime,
362
and using this String will throw a runtime exception when we try to format now
with this formatter. (OCP Objectives 7.1 and 7.2)
11. C is correct because of the below.
A, B, D, and E are incorrect. B and D show the time, and we are displaying
LocalDate values that have no time associated with them. A has the incorrect value
for d3, and E has the wrong value for d4. (OCP Objectives 7.1 and 7.3)
q
12. A is correct. We first get the zoneId for “US/Eastern" time, which is GMT-
5:00, and the locale to US. We then create an Instant for “now,” which is 19:12:53
on October 27, 2017 (7:12:53 PM PDT, which is 10:12:53 PM EDT). We then
create a ZonedDateTime from the Instant, using the zoneId for "US/Eastern"
and format it using DateTimeFormatter.ofLocalizedTime(), which turns the
ZonedDateTime into a LocalTime (dropping the date and zone information) and
display it in the MEDIUM format style for the US locale, resulting in 10:12:53 PM.
Format styles depend on local configuration, but we know this answer is correct
because B, C, and D show the incorrect times, and E shows the date.
B, C, D, and E are incorrect. B, C, and D show the incorrect times, and E shows
the date, which we dropped when we formatted zdt to a localized time. (OCP
Objectives 7.2)
363
364