Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Learn Java with Projects

You're reading from   Learn Java with Projects A concise practical guide to learning everything a Java professional really needs to know

Arrow left icon
Product type Paperback
Published in Nov 2023
Publisher Packt
ISBN-13 9781837637188
Length 598 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
Maaike van Putten Maaike van Putten
Author Profile Icon Maaike van Putten
Maaike van Putten
Dr. Seán Kennedy Dr. Seán Kennedy
Author Profile Icon Dr. Seán Kennedy
Dr. Seán Kennedy
Arrow right icon
View More author details
Toc

Table of Contents (22) Chapters Close

Preface 1. Part 1: Java Fundamentals
2. Chapter 1: Getting Started with Java FREE CHAPTER 3. Chapter 2: Variables and Primitive Data Types 4. Chapter 3: Operators and Casting 5. Chapter 4: Conditional Statements 6. Chapter 5: Understanding Iteration 7. Chapter 6: Working with Arrays 8. Chapter 7: Methods 9. Part 2: Object-Oriented Programming
10. Chapter 8: Classes, Objects, and Enums 11. Chapter 9: Inheritance and Polymorphism 12. Chapter 10: Interfaces and Abstract Classes 13. Chapter 11: Dealing with Exceptions 14. Chapter 12: Java Core API 15. Part 3: Advanced Topics
16. Chapter 13: Generics and Collections 17. Chapter 14: Lambda Expressions 18. Chapter 15: Streams – Fundamentals 19. Chapter 16: Streams: Advanced Concepts 20. Chapter 17: Concurrency 21. Index

Exploring the Date API

The java.time package was introduced in Java 8 and was designed to replace the previous java.util.Date, java.util.Calendar, and java.text.DateFormat classes. The classes in java.time represent dates, times, timezones, instants, periods, and durations. The ISO calendar system is followed, which is the de facto world calendar (following Gregorian rules). All the classes are immutable and thread-safe.

It is a large API (https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/package-summary.html) with a large number of classes for dealing with dates, with relatively fewer classes dealing with times. Thankfully, despite the large number of methods available, the consistent use of method prefixes makes this manageable. We will look at these API prefixes shortly. But before we do that, let’s discuss the more important date and time classes.

Coordinated Universal Time (UTC)

UTC is the standard by which the world regulates clocks and time. It is effectively a successor to Greenwich Mean Time (GMT). UTC makes no adjustment for daylight savings time.

The time zone uses UTC+/-00:00, which is sometimes denoted by the letter Z – a reference to the equivalent nautical time zone (GMT). Since the NATO phonetic alphabet word for Z is “Zulu”, UTC is sometimes referred to as “Zulu time.”

Dates and times

There are five important classes here. Let’s examine each in turn:

  • Instant: An instant is a numeric timestamp. It is useful for logging and persistence. Historically, System.currentTimeMillis() would have been used. System.currentTimeMillis() returns the number of milliseconds since the “epoch day” (Jan 1st, 1970 at 00:00:00 UTC). The epoch is a fixed time from which all timestamps are calculated.
  • LocalDate: Stores a date without a time. This is useful for representing birthdays such as 2000-10-21. As it follows ISO-8601, the format is year-month-day.
  • LocalTime: Stores a time without a date. This is useful for representing opening/closing hours such as 09:00.
  • LocalDateTime: Stores a date and time such as 2000-10-21T17:00. Note the “T” used as a date and time separator. This is useful for representing the date and time of a scheduled event, such as a concert.
  • ZonedDateTime: Represents a “full” date-time with a time zone and resolved offset from UTC. For example, 2023-02-14T16:45+01:00[Europe/Zurich] is the date-time for the Europe/Zurich time zone and is 1 hour ahead of UTC.

Duration and Period

In addition to dates and times, the API also represents durations and periods of time. Let’s look at these now.

  • Duration: An amount of time, represented in seconds (and nanoseconds); for example, “54 seconds.”
  • Period: Represents an amount of time in units more meaningful to humans, such as years or days. For example, “3 years, 6 months, and 12 days.”

Additional interesting types

Other types are interesting also. Let’s examine some of these now.

  • Month: Represents a month on its own; for example, JANUARY.
  • DayOfWeek: Represents a day-of-week on its own; for example, FRIDAY.
  • YearMonth: Represents a year and month, without a day or time; for example, 2025-12. This could be useful for a credit card expiry date.
  • MonthDay: Represents a month and day, without a year or time; for example, --08-09. This could be useful for an annual event, such as an anniversary.
  • ZoneOffset: Represents a time zone offset from GMT/UTC, such as +2:00.

As stated earlier, there are a large number of methods across the classes. However, as the prefixes are consistently applied, this is manageable. Table 12.5 represents these prefixes.

Method Prefix

Description

Note: ld2 and so forth used in these examples are related.

of

A static factory method for creating instances – for example,

LocalDate ld1 = LocalDate.of(2023, 3, 17);

parse

A static factory method for creating instances – for example,

LocalDate ld2 = LocalDate.parse("2023-03-17");

get

Gets the value of something – for example,

int dayOfMonth = ld2.getDayOfMonth(); // 17

is

Checks if something is true – for example,

boolean isLeapYear = ld2.isLeapYear(); // false

with

The immutable equivalent of a setter method – for example,

LocalDate ld3 = ld2.withDayOfMonth(25); // 2023-03-25

plus

Adds an amount to an object – for example,

LocalDate ld4 = ld3.plusDays(2); // 2023-03-27

minus

Subtracts an amount from an object – for example,

LocalDate ld5 = ld4.minusMonths(2); // 2023-01-27

at

Combines this object with another – for example,

LocalDateTime ldt1 = ld5.atTime(13, 45, 10); // 2023-01-27T13:45:10

Table 12.5 – Date API method prefixes

Now that we have had a look at the prefixes in the API, let’s look at some sample code to reinforce them. Figure 12.21 shows some code for manipulating dates and times:

Figure 12.21 – Code for manipulating dates and times

Figure 12.21 – Code for manipulating dates and times

In this figure, line 13 creates LocalDate using the factory now() method. This creates a LocalDate object based on the system clock setting for the default locale. Also, using the now() method, lines 14-15 create LocalTime and LocalDateTime objects, respectively. Line 16 shows another way to create LocalDateTime objects by using the of() factory method to pass in both LocalDate and LocalTime objects. Line 17 shows the output of the LocalDateTime object to be yyyy-mm-ddThh:mm:ss:nnnnnnnnn. The date part comes first, then "T", which separates the date from the time, where n in the time part represent nanoseconds.

Next, we want to create LocalDate values representing St. Patrick’s Day (March 17), 2025. Line 20 uses the of() factory method and passes in numeric values for the year, month, and day. Note that the months start at 1 and not 0. Thus, March is represented as 3.

Line 21 uses an alternative factory method, namely parse(String), which accepts a String and creates a LocalDate accordingly. If the string cannot be parsed, an exception will occur.

Line 22 outputs what day of the week, March 17, 2025, occurs (which is a Monday). Line 23 “modifies” the months, changing it from 3 to 5 (March to May). As the Date API types are immutable, the change is made to a new object in the background (ld2 is untouched). The ld3 reference refers to this new object (2025-05-17).

Line 25 adds a year, so we now have 2026-05-17. Line 27 subtracts 5 days, so we now have 2026-05-12. Lastly, on line 29, we “change” our LocalDate to LocalDateTime. As we already have a date, we just provided the time elements. The nanoseconds, which are not provided, are set to 0 and are not displayed as a result.

Now, let’s look at a ZonedDateTime example in Figure 12.22:

Figure 12.22 – ZonedDateTime example

Figure 12.22 – ZonedDateTime example

In this figure, a flight leaves Dublin for Paris at 1 PM local time. The flight duration is 1 hour 45 minutes. We are trying to calculate the local time in Paris when the flight lands. The solution presented here is one option.

Lines 34-38 create a LocalDateTime object for the departure date and time (November 24th, 2023, at 1 P.M.). Line 39 zones the date-time object using the atZone() method by passing in the relevant time zone (a ZoneId). To get the time zone ZoneId object, simply call the factory of() method while passing in the relevant time zone string. In this example, it is "Europe/Dublin". Line 40 shows the format of the ZonedDateTime object. Note "Z" for Zulu time (UTC). At that time of year, as summertime has ended, Dublin is in line with UTC.

Lines 42-45 represent the calculation of the local arrival time in Paris. Line 43 calculates what time is it in Paris when the flight leaves Dublin using the withZoneSameInstant() method. Now, all we have to do is add on the flight time of 1 hour and 45 minutes.

Line 47 shows the ZonedDateTime for the arrival time. The time and zoned offset elements are interesting. The local time allows for the fact that Paris is 1 hour ahead of Dublin. This time difference is reflected in the offset of +1:00. Thus, Paris is 1 hour ahead of UTC.

Now, let’s look at some code that uses Period and Duration. Figure 12.23 presents an example:

Figure 12.23 – An example using Period and Duration

Figure 12.23 – An example using Period and Duration

In this figure, both Period and Duration are demonstrated. Period is suited for time blocks of greater than 1 day; for example, 2 years, 5 months, and 11 days. Duration is more suited to blocks of time of less than 1 day; for example, 8 hours and 20 seconds.

Lines 57-63 calculate and output the number of years, months, and days the American Civil War lasted. Firstly, we create LocalDate objects for the start and end dates (lines 57-58). Line 59 creates a Period object using the static Period.between() method, passing in the relevant start and end dates. Line 60 outputs the period object, P3Y11M28D, which represents a Period of 3 years, 11 months, and 28 days (weeks are represented in days). Lines 61-63 output the years, months, and days values separately.

Next, we will look at Duration. In this case, we use two LocalTime objects; one representing 12:00:20 (line 66) and the other representing 14:45:40 (line 67). Line 68 calculates the time difference between both and line 69 outputs the result. Note that there is no Y, M, or D (years, months, or days) as there was on line 60 (Period). Now, on line 69, we have a Duration of PT2H45M20S representing 2 hours, 45 minutes, and 20 seconds.

Lastly, let’s look at how to format dates and times.

Formatting dates and times

A formatter can work in both directions: formatting your temporal (time-related) object as a string or parsing a string into a temporal object. Both approaches work with formatters. This is represented by the following code from the API:

LocalDate date = LocalDate.now();String text = date.format(formatter);
LocalDate parsedDate = LocalDate.parse(text, formatter);

We will focus on how to create formatters for the format() method. However, as formatters are common to both formatting and parsing, what we say for one applies to the other.

We have a lot of flexibility in how we specify the format for our dates and times. Firstly, there are pre-defined standard formats available for us. In addition, we can specify custom formats. When specifying custom formats, the letters A-Z and a-z are reserved and have specific semantics. Importantly, the number of format letters is important – for example, MMM formats the month to Aug, whereas MM produces 08.

There are two common approaches to formatting your dates and times. One is to use format(DateTimeFormatter) in the LocalDate, LocalTime, LocalDateTime, and ZonedDateTime temporal classes. Its signature accepts a parameter of the DateTimeFormatter type. The other approach is to use format(TemporalAccessor) in the DateTimeFormatter class itself. TemporalAccessor is an interface that’s implemented by the temporal classes just mentioned.

Before we look at some example code, we must cover the more popular pre-defined formatters and format patterns. There are quite a few and we encourage you to look up the API for further details.

Pre-defined formatters

The easiest way to access these formatters is to use the constants in the DateTimeFormatter class or by calling the factory “of” methods in DateTimeFormatter. Table 12.6 presents an overview of the more popular ones. Please see the API for further details. Note that ISO stands for International Organization for Standardization:

Formatter

Description

Example

ofLocalizedDate (dateStyle)

Formatter with the date style from the locale

This depends on the style that’s passed in. An example is “Monday 10 July 2023”.

ofLocalizedTime (timeStyle)

Formatter with the time style from the locale

This depends on the style that’s passed in. An example is “15:47”.

ofLocalizedDateTime (dateTimeStyle)

Formatter with the date and time styles from the locale

This depends on the style that’s passed in. An example is

“3 July 2018 09:19”.

ISO_DATE

ISO date (may contain offset)

2023-07-10”, “2023-07-10+01:00”.

ISO_TIME

ISO time (may contain offset)

15:47:13”, “15:47:13+01:00”.

ISO_LOCAL_DATE

ISO local date (no offset)

2023-07-10”.

ISO_LOCAL_TIME

ISO local time (no offset)

16:00:03”.

ISO_ZONED_DATE_TIME

Zoned date time

2023-07-12T09:33:03+01:00 [Europe/Dublin]”.

Table 12.6 – Date API pre-defined formatters

Now, let’s examine some code that uses pre-defined formatters.

Figure 12.24 presents code that uses these pre-defined formatters:

Figure 12.24 – Code example using pre-defined formatters

Figure 12.24 – Code example using pre-defined formatters

In this figure, we represent the current date (line 74) and the current time (line 81) in various formats, based on the pre-defined formats available in DateTimeFormatter. First up is ISO_DATE (line 75). Its output (in comments on line 76) is 2023-07-10, which is in yyyy-mm-dd format.

Line 78 uses the ofLocalizedDate() factory method to create a format. By passing in the FormatStyle.FULL enum constant, we are requesting as much detail as possible. As a result, this format outputs (line 79) Monday 10 July 2023. As can be seen, this is more detailed than the ISO_DATE format.

Line 82 creates an ISO_TIME formatter and applies it (line 83) to the time object that’s already been created (line 81). Line 85 uses the ofLocalizedTime() factory method. The FormatStyle.SHORT enum returns the fewest details, typically numeric.

That covers the pre-defined formatters. Now, let’s discuss how to specify custom formatters.

Custom formatters

Custom formatters are defined using pattern letters, where the number of letters used is significant. Let’s discuss the most commonly used pattern letters first and then present some code that utilizes them. Table 12.7 presents a summary of the pattern letters:

Letter

Description

Examples

y

Year

2023; 23

M

Month

8; 08; Aug; August

d

Day of the month

16

E

Day of the week

Wed; Wednesday

D

Day of the year

145

h

Hour of the day; 12-hour clock (1-12)

10

H

Hour of the day; 24-hour clock (0-23)

19

m

Minute of the hour

32

s

Second of the minute

55

a

A.M. or P.M.

PM

z

Timezone

GMT

G

Era

AD

Table 12.7 – Date API pattern letters overview

This table is best explained with the aid of an example. Figure 12.25 presents an example that uses the pattern letters from Table 12.7:

Figure 12.25 – Code example using pattern letters

Figure 12.25 – Code example using pattern letters

In this figure, line 91 gets the current date and time for this timezone, which is Irish Standard Time (IST).

Irish Standard Time (IST)

This is the timezone that’s used in Ireland. In Ireland, we utilize daylight savings time (“summertime”). This means that during the summer months, we advance the clocks forward 1 hour so that darkness falls at a later clock time. Therefore, in March, we put the clocks forward 1 hour, and in October, we put the clocks back 1 hour.

There is no “summertime” in UTC. Because of this and the fact that it is July right now, IST is +1:00 hours ahead of UTC.

The output for line 92 is in a comment to the right. The date and time are separated, as usual, by “T.” The zone offset is “+1:00,” indicating that this zoned time is 1 hour ahead of UTC. The zone ID is “[Europe/Dublin].”

We will first look at a date-related formatter. Line 93 creates a formatter using the yy-MMM-dd E D pattern. The output it generates is 23-Jul-11 Tue 192 (line 94). Thus, the current year of 2023 is output as 23 because we only provided yy in the format (as opposed to yyyy). Note that, had it been yyyy in the format, 2023 would have been output. This is why the number of pattern letters is important. The capital M is for the month. M produces 7, MM produces 07, MMM (as in the pattern) produces Jul, and MMMM produces July. Again, this demonstrates that the number of pattern letters is important.

The dd pattern outputs the day of the month. This gives us 11 for the 11th. E gives us the day of the week, which is Tue. Note that EEEE returns Tuesday. D represents the day of the year; the 192nd in this example.

Note that the dashes and spaces are simply inserted into the output. This is because, unlike letters, they are not reserved. We will learn how to insert words (containing letters) into the output without causing exceptions shortly.

Now, let’s examine a time-related formatter. Line 96 creates a formatter using the hh:mm:ss a z G pattern, which generates the output (line 97) of 09:05:50 a.m. IST AD. The hh:mm:ss pattern returns the current time in hours (12-hour clock), minutes, and seconds format. a returns whether it is A.M. or P.M. Right now, it is the morning, so am is returned. The z pattern letter returns the abbreviated zone name, IST. Expanding this to zzzz returns Irish Standard Time. Lastly, G returns the era, AD (Anno Domini).

Now, let’s learn how to insert text into our formatter. As we know, the letters a-z and A-Z are reserved. So, how do we insert letters as regular letters and not pattern letters? To do this, we must surround the regular letters with single quotes. Line 101 specifies a pattern that uses both regular letters and pattern letters. The pattern is “’Year: ‘yyyy’. Month: ‘MMMM’. Day: ‘dd’.’”. The pattern letters are in italics. Any other characters are enclosed in single quotes.

Year: 2023. Month: July. Day: 11. is generated as output.

As we can see, the year value, 2023, is preceded by the text "Year: ". This was achieved by surrounding the text with single quotes: 'Year: '. Following the year pattern yyyy, the regular text '. Month: ' is inserted. Thus, the capital M is treated as simply a capital M, instead of a month pattern letter. After that, '. Day: ' is inserted to precede the day of the month, which is 11. Lastly, a period is inserted at the end by enclosing it in single quotes also. Note that the period without single quotes is also fine as it is not a reserved character.

Lastly, let’s look at an example of parsing where we can create temporal objects from String values. Line 105 declares a string of "2023-07-10 22:10". Line 106 then declares a pattern that will be able to parse this string. The pattern is "yyyy-MM-dd HH:mm". Note that "HH" represents the 24-hour clock. This will enable us to parse the time of "22" in the string.

Line 107 creates a LocalDateTime object by parsing the string according to the pattern provided. Line 108 outputs the LocalDateTime object, producing "2023-07-10T22:10", which is what the string represents.

That completes our discussion on custom formatters and concludes Chapter 12. Now, let’s put that knowledge into practice to reinforce the concepts we’ve covered.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image